Android 启动时应用的安装解析过程《二》
上一篇内容说到InitAppsHelper这个类的initSystemApps函数,只说了一下几个重要参数的来源还没展开,这里继续,有兴趣的可以看链接: Android 启动时应用的安装解析过程《一》
一、系统应用的扫描安装
/**
* Install apps from system dirs.
*/
@GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
public OverlayConfig initSystemApps(PackageParser2 packageParser,
WatchedArrayMap<String, PackageSetting> packageSettings,
int[] userIds, long startTime) {
// Prepare apex package info before scanning APKs, this information is needed when
// scanning apk in apex.
// 扫描系统中apex信息,为扫描APK做准备,apex是系统中一种新的模块化组件
final List<ApexManager.ScanResult> apexScanResults = scanApexPackagesTraced(packageParser);
mApexManager.notifyScanResult(apexScanResults);
// 扫描系统目录中的apk
scanSystemDirs(packageParser, mExecutorService);
// Parse overlay configuration files to set default enable state, mutability, and
// priority of system overlays.
final ArrayMap<String, File> apkInApexPreInstalledPaths = new ArrayMap<>();
for (ApexManager.ActiveApexInfo apexInfo : mApexManager.getActiveApexInfos()) {
final String apexPackageName = mApexManager.getActivePackageNameForApexModuleName(
apexInfo.apexModuleName);
for (String packageName : mApexManager.getApksInApex(apexPackageName)) {
apkInApexPreInstalledPaths.put(packageName, apexInfo.preInstalledApexPath);
}
}
// 解析系统目录的overlay并把它使能
final OverlayConfig overlayConfig = OverlayConfig.initializeSystemInstance(
consumer -> mPm.forEachPackageState(mPm.snapshotComputer(),
packageState -> {
var pkg = packageState.getPkg();
if (pkg != null) {
consumer.accept(pkg, packageState.isSystem(),
apkInApexPreInstalledPaths.get(pkg.getPackageName()));
}
}));
// do this first before mucking with mPackages for the "expecting better" case
// 做一些优化,比如禁用一些需要禁用的apk,删除掉已经被删除的应用缓存
updateStubSystemAppsList(mStubSystemApps);
mInstallPackageHelper.prepareSystemPackageCleanUp(packageSettings,
mPossiblyDeletedUpdatedSystemApps, mExpectingBetter, userIds);
logSystemAppsScanningTime(startTime);
return overlayConfig;
}
1.扫描系统目录
private void scanSystemDirs(PackageParser2 packageParser, ExecutorService executorService) {
File frameworkDir = new File(Environment.getRootDirectory(), "framework");
// Collect vendor/product/system_ext overlay packages. (Do this before scanning
// any apps.)
// For security and version matching reason, only consider overlay packages if they
// reside in the right directory.
for (int i = mDirsToScanAsSystem.size() - 1; i >= 0; i--) {
final ScanPartition partition = mDirsToScanAsSystem.get(i);
if (partition.getOverlayFolder() == null) {
continue;
}
// 扫描系统分区的overlay 文件夹
scanDirTracedLI(partition.getOverlayFolder(),
mSystemParseFlags, mSystemScanFlags | partition.scanFlag,
packageParser, executorService, partition.apexInfo);
}
// 扫描system/framework文件夹
scanDirTracedLI(frameworkDir,
mSystemParseFlags, mSystemScanFlags | SCAN_NO_DEX | SCAN_AS_PRIVILEGED,
packageParser, executorService, null);
if (!mPm.mPackages.containsKey("android")) {
throw new IllegalStateException(
"Failed to load frameworks package; check log for warnings");
}
for (int i = 0, size = mDirsToScanAsSystem.size(); i < size; i++) {
final ScanPartition partition = mDirsToScanAsSystem.get(i);
if (partition.getPrivAppFolder() != null) {
// 扫描系统分区的priv-app文件夹
scanDirTracedLI(partition.getPrivAppFolder(),
mSystemParseFlags,
mSystemScanFlags | SCAN_AS_PRIVILEGED | partition.scanFlag,
packageParser, executorService, partition.apexInfo);
}
// 扫描系统分区的app文件夹
scanDirTracedLI(partition.getAppFolder(),
mSystemParseFlags, mSystemScanFlags | partition.scanFlag,
packageParser, executorService, partition.apexInfo);
}
}
上面可以看到,这个函数里面将系统分区的overlay,app,还有jar包都扫描了个遍,然后我们看下到底这个scanDirTracedLI函数里面做了哪些事情
private void scanDirTracedLI(File scanDir, int parseFlags, int scanFlags,
PackageParser2 packageParser, ExecutorService executorService,
@Nullable ApexManager.ActiveApexInfo apexInfo) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanDir [" + scanDir.getAbsolutePath() + "]");
try {
if ((scanFlags & SCAN_AS_APK_IN_APEX) != 0) {
// when scanning apk in apexes, we want to check the maxSdkVersion
parseFlags |= PARSE_APK_IN_APEX;
}
// 调用到了InstallPackageHelper的installPackagesFromDir
mInstallPackageHelper.installPackagesFromDir(scanDir, parseFlags,
scanFlags, packageParser, executorService, apexInfo);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}
InstallPackageHelper
public void installPackagesFromDir(File scanDir, int parseFlags,
int scanFlags, PackageParser2 packageParser, ExecutorService executorService,
@Nullable ApexManager.ActiveApexInfo apexInfo) {
//首先将所有的文件列出来
final File[] files = scanDir.listFiles();
if (ArrayUtils.isEmpty(files)) {
Log.d(TAG, "No files in app dir " + scanDir);
return;
}
if (DEBUG_PACKAGE_SCANNING) {
Log.d(TAG, "Scanning app dir " + scanDir + " scanFlags=" + scanFlags
+ " flags=0x" + Integer.toHexString(parseFlags));
}
// 这个又是个专门解析包的类
ParallelPackageParser parallelPackageParser =
new ParallelPackageParser(packageParser, executorService);
// Submit files for parsing in parallel
int fileCount = 0;
for (File file : files) {
// 这里判断是不是包:是apk文件或者是文件夹并且不是stageName,这个stageName是指
// vmdl*.tmp,smdl*.tmp,smdl2tmp这些类的文件
final boolean isPackage = (isApkFile(file) || file.isDirectory())
&& !PackageInstallerService.isStageName(file.getName());
if (!isPackage) {
// Ignore entries which are not packages
continue;
}
// 这里如果携带了SCAN_DROP_CACHE的flag会清除缓存
if ((scanFlags & SCAN_DROP_CACHE) != 0) {
final PackageCacher cacher = new PackageCacher(mPm.getCacheDir(),
mPm.mPackageParserCallback);
Log.w(TAG, "Dropping cache of " + file.getAbsolutePath());
cacher.cleanCachedResult(file);
}
// 这里提交给解析器解析
parallelPackageParser.submit(file, parseFlags);
fileCount++;
}
// Process results one by one
for (; fileCount > 0; fileCount--) {
ParallelPackageParser.ParseResult parseResult = parallelPackageParser.take();
Throwable throwable = parseResult.throwable;
int errorCode = PackageManager.INSTALL_SUCCEEDED;
String errorMsg = null;
// 如果没有异常就表示解析成功另外两个条件属于失败了
if (throwable == null) {
try {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "addForInitLI");
// 这里初始化
addForInitLI(parseResult.parsedPackage, parseFlags, scanFlags,
new UserHandle(UserHandle.USER_SYSTEM), apexInfo);
} catch (PackageManagerException e) {
errorCode = e.error;
errorMsg = "Failed to scan " + parseResult.scanFile + ": " + e.getMessage();
Slog.w(TAG, errorMsg);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
} else if (throwable instanceof PackageManagerException) {
PackageManagerException e = (PackageManagerException) throwable;
errorCode = e.error;
errorMsg = "Failed to parse " + parseResult.scanFile + ": " + e.getMessage();
Slog.w(TAG, errorMsg);
} else {
throw new IllegalStateException("Unexpected exception occurred while parsing "
+ parseResult.scanFile, throwable);
}
// apex的安装单独报给ApexManager
if ((scanFlags & SCAN_AS_APK_IN_APEX) != 0 && errorCode != INSTALL_SUCCEEDED) {
mApexManager.reportErrorWithApkInApex(scanDir.getAbsolutePath(), errorMsg);
}
// Delete invalid userdata apps
// 没安装成功的会在这里清除
if ((scanFlags & SCAN_AS_SYSTEM) == 0
&& errorCode != PackageManager.INSTALL_SUCCEEDED) {
logCriticalInfo(Log.WARN,
"Deleting invalid package at " + parseResult.scanFile);
mRemovePackageHelper.removeCodePath(parseResult.scanFile);
}
}
}
从上面可以看出主要就做了两件事情,把文件夹下面的包一一解析,并做初始化,看看如何解析的先:
ParallelPackageParser submit之后
2.开始解析
/*---------------------------ParallelPackageParser start ---------------------------*/
public void submit(File scanFile, int parseFlags) {
// 直接开线程开始解析
mExecutorService.submit(() -> {
ParseResult pr = new ParseResult();
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parallel parsePackage [" + scanFile + "]");
try {
pr.scanFile = scanFile;
// 解析
pr.parsedPackage = parsePackage(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();
// Propagate result to callers of take().
// This is helpful to prevent main thread from getting stuck waiting on
// ParallelPackageParser to finish in case of interruption
mInterruptedInThread = Thread.currentThread().getName();
}
});
}
protected ParsedPackage parsePackage(File scanFile, int parseFlags)
throws PackageManagerException {
try {
// 发现这里又交给PackageParser2去做了
return mPackageParser.parsePackage(scanFile, parseFlags, true);
} catch (PackageParserException e) {
throw new PackageManagerException(e.error, e.getMessage(), e);
}
}
/*---------------------------ParallelPackageParser end---------------------------*/
/*---------------------------PackageParser2 start---------------------------*/
public ParsedPackage parsePackage(File packageFile, int flags, boolean useCaches)
throws PackageParserException {
// 列出所有的文件
var files = packageFile.listFiles();
// Apk directory is directly nested under the current directory
if (ArrayUtils.size(files) == 1 && files[0].isDirectory()) {
packageFile = files[0];
}
if (useCaches && mCacher != null) {
// 这里会去检查有没有缓存,缓存是存在data/system/目录下如果有缓存则不再解析
ParsedPackage parsed = mCacher.getCachedResult(packageFile, flags);
if (parsed != null) {
return parsed;
}
}
long parseTime = LOG_PARSE_TIMINGS ? SystemClock.uptimeMillis() : 0;
ParseInput input = mSharedResult.get().reset();
// 这里又交给了ParsingPackageUtils这个类去干活了
ParseResult<ParsingPackage> result = mParsingUtils.parsePackage(input, packageFile, flags);
if (result.isError()) {
throw new PackageParserException(result.getErrorCode(), result.getErrorMessage(),
result.getException());
}
ParsedPackage parsed = (ParsedPackage) result.getResult().hideAsParsed();
long cacheTime = LOG_PARSE_TIMINGS ? SystemClock.uptimeMillis() : 0;
// 这里解析好的结果保存到缓存,也就是只要APK被解析过一次就会存在缓存了
if (mCacher != null) {
mCacher.cacheResult(packageFile, flags, parsed);
}
if (LOG_PARSE_TIMINGS) {
parseTime = cacheTime - parseTime;
cacheTime = SystemClock.uptimeMillis() - cacheTime;
if (parseTime + cacheTime > LOG_PARSE_TIMINGS_THRESHOLD_MS) {
Slog.i(TAG, "Parse times for '" + packageFile + "': parse=" + parseTime
+ "ms, update_cache=" + cacheTime + " ms");
}
}
return parsed;
}
/*---------------------------PackageParser2 end---------------------------*/
/*---------------------------ParsingPackageUtils start---------------------------*/
public ParseResult<ParsingPackage> parsePackage(ParseInput input, File packageFile, int flags) {
// 如果是文件夹则走if 文件则走else,这里为什么会这样分,那/system/app目录来说,
// 系统在预置APP的时候可能是先在该目录下新建一层目录再放APK,也有可能直接放置一个APK在该目录下
if (packageFile.isDirectory()) {
return parseClusterPackage(input, packageFile, flags);
} else {
return parseMonolithicPackage(input, packageFile, flags);
}
}
// 先看下解析目录parseClusterPackage
private ParseResult<ParsingPackage> parseClusterPackage(ParseInput input, File packageDir,
int flags) {
int liteParseFlags = 0;
// 这里判断是不是在解析APEX的时候解析APK
if ((flags & PARSE_APK_IN_APEX) != 0) {
liteParseFlags |= PARSE_APK_IN_APEX;
}
// 轻量解析APK,这里会解析APK的签名信息,AndroidManifest.xml中除了Activity,Service,ContentProvider及权限以外的信息,
// 顺便会做一些校验比如SDK版本,签名校验,系统目录的APK 可以跳过签名校验(签名校验从v4到v1逐一校验),这一部分设计是为了节省时间
final ParseResult<PackageLite> liteResult =
ApkLiteParseUtils.parseClusterPackageLite(input, packageDir, liteParseFlags);
// 没有错误说明各种校验通过了
if (liteResult.isError()) {
return input.error(liteResult);
}
final PackageLite lite = liteResult.getResult();
// Build the split dependency tree.
SparseArray<int[]> splitDependencies = null;
final SplitAssetLoader assetLoader;
// 如果是APKS 格式的这里会创建资源依赖树
if (lite.isIsolatedSplits() && !ArrayUtils.isEmpty(lite.getSplitNames())) {
try {
splitDependencies = SplitAssetDependencyLoader.createDependenciesFromPackage(lite);
assetLoader = new SplitAssetDependencyLoader(lite, splitDependencies, flags);
} catch (SplitAssetDependencyLoader.IllegalDependencyException e) {
return input.error(INSTALL_PARSE_FAILED_BAD_MANIFEST, e.getMessage());
}
} else {
assetLoader = new DefaultSplitAssetLoader(lite, flags);
}
try {
final File baseApk = new File(lite.getBaseApkPath());
boolean shouldSkipComponents = lite.isIsSdkLibrary() && disallowSdkLibsToBeApps();
// 这里解析APK
final ParseResult<ParsingPackage> result = parseBaseApk(input, baseApk,
lite.getPath(), assetLoader, flags, shouldSkipComponents);
if (result.isError()) {
return input.error(result);
}
ParsingPackage pkg = result.getResult();
// 如果是APKS格式进行另外的解析
if (!ArrayUtils.isEmpty(lite.getSplitNames())) {
pkg.asSplit(
lite.getSplitNames(),
lite.getSplitApkPaths(),
lite.getSplitRevisionCodes(),
splitDependencies
);
final int num = lite.getSplitNames().length;
for (int i = 0; i < num; i++) {
final AssetManager splitAssets = assetLoader.getSplitAssetManager(i);
final ParseResult<ParsingPackage> split =
parseSplitApk(input, pkg, i, splitAssets, flags);
if (split.isError()) {
return input.error(split);
}
}
}
pkg.set32BitAbiPreferred(lite.isUse32bitAbi());
return input.success(pkg);
} catch (IllegalArgumentException e) {
return input.error(e.getCause() instanceof IOException ? INSTALL_FAILED_INVALID_APK
: INSTALL_PARSE_FAILED_NOT_APK, e.getMessage(), e);
} finally {
IoUtils.closeQuietly(assetLoader);
}
}
/*******parseBaseApk******/
private ParseResult<ParsingPackage> parseBaseApk(ParseInput input, File apkFile,
String codePath, SplitAssetLoader assetLoader, int flags,
boolean shouldSkipComponents) {
......
// 又开始解析AndroidManifest.xml
try (XmlResourceParser parser = assets.openXmlResourceParser(cookie,
ANDROID_MANIFEST_FILENAME)) {
final Resources res = new Resources(assets, mDisplayMetrics, null);
// 这里又一个重载解析APK
ParseResult<ParsingPackage> result = parseBaseApk(input, apkPath, codePath, res,
parser, flags, shouldSkipComponents);
if (result.isError()) {
return input.error(result.getErrorCode(),
apkPath + " (at " + parser.getPositionDescription() + "): "
+ result.getErrorMessage());
}
final ParsingPackage pkg = result.getResult();
if (assets.containsAllocatedTable()) {
final ParseResult<?> deferResult = input.deferError(
"Targeting R+ (version " + Build.VERSION_CODES.R + " and above) requires"
+ " the resources.arsc of installed APKs to be stored uncompressed"
+ " and aligned on a 4-byte boundary",
DeferredError.RESOURCES_ARSC_COMPRESSED);
if (deferResult.isError()) {
return input.error(INSTALL_PARSE_FAILED_RESOURCES_ARSC_COMPRESSED,
deferResult.getErrorMessage());
}
}
ApkAssets apkAssets = assetLoader.getBaseApkAssets();
boolean definesOverlayable = false;
try {
// 查询该应用是否支持overlay,一般定义了相应的overlayable
definesOverlayable = apkAssets.definesOverlayable();
} catch (IOException ignored) {
// Will fail if there's no packages in the ApkAssets, which can be treated as false
}
// 如果可以这里会查找到可以被overlay的域
if (definesOverlayable) {
SparseArray<String> packageNames = assets.getAssignedPackageIdentifiers();
int size = packageNames.size();
for (int index = 0; index < size; index++) {
String packageName = packageNames.valueAt(index);
Map<String, String> overlayableToActor = assets.getOverlayableMap(packageName);
if (overlayableToActor != null && !overlayableToActor.isEmpty()) {
for (String overlayable : overlayableToActor.keySet()) {
pkg.addOverlayable(overlayable, overlayableToActor.get(overlayable));
}
}
}
}
pkg.setVolumeUuid(volumeUuid);
// 这里又是查询验证签名信息
if ((flags & PARSE_COLLECT_CERTIFICATES) != 0) {
final ParseResult<SigningDetails> ret =
getSigningDetails(input, pkg, false /*skipVerify*/);
if (ret.isError()) {
return input.error(ret);
}
pkg.setSigningDetails(ret.getResult());
} else {
pkg.setSigningDetails(SigningDetails.UNKNOWN);
}
if (Flags.aslInApkAppMetadataSource()) {
try (InputStream in = assets.open(APP_METADATA_FILE_NAME)) {
pkg.setAppMetadataFileInApk(true);
} catch (Exception e) { }
}
return input.success(pkg);
} catch (Exception e) {
return input.error(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
"Failed to read manifest from " + apkPath, e);
}
}
/*******parseBaseApk 重载******/
private ParseResult<ParsingPackage> parseBaseApk(ParseInput input, String apkPath,
String codePath, Resources res, XmlResourceParser parser, int flags,
boolean shouldSkipComponents)
throws XmlPullParserException, IOException {
final String splitName;
final String pkgName;
// 轻量解析,包名信息之类的
ParseResult<Pair<String, String>> packageSplitResult =
ApkLiteParseUtils.parsePackageSplitNames(input, parser);
if (packageSplitResult.isError()) {
return input.error(packageSplitResult);
}
Pair<String, String> packageSplit = packageSplitResult.getResult();
pkgName = packageSplit.first;
splitName = packageSplit.second;
if (!TextUtils.isEmpty(splitName)) {
return input.error(
PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME,
"Expected base APK, but found split " + splitName
);
}
final TypedArray manifestArray = res.obtainAttributes(parser, R.styleable.AndroidManifest);
try {
// 是否是core app ,类似系统设置
final boolean isCoreApp = parser.getAttributeBooleanValue(null /*namespace*/,
"coreApp", false);
final ParsingPackage pkg = mCallback.startParsingPackage(
pkgName, apkPath, codePath, manifestArray, isCoreApp);
// 解析AndroidManinfest里面的各个TAG,真正解析的开始
final ParseResult<ParsingPackage> result =
parseBaseApkTags(input, pkg, manifestArray, res, parser, flags,
shouldSkipComponents);
if (result.isError()) {
return result;
}
return input.success(pkg);
} finally {
manifestArray.recycle();
}
}
/**** parseBaseApkTags ****/
private ParseResult<ParsingPackage> parseBaseApkTags(ParseInput input, ParsingPackage pkg,
TypedArray sa, Resources res, XmlResourceParser parser, int flags,
boolean shouldSkipComponents) throws XmlPullParserException, IOException {
// 解析共享uid
ParseResult<ParsingPackage> sharedUserResult = parseSharedUser(input, pkg, sa);
if (sharedUserResult.isError()) {
return sharedUserResult;
}
// 用于标记系统为可更新,通常和动态系统分区及增量更新机制有关。
final boolean updatableSystem = parser.getAttributeBooleanValue(null /*namespace*/,
"updatableSystem", true);
// 用于设备恢复模式下的紧急安装或恢复。
final String emergencyInstaller = parser.getAttributeValue(null /*namespace*/,
"emergencyInstaller");
pkg.setInstallLocation(anInteger(PARSE_DEFAULT_INSTALL_LOCATION,
R.styleable.AndroidManifest_installLocation, sa))
.setTargetSandboxVersion(anInteger(PARSE_DEFAULT_TARGET_SANDBOX,
R.styleable.AndroidManifest_targetSandboxVersion, sa))
/* Set the global "on SD card" flag */
.setExternalStorage((flags & PARSE_EXTERNAL_STORAGE) != 0)
.setUpdatableSystem(updatableSystem)
.setEmergencyInstaller(emergencyInstaller);
boolean foundApp = false;
final int depth = parser.getDepth();
int type;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG
|| parser.getDepth() > depth)) {
if (type != XmlPullParser.START_TAG) {
continue;
}
if (sAconfigFlags.skipCurrentElement(parser)) {
continue;
}
String tagName = parser.getName();
final ParseResult result;
// <application> has special logic, so it's handled outside the general method
// 解析Application TAG配置及四大组件信息
if (TAG_APPLICATION.equals(tagName)) {
if (foundApp) {
if (RIGID_PARSER) {
result = input.error("<manifest> has more than one <application>");
} else {
Slog.w(TAG, "<manifest> has more than one <application>");
result = input.success(null);
}
} else {
foundApp = true;
result = parseBaseApplication(input, pkg, res, parser, flags,
shouldSkipComponents);
}
} else {
// Application 以外的TAG信息
result = parseBaseApkTag(tagName, input, pkg, res, parser, flags);
}
if (result.isError()) {
return input.error(result);
}
}
if (!foundApp && ArrayUtils.size(pkg.getInstrumentations()) == 0) {
ParseResult<?> deferResult = input.deferError(
"<manifest> does not contain an <application> or <instrumentation>",
DeferredError.MISSING_APP_TAG);
if (deferResult.isError()) {
return input.error(deferResult);
}
}
return validateBaseApkTags(input, pkg, flags);
}
后面都是一些解析细节了,不在追述,值得注意的是大部分APK 只会解析一次会将APK 的信息存储在/data/system/package_caches,下面除非apk出现了更新否则不会再重新解析