本篇博客分析PackageInstaller源码目的是分析Android权限机制,Android App的权限在应用被安装时,用户选择授予或者拒绝。所以,分析Android权限机制源码的第一步分析应用程序安装时的行为。
此次阅读源码旨在解决的问题:Android权限是一次性授予的,即用户在同意安装后,App就获得了申请的权限。那这个过程是怎样的,即:用户点击同意——>App获得权限,经理了怎样的调用过程。
一、源码分析追踪过程
Android源码中,负责应用程序安装的是PackageInstaller.apk。其源码路径/packages/apps/PackageInstaller。从Manifest文件可以看到,PackageInstallersActivity是其入口Activity,也是主要负责安装apk的Activity。所以首先分析PackageInstallersActivity。
这次分析过程是基于源码调试的,便于追踪代码执行流程。首先利用一个Intent进行测试,am start -n com.android.packageinstaller/com.android.packageinstaller.PackageInstallerActivity -d file:///data/local/tmp/qqmusic.apk,是从手机中的文件来安装apk。
<activity android:name=".PackageInstallerActivity" android:configChanges="orientation|keyboardHidden|screenSize" android:excludeFromRecents="true">
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<action android:name="android.intent.action.INSTALL_PACKAGE"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:scheme="file"/>
<data android:mimeType="application/vnd.android.package-archive"/>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.INSTALL_PACKAGE"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:scheme="file"/>
<data android:scheme="package"/>
</intent-filter>
<intent-filter>
<action android:name="android.content.pm.action.CONFIRM_PERMISSIONS"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
以下就是源码分析过程。
进入onCreate()函数后,首先从Intent获取相关数据,然后判断用户是否允许安装非官方应用。
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
``````
final boolean unknownSourcesAllowedByAdmin = isUnknownSourcesAllowedByAdmin();
final boolean unknownSourcesAllowedByUser = isUnknownSourcesEnabled();
```````
}
接着的一段代码也是关于从第三方应用市场(含本地)安装的一些安全设定工作。
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
``````
final boolean unknownSourcesAllowedByAdmin = isUnknownSourcesAllowedByAdmin();
final boolean unknownSourcesAllowedByUser = isUnknownSourcesEnabled();
boolean requestFromUnknownSource = isInstallRequestFromUnknownSource(intent);
mInstallFlowAnalytics = new InstallFlowAnalytics();
mInstallFlowAnalytics.setContext(this);
mInstallFlowAnalytics.setStartTimestampMillis(SystemClock.elapsedRealtime());
mInstallFlowAnalytics.setInstallsFromUnknownSourcesPermitted(unknownSourcesAllowedByAdmin
&& unknownSourcesAllowedByUser);
mInstallFlowAnalytics.setInstallRequestFromUnknownSource(requestFromUnknownSource);
mInstallFlowAnalytics.setVerifyAppsEnabled(isVerifyAppsEnabled());
mInstallFlowAnalytics.setAppVerifierInstalled(isAppVerifierInstalled());
mInstallFlowAnalytics.setPackageUri(mPackageURI.toString());
```````
}
接着,从data中的取出scheme,并根据是都是file,package,和没有data进入不同的分支,测试程序是利用file安装程序,所以进入的是else分支。
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
`````````
if (scheme != null && !"file".equals(scheme) && !"package".equals(scheme)) {
Log.w(TAG, "Unsupported scheme " + scheme);
setPmResult(PackageManager.INSTALL_FAILED_INVALID_URI);
mInstallFlowAnalytics.setFlowFinished(
InstallFlowAnalytics.RESULT_FAILED_UNSUPPORTED_SCHEME);
finish();
return;
}
final PackageUtil.AppSnippet as;
if ("package".equals(mPackageURI.getScheme())) {
mInstallFlowAnalytics.setFileUri(false);
try {
mPkgInfo = mPm.getPackageInfo(mPackageURI.getSchemeSpecificPart(),
PackageManager.GET_PERMISSIONS | PackageManager.GET_UNINSTALLED_PACKAGES);
} catch (NameNotFoundException e) {
}
if (mPkgInfo == null) {
Log.w(TAG, "Requested package " + mPackageURI.getScheme()
+ " not available. Discontinuing installation");
showDialogInner(DLG_PACKAGE_ERROR);
setPmResult(PackageManager.INSTALL_FAILED_INVALID_APK);
mInstallFlowAnalytics.setPackageInfoObtained();
mInstallFlowAnalytics.setFlowFinished(
InstallFlowAnalytics.RESULT_FAILED_PACKAGE_MISSING);
return;
}
as = new PackageUtil.AppSnippet(mPm.getApplicationLabel(mPkgInfo.applicationInfo),
mPm.getApplicationIcon(mPkgInfo.applicationInfo));
} else {
mInstallFlowAnalytics.setFileUri(true);
final File sourceFile = new File(mPackageURI.getPath());
PackageParser.Package parsed = PackageUtil.getPackageInfo(sourceFile);
// Check for parse errors
if (parsed == null) {
Log.w(TAG, "Parse error when parsing manifest. Discontinuing installation");
showDialogInner(DLG_PACKAGE_ERROR);
setPmResult(PackageManager.INSTALL_FAILED_INVALID_APK);
mInstallFlowAnalytics.setPackageInfoObtained();
mInstallFlowAnalytics.setFlowFinished(
InstallFlowAnalytics.RESULT_FAILED_TO_GET_PACKAGE_INFO);
return;
}
mPkgInfo = PackageParser.generatePackageInfo(parsed, null,
PackageManager.GET_PERMISSIONS, 0, 0, null,
new PackageUserState());
mPkgDigest = parsed.manifestDigest;
as = PackageUtil.getAppSnippet(this, mPkgInfo.applicationInfo, sourceFile);
}
mInstallFlowAnalytics.setPackageInfoObtained();
```````
}
在else分支里面,判断能否正确解析apk文件,变量parsed是Package实例,包含了解析apk的结果,解析过程的关键API是(这个解析过程的API值得具体分析):
PackageParser.Package parsed = PackageUtil.getPackageInfo(sourceFile);
Package包含了解析apk文件的结果,具体可以查看Package类文件,(在单步调试这一步时候需要等待很长时间)。这个API基本对apk文件的所有重要信息都做了分析,其实应该就是分析manifest文件,包括apk中的Activity,service等四大组件,以及自定义权限和申请的权限。
===============
else分支后,设置程序apk以获取,这个API不重要。接着就是设置Activity界面,注意下面的最后一行代码:
protected void onCreate(Bundle icicle) {
````````
setContentView(R.layout.install_start);
mInstallConfirm = findViewById(R.id.install_confirm_panel);
mInstallConfirm.setVisibility(View.INVISIBLE);
PackageUtil.initSnippetForNewApp(this, as, R.id.app_snippet);
mOriginatingUid = getOriginatingUid(intent);
```````
final boolean isManagedProfile = mUserManager.isManagedProfile();
if (!unknownSourcesAllowedByAdmin
|| (!unknownSourcesAllowedByUser && isManagedProfile)) {
showDialogInner(DLG_ADMIN_RESTRICTS_UNKNOWN_SOURCES);
mInstallFlowAnalytics.setFlowFinished(
InstallFlowAnalytics.RESULT_BLOCKED_BY_UNKNOWN_SOURCES_SETTING);
} else if (!unknownSourcesAllowedByUser) {
// Ask user to enable setting first
showDialogInner(DLG_UNKNOWN_SOURCES);
mInstallFlowAnalytics.setFlowFinished(
InstallFlowAnalytics.RESULT_BLOCKED_BY_UNKNOWN_SOURCES_SETTING);
} else {
initiateInstall();
}
}
最后进入initiateInstall()函数。
private void initiateInstall() {
String pkgName = mPkgInfo.packageName;
// Check if there is already a package on the device with this name
// but it has been renamed to something else.
String[] oldName = mPm.canonicalToCurrentPackageNames(new String[] { pkgName });
if (oldName != null && oldName.length > 0 && oldName[0] != null) {
pkgName = oldName[0];
mPkgInfo.packageName = pkgName;
mPkgInfo.applicationInfo.packageName = pkgName;
}
// Check if package is already installed. display confirmation dialog if replacing pkg
try {
// This is a little convoluted because we want to get all uninstalled
// apps, but this may include apps with just data, and if it is just
// data we still want to count it as "installed".
mAppInfo = mPm.getApplicationInfo(pkgName,
PackageManager.GET_UNINSTALLED_PACKAGES);
if ((mAppInfo.flags&ApplicationInfo.FLAG_INSTALLED) == 0) {
mAppInfo = null;
}
} catch (NameNotFoundException e) {
mAppInfo = null;
}
mInstallFlowAnalytics.setReplace(mAppInfo != null);
mInstallFlowAnalytics.setSystemApp(
(mAppInfo != null) && ((mAppInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0));
startInstallConfirm();
}
这个函数内部就是对应用的包名做一些处理,以及判断是否是升级,是否是系统应用等,这些和权限机制基本没有关系。判断完毕之后,进入startInstallConfirm()函数。
private void startInstallConfirm() {
TabHost tabHost = (TabHost)findViewById(android.R.id.tabhost);
tabHost.setup();
ViewPager viewPager = (ViewPager)findViewById(R.id.pager);
TabsAdapter adapter = new TabsAdapter(this, tabHost, viewPager);
adapter.setOnTabChangedListener(new TabHost.OnTabChangeListener() {
@Override
public void onTabChanged(String tabId) {
if (TAB_ID_ALL.equals(tabId)) {
mInstallFlowAnalytics.setAllPermissionsDisplayed(true);
} else if (TAB_ID_NEW.equals(tabId)) {
mInstallFlowAnalytics.setNewPermissionsDisplayed(true);
}
}
});
boolean permVisible = false;
mScrollView = null;
mOkCanInstall = false;
int msg = 0;
if (mPkgInfo != null) {
AppSecurityPermissions perms = new AppSecurityPermissions(this, mPkgInfo);
final int NP = perms.getPermissionCount(AppSecurityPermissions.WHICH_PERSONAL);
final int ND = perms.getPermissionCount(AppSecurityPermissions.WHICH_DEVICE);
if (mAppInfo != null) {
msg = (mAppInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0
? R.string.install_confirm_question_update_system
: R.string.install_confirm_question_update;
mScrollView = new CaffeinatedScrollView(this);
mScrollView.setFillViewport(true);
boolean newPermissionsFound =
(perms.getPermissionCount(AppSecurityPermissions.WHICH_NEW) > 0);
mInstallFlowAnalytics.setNewPermissionsFound(newPermissionsFound);
if (newPermissionsFound) {
permVisible = true;
mScrollView.addView(perms.getPermissionsView(
AppSecurityPermissions.WHICH_NEW));
} else {
LayoutInflater inflater = (LayoutInflater)getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
TextView label = (TextView)inflater.inflate(R.layout.label, null);
label.setText(R.string.no_new_perms);
mScrollView.addView(label);
}
adapter.addTab(tabHost.newTabSpec(TAB_ID_NEW).setIndicator(
getText(R.string.newPerms)), mScrollView);
} else {
findViewById(R.id.tabscontainer).setVisibility(View.GONE);
findViewById(R.id.divider).setVisibility(View.VISIBLE);
}
if (NP > 0 || ND > 0) {
permVisible = true;
LayoutInflater inflater = (LayoutInflater)getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
View root = inflater.inflate(R.layout.permissions_list, null);
if (mScrollView == null) {
mScrollView = (CaffeinatedScrollView)root.findViewById(R.id.scrollview);
}
if (NP > 0) {
((ViewGroup)root.findViewById(R.id.privacylist)).addView(
perms.getPermissionsView(AppSecurityPermissions.WHICH_PERSONAL));
} else {
root.findViewById(R.id.privacylist).setVisibility(View.GONE);
}
if (ND > 0) {
((ViewGroup)root.findViewById(R.id.devicelist)).addView(
perms.getPermissionsView(AppSecurityPermissions.WHICH_DEVICE));
} else {
root.findViewById(R.id.devicelist).setVisibility(View.GONE);
}
adapter.addTab(tabHost.newTabSpec(TAB_ID_ALL).setIndicator(
getText(R.string.allPerms)), root);
}
}
mInstallFlowAnalytics.setPermissionsDisplayed(permVisible);
if (!permVisible) {
if (mAppInfo != null) {
// This is an update to an application, but there are no
// permissions at all.
msg = (mAppInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0
? R.string.install_confirm_question_update_system_no_perms
: R.string.install_confirm_question_update_no_perms;
} else {
// This is a new application with no permissions.
msg = R.string.install_confirm_question_no_perms;
}
tabHost.setVisibility(View.GONE);
mInstallFlowAnalytics.setAllPermissionsDisplayed(false);
mInstallFlowAnalytics.setNewPermissionsDisplayed(false);
findViewById(R.id.filler).setVisibility(View.VISIBLE);
findViewById(R.id.divider).setVisibility(View.GONE);
mScrollView = null;
}
if (msg != 0) {
((TextView)findViewById(R.id.install_confirm_question)).setText(msg);
}
mInstallConfirm.setVisibility(View.VISIBLE);
mOk = (Button)findViewById(R.id.ok_button);
mCancel = (Button)findViewById(R.id.cancel_button);
mOk.setOnClickListener(this);
mCancel.setOnClickListener(this);
if (mScrollView == null) {
// There is nothing to scroll view, so the ok button is immediately
// set to install.
mOk.setText(R.string.install);
mOkCanInstall = true;
} else {
mScrollView.setFullScrollAction(new Runnable() {
@Override
public void run() {
mOk.setText(R.string.install);
mOkCanInstall = true;
}
});
}
}
从文件的134行起和权限有,进入AppSecurityPermissions(Context context, PackageInfo info)构造函数内部查看具体逻辑,AppSecurityPermissions(Context context, PackageInfo info)构造函数所在位置:/frameworks/base/core/java/android/widget/AppSecurityPermissions.java;
public AppSecurityPermissions(Context context, PackageInfo info) {
this(context);
Set<MyPermissionInfo> permSet = new HashSet<MyPermissionInfo>();
if(info == null) {
return;
}
mPackageName = info.packageName;
// Convert to a PackageInfo
PackageInfo installedPkgInfo = null;
// Get requested permissions
if (info.requestedPermissions != null) {
try {
installedPkgInfo = mPm.getPackageInfo(info.packageName,
PackageManager.GET_PERMISSIONS);
} catch (NameNotFoundException e) {
}
extractPerms(info, permSet, installedPkgInfo);
}
// Get permissions related to shared user if any
if (info.sharedUserId != null) {
int sharedUid;
try {
sharedUid = mPm.getUidForSharedUser(info.sharedUserId);
getAllUsedPermissions(sharedUid, permSet);
} catch (NameNotFoundException e) {
Log.w(TAG, "Couldn't retrieve shared user id for: " + info.packageName);
}
}
// Retrieve list of permissions
mPermsList.addAll(permSet);
setPermissions(mPermsList);
}
此函数调用了extractPerms(info, permSet, installedPkgInfo)函数,这个函数也在AppSecurityPermissions.java文件内部,查看函数定义。
private void extractPerms(PackageInfo info, Set<MyPermissionInfo> permSet, PackageInfo installedPkgInfo) {
``````
myPerm.mNew = newPerm;
permSet.add(myPerm);
````
}
private final Map<String, MyPermissionGroupInfo> mPermGroups
= new HashMap<String, MyPermissionGroupInfo>();
在AppSecurityPermissions类中定义了mPermGroups全局变量,以permissionGroup为键的键值对。extractPerms(PackageInfo info,Set《AppSecurityPermissions.MyPermissionInfo> permSet,PackageInfo installedPkgInfo)函数就是从第一个参数PackageInfo中获取的权限信息保存到第二个参数。
public AppSecurityPermissions(Context context, PackageInfo info) {
````
if (info.sharedUserId != null) {
int sharedUid;
try {
sharedUid = mPm.getUidForSharedUser(info.sharedUserId);
getAllUsedPermissions(sharedUid, permSet);
} catch (NameNotFoundException e) {
Log.w(TAG, "Couldn't retrieve shared user id for: " + info.packageName);
}
````
}
在这个if分支里面,处理了shareUid的情况,即getAllUsedPermissions(sharedUid, permSet)函数,结果也存储在第二参数即permSet参数。AppSecurityPermissions类中有一个变量,即mPermsList变量,用于存储从apk中分析得到的permission信息。
private final PermissionGroupInfoComparator mPermGroupComparator = new PermissionGroupInfoComparator();
private final PermissionInfoComparator mPermComparator = new PermissionInfoComparator();
private final List<MyPermissionInfo> mPermsList = new ArrayList<MyPermissionInfo>();
private final CharSequence mNewPermPrefix;
private String mPackageName;
安装流程至此,所有和权限相关的就是从apk中提取权限,然后把提取出的权限存放在AppSecurityPermissions类的mPermsList变量变量中,最后显示出来。最后,界面上“下一步”和“取消”按钮分别对应于onClick()函数,我们关注用户同意以后的操作。
public void onClick(View v) {
if (v == mOk) {
if (mOkCanInstall || mScrollView == null) {
mInstallFlowAnalytics.setInstallButtonClicked();
if (mSessionId != -1) {
mInstaller.setPermissionsResult(mSessionId, true);
// We're only confirming permissions, so we don't really know how the
// story ends; assume success.
mInstallFlowAnalytics.setFlowFinishedWithPackageManagerResult(
PackageManager.INSTALL_SUCCEEDED);
} else {
// Start subactivity to actually install the application
Intent newIntent = new Intent();
newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO,
mPkgInfo.applicationInfo);
newIntent.setData(mPackageURI);
newIntent.setClass(this, InstallAppProgress.class);
newIntent.putExtra(InstallAppProgress.EXTRA_MANIFEST_DIGEST, mPkgDigest);
newIntent.putExtra(
InstallAppProgress.EXTRA_INSTALL_FLOW_ANALYTICS, mInstallFlowAnalytics);
String installerPackageName = getIntent().getStringExtra(
Intent.EXTRA_INSTALLER_PACKAGE_NAME);
if (mOriginatingURI != null) {
newIntent.putExtra(Intent.EXTRA_ORIGINATING_URI, mOriginatingURI);
}
if (mReferrerURI != null) {
newIntent.putExtra(Intent.EXTRA_REFERRER, mReferrerURI);
}
if (mOriginatingUid != VerificationParams.NO_UID) {
newIntent.putExtra(Intent.EXTRA_ORIGINATING_UID, mOriginatingUid);
}
if (installerPackageName != null) {
newIntent.putExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME,
installerPackageName);
}
if (getIntent().getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false)) {
newIntent.putExtra(Intent.EXTRA_RETURN_RESULT, true);
newIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
}
if(localLOGV) Log.i(TAG, "downloaded app uri="+mPackageURI);
startActivity(newIntent);
}
finish();
} else {
mScrollView.pageScroll(View.FOCUS_DOWN);
}
} else if(v == mCancel) {
// Cancel and finish
setResult(RESULT_CANCELED);
if (mSessionId != -1) {
mInstaller.setPermissionsResult(mSessionId, false);
}
mInstallFlowAnalytics.setFlowFinished(
InstallFlowAnalytics.RESULT_CANCELLED_BY_USER);
finish();
}
}
继续分析前,把上面的函数调用流程总结一下,见下图。
把AppSecurityPermissions实例化过程也总结一下。
点击同意后,就是一个startActivity的操作,也就是打开InstallAppProcess.java这个Activity。在Oncreate()函数中调用了PackageManager的pm.installPackageWithVerificationAndEncryption(mPackageURI, observer, installFlags, installerPackageName, verificationParams, null)函数。PackageManager是抽象内,其方法实现在子类:/frameworks/base/core/java/android/app/ApplicationPackageManager.java
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
```
initView();
}
public void initView() {
setContentView(R.layout.op_progress);
```
pm.installPackageWithVerificationAndEncryption(mPackageURI,observer, installFlags,installerPackageName, verificationParams, null);
}
@Override
public void installPackageWithVerificationAndEncryption(Uri packageURI,
PackageInstallObserver observer, int flags, String installerPackageName,
VerificationParams verificationParams, ContainerEncryptionParams encryptionParams) {
installCommon(packageURI, observer, flags, installerPackageName, verificationParams,
encryptionParams);
}
这个函数调用了自身文件内定义的installCommon(Uri packageURI, PackageInstallObserver observer, int flags, String installerPackageName,VerificationParams verificationParams, ContainerEncryptionParams encryptionParams)函数。
private void installCommon(Uri packageURI,
PackageInstallObserver observer, int flags, String installerPackageName,
VerificationParams verificationParams, ContainerEncryptionParams encryptionParams) {
if (!"file".equals(packageURI.getScheme())) {
throw new UnsupportedOperationException("Only file:// URIs are supported");
}
if (encryptionParams != null) {
throw new UnsupportedOperationException("ContainerEncryptionParams not supported");
}
final String originPath = packageURI.getPath();
try {
mPM.installPackage(originPath, observer.getBinder(), flags, installerPackageName,
verificationParams, null);
} catch (RemoteException ignored) {
}
}
而这个函数的关键是调用了installPackage函数,继续追中installPackag()函数,发现并不能跟踪到。原来,ApplicationPackageManager中的mPM成员变量一个IPackageManager变量,而IPackageManager是通过AIDL定义的类,就是有程序生成的。IPackageManager.java文件在编译过程中是经aidl工具处理IPackageManager.aidl后得到,最终的文件位置在Android源码/out/target。IPackageManager.aidl文件所在路径:\frameworks\base\core\java\android\content\pm\IPackageManager.aidl。
所以实际调用了PackageManager.java的installPackage()函数,所在路径:\frameworks\base\services\core\java\com\android\server\pm\PackageManagerService.java。
public void installPackage(String originPath, IPackageInstallObserver2 observer,
int installFlags, String installerPackageName, VerificationParams verificationParams,
String packageAbiOverride) {
installPackageAsUser(originPath, observer, installFlags, installerPackageName, verificationParams,
packageAbiOverride, UserHandle.getCallingUserId());
}
@Override
public void installPackageAsUser(String originPath, IPackageInstallObserver2 observer,
int installFlags, String installerPackageName, VerificationParams verificationParams,
String packageAbiOverride, int userId) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, null);
final int callingUid = Binder.getCallingUid();
enforceCrossUserPermission(callingUid, userId, true, true, "installPackageAsUser");
if (isUserRestricted(userId, UserManager.DISALLOW_INSTALL_APPS)) {
try {
if (observer != null) {
observer.onPackageInstalled("", INSTALL_FAILED_USER_RESTRICTED, null, null);
}
} catch (RemoteException re) {
}
return;
}
if ((callingUid == Process.SHELL_UID) || (callingUid == Process.ROOT_UID)) {
installFlags |= PackageManager.INSTALL_FROM_ADB;
} else {
// Caller holds INSTALL_PACKAGES permission, so we're less strict
// about installerPackageName.
installFlags &= ~PackageManager.INSTALL_FROM_ADB;
installFlags &= ~PackageManager.INSTALL_ALL_USERS;
}
UserHandle user;
if ((installFlags & PackageManager.INSTALL_ALL_USERS) != 0) {
user = UserHandle.ALL;
} else {
user = new UserHandle(userId);
}
verificationParams.setInstallerUid(callingUid);
final File originFile = new File(originPath);
final OriginInfo origin = OriginInfo.fromUntrustedFile(originFile);
final Message msg = mHandler.obtainMessage(INIT_COPY);
msg.obj = new InstallParams(origin, observer, installFlags,
installerPackageName, verificationParams, user, packageAbiOverride);
mHandler.sendMessage(msg);
}
installPackage()函数调用了installPackageAsUser()函数,在这个函数中,首先调用mContext.enforceCallingOrSelfPermission()检查调用者是否有安装apk的权限,即判断否申请了android.Manifest.permission.INSTALL_PACKAGES权限。接着判断调用者是否是受限制的app,即只有root,shell以及拥有系统签名的app才有执行权限。
由于安装程序是一个耗时的过程,所以函数使用了Handler消息传递机制,PackageManagerService中定义了PackageHandler的Handler类。Handler首先处理installPackageAsUser()方法发出的INIT_COPY消息,接着跳转到MCS_BOUND消息的处理。在MCS_BOUND消息处理中调用了startCopy()函数,接着调用handleStartCopy()函数,最后调用copyApk()函数,这个函数完成apk的拷贝以及所需文件夹子的创建。handlerStartCopy()函数调用完成之后,调用了handleReturnCode()方法。
final boolean startCopy() {
boolean res;
try {
if (DEBUG_INSTALL) Slog.i(TAG, "startCopy " + mUser + ": " + this);
if (++mRetries > MAX_RETRIES) {
Slog.w(TAG, "Failed to invoke remote methods on default container service. Giving up");
mHandler.sendEmptyMessage(MCS_GIVE_UP);
handleServiceError();
return false;
} else {
handleStartCopy();
res = true;
}
} catch (RemoteException e) {
if (DEBUG_INSTALL) Slog.i(TAG, "Posting install MCS_RECONNECT");
mHandler.sendEmptyMessage(MCS_RECONNECT);
res = false;
}
handleReturnCode();
return res;
}
public void handleStartCopy() throws RemoteException {
``````
final InstallArgs args = createInstallArgs(this);
`````
ret = args.copyApk(mContainerService, true);
}
int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException {
if (origin.staged) {
Slog.d(TAG, origin.file + " already staged; skipping copy");
codeFile = origin.file;
resourceFile = origin.file;
return PackageManager.INSTALL_SUCCEEDED;
}
try {
final File tempDir = mInstallerService.allocateInternalStageDirLegacy();
codeFile = tempDir;
resourceFile = tempDir;
} catch (IOException e) {
Slog.w(TAG, "Failed to create copy file: " + e);
return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
}
final IParcelFileDescriptorFactory target = new IParcelFileDescriptorFactory.Stub() {
@Override
public ParcelFileDescriptor open(String name, int mode) throws RemoteException {
if (!FileUtils.isValidExtFilename(name)) {
throw new IllegalArgumentException("Invalid filename: " + name);
}
try {
final File file = new File(codeFile, name);
final FileDescriptor fd = Os.open(file.getAbsolutePath(),
O_RDWR | O_CREAT, 0644);
Os.chmod(file.getAbsolutePath(), 0644);
return new ParcelFileDescriptor(fd);
} catch (ErrnoException e) {
throw new RemoteException("Failed to open: " + e.getMessage());
}
}
};
int ret = PackageManager.INSTALL_SUCCEEDED;
ret = imcs.copyPackage(origin.file.getAbsolutePath(), target);
if (ret != PackageManager.INSTALL_SUCCEEDED) {
Slog.e(TAG, "Failed to copy package");
return ret;
}
final File libraryRoot = new File(codeFile, LIB_DIR_NAME);
NativeLibraryHelper.Handle handle = null;
try {
handle = NativeLibraryHelper.Handle.create(codeFile);
ret = NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libraryRoot,
abiOverride);
} catch (IOException e) {
Slog.e(TAG, "Copying native libraries failed", e);
ret = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
} finally {
IoUtils.closeQuietly(handle);
}
return ret;
}
这个函数中调用了copyPackage()函数,这个函数也是调用了系统服务,这个方法在/frameworks/base/core/java/com/android/internal/app/IMediaContainerService.aidl中定义,在/frameworks/base/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java中实现:
/**
* Copy package to the target location.
*
* @param packagePath absolute path to the package to be copied. Can be
* a single monolithic APK file or a cluster directory
* containing one or more APKs.
* @return returns status code according to those in
* {@link PackageManager}
*/
@Override
public int copyPackage(String packagePath, IParcelFileDescriptorFactory target) {
if (packagePath == null || target == null) {
return PackageManager.INSTALL_FAILED_INVALID_URI;
}
PackageLite pkg = null;
try {
final File packageFile = new File(packagePath);
pkg = PackageParser.parsePackageLite(packageFile, 0);
return copyPackageInner(pkg, target);
} catch (PackageParserException | IOException | RemoteException e) {
Slog.w(TAG, "Failed to copy package at " + packagePath + ": " + e);
return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
}
}
查看copyPackageInner()函数:
private int copyPackageInner(PackageLite pkg, IParcelFileDescriptorFactory target)
throws IOException, RemoteException {
copyFile(pkg.baseCodePath, target, "base.apk");
if (!ArrayUtils.isEmpty(pkg.splitNames)) {
for (int i = 0; i < pkg.splitNames.length; i++) {
copyFile(pkg.splitCodePaths[i], target, "split_" + pkg.splitNames[i] + ".apk");
}
}
return PackageManager.INSTALL_SUCCEEDED;
}
copyFile()函数:
private void copyFile(String sourcePath, IParcelFileDescriptorFactory target, String targetName)
throws IOException, RemoteException {
Slog.d(TAG, "Copying " + sourcePath + " to " + targetName);
InputStream in = null;
OutputStream out = null;
try {
in = new FileInputStream(sourcePath);
out = new ParcelFileDescriptor.AutoCloseOutputStream(
target.open(targetName, ParcelFileDescriptor.MODE_READ_WRITE));
Streams.copy(in, out);
} finally {
IoUtils.closeQuietly(out);
IoUtils.closeQuietly(in);
}
}
所以copyPackage()就是把apk文件拷贝到/data/app/packageName/base.apk.而NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libraryRoot,abiOverride);就是将apk中的so文件拷贝到/data/app/packageName/lib目录下。
至此,startCopy()方法漫长的函数调用链追踪完毕,我们在回顾一下这个方法的调用流程。
startCopy()——>handleStartCopy()——>copyApk();copyApk()分别调用了copyPackage()就是把apk文件拷贝到/data/app/packageName/base.apk,以及NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libraryRoot,abiOverride);就是将apk中的so文件拷贝到/data/app/packageName/lib目录下,我们还是以流程图的形式总结一下。
回到startCopy()函数,在handleStartCopy()调用完成后,还调用了一个重要的方法handleReturnCode()。继续查看handleReturnCode()源码。
void handleReturnCode() {
// If mArgs is null, then MCS couldn't be reached. When it
// reconnects, it will try again to install. At that point, this
// will succeed.
if (mArgs != null) {
processPendingInstall(mArgs, mRet);
}
}
private void processPendingInstall(final InstallArgs args, final int currentStatus) {
// Queue up an async operation since the package installation may take a little while.
mHandler.post(new Runnable() {
public void run() {
mHandler.removeCallbacks(this);
// Result object to be returned
PackageInstalledInfo res = new PackageInstalledInfo();
res.returnCode = currentStatus;
res.uid = -1;
res.pkg = null;
res.removedInfo = new PackageRemovedInfo();
if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
args.doPreInstall(res.returnCode);
synchronized (mInstallLock) {
installPackageLI(args, res);
}
args.doPostInstall(res.returnCode, res.uid);
}
// A restore should be performed at this point if (a) the install
// succeeded, (b) the operation is not an update, and (c) the new
// package has not opted out of backup participation.
final boolean update = res.removedInfo.removedPackage != null;
final int flags = (res.pkg == null) ? 0 : res.pkg.applicationInfo.flags;
boolean doRestore = !update
&& ((flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0);
// Set up the post-install work request bookkeeping. This will be used
// and cleaned up by the post-install event handling regardless of whether
// there's a restore pass performed. Token values are >= 1.
int token;
if (mNextInstallToken < 0) mNextInstallToken = 1;
token = mNextInstallToken++;
PostInstallData data = new PostInstallData(args, res);
mRunningInstalls.put(token, data);
if (DEBUG_INSTALL) Log.v(TAG, "+ starting restore round-trip " + token);
if (res.returnCode == PackageManager.INSTALL_SUCCEEDED && doRestore) {
// Pass responsibility to the Backup Manager. It will perform a
// restore if appropriate, then pass responsibility back to the
// Package Manager to run the post-install observer callbacks
// and broadcasts.
IBackupManager bm = IBackupManager.Stub.asInterface(
ServiceManager.getService(Context.BACKUP_SERVICE));
if (bm != null) {
if (DEBUG_INSTALL) Log.v(TAG, "token " + token
+ " to BM for possible restore");
try {
if (bm.isBackupServiceActive(UserHandle.USER_OWNER)) {
bm.restoreAtInstall(res.pkg.applicationInfo.packageName, token);
} else {
doRestore = false;
}
} catch (RemoteException e) {
// can't happen; the backup manager is local
} catch (Exception e) {
Slog.e(TAG, "Exception trying to enqueue restore", e);
doRestore = false;
}
} else {
Slog.e(TAG, "Backup Manager not found!");
doRestore = false;
}
}
if (!doRestore) {
// No restore possible, or the Backup Manager was mysteriously not
// available -- just fire the post-install work request directly.
if (DEBUG_INSTALL) Log.v(TAG, "No restore - queue post-install for " + token);
Message msg = mHandler.obtainMessage(POST_INSTALL, token, 0);
mHandler.sendMessage(msg);
}
}
});
}
installPackageLI()方法的代码太长,所以还是分解分析。首先还是通过PackageParser来获得Package实例,collectCertificates()函数调用链中,完成了App安装过程中(非升级App)对App签名的检验工作,具体可参看另一篇博客Android App签名(证书)校验过程源码分析。collectManifestDigest()函数负责计算Manifest.xml的文件摘要,并且存在了Package的manifestDigest成员下。
private void installPackageLI(InstallArgs args, PackageInstalledInfo res) {
````
try {
pp.collectCertificates(pkg, parseFlags);
pp.collectManifestDigest(pkg);
} catch (PackageParserException e) {
res.setError("Failed collect during installPackageLI", e);
return;
}
````
}
接着,安装程序比较apk中manifest.xml文件的摘要值:将从apk文件中提提取的manifest计算摘要和之前installer解析到的进行比较,若不匹配,则抛出异常。
private void installPackageLI(InstallArgs args, PackageInstalledInfo res) {
````
if (args.manifestDigest != null) {
if (DEBUG_INSTALL) {
final String parsedManifest = pkg.manifestDigest == null ? "null"
: pkg.manifestDigest.toString();
Slog.d(TAG, "Comparing manifests: " + args.manifestDigest.toString() + " vs. "
+ parsedManifest);
}
if (!args.manifestDigest.equals(pkg.manifestDigest)) {
res.setError(INSTALL_FAILED_PACKAGE_CHANGED, "Manifest digest changed");
return;
}
} else if (DEBUG_INSTALL) {
final String parsedManifest = pkg.manifestDigest == null
? "null" : pkg.manifestDigest.toString();
Slog.d(TAG, "manifestDigest was not present, but parser got: " + parsedManifest);
}
````
}
接下来,是对App升级或者使用了shareUserId属性时的证书判断。
private void installPackageLI(InstallArgs args, PackageInstalledInfo res) {
````
if (!ps.keySetData.isUsingUpgradeKeySets() || ps.sharedUser != null) {
try {
verifySignaturesLP(ps, pkg);
} catch (PackageManagerException e) {
res.setError(e.error, e.getMessage());
return;
}
}
````
}
上面if中的!ps.keySetData.isUsingUpgradeKeySets()给出了不是采用Upgrade keysets升级的情况,是不是采用Upgrade keysets升级时根据PackageKeySetData中的long[] mUpgradeKeySets成员是否空或者长度是否为零决定的。关于Upgrade keysets,后面可能会给出说明。对于不是Upgrade keysets升级或者shareUid情况的,调用了verifySignaturesLP(PackageSetting pkgSetting, PackageParser.Package pkg)函数来检验签名,若不匹配,抛出PackageManagerException异常。
private void verifySignaturesLP(PackageSetting pkgSetting, PackageParser.Package pkg)throws PackageManagerException {
if (pkgSetting.signatures.mSignatures != null) {
// Already existing package. Make sure signatures match
boolean match = compareSignatures(pkgSetting.signatures.mSignatures, pkg.mSignatures)
== PackageManager.SIGNATURE_MATCH;
if (!match) {
match = compareSignaturesCompat(pkgSetting.signatures, pkg)
== PackageManager.SIGNATURE_MATCH;
}
if (!match) {
match = compareSignaturesRecover(pkgSetting.signatures, pkg)
== PackageManager.SIGNATURE_MATCH;
}
if (!match) {
throw new PackageManagerException(INSTALL_FAILED_UPDATE_INCOMPATIBLE, "Package "
+ pkg.packageName + " signatures do not match the "
+ "previously installed version; ignoring!");
}
}
// Check for shared user signatures
if (pkgSetting.sharedUser != null && pkgSetting.sharedUser.signatures.mSignatures != null) {
// Already existing package. Make sure signatures match
boolean match = compareSignatures(pkgSetting.sharedUser.signatures.mSignatures,
pkg.mSignatures) == PackageManager.SIGNATURE_MATCH;
if (!match) {
match = compareSignaturesCompat(pkgSetting.sharedUser.signatures, pkg)
== PackageManager.SIGNATURE_MATCH;
}
if (!match) {
match = compareSignaturesRecover(pkgSetting.sharedUser.signatures, pkg)
== PackageManager.SIGNATURE_MATCH;
}
if (!match) {
throw new PackageManagerException(INSTALL_FAILED_SHARED_USER_INCOMPATIBLE,
"Package " + pkg.packageName
+ " has no signatures that match those in shared user "
+ pkgSetting.sharedUser.name + "; ignoring!");
}
}
}
校验过程主要调用了三个函数,其实这三个函数都是比较旧的apk中的Signatures[]和新的apk中的Signatures[]是否相同。但这里有几个不明确的点,就是如何从apk中或得Signatures[],这个以后单独分析。
在installPackageLI()函数中,接着是对采用Upgrade keysets升级时检验签名的处理,即调用checkUpgradeKeySetLP()检验新的apk中是都全部包含老的apk中的公钥信息,若不是,则抛出异常。checkUpgradeKeySetLP()用到了证书公钥信息,这解决了一点点在PackageInstaller源码分析中提出疑问。
private void installPackageLI(InstallArgs args, PackageInstalledInfo res) {
````
else {
if (!checkUpgradeKeySetLP(ps, pkg)) {
res.setError(INSTALL_FAILED_UPDATE_INCOMPATIBLE, "Package "
+ pkg.packageName + " upgrade keys do not match the "
+ "previously installed version");
return;
}
}
````
}
private boolean checkUpgradeKeySetLP(PackageSetting oldPS, PackageParser.Package newPkg) {
// Upgrade keysets are being used. Determine if new package has a superset of the
// required keys.
long[] upgradeKeySets = oldPS.keySetData.getUpgradeKeySets();
KeySetManagerService ksms = mSettings.mKeySetManagerService;
for (int i = 0; i < upgradeKeySets.length; i++) {
Set<PublicKey> upgradeSet = ksms.getPublicKeysFromKeySetLPr(upgradeKeySets[i]);
if (newPkg.mSigningKeys.containsAll(upgradeSet)) {
return true;
}
}
return false;
}
接下来,处理安装的apk申明的权限,分别对apk为系统apk申明系统权限和非系统apk申明系统权限做了处理。
private boolean checkUpgradeKeySetLP(PackageSetting oldPS, PackageParser.Package newPkg) {
`````
// Check whether the newly-scanned package wants to define an already-defined perm
int N = pkg.permissions.size();
for (int i = N-1; i >= 0; i--) {
PackageParser.Permission perm = pkg.permissions.get(i);
BasePermission bp = mSettings.mPermissions.get(perm.info.name);
if (bp != null) {
// If the defining package is signed with our cert, it's okay. This
// also includes the "updating the same package" case, of course.
// "updating same package" could also involve key-rotation.
final boolean sigsOk;
if (!bp.sourcePackage.equals(pkg.packageName)
|| !(bp.packageSetting instanceof PackageSetting)
|| !bp.packageSetting.keySetData.isUsingUpgradeKeySets()
|| ((PackageSetting) bp.packageSetting).sharedUser != null) {
sigsOk = compareSignatures(bp.packageSetting.signatures.mSignatures,
pkg.mSignatures) == PackageManager.SIGNATURE_MATCH;
} else {
sigsOk = checkUpgradeKeySetLP((PackageSetting) bp.packageSetting, pkg);
}
if (!sigsOk) {
// If the owning package is the system itself, we log but allow
// install to proceed; we fail the install on all other permission
// redefinitions.
if (!bp.sourcePackage.equals("android")) {
res.setError(INSTALL_FAILED_DUPLICATE_PERMISSION, "Package "
+ pkg.packageName + " attempting to redeclare permission "
+ perm.info.name + " already owned by " + bp.sourcePackage);
res.origPermission = perm.info.name;
res.origPackage = bp.sourcePackage;
return;
} else {
Slog.w(TAG, "Package " + pkg.packageName
+ " attempting to redeclare system permission "
+ perm.info.name + "; ignoring new declaration");
pkg.permissions.remove(i);
}
}
}
}
````
}
最后,针对升级App还是安装新App分别交由不同的函数执行,升级App调用replacePackageLI()函数,安装新App调用installNewPackageLI()。
private boolean checkUpgradeKeySetLP(PackageSetting oldPS, PackageParser.Package newPkg) {
````
if (replace) {
replacePackageLI(pkg, parseFlags, scanFlags | SCAN_REPLACING, args.user,
installerPackageName, res);
} else {
installNewPackageLI(pkg, parseFlags, scanFlags | SCAN_DELETE_DATA_ON_FAILURES,
args.user, installerPackageName, res);
}
````
}
在分析这两个函数之前,还是对installPackageLI()函数做个总结,我们看看在这个函数中都做了哪些事。
- 首先,调用了collectCertificates()函数对apk的签名做了校验,确保apk没有被非法修改(修改其中的文件);
- 接着,调用collectManifestDigest()函数计算了Manifest.xml的文件摘要,并且存在了Package的manifestDigest成员下;
- 接着,将从apk文件中提提取的manifest计算摘要和之前installer解析到的进行比较,若不匹配,则抛出异常。
- 然后,对App升级或者使用了共享属性sharedUid的情况校验待安装的App的证书是否匹配,并就升级方式做了具体处理,这里有两个问题,就是升级过程中的Upgrade keysets升级方式和Signature[]的获得不明朗。
- 最后,就到了根据是升级App还是安装新App,调用不同的函数进行处理环节。
鉴于不想本篇博文篇幅过长,对App升级还是安装新App过程分析放到PackageInstaller源码分析(二)。下面总结一下。
二、总结
PackageInstaller负责App的安装工作,根据既有知识,Android权限是在App安装时候一次性授予的,所以在App安装过程要对权限处理。原本分析PackageInstaller源码,旨在分析权限授予过程。由于App安装过程中牵扯很多的安全策略,就一并分析了,包括对apk证书的校验等。
下面对前面的分析做个梳理。
- PackageInstallerActivity的onCreate();
- 根据从Intent中获取的Data的scheme的不同,调用不同的处理逻辑安装App,这篇是从File安装的;
- 解析App文件到Package对象;
- initInstall()函数获取apk中的权限信息;
- onClick()监听用户点击同意安装按钮;
- 启动InstallAppProcess Activity;
- onCrete()函数调用了installPackageWithVerificationAndEncryption()函数;
- 继续调用installPackage()函数;
- 通过Handler机制启动了startCopy()函数;
- startCopy()调用了启动了handlerStartCopy()将apk拷贝到/data/app、pkg-name/和把.so文件拷贝到/data/app/pkg-name/lib/的handlerReturnCode()函数;
- handlerReturnCode调用了installPackageLI()函数;
- 在installPackageLI()中,首先调用了collectCertificates()函数对apk的签名做了校验,确保apk没有被非法修改(修改其中的文件);
- 接着,调用collectManifestDigest()函数计算了Manifest.xml的文件摘要,并且存在了Package的manifestDigest成员下;
- 接着,将从apk文件中提提取的manifest计算摘要和之前installer解析到的进行比较,若不匹配,则抛出异常。
- 然后,对App升级或者使用了共享属性sharedUid的情况校验待安装的App的证书是否匹配,并就升级方式做了具体处理,这里有两个问题,就是升级过程中的Upgrade keysets升级方式和Signature[]的获得不明朗。
- 最后,就到了根据是升级App还是安装新App,调用不同的函数进行处理环节。
PackageInstaller源码分析(一)暂时处理到这,这里还有几个模糊的地方,前面分析也提到了。一是升级过程中的Upgrade keysets升级方式,二是Signature[]的结构和怎么获得的不够明朗。后续分析即关于是升级App还是安装新App的处理见PackageInstaller源码分析(二)。
本文详细分析了Android PackageInstaller的源码,探讨了Android权限在应用安装过程中的作用。通过追踪源码,从PackageInstallerActivity开始,经过Intent处理、文件解析、权限提取,到最终的权限验证和安装流程,揭示了用户同意安装后App如何获取权限的全过程。重点关注了证书校验、权限处理和文件拷贝等关键步骤。

5300

被折叠的 条评论
为什么被折叠?



