在 PackageManagerService(1)扫描前的准备 文章中,分析了 PKMS ( PackageManagerService ) 在扫描之前的准备工作,这篇文章来分析扫描目录并解析APK的过程。
扫描目录并解析APK这个过程,伴随着大量升级相关的代码,但是我要提醒一句,我们现在分析的目标是系统首次开机,并不会把系统升级的代码混淆在一起分析。
扫描目录,解析APK
public PackageManagerService(Context context, Installer installer,
boolean factoryTest, boolean onlyCore) {
synchronized (mInstallLock) {
// writer
synchronized (mPackages) {
// 判断是否首次开机
mFirstBoot = !mSettings.readLPw(sUserManager.getUsers(false));
// 承接上文继续分析...
// 创建缓存目录,用于缓存扫描结果
mCacheDir = preparePackageParserCache();
int scanFlags = SCAN_BOOTING | SCAN_INITIAL;
if (mIsUpgrade || mFirstBoot) {
// 注意,首次开机还要加上这个扫描flag
scanFlags = scanFlags | SCAN_FIRST_BOOT_OR_UPGRADE;
}
// 扫描目录并解析APK
//先扫描Overlay APK
scanDirTracedLI(new File(VENDOR_OVERLAY_DIR),
mDefParseFlags | PackageParser.PARSE_IS_SYSTEM_DIR,
scanFlags | SCAN_AS_SYSTEM | SCAN_AS_VENDOR,
0);
// 还会扫描其它分区的overlay目录,例如/product/overlay, /product_services/overlay 等等。
// ...
// 保存静态overlay包的信息,这些信息会在后面解析APK时用到
mParallelPackageParserCallback.findStaticOverlayPackages();
// /system/framework目录,这个目录下只有一个apk,那就是framework-res.apk
scanDirTracedLI(frameworkDir,
mDefParseFlags | PackageParser.PARSE_IS_SYSTEM_DIR,
scanFlags | SCAN_NO_DEX | SCAN_AS_SYSTEM | SCAN_AS_PRIVILEGED,
0);
// 扫描其它分区的目录, 方法与上面的一致....
}
}
}
在扫描之前,会通过preparePackageParserCache()
创建一个缓存目录,用来缓存扫描APK结果。如此一来,下次开机,如果缓存有效,就可以直接从缓存中获取apk信息,而不用再扫描APK了,这样加快了不是首次开机的速度。
开始扫描APK时,首先扫描的是Overlay APK,目录有/vendor/overlay
, /product/overlay
, /product_services/overlay
, /odm/overlay
, /oem/overlay
。因此如果你想生成Overlay APK,那么必须编译到这些目录下。
什么是Overlay APK?Overlay APK是用来覆盖目标APK的资源的,然而仅限于res/values
目录下的资源,例如替换应用的名字,主题等等。因此也称为运行时资源覆盖(Runtime Resource Overlay)。
然而,为了保证RRO生效,需要在使用静态的RRO,例如
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.qualcomm.qti.optinoverlay">
<overlay
android:isStatic="true"
android:priority="1"
android:targetPackage="com.qualcomm.location.XT" />
</manifest>
其中isStatic
属性为true
表示这个RRO是静态的,它会覆盖目标应用(targetPackage
属性指定的应用)的资源。
mParallelPackageParserCallback.findStaticOverlayPackages()
方法保存的就是静态RRO。这个将在后面解析APK时用到。
本文的目标是PackageManagerService扫描并解析APK,至于Overlay APK的代码,还请大家自行分析。
那么现在我们来看看scanDirTracedLI()
是如何扫描目录并解析APK的
private void scanDirLI(File scanDir, int parseFlags, int scanFlags, long currentTime) {
final File[] files = scanDir.listFiles();
if (ArrayUtils.isEmpty(files)) {
Log.d(TAG, "No files in app dir " + scanDir);
return;
}
/**
* mCacheDir用于缓存apk信息
* mParallelPackageParserCallback用于在解析时提供overlay资源
*/
try (ParallelPackageParser parallelPackageParser = new ParallelPackageParser(
mSeparateProcesses, mOnlyCore, mMetrics, mCacheDir,
mParallelPackageParserCallback)) {
int fileCount = 0;
for (File file : files) {
// 必须是.apk或者目录
final boolean isPackage = (isApkFile(file) || file.isDirectory())
&& !PackageInstallerService.isStageName(file.getName());
if (!isPackage) {
continue;
}
// 1. 提交APK文件,并行解析
parallelPackageParser.submit(file, parseFlags);
fileCount++;
}
// 2. 处理扫描结果
for (; fileCount > 0; fileCount--) {
// APK数据保存在ParseResult.pkg中,类型为PackageParser.Package
ParallelPackageParser.ParseResult parseResult = parallelPackageParser.take();
Throwable throwable = parseResult.throwable;
int errorCode = PackageManager.INSTALL_SUCCEEDED;
if (throwable == null) {
// 使用<static-library>生成的静态库,需要重新设置包名,形式为package_libVersion
if (parseResult.pkg.applicationInfo.isStaticSharedLibrary()) {
renameStaticSharedLibraryPackage(parseResult.pkg);
}
try {
// 扫描,并返回一个新的结果
// 注意最后一个参数user,值为null
scanPackageChildLI(parseResult.pkg, parseFlags, scanFlags,
currentTime, null);
}
// ...
}
// ...
}
}
}
ParallelPackageParser
是一个并行的APK解析器,通过ParallelPackageParser#submit()
方法,向线程池提交待解析的APK,然后通过ParallelPackageParser#take()
来不断地获取结果,用于扫描处理。
提交并解析APK
现在来看下 ParallelPackageParser#submit()
如何提交并解析APK
public void submit(File scanFile, int parseFlags) {
mService.submit(() -> {
// ParseResult只有三个变量
// pkg变量保存扫描的结果
// scanFile保存扫描的文件
// throwable保存扫描出现的异常
ParseResult pr = new ParseResult();
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parallel parsePackage [" + scanFile + "]");
try {
PackageParser packageParser = new PackageParser();
packageParser.setSeparateProcesses(mSeparateProcesses);
packageParser.setOnlyCoreApps(mOnlyCore);
packageParser.setDisplayMetrics(mMetrics);
packageParser.setCacheDir(mCacheDir);
packageParser.setCallback(mPackageParserCallback);
pr.scanFile = scanFile;
// 通过PackageParser解析APK,并返回解析结果
// 解析结果的类型为 PackageParser.Package
pr.pkg = parsePackage(packageParser, scanFile, parseFlags);
} catch (Throwable e) {
pr.throwable = e;
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
try {
// 把解析的结果添加到队列中
mQueue.put(pr);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
mInterruptedInThread = Thread.currentThread().getName();
}
});
}
这里向线程池中提交了一个请求,在这个请求中,会通过parsePackage()
解析APK,然后把解析结果添加到一个类型为BlockingQueue
的mQueue
中。后面在处理解析结果的时候,那个解析结果就是从mQueue
中获取的。
现在来看下 parsePackage()
是如何解析APK的
protected PackageParser.Package parsePackage(PackageParser packageParser, File scanFile,
int parseFlags) throws PackageParser.PackageParserException {
// 第三个参数表示是否使用缓存,这样会提升开机效率
return packageParser.parsePackage(scanFile, parseFlags, true /* useCaches */);
}
原来就是通过PackageParser#parsePackage()
来解析的,注意第三个参数的值为 true ,它代表对解析结果使用缓存。
// frameworks/base/core/java/android/content/pm/PackageParser.java
public Package parsePackage(File packageFile, int flags, boolean useCaches)
throws PackageParserException {
// 1. 获取有效的缓存结果
Package parsed = useCaches ? getCachedResult(packageFile, flags) : null;
if (parsed != null) {
return parsed;
}
long parseTime = LOG_PARSE_TIMINGS ? SystemClock.uptimeMillis() : 0;
if (packageFile.isDirectory()) {
parsed = parseClusterPackage(packageFile, flags);
} else {
// 2. 解析APK
parsed = parseMonolithicPackage(packageFile, flags);
}
long cacheTime = LOG_PARSE_TIMINGS ? SystemClock.uptimeMillis() : 0;
// 3. 把解析的结果进行缓存
// 原理是把解析的数据分解为字节数组,然后写入到缓存文件中
cacheResult(packageFile, flags, parsed);
return parsed;
}
从这里可以看出,整个解析过程使用了缓存,但是本文不打算分析这个缓存的机制,有兴趣的同学可以研究下,并不难。
而解析APK使用的是parseMonolithicPackage()
public Package parseMonolithicPackage(File apkFile, int flags) throws PackageParserException {
// 这里只是简单解析了AndroidManifest.xml的某些属性,这里主要是为了Split APK功能,本文不讨论
final PackageLite lite = parseMonolithicPackageLite(apkFile, flags);
// ...
final SplitAssetLoader assetLoader = new DefaultSplitAssetLoader(lite, flags);
try {
final Package pkg = parseBaseApk(apkFile, assetLoader.getBaseAssetManager(), flags);
// PackageParser.Package.codePath存储了apk的全路径
pkg.setCodePath(apkFile.getCanonicalPath());
pkg.setUse32bitAbi(lite.use32bitAbi);
return pkg;
}
// ...
}
这一步解析加入了Split APK解析功能,如果大家想了解Split APK功能是如何实现的,看完本文可自行分析。
那么接下来是使用parseBaseApk()
来继续解析APK
private Package parseBaseApk(File apkFile, AssetManager assets, int flags)
throws PackageParserException {
final String apkPath = apkFile.getAbsolutePath();
// ...
mParseError = PackageManager.INSTALL_SUCCEEDED;
mArchiveSourcePath = apkFile.getAbsolutePath();
XmlResourceParser parser = null;
try {
final int cookie = assets.findCookieForPath(apkPath);
if (cookie == 0) {
throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST,
"Failed adding asset path: " + apkPath);
}
// 1. 打开AndroidManifest.xml的解析器
parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);
// 2. 获取APK的资源
final Resources res = new Resources(assets, mMetrics, null);
final String[] outError = new String[1];
// 3. 解析AndroidManifest.xml
final Package pkg = parseBaseApk(apkPath, res, parser, flags, outError);
if (pkg == null) {
throw new PackageParserException(mParseError,
apkPath + " (at " + parser.getPositionDescription() + "): " + outError[0]);
}
pkg.setVolumeUuid(volumeUuid);
pkg.setApplicationVolumeUuid(volumeUuid);
pkg.setBaseCodePath(apkPath);
pkg.setSigningDetails(SigningDetails.UNKNOWN);
return pkg;
}
// ...
}
第一步,打开了AndroidManifest.xml的解析器,第二步,获取了APK的资源 ,第三步正式解析AndroidManifest.xml
private Package parseBaseApk(String apkPath, Resources res, XmlResourceParser parser, int flags,
String[] outError) throws XmlPullParserException, IOException {
// 省略Split APK功能的代码...
if (mCallback != null) {
// 这里获取的就是刚才解析的静态RRO路径
String[] overlayPaths = mCallback.getOverlayPaths(pkgName, apkPath);
if (overlayPaths != null && overlayPaths.length > 0) {
for (String overlayPath : overlayPaths) {
// 向Resource添加Overlay资源,如此一来,RRO资源是生效了
res.getAssets().addOverlayPath(overlayPath);
}
}
}
// 这个Package就是保存解析APK结果的地方
final Package pkg = new Package(pkgName);
// 以下解析并保存了<manifest>标签的属性
TypedArray sa = res.obtainAttributes(parser,
com.android.internal.R.styleable.AndroidManifest);
pkg.mVersionCode = sa.getInteger(
com.android.internal.R.styleable.AndroidManifest_versionCode, 0);
pkg.mVersionCodeMajor = sa.getInteger(
com.android.internal.R.styleable.AndroidManifest_versionCodeMajor, 0);
pkg.applicationInfo.setVersionCode(pkg.getLongVersionCode());
pkg.baseRevisionCode = sa.getInteger(
com.android.internal.R.styleable.AndroidManifest_revisionCode, 0);
pkg.mVersionName = sa.getNonConfigurationString(
com.android.internal.R.styleable.AndroidManifest_versionName, 0);
if (pkg.mVersionName != null) {
pkg.mVersionName = pkg.mVersionName.intern();
}
pkg.coreApp = parser.getAttributeBooleanValue(null, "coreApp", false);
pkg.mCompileSdkVersion = sa.getInteger(
com.android.internal.R.styleable.AndroidManifest_compileSdkVersion, 0);
pkg.applicationInfo.compileSdkVersion = pkg.mCompileSdkVersion;
pkg.mCompileSdkVersionCodename = sa.getNonConfigurationString(
com.android.internal.R.styleable.AndroidManifest_compileSdkVersionCodename, 0);
if (pkg.mCompileSdkVersionCodename != null) {
pkg.mCompileSdkVersionCodename = pkg.mCompileSdkVersionCodename.intern();
}
pkg.applicationInfo.compileSdkVersionCodename = pkg.mCompileSdkVersionCodename;
sa.recycle();
// 继续解析
return parseBaseApkCommon(pkg, null, res, parser, flags, outError);
}
在这一步中,我们可以看到,它把静态的RRO资源加入到了APK资源中,如此一来,RRO资源就会覆盖APK的资源 。
之后创建了一个PackageParser.Package
对象,这个对象就是用来保存解析结果的,在这一步中,解析了<manifest>
标签的属性。然后通过parseBaseApkCommon()
继续解析
我们在看代码的时候 ,一定是思路明确,搞清楚代码到底在做什么,这样就会形成一个完整的逻辑。
private Package parseBaseApkCommon(Package pkg, Set<String> acceptedTags, Resources res,
XmlResourceParser parser, int flags, String[] outError) throws ...{
TypedArray sa = res.obtainAttributes(parser,
com.android.internal.R.styleable.AndroidManifest);
// 解析sharedUserId属性
String str = sa.getNonConfigurationString(
com.android.internal.R.styleable.AndroidManifest_sharedUserId, 0);
if (str != null && str.length() > 0) {
String nameError = validateName(str, true, true);
if (nameError != null && !"android".equals(pkg.packageName)) {
outError[0] = "<manifest> specifies bad sharedUserId name \""
+ str + "\": " + nameError;
mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID;
return null;
}
pkg.mSharedUserId = str.intern();
pkg.mSharedUserLabel = sa.getResourceId(
com.android.internal.R.styleable.AndroidManifest_sharedUserLabel, 0);
}
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
if (tagName.equals(TAG_APPLICATION)) {
// ...
foundApp = true;
// 解析<application>各种属性以及各种子标签
if (!parseBaseApplication(pkg, res, parser, flags, outError)) {
return null;
}
} else if (tagName.equals(TAG_OVERLAY)) {
} else if (tagName.equals(TAG_PERMISSION)) {
} else if (tagName.equals(TAG_ORIGINAL_PACKAGE)) {
}
// ...
}
return pkg;
}
从整体看,这一步主要就是为解析<manifest>
下的各种子标签,例如我们最熟悉的<application>
标签。
如果你想知道Overlay APK的事情,在这里可以分析下
TAG_OVERLAY
分支的代码。
当然,我不可能把这里所有标签都分析完,这肯定不现实。但是后面的分析可能会用到,那怎么办呢?我们可以大致看一下有个映象,如果后面分析到相关的地方,再回头来查询一下即可。用一句话总结就是,按需分析。这样可以大提升了我们分析源码的效率。
现在,对于提交APK解析这一步,解析APK过程已经分析完毕,让我们回到这一步的出发点的代码
public void submit(File scanFile, int parseFlags) {
mService.submit(() -> {
// ParseResult只有三个变量
// pkg变量保存扫描的结果
// scanFile保存扫描的文件
// throwable保存扫描出现的异常
ParseResult pr = new ParseResult();
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parallel parsePackage [" + scanFile + "]");
try {
PackageParser pp = new PackageParser();
pp.setSeparateProcesses(mSeparateProcesses);
pp.setOnlyCoreApps(mOnlyCore);
pp.setDisplayMetrics(mMetrics);
pp.setCacheDir(mCacheDir);
pp.setCallback(mPackageParserCallback);
pr.scanFile = scanFile;
// 1. 解析APK,并将结果保存到ParseResult.pkg中
pr.pkg = parsePackage(pp, scanFile, parseFlags);
} catch (Throwable e) {
pr.throwable = e;
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
try {
// 2. 把解析的结果添加到队列中
mQueue.put(pr);
} catch (InterruptedException e) {
// ...
}
});
}
解析APK完成后,就会把结果添加到mQueue
中。那么在下一步中,会不断从mQueue
中取出结果来处理。
framework的分析是一个异常艰辛的过程,每次分析完一大步后,需要给自己一点点掌声鼓励自己继续坚持下去。
处理APK解析结果
让我们再回到扫描开始的地方
private void scanDirLI(File scanDir, int parseFlags, int scanFlags, long currentTime) {
final File[] files = scanDir.listFiles();
if (ArrayUtils.isEmpty(files)) {
Log.d(TAG, "No files in app dir " + scanDir);
return;
}
/**
* mCacheDir用于缓存apk信息
* mParallelPackageParserCallback用于在解析时提供overlay资源
*/
try (ParallelPackageParser parallelPackageParser = new ParallelPackageParser(
mSeparateProcesses, mOnlyCore, mMetrics, mCacheDir,
mParallelPackageParserCallback)) {
int fileCount = 0;
for (File file : files) {
// 必须是.apk或者目录
final boolean isPackage = (isApkFile(file) || file.isDirectory())
&& !PackageInstallerService.isStageName(file.getName());
if (!isPackage) {
continue;
}
// 1. 提交APK文件,并行解析
parallelPackageParser.submit(file, parseFlags);
fileCount++;
}
// 2. 处理扫描结果
for (; fileCount > 0; fileCount--) {
// APK数据保存在ParseResult.pkg中,类型为PackageParser.Package
ParallelPackageParser.ParseResult parseResult = parallelPackageParser.take();
Throwable throwable = parseResult.throwable;
int errorCode = PackageManager.INSTALL_SUCCEEDED;
if (throwable == null) {
// 使用<static-library>生成的静态库,需要重新设置包名,形式为package_libVersion
if (parseResult.pkg.applicationInfo.isStaticSharedLibrary()) {
renameStaticSharedLibraryPackage(parseResult.pkg);
}
try {
// 扫描,并返回一个新的结果
// 注意最后一个参数user,值为null
scanPackageChildLI(parseResult.pkg, parseFlags, scanFlags,
currentTime, null);
}
// ...
}
// ...
}
}
}
现在我们已经分析完了APK解析的过程,现在来分析处理处理结果的过程,这个处理过程包含了大量的系统升级的代码,这个过程也是异常的复杂。但是本文的目标是系统首次开机,因此我会过滤掉系统升级的代码。现在我们来看看scanPackageChildLI()
如何处理扫描的结果
private PackageParser.Package scanPackageChildLI(PackageParser.Package pkg,
final @ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime,
@Nullable UserHandle user)
throws PackageManagerException {
if ((scanFlags & SCAN_CHECK_ONLY) == 0) {
// <application>下的<package>标签只对debug版本有用
// 可以发现SCAN_CHECK_ONLY与<package>相关,也就是与debug版本有关
if (pkg.childPackages != null && pkg.childPackages.size() > 0) {
scanFlags |= SCAN_CHECK_ONLY;
}
} else {
scanFlags &= ~SCAN_CHECK_ONLY;
}
// Scan the parent
// 继续处理扫描的结果
PackageParser.Package scannedPkg = addForInitLI(pkg, parseFlags,
scanFlags, currentTime, user);
// Scan the children
// <application>下的<package>标签只对debug版本有用
final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
for (int i = 0; i < childCount; i++) {
PackageParser.Package childPackage = pkg.childPackages.get(i);
addForInitLI(childPackage, parseFlags, scanFlags,
currentTime, user);
}
if ((scanFlags & SCAN_CHECK_ONLY) != 0) {
return scanPackageChildLI(pkg, parseFlags, scanFlags, currentTime, user);
}
// 然而这个返回结果好像并没有用到
return scannedPkg;
}
这一步正如方法名 scanPackageChildLI 所示,处理的是<manifest>
下的<package>
标签,并且标志位SCAN_CHECK_ONLY
也就为此面设置的。但是这个功能只与DEBUG版本有关,大家可以自行查询<pacakge>
标签的解析过程,所以以后关于SCAN_CHECK_ONLY
这方面的代码一概忽略。
在这一步中,使用了addForInitLI()
来继续处理扫描的结果。
/**
* 注意,第一个参数pkg指的是解析APK的结果
*/
private PackageParser.Package addForInitLI(PackageParser.Package pkg,
@ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime,
@Nullable UserHandle user)
// 省略了大量的关于系统升级的代码 ...
// 继续处理
final ScanResult scanResult = scanPackageNewLI(pkg, parseFlags, scanFlags
| SCAN_UPDATE_SIGNATURE, currentTime, user);
if (scanResult.success) {
synchronized (mPackages) {
boolean appIdCreated = false;
try {
final String pkgName = scanResult.pkgSetting.name;
// 对重新扫描的结果做一些调整
final Map<String, ReconciledPackage> reconcileResult = reconcilePackagesLocked(
new ReconcileRequest(
Collections.singletonMap(pkgName, scanResult),
mSharedLibraries, // 保存了系统配置文件中<library>信息
mPackages, // 系统首次启动时,还是空的
Collections.singletonMap(
pkgName, getSettingsVersionForPackage(pkg)),
Collections.singletonMap(pkgName,
getSharedLibLatestVersionSetting(scanResult))),
mSettings.mKeySetManagerService);
// 赋予uid
appIdCreated = optimisticallyRegisterAppId(scanResult);
// 提交信息进行保存
commitReconciledScanResultLocked(reconcileResult.get(pkgName));
} catch (PackageManagerException e) {
if (appIdCreated) {
cleanUpAppIdCreation(scanResult);
}
throw e;
}
}
}
// ...
return scanResult.pkgSetting.pkg;
}
这里省略了大量的关于系统升级的代码,剩下的这些就是关于首次开机过程中,处理扫描结果的代码,可以看到分为了三步
scanPackageNewLI()
用于重新扫描,记住,第一个参数pkg
的类型为PackageParser.Package
,它代表首次扫描的结果。reconcilePackagesLocked()
对重新扫描的结果进行一些小的调整。commitReconciledScanResultLocked()
提交结果进行保存,例如把结果写到packages.xml
中。
下面我们再分三步来解析这个过程。
重新扫描
重新扫描调用如下代码
final ScanResult scanResult = scanPackageNewLI(pkg, parseFlags, scanFlags
| SCAN_UPDATE_SIGNATURE, currentTime, user);
可以看到扫描参数scanFlags
添加了一个标志位SCAN_UPDATE_SIGNATURE
,这表示在这次重新扫描的过程中,需要更新签名信息。
private ScanResult scanPackageNewLI(@NonNull PackageParser.Package pkg,
final @ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime,
@Nullable UserHandle user) throws PackageManagerException {
// ...
// 调整标志位
scanFlags = adjustScanFlags(scanFlags, pkgSetting, disabledPkgSetting, user, pkg);
synchronized (mPackages) {
// 向PackageParser.Package.applicationInfo中设置一些标志信息,例如
applyPolicy(pkg, parseFlags, scanFlags, mPlatformPackage);
assertPackageIsValid(pkg, parseFlags, scanFlags);
SharedUserSetting sharedUserSetting = null;
// 获取共享进程的SharedUserSetting对象
if (pkg.mSharedUserId != null) {
sharedUserSetting = mSettings.getSharedUserLPw(
pkg.mSharedUserId, 0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/, true /*create*/);
}
// 重新封闭一个请求,第一个参数表示上一次扫描的结果
final ScanRequest request = new ScanRequest(pkg, sharedUserSetting,
pkgSetting == null ? null : pkgSetting.pkg, pkgSetting, disabledPkgSetting,
originalPkgSetting, realPkgName, parseFlags, scanFlags,
(pkg == mPlatformPackage), user);
// 发送扫描请求
return scanPackageOnlyLI(request, mFactoryTest, currentTime);
}
}
在开始再次扫描前,通过adjustScanFlags()
调整了一下标志位,但是对于首次开机,只对一种特殊情况进行调整。这种情况就是,当扫描vendor/app目录,也就是扫描非特权应用,而这个非特权应用通过sharedUserId
属性共享了一个有特权的应用,并且这个应用拥有平台签名,那么扫描的标志位会添加SCAN_AS_PRIVILEGED
。
然后通过applyPolicy()
向上一次的扫描结果中添加一些标志位,例如
- 扫描的是系统APK,会为
PackageParser.Package.applicationInfo.flags
添加ApplicationInfo.FLAG_SYSTEM
标志位。 - 扫描的是特权目录下的APK,会为
PackageParser.Package.applicationInfo.privateFlags
添加ApplicationInfo.PRIVATE_FLAG_PRIVILEGED
标志位。 - 如果APK拥有平台签名,那么会为
PackageParser.Package.applicationInfo.privateFlags
添加ApplicationInfo.PRIVATE_FLAG_SIGNED_WITH_PLATFORM_KEY
标志位。
最后,封装一个请求ScanRequest
,通过scanPackageOnlyLI()
再次扫描。
private static @NonNull
ScanResult scanPackageOnlyLI(@NonNull ScanRequest request,
boolean isUnderFactoryTest, long currentTime)
throws PackageManagerException {
// 这个表示上一次扫描APK的结果
final PackageParser.Package pkg = request.pkg;
// 首次开机,这个PackageSetting为空
PackageSetting pkgSetting = request.pkgSetting;
// 首次开机,值为null
final PackageSetting disabledPkgSetting = request.disabledPkgSetting;
// 首次开机,值为null
final PackageSetting originalPkgSetting = request.originalPkgSetting;
final @ParseFlags int parseFlags = request.parseFlags;
final @ScanFlags int scanFlags = request.scanFlags;
// 这是设置了<original-pacakge>标签的功能,为系统升级功能
final String realPkgName = request.realPkgName;
final SharedUserSetting sharedUserSetting = request.sharedUserSetting;
final UserHandle user = request.user;
// 包名为android的apk,也就是framework-res.apk
final boolean isPlatformPackage = request.isPlatformPackage;
// Initialize package source and resource directories
final File scanFile = new File(pkg.codePath);
final File destCodeFile = new File(pkg.applicationInfo.getCodePath());
final File destResourceFile = new File(pkg.applicationInfo.getResourcePath());
// <uses-static-library>标签的内容
String[] usesStaticLibraries = null;
if (pkg.usesStaticLibraries != null) {
usesStaticLibraries = new String[pkg.usesStaticLibraries.size()];
pkg.usesStaticLibraries.toArray(usesStaticLibraries);
}
// 首次启动,需要创建PackageSetting
final boolean createNewPackage = (pkgSetting == null);
if (createNewPackage) {
// 这个parentPackage不知哪里赋值的
final String parentPackageName = (pkg.parentPackage != null)
? pkg.parentPackage.packageName : null;
final boolean instantApp = (scanFlags & SCAN_AS_INSTANT_APP) != 0;
final boolean virtualPreload = (scanFlags & SCAN_AS_VIRTUAL_PRELOAD) != 0;
// 创建PackageSetting
pkgSetting = Settings.createNewSetting(pkg.packageName, originalPkgSetting,
disabledPkgSetting, realPkgName, sharedUserSetting, destCodeFile,
destResourceFile, pkg.applicationInfo.nativeLibraryRootDir,
pkg.applicationInfo.primaryCpuAbi, pkg.applicationInfo.secondaryCpuAbi,
pkg.mVersionCode, pkg.applicationInfo.flags, pkg.applicationInfo.privateFlags,
user, true /*allowInstall*/, instantApp, virtualPreload,
parentPackageName, pkg.getChildPackageNames(),
UserManagerService.getInstance(), usesStaticLibraries,
pkg.usesStaticLibrariesVersions);
} else {
// ...
}
// ...
final int userId = (user == null ? UserHandle.USER_SYSTEM : user.getIdentifier());
// ...
final int targetSdkVersion =
((sharedUserSetting != null) && (sharedUserSetting.packages.size() != 0)) ?
sharedUserSetting.seInfoTargetSdkVersion : pkg.applicationInfo.targetSdkVersion;
final boolean isPrivileged = (sharedUserSetting != null) ?
sharedUserSetting.isPrivileged() | pkg.isPrivileged() : pkg.isPrivileged();
pkg.applicationInfo.seInfo = SELinuxMMAC.getSeInfo(pkg, isPrivileged,
pkg.applicationInfo.targetSandboxVersion, targetSdkVersion);
pkg.applicationInfo.seInfoUser = SELinuxUtil.assignSeinfoUser(pkgSetting.readUserState(
userId == UserHandle.USER_ALL ? UserHandle.USER_SYSTEM : userId));
// PacakgeParser.Package.mExtras保存了PackageSetting
pkg.mExtras = pkgSetting;
// 如果没有声明process属性,那么进程名子就是包名,否则为process定义的名子
pkg.applicationInfo.processName = fixProcessName(
pkg.applicationInfo.packageName,
pkg.applicationInfo.processName);
if (!isPlatformPackage) {
// 创建数据保存的目录
pkg.applicationInfo.initForUser(UserHandle.USER_SYSTEM);
}
// ...
// 系统应用,设置标记PacakgeSetting.isOrphaned标志
if (isSystemApp(pkg)) {
pkgSetting.isOrphaned = true;
}
// 更新时间
final long scanFileTime = getLastModifiedTime(pkg);
if (currentTime != 0) {
if (pkgSetting.firstInstallTime == 0) {
pkgSetting.firstInstallTime = pkgSetting.lastUpdateTime = currentTime;
} else if ((scanFlags & SCAN_UPDATE_TIME) != 0) {
pkgSetting.lastUpdateTime = currentTime;
}
} else if (pkgSetting.firstInstallTime == 0) {
// We need *something*. Take time time stamp of the file.
pkgSetting.firstInstallTime = pkgSetting.lastUpdateTime = scanFileTime;
} else if ((parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) != 0) {
if (scanFileTime != pkgSetting.timeStamp) {
// A package on the system image has changed; consider this
// to be an update.
pkgSetting.lastUpdateTime = scanFileTime;
}
}
pkgSetting.setTimeStamp(scanFileTime);
// PackageSetting.pkg保存了上一次扫描的结果
pkgSetting.pkg = pkg;
// PackageSetting.pkgFlags保存了标志位
pkgSetting.pkgFlags = pkg.applicationInfo.flags;
if (pkg.getLongVersionCode() != pkgSetting.versionCode) {
pkgSetting.versionCode = pkg.getLongVersionCode();
}
// ...
// <static-library> -> Android静态库
SharedLibraryInfo staticSharedLibraryInfo = null;
if (!TextUtils.isEmpty(pkg.staticSharedLibName)) {
staticSharedLibraryInfo = SharedLibraryInfo.createForStatic(pkg);
}
// <library> -> Android动态库
List<SharedLibraryInfo> dynamicSharedLibraryInfos = null;
if (!ArrayUtils.isEmpty(pkg.libraryNames)) {
dynamicSharedLibraryInfos = new ArrayList<>(pkg.libraryNames.size());
for (String name : pkg.libraryNames) {
dynamicSharedLibraryInfos.add(SharedLibraryInfo.createForDynamic(pkg, name));
}
}
return new ScanResult(request, true, pkgSetting, changedAbiCodePath,
!createNewPackage /* existingSettingCopied */, staticSharedLibraryInfo,
dynamicSharedLibraryInfos);
}
这一步,再次向APK的解析结果PackageParser.Package
中添加数据,然后创建了一个 PackageSetting
,并且PackageSetting
和PackageParser.Package
相互保存,最后返回一个数据包装类ScanResult
表示扫描的结果。
提交扫描结果
我们已经兜兜转转几个来回了,可以已经忘记最初的出发点了。不过没有关系,我们现在处在再次扫描的过程中,只不过再次扫描的动作已经完成,现在要提交再次扫描的结果了。
private PackageParser.Package addForInitLI(PackageParser.Package pkg,
@ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime,
@Nullable UserHandle user)
// ...
// 重新扫描
final ScanResult scanResult = scanPackageNewLI(pkg, parseFlags, scanFlags
| SCAN_UPDATE_SIGNATURE, currentTime, user);
if (scanResult.success) {
synchronized (mPackages) {
boolean appIdCreated = false;
try {
final String pkgName = scanResult.pkgSetting.name;
// 对重新扫描的结果做一些调整
final Map<String, ReconciledPackage> reconcileResult = reconcilePackagesLocked(
new ReconcileRequest(
Collections.singletonMap(pkgName, scanResult),
mSharedLibraries, // 保存了系统配置文件中<library>信息
mPackages, // 系统首次启动时,还是空的
Collections.singletonMap(
pkgName, getSettingsVersionForPackage(pkg)),
Collections.singletonMap(pkgName,
getSharedLibLatestVersionSetting(scanResult))),
mSettings.mKeySetManagerService);
// 赋予uid
appIdCreated = optimisticallyRegisterAppId(scanResult);
// 提交信息进行保存
commitReconciledScanResultLocked(reconcileResult.get(pkgName));
} catch (PackageManagerException e) {
if (appIdCreated) {
cleanUpAppIdCreation(scanResult);
}
throw e;
}
}
}
// ...
return scanResult.pkgSetting.pkg;
}
前面我们已经对scanPackageNewLI()
的再次扫描过程进行了分析,现在我们继续往下看,reconcilePackagesLocked()
对扫描的结果再次进行了调整和包装
private static Map<String, ReconciledPackage> reconcilePackagesLocked(
final ReconcileRequest request, KeySetManagerService ksms)
throws ReconcileFailure {
// 这个Map中的ScanResult就是重新扫描的结果,String为包名
final Map<String, ScanResult> scannedPackages = request.scannedPackages;
// 这就是返回值
final Map<String, ReconciledPackage> result = new ArrayMap<>(scannedPackages.size());
// make a copy of the existing set of packages so we can combine them with incoming packages
// request.allPackages就是 mPackages
// 这个combinedPackages表示所有的已经解析的过的APK的集合
final ArrayMap<String, PackageParser.Package> combinedPackages =
new ArrayMap<>(request.allPackages.size() + scannedPackages.size());
combinedPackages.putAll(request.allPackages);
// 这个incomingSharedLibraries保存了<static-library>和<library>信息
final Map<String, LongSparseArray<SharedLibraryInfo>> incomingSharedLibraries =
new ArrayMap<>();
for (String installPackageName : scannedPackages.keySet()) {
// 这个就是重新扫描的结果
final ScanResult scanResult = scannedPackages.get(installPackageName);
// 把解析过的APK数据加入到combinedPackages
combinedPackages.put(scanResult.pkgSetting.name, scanResult.request.pkg);
// request.sharedLibrarySource就是mSharedLibraries, 保存了系统配置文件中<library>信息
// 这里优先返回静态库的信息,也是<static-library>,其次是返回<library>信息
final List<SharedLibraryInfo> allowedSharedLibInfos =
getAllowedSharedLibInfos(scanResult, request.sharedLibrarySource);
final SharedLibraryInfo staticLib = scanResult.staticSharedLibraryInfo;
if (allowedSharedLibInfos != null) {
for (SharedLibraryInfo info : allowedSharedLibInfos) {
// 把库的信息添加到 incomingSharedLibraries 中
if (!addSharedLibraryToPackageVersionMap(incomingSharedLibraries, info)) {
throw new ReconcileFailure("Static Shared Library " + staticLib.getName()
+ " is being installed twice in this set!");
}
}
}
if (isInstall && prepareResult.replace && !prepareResult.system) {
// 省略APK的安装流程
} else {
deletePackageAction = null;
}
// 省略更新签名信息的流程...
result.put(installPackageName,
new ReconciledPackage(request, installArgs, scanResult.pkgSetting,
res, request.preparedPackages.get(installPackageName), scanResult,
deletePackageAction, allowedSharedLibInfos, signingDetails,
sharedUserSignaturesChanged, removeAppKeySetData));
}
// 如果安装的APK是一个库,这里会收集库的信息,代码省略....
return result;
}
这一步,其实提炼了两个信息,一个是签名信息,一个是APK本身为库的信息。APK库的信息提炼到了allowedSharedLibInfos
中,签名信息提炼到了signingDetails
和sharedUserSignaturesChanged
中。
然后通过optimisticallyRegisterAppId()
为进程赋予了uid,这个过程比较简单,不分析。
最后通过commitReconciledScanResultLocked()
提交了最终的结果,注意这个结果的类型为ReconciledPackage
,但是最终会调用如下代码
// pkg表示第一次扫描的结果
// oldPkg在首次开机扫描时,为null
// pkgSetting表示再次扫描的结果
// scanFlags表示扫描的参数
// chatty在首次开机扫描时,值为false
// reconciledPkg为再次扫描调整后的结果
commitPackageSettings(pkg, oldPkg, pkgSetting, scanFlags,
(parseFlags & PackageParser.PARSE_CHATTY) != 0 /*chatty*/, reconciledPkg);
参数的注释已经给出,下面我们来看看这个这个提交过程
private void commitPackageSettings(PackageParser.Package pkg,
@Nullable PackageParser.Package oldPkg, PackageSetting pkgSetting,
final @ScanFlags int scanFlags, boolean chatty, ReconciledPackage reconciledPkg) {
final String pkgName = pkg.packageName;
// mCustomResolverComponentName 默认为空
if (mCustomResolverComponentName != null &&
mCustomResolverComponentName.getPackageName().equals(pkg.packageName)) {
setUpCustomResolverActivity(pkg);
}
// 针对framework-res.apk
if (pkg.packageName.equals("android")) {
synchronized (mPackages) {
if ((scanFlags & SCAN_CHECK_ONLY) == 0) {
// Set up information for our fall-back user intent resolution activity.
mPlatformPackage = pkg;
pkg.mVersionCode = mSdkVersion;
pkg.mVersionCodeMajor = 0;
mAndroidApplication = pkg.applicationInfo;
if (!mResolverReplaced) {
mResolveActivity.applicationInfo = mAndroidApplication;
// ResolverActivity 是当启动一个Intent时,有多个Activity匹配时出现的界面
mResolveActivity.name = ResolverActivity.class.getName();
mResolveActivity.packageName = mAndroidApplication.packageName;
mResolveActivity.processName = "system:ui";
mResolveActivity.launchMode = ActivityInfo.LAUNCH_MULTIPLE;
mResolveActivity.documentLaunchMode = ActivityInfo.DOCUMENT_LAUNCH_NEVER;
mResolveActivity.flags = ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS;
mResolveActivity.theme = R.style.Theme_Material_Dialog_Alert;
mResolveActivity.exported = true;
mResolveActivity.enabled = true;
mResolveActivity.resizeMode = ActivityInfo.RESIZE_MODE_RESIZEABLE;
mResolveActivity.configChanges = ActivityInfo.CONFIG_SCREEN_SIZE
| ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE
| ActivityInfo.CONFIG_SCREEN_LAYOUT
| ActivityInfo.CONFIG_ORIENTATION
| ActivityInfo.CONFIG_KEYBOARD
| ActivityInfo.CONFIG_KEYBOARD_HIDDEN;
mResolveInfo.activityInfo = mResolveActivity;
mResolveInfo.priority = 0;
mResolveInfo.preferredOrder = 0;
mResolveInfo.match = 0;
mResolveComponentName = new ComponentName(
mAndroidApplication.packageName, mResolveActivity.name);
}
}
}
}
ArrayList<PackageParser.Package> clientLibPkgs = null;
// writer
synchronized (mPackages) {
if (!ArrayUtils.isEmpty(reconciledPkg.allowedSharedLibraryInfos)) {
// 用 mSharedLibraries 和 mStaticLibsByDeclaringPackage 保存<library>和<static-library>信息
for (SharedLibraryInfo info : reconciledPkg.allowedSharedLibraryInfos) {
commitSharedLibraryInfoLocked(info);
}
final Map<String, PackageParser.Package> combinedPackages =
reconciledPkg.getCombinedPackages();
try {
// Shared libraries for the package need to be updated.
// 用pkg.usesLibraryFiles和pkg.usesLibraryInfos保存<uses-library>和<uses-static-library>信息
updateSharedLibrariesLocked(pkg, null, combinedPackages);
} catch (PackageManagerException e) {
Slog.e(TAG, "updateSharedLibrariesLPr failed: ", e);
}
// Update all applications that use this library. Skip when booting
// since this will be done after all packages are scaned.
if ((scanFlags & SCAN_BOOTING) == 0) {
clientLibPkgs = updateAllSharedLibrariesLocked(pkg, combinedPackages);
}
}
}
// ...
// writer
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "updateSettings");
synchronized (mPackages) {
// 用Settings中的mPackage保存
mSettings.insertPackageSettingLPw(pkgSetting, pkg);
// 保存到PKMS的mPackages数据结构中
mPackages.put(pkg.applicationInfo.packageName, pkg);
KeySetManagerService ksms = mSettings.mKeySetManagerService;
// 这个好像是保存public key
ksms.addScannedPackageLPw(pkg);
// ComponentResolver保存了四在组件的信息,以用于提供匹配
mComponentResolver.addAllComponents(pkg, chatty);
if ((scanFlags & SCAN_AS_INSTANT_APP) != 0) {
// ...
} else {
// PermissionManagerService中用PermissionSetting保存<permission-group>信息
mPermissionManager.addAllPermissionGroups(pkg, chatty);
}
if ((scanFlags & SCAN_AS_INSTANT_APP) != 0) {
// ...
} else {
// PermissionManagerService用PermissionSetting保存<permission>和<permission-tree>信息
mPermissionManager.addAllPermissions(pkg, chatty);
}
// <instrumentation>信息
int collectionSize = pkg.instrumentation.size();
StringBuilder r = null;
int i;
for (i = 0; i < collectionSize; i++) {
PackageParser.Instrumentation a = pkg.instrumentation.get(i);
a.info.packageName = pkg.applicationInfo.packageName;
// ...
a.info.secondaryNativeLibraryDir = pkg.applicationInfo.secondaryNativeLibraryDir;
// mInstrumentation保存了<instrumentation>
mInstrumentation.put(a.getComponentName(), a);
}
if (pkg.protectedBroadcasts != null) {
collectionSize = pkg.protectedBroadcasts.size();
synchronized (mProtectedBroadcasts) {
for (i = 0; i < collectionSize; i++) {
// 保护广播,全部保存到PKMS的mProtectedBroadcasts中
mProtectedBroadcasts.add(pkg.protectedBroadcasts.get(i));
}
}
}
// ...
}
}
现在总结下,数据的保存地方
- APK再次扫描的结果PackageSetting,保存到了Settings.mPackages中,也保存到PackageManagerService的mPackages中。
- 如果APK是一个库,它被保存到了PackageManagerService.mSharedLibraries和PackageManagerService.mStaticLibsByDeclaringPackage中。
- 四大组件的信息,保存到了ComponentResolver.mActivities, ComponentResolver.mProviders, ComponentResolver.mReceivers, ComponentResolver.mServices中。
- APK的权限(<permission>, <permission-tree>, <permission-group>)保存到了PermissionManagerService.mSettings中,具体为PermissionSetting.permissions、PermissionSetting.permissionGroups、PermissionSetting.permissionTrees。
- APK声明的<instrumentation>保存到了PackageManagerService.mInsturmentations中。
- 保护广播保存到了PackageManagerService.mProtectedBroadcasts中。