本篇博客分析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.per