在上一篇文章中,分析了PKMS的启动过程,在启动过程中有一步是通过 new PackageManagerService 创建一个 PackageManagerService 实例,本文就从SystemServer开始,进一步具体分析下PKMS的构造函数。
本文以android 13代码为例进行分析。
1,在frameworks/base/services/java/com/android/server/SystemServer.java中
private SystemServiceManager mSystemServiceManager; // 在run函数中进行初始化,即 mSystemServiceManager = new SystemServiceManager(mSystemContext);
private void startBootstrapServices(@NonNull TimingsTraceAndSlog t) {
t.traceBegin("StartInstaller");
Installer installer = mSystemServiceManager.startService(Installer.class); // 启动安装器服务,确保在初始化其他服务之前,系统安装器已经完成了必要的初始化工作,比如创建关键目录和设置适当的权限。
t.traceEnd();
IPackageManager iPackageManager;
t.traceBegin("StartPackageManagerService");
try {
// 调用了Watchdog的实例来暂停对当前线程的监控,避免在PackageManagerService启动过程中因为操作时间过长而被系统认为是无响应(ANR)。"packagemanagermain"是这里用于标识线程的字符串。
Watchdog.getInstance().pauseWatchingCurrentThread("packagemanagermain");
// 通过调用PackageManagerService.main(...)方法启动PMS
Pair<PackageManagerService, IPackageManager> pmsPair = PackageManagerService.main(
mSystemContext, installer, domainVerificationService,
mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);
mPackageManagerService = pmsPair.first; // 将Pair对象的first(即PackageManagerService的实例)保存到mPackageManagerService变量中。
iPackageManager = pmsPair.second; // 将Pair对象的second(即IPackageManager的实现)保存到iPackageManager变量中,用于后续跨进程通信。
} finally {
Watchdog.getInstance().resumeWatchingCurrentThread("packagemanagermain"); // 在PMS启动完毕后,恢复对当前线程的监控。
}
t.traceEnd();
}
上面的代码中调用了 PackageManagerService.main 函数。
2,在frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java中
public static Pair<PackageManagerService, IPackageManager> main(Context context,
Installer installer, @NonNull DomainVerificationService domainVerificationService,
boolean factoryTest, boolean onlyCore) {
...
// 创建一个PackageManagerService的实例 m,传入多个参数。
PackageManagerService m = new PackageManagerService(injector, onlyCore, factoryTest,PackagePartitions.FINGERPRINT, Build.IS_ENG, Build.IS_USERDEBUG, Build.VERSION.SDK_INT, Build.VERSION.INCREMENTAL);
...
}
上面的代码中通过 new PackageManagerService 创建了 PKMS对象,下面分析下 PackageManagerService 构造函数。
PackageManagerService 构造函数很复杂,可以根据日志划分为 BOOT_PROGRESS_PMS_START,
BOOT_PROGRESS_PMS_SYSTEM_SCAN_START,
BOOT_PROGRESS_PMS_SCAN_END,
BOOT_PROGRESS_PMS_READY 等阶段。
public PackageManagerService(PackageManagerServiceInjector injector, boolean onlyCore,
boolean factoryTest, final String buildFingerprint, final boolean isEngBuild,
final boolean isUserDebugBuild, final int sdkVersion, final String incrementalVersion) {
...
// 记录系统启动进度事件,并写入事件日志。
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_START, SystemClock.uptimeMillis());
// 通过injector获取系统设置(Settings),并将其赋值给成员变量mSettings。这些设置包括用户偏好和系统配置。
mSettings = injector.getSettings();
mSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
...
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SYSTEM_SCAN_START,startTime);
// 获取缓存目录
mCacheDir = PackageManagerServiceUtils.preparePackageParserCache(
mIsEngBuild, mIsUserDebugBuild, mIncrementalVersion);
final int[] userIds = mUserManager.getUserIds();
// 获取PackageParser2对象
PackageParser2 packageParser = mInjector.getScanningCachingPackageParser();
// 扫描系统app
mOverlayConfig = mInitAppsHelper.initSystemApps(packageParser, packageSettings, userIds,
startTime);
// 系统app以为的app
mInitAppsHelper.initNonSystemApps(packageParser, userIds, startTime); //调用 initNonSystemApps 函数会走到 BOOT_PROGRESS_PMS_DATA_SCAN_START
packageParser.close();
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SCAN_END,SystemClock.uptimeMillis());
mPermissionManager.onStorageVolumeMounted(
StorageManager.UUID_PRIVATE_INTERNAL, mIsUpgrade);
ver.sdkVersion = mSdkVersion;
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_READY,
SystemClock.uptimeMillis());
mRequiredPermissionControllerPackage = getRequiredPermissionControllerLPr(computer);
mSettings.setPermissionControllerVersion(
computer.getPackageInfo(mRequiredPermissionControllerPackage, 0,
UserHandle.USER_SYSTEM).getLongVersionCode());
t.traceBegin("GC");
VMRuntime.getRuntime().requestConcurrentGC();
t.traceEnd();
}
首先看下 BOOT_PROGRESS_PMS_START 阶段。
在 BOOT_PROGRESS_PMS_START 阶段做的基本是基础准备工作,获取了需要的一系列对象和路径,创建了 Settings。
final Settings mSettings;//在PKMS的构造函数中通过 injector.getSettings() 进行赋值,即 mSettings = injector.getSettings();
//通过循环或逐个调用addSharedUserLPw方法,向mSettings(系统设置对象)中添加多个共享用户。每个调用都指定了共享用户的名称、UID(用户标识符)、系统标志和私有特权标志。
mSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID,ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
下面分析下Settings.java类。
3,在frameworks/base/services/core/java/com/android/server/pm/Settings.java中
public final class Settings implements Watchable, Snappable {
Settings(File dataDir, RuntimePermissionsPersistence runtimePermissionsPersistence,
LegacyPermissionDataProvider permissionDataProvider,
@NonNull DomainVerificationManagerInternal domainVerificationManager,
@NonNull Handler handler,
@NonNull PackageManagerTracedLock lock) {
mPackages = new WatchedArrayMap<>(); // 使用WatchedArrayMap初始化,用于存储应用包信息。
mPackagesSnapshot =
new SnapshotCache.Auto<>(mPackages, mPackages, "Settings.mPackages"); // 基于mPackages创建一个快照缓存,用于高效访问。
mKernelMapping = new WatchedArrayMap<>(); // 用于存储内核映射信息及其快照。
mKernelMappingSnapshot =
new SnapshotCache.Auto<>(mKernelMapping, mKernelMapping, "Settings.mKernelMapping");
mInstallerPackages = new WatchedArraySet<>(); // 用于存储安装器包信息及其快照。
mInstallerPackagesSnapshot =
new SnapshotCache.Auto<>(mInstallerPackages, mInstallerPackages,
"Settings.mInstallerPackages");
mKeySetManagerService = new KeySetManagerService(mPackages); // 初始化KeySetManagerService,可能用于管理应用签名密钥集。
mHandler = handler;
mLock = lock;
mAppIds = new AppIdSettingMap(); // 使用AppIdSettingMap初始化,用于存储应用ID设置。
mPermissions = new LegacyPermissionSettings(lock); // 使用LegacyPermissionSettings初始化,管理旧版权限设置。
mRuntimePermissionsPersistence = new RuntimePermissionPersistence(
runtimePermissionsPersistence, new Consumer<Integer>() {
@Override
public void accept(Integer userId) {
mRuntimePermissionsPersistence.writeStateForUser(userId, mPermissionDataProvider,
mPackages, mSharedUsers, mHandler, mLock, /*sync=*/false);
}
}); // mRuntimePermissionsPersistence 初始化运行时权限持久化对象,包含一个用户ID的回调,用于写入用户状态。
mPermissionDataProvider = permissionDataProvider;
mSystemDir = new File(dataDir, "system"); // 初始化系统目录(mSystemDir),并创建目录和设置权限。
mSystemDir.mkdirs();
FileUtils.setPermissions(mSystemDir.toString(),
FileUtils.S_IRWXU|FileUtils.S_IRWXG
|FileUtils.S_IROTH|FileUtils.S_IXOTH,
-1, -1);
// 初始化设置文件(mSettingsFilename、mBackupSettingsFilename、mPackageListFilename),并设置mPackageListFilename的权限。
mSettingsFilename = new File(mSystemDir, "packages.xml");
mBackupSettingsFilename = new File(mSystemDir, "packages-backup.xml");
mPackageListFilename = new File(mSystemDir, "packages.list");
FileUtils.setPermissions(mPackageListFilename, 0640, SYSTEM_UID, PACKAGE_INFO_GID);
// 尝试设置内核映射文件目录(mKernelMappingFilename),如果/config/sdcardfs目录存在。
final File kernelDir = new File("/config/sdcardfs");
mKernelMappingFilename = kernelDir.exists() ? kernelDir : null;
// Deprecated: Needed for migration
mStoppedPackagesFilename = new File(mSystemDir, "packages-stopped.xml");
mBackupStoppedPackagesFilename = new File(mSystemDir, "packages-stopped-backup.xml");
mDomainVerificationManager = domainVerificationManager; // 初始化为传入的域验证管理器。
registerObservers(); // 注册观察者以监听设置的变化。
Watchable.verifyWatchedAttributes(this, mObserver);
mSnapshot = makeCache(); // 调用makeCache()方法创建快照缓存。
}
}
Settings中保存了安装包信息,主要文件就是 packages.xml,packages-backup.xml,packages.list.
packages.xml文件:PKMS扫描完目标文件夹后会创建该文件,当系统进行程序安装、卸载、更新等操作时均会更新该文件,该文件保存了系统中与package相关的一些信息。
packages.list文件:描述系统中存在的所有非系统自带的APK信息,当这些程序有变动时,PKMS会更新该文件。
接着看下 BOOT_PROGRESS_PMS_SYSTEM_SCAN_START 阶段。
在 BOOT_PROGRESS_PMS_SYSTEM_SCAN_START 阶段重要的是通过 mInitAppsHelper 获取系统apk和其它apk信息。
private final InitAppsHelper mInitAppsHelper; //在构造函数中进行初始化,即 mInitAppsHelper = new InitAppsHelper(this, mApexManager, mInstallPackageHelper,mInjector.getSystemPartitions());
//获取PackageParser2对象
PackageParser2 packageParser = mInjector.getScanningCachingPackageParser();
//扫描系统app
mOverlayConfig = mInitAppsHelper.initSystemApps(packageParser, packageSettings, userIds,
startTime);
//系统app以为的app
mInitAppsHelper.initNonSystemApps(packageParser, userIds, startTime);
packageParser.close();
上面的代码中调用了 InitAppsHelper 类的 initSystemApps 和 initNonSystemApps 函数。
首先分析下 initSystemApps 函数。
4,在frameworks/base/services/core/java/com/android/server/pm/InitAppsHelper.java中
/**
* Install apps from system dirs.
*/
@GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
public OverlayConfig initSystemApps(PackageParser2 packageParser,
WatchedArrayMap<String, PackageSetting> packageSettings,
int[] userIds, long startTime) { // 方法的参数定义。PackageParser2 packageParser用于解析APK文件,WatchedArrayMap<String, PackageSetting> packageSettings存储应用的设置信息,int[] userIds是用户ID数组,long startTime记录方法开始执行的时间,用于后续计算执行时间。
// Prepare apex package info before scanning APKs, this information is needed when
// scanning apk in apex.
// 在扫描APK文件之前,准备Apex包的信息。Apex是Android 10引入的一种模块化系统,允许将系统服务和应用以模块化的形式打包。这里调用mApexManager.scanApexPackagesTraced方法来扫描Apex包,这是因为在扫描Apex中的APK时需要这些信息。
mApexManager.scanApexPackagesTraced(packageParser, mExecutorService);
// 调用scanSystemDirs方法来扫描系统目录中的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<>(); //创建一个ArrayMap<String, File>,用于存储APK在Apex中预安装的路径信息。
// 遍历所有激活的Apex信息,对于每个Apex模块,获取其中包含的APK包名,并将这些APK的包名和对应的Apex预安装路径存储到apkInApexPreInstalledPaths中。
for (ApexManager.ActiveApexInfo apexInfo : mApexManager.getActiveApexInfos()) {
for (String packageName : mApexManager.getApksInApex(apexInfo.apexModuleName)) {
apkInApexPreInstalledPaths.put(packageName, apexInfo.preInstalledApexPath);
}
}
// 初始化OverlayConfig实例。这里通过调用OverlayConfig.initializeSystemInstance方法,并传入一个lambda表达式,该表达式遍历所有包,使用包管理器(mPm)为每个包调用一个消费者函数,该函数接受包信息、是否为系统包以及APK在Apex中的预安装路径。
final OverlayConfig overlayConfig = OverlayConfig.initializeSystemInstance(
consumer -> mPm.forEachPackage(mPm.snapshotComputer(),
pkg -> consumer.accept(pkg, pkg.isSystem(),
apkInApexPreInstalledPaths.get(pkg.getPackageName()))));
// 如果不是仅处理核心应用(mIsOnlyCoreApps为false),则首先更新存根系统应用列表(updateStubSystemAppsList),然后准备系统应用的清理工作(prepareSystemPackageCleanUp),这可能包括删除或更新一些系统应用。
if (!mIsOnlyCoreApps) {
// do this first before mucking with mPackages for the "expecting better" case
updateStubSystemAppsList(mStubSystemApps);
mInstallPackageHelper.prepareSystemPackageCleanUp(packageSettings,
mPossiblyDeletedUpdatedSystemApps, mExpectingBetter, userIds);
}
logSystemAppsScanningTime(startTime);// 使用开始时间startTime记录系统应用扫描的时间,并打印日志。
return overlayConfig; // 返回OverlayConfig实例。
}
上面的 initSystemApps 方法的参数定义如下:
PackageParser2 packageParser 用于解析APK文件,
WatchedArrayMap<String, PackageSetting> packageSettings 存储应用的设置信息,
int[] userIds 是用户ID数组,long startTime记录方法开始执行的时间,用于后续计算执行时间。
上面的代码调用 scanSystemDirs 扫描系统目录中的APK文件。
/**
* First part of init dir scanning
*/
@GuardedBy({"mPm.mInstallLock", "mPm.mLock"}) // @GuardedBy({"mPm.mInstallLock", "mPm.mLock"})注解表明,这个方法在执行时必须持有mPm.mInstallLock和mPm.mLock这两个锁的至少一个,以确保线程安全和数据一致性。
private void scanSystemDirs(PackageParser2 packageParser, ExecutorService executorService) { // 参数:PackageParser2实例用于解析APK文件,ExecutorService用于异步执行任务。
// 创建了一个指向系统框架目录的File对象。
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.
// 循环(从mDirsToScanAsSystem列表的末尾开始)处理的是被视为系统级的覆盖包(overlay packages),这些包可能来自vendor、product或system_ext分区。这些覆盖包在扫描任何应用之前被处理,以确保安全性和版本匹配。
for (int i = mDirsToScanAsSystem.size() - 1; i >= 0; i--) {
final ScanPartition partition = mDirsToScanAsSystem.get(i);
if (partition.getOverlayFolder() == null) {
continue;
}
scanDirTracedLI(partition.getOverlayFolder(), /* frameworkSplits= */ null,
mSystemParseFlags, mSystemScanFlags | partition.scanFlag,
packageParser, executorService);
}
// 使用 scanDirTracedLI 方法扫描框架目录,特别指定了SCAN_NO_DEX和SCAN_AS_PRIVILEGED标志,意味着这些APK不包含DEX文件(可能已经被预先优化),并且被视为特权应用。
scanDirTracedLI(frameworkDir, null,
mSystemParseFlags,
mSystemScanFlags | SCAN_NO_DEX | SCAN_AS_PRIVILEGED,
packageParser, executorService);
// 如果系统包管理器(mPm)的mPackages映射中不包含键为"android"的条目,则抛出IllegalStateException异常。这确保了Android框架包被成功加载。
if (!mPm.mPackages.containsKey("android")) {
throw new IllegalStateException(
"Failed to load frameworks package; check log for warnings");
}
// 再次遍历 mDirsToScanAsSystem 列表,这次处理的是私有应用目录和普通应用目录。对于私有应用目录,使用SCAN_AS_PRIVILEGED标志;对于所有目录,都应用了分区特定的扫描标志。
for (int i = 0, size = mDirsToScanAsSystem.size(); i < size; i++) {
final ScanPartition partition = mDirsToScanAsSystem.get(i);
if (partition.getPrivAppFolder() != null) {
scanDirTracedLI(partition.getPrivAppFolder(), /* frameworkSplits= */ null,
mSystemParseFlags,
mSystemScanFlags | SCAN_AS_PRIVILEGED | partition.scanFlag,
packageParser, executorService);
}
scanDirTracedLI(partition.getAppFolder(), /* frameworkSplits= */ null,
mSystemParseFlags, mSystemScanFlags | partition.scanFlag,
packageParser, executorService);
}
}
上面的函数 scanSystemDirs 扫描系统目录以加载和解析系统级别的APK(Android应用程序包)。
这个方法是在Android的包管理器(PackageManager)的上下文中执行的,用于在系统启动时或特定情况下加载和验证系统应用。
scanSystemDirs方法是Android系统启动过程中的一个关键步骤,负责加载和解析系统级别的APK,包括框架包、覆盖包、私有应用和普通应用。
上面的代码中调用了 scanDirTracedLI 函数扫描框架目录。
private final InstallPackageHelper mInstallPackageHelper; // mInstallPackageHelper 通过 InitAppsHelper 构造函数通过传入的参数进行赋值。
@GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
private void scanDirTracedLI(File scanDir, List<File> frameworkSplits,
int parseFlags, int scanFlags,
PackageParser2 packageParser, ExecutorService executorService) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanDir [" + scanDir.getAbsolutePath() + "]");
try {
// 如果scanFlags中包含SCAN_AS_APK_IN_APEX标志(表示正在扫描位于APEX模块中的APK文件),则将PARSE_APK_IN_APEX标志添加到parseFlags中。这是为了在处理APEX模块中的APK文件时,能够检查其maxSdkVersion属性。
if ((scanFlags & SCAN_AS_APK_IN_APEX) != 0) {
// when scanning apk in apexes, we want to check the maxSdkVersion
parseFlags |= PARSE_APK_IN_APEX;
}
// 调用了mInstallPackageHelper的installPackagesFromDir方法,它是实际执行APK文件安装和解析的地方。该方法接收扫描目录、框架拆分文件列表、解析标志、扫描标志、PackageParser2实例和ExecutorService作为参数。
mInstallPackageHelper.installPackagesFromDir(scanDir, frameworkSplits, parseFlags,
scanFlags, packageParser, executorService);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}
上面的函数 scanDirTracedLI 扫描指定的目录(scanDir),以加载和解析其中的APK(Android应用程序包)文件,它通过适当的锁机制确保线程安全,使用性能追踪帮助开发者优化系统性能,并支持对APEX模块中APK文件的特殊处理。
上面的函数 scanDirTracedLI 中通过 mInstallPackageHelper 调用 installPackagesFromDir 函数。
5,在frameworks/base/services/core/java/com/android/server/pm/InstallPackageHelper.java中
@GuardedBy({"mPm.mInstallLock", "mPm.mLock"}) // 注解表明,在调用此方法时,必须持有mPm.mInstallLock或mPm.mLock这两个锁之一,以确保线程安全。
public void installPackagesFromDir(File scanDir, List<File> frameworkSplits, int parseFlags,
int scanFlags, PackageParser2 packageParser,
ExecutorService executorService) {
// 获取指定目录下的所有文件和子目录。
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实例,用于并行解析APK文件。该实例接收PackageParser2、ExecutorService和框架拆分文件列表作为参数。
ParallelPackageParser parallelPackageParser =
new ParallelPackageParser(packageParser, executorService, frameworkSplits);
// Submit files for parsing in parallel
int fileCount = 0;
for (File file : files) {
final boolean isPackage = (isApkFile(file) || file.isDirectory())
&& !PackageInstallerService.isStageName(file.getName());
if (!isPackage) {
// Ignore entries which are not packages
continue;
}
// 如果满足条件,并且设置了SCAN_DROP_CACHE标志,则删除该文件的缓存结果。
if ((scanFlags & SCAN_DROP_CACHE) != 0) {
final PackageCacher cacher = new PackageCacher(mPm.getCacheDir());
Log.w(TAG, "Dropping cache of " + file.getAbsolutePath());
cacher.cleanCachedResult(file);
}
// 将文件提交给ParallelPackageParser进行解析。
parallelPackageParser.submit(file, parseFlags);
fileCount++;
}
// Process results one by one
for (; fileCount > 0; fileCount--) { // 使用循环从ParallelPackageParser中获取解析结果。
ParallelPackageParser.ParseResult parseResult = parallelPackageParser.take();
Throwable throwable = parseResult.throwable;
int errorCode = PackageManager.INSTALL_SUCCEEDED;
String errorMsg = null;
if (throwable == null) { // 如果解析成功(没有抛出异常)
// TODO(b/194319951): move lower in the scan chain
// Static shared libraries have synthetic package names
if (parseResult.parsedPackage.isStaticSharedLibrary()) { // 检查解析的包是否为静态共享库,并相应地重命名。
PackageManagerService.renameStaticSharedLibraryPackage(
parseResult.parsedPackage);
}
try {
addForInitLI(parseResult.parsedPackage, parseFlags, scanFlags,
null);
} catch (PackageManagerException e) {
errorCode = e.error;
errorMsg = "Failed to scan " + parseResult.scanFile + ": " + e.getMessage();
Slog.w(TAG, errorMsg);
}
} else if (throwable instanceof PackageManagerException) { // 如果解析过程中抛出了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);
}
// 如果设置了SCAN_AS_APK_IN_APEX标志,并且解析失败,则向APEX管理器报告错误。
if ((scanFlags & SCAN_AS_APK_IN_APEX) != 0 && errorCode != INSTALL_SUCCEEDED) {
mApexManager.reportErrorWithApkInApex(scanDir.getAbsolutePath(), errorMsg);
}
// Delete invalid userdata apps
// 如果没有设置SCAN_AS_SYSTEM标志,并且解析失败,则记录警告日志,并删除无效的应用代码路径。
if ((scanFlags & SCAN_AS_SYSTEM) == 0
&& errorCode != PackageManager.INSTALL_SUCCEEDED) {
logCriticalInfo(Log.WARN,
"Deleting invalid package at " + parseResult.scanFile);
mRemovePackageHelper.removeCodePathLI(parseResult.scanFile);
}
}
}
上面的函数 installPackagesFromDir 从指定的目录(scanDir)中安装和解析APK文件,是Android系统包管理器服务中用于从指定目录中安装和解析APK文件的关键方法。
它通过并行解析提高了效率,并通过适当的锁机制确保了线程安全。同时,它还支持对APEX模块中APK文件的特殊处理,并能够删除无效的用户数据应用。
上面的代码中通过 ParallelPackageParser 的 submit 提交安装任务,take获取任务结果。
6,在frameworks/base/services/core/java/com/android/server/pm/ParallelPackageParser.java中
public void submit(File scanFile, int parseFlags) {
// 使用mExecutorService.submit()方法提交一个任务到线程池mExecutorService中执行。mExecutorService是一个执行服务的实例,用于管理线程池。
mExecutorService.submit(() -> {
// 在提交的任务中,首先创建一个ParseResult实例pr,用于存储解析结果。
ParseResult pr = new ParseResult();
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parallel parsePackage [" + scanFile + "]");
try {
pr.scanFile = scanFile;
// 调用 parsePackage(scanFile, parseFlags)方法解析文件,将解析结果赋值给pr.parsedPackage。
pr.parsedPackage = parsePackage(scanFile, parseFlags);
} catch (Throwable e) {
pr.throwable = e;
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
try {
// 将解析结果pr放入mQueue队列中。mQueue可能是一个用于存储解析结果的阻塞队列。
mQueue.put(pr);
} catch (InterruptedException e) {
// 如果在将结果放入队列时线程被中断(InterruptedException),则重新设置当前线程的中断状态(Thread.currentThread().interrupt()),并记录被中断的线程名称到mInterruptedInThread变量中。
// 这一步骤是为了确保如果主线程在等待解析完成时被中断,它不会无限期地等待下去。
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();
}
});
}
上面的 submit 函数代码在后台线程中异步解析一个文件,并将解析结果放入一个队列中,以便后续处理。如果在解析或结果放入队列的过程中发生异常或线程被中断,它也有相应的处理逻辑来确保系统的稳定性和响应性。
下面看下 take 函数。
public ParseResult take() {
try {
// 检查mInterruptedInThread变量是否为null。如果mInterruptedInThread不为null,说明之前在某个线程中解析文件并放入队列mQueue的过程中,该线程被中断了,并且中断的线程名称被记录在了mInterruptedInThread中。
// 此时,方法会抛出一个InterruptedException,并附带一个消息,指出在哪个线程中发生了中断。
if (mInterruptedInThread != null) {
throw new InterruptedException("Interrupted in " + mInterruptedInThread);
}
// 如果mInterruptedInThread为null,则继续执行,从mQueue队列中取出一个ParseResult对象并返回。这里使用了mQueue.take()方法,该方法会阻塞当前线程,直到队列中有元素可取。
return mQueue.take();
} catch (InterruptedException e) {
// We cannot recover from interrupt here
// 如果在从队列取元素的过程中当前线程被中断(InterruptedException),则捕获这个异常。
// 在catch块中,首先重新设置当前线程的中断状态(Thread.currentThread().interrupt()),因为Java中的中断状态在抛出InterruptedException后会被清除。
// 然后,抛出一个新的IllegalStateException,并将原始的InterruptedException作为原因传递给它。这里的注释// We cannot recover from interrupt here表明,在这个方法内部,从中断中恢复是不可能的,因此选择抛出异常来处理这种情况。
Thread.currentThread().interrupt();
throw new IllegalStateException(e);
}
}
上面的 take 函数代码用于从某个阻塞队列mQueue中获取一个ParseResult对象。这个take方法的设计表明,它期望在调用之前或调用过程中,没有其他线程会修改mInterruptedInThread变量的值(除非是通过中断机制)。 此外,由于InterruptedException是一个受检异常(checked exception),调用take方法的代码需要处理这个异常,或者声明它自己的方法也抛出这个异常。 在实际应用中,这种设计可能用于确保在并发环境下,当某个操作被中断时,能够及时地通知调用者,并且调用者可以根据中断信息做出适当的响应,比如重试操作、放弃操作或者进行其他清理工作。
上面的 submit 和 take 是通过一个 BlockingQueue 实现解析和获取任务,解析是 parsePackage 函数实现的。
private final PackageParser2 mPackageParser; // 在 ParallelPackageParser 构造函数中被传入的参数赋值
@VisibleForTesting
protected ParsedPackage parsePackage(File scanFile, int parseFlags)
throws PackageManagerException {
return mPackageParser.parsePackage(scanFile, parseFlags, true, mFrameworkSplits);
}
上面的代码通过 PackageParser2 类对象 mPackageParser 调用 parsePackage 函数。
7,在frameworks/base/services/core/java/com/java/com/android/server/pm/parsing/PackageParser2.java中
private ParsingPackageUtils parsingUtils; // 在该类的构造函数中通过new创建新对象,即 parsingUtils = new ParsingPackageUtils(onlyCoreApps, separateProcesses, displayMetrics,splitPermissions, callback);
@AnyThread // @AnyThread表明这个方法可以在任何线程上调用,即它不是线程受限的。
public ParsedPackage parsePackage(File packageFile, int flags, boolean useCaches,
List<File> frameworkSplits) throws PackageManagerException {
// 如果useCaches为true且mCacher不为null,则尝试从缓存中获取解析结果。如果找到了缓存的结果,则直接返回它。
if (useCaches && mCacher != null) {
ParsedPackage parsed = mCacher.getCachedResult(packageFile, flags);
if (parsed != null) {
return parsed;
}
}
// 如果启用了日志记录(LOG_PARSE_TIMINGS为true),则记录解析开始的时间。
long parseTime = LOG_PARSE_TIMINGS ? SystemClock.uptimeMillis() : 0;
// 获取一个ParseInput对象,并重置它
ParseInput input = mSharedResult.get().reset();
// 调用parsingUtils.parsePackage方法来解析包文件。这个方法返回一个ParseResult<ParsingPackage>对象,其中包含了解析的结果或错误信息。
ParseResult<ParsingPackage> result = parsingUtils.parsePackage(input, packageFile, flags,
frameworkSplits);
// 如果解析结果表示发生了错误(result.isError()为true),则抛出一个PackageManagerException异常,包含错误代码、错误消息和异常对象。
if (result.isError()) {
throw new PackageManagerException(result.getErrorCode(), result.getErrorMessage(),
result.getException());
}
// 从解析结果中获取ParsedPackage对象。
ParsedPackage parsed = (ParsedPackage) result.getResult().hideAsParsed();
long cacheTime = LOG_PARSE_TIMINGS ? SystemClock.uptimeMillis() : 0;
// 如果启用了缓存且mCacher不为null,则将解析结果缓存起来。
if (mCacher != null) {
mCacher.cacheResult(packageFile, flags, parsed);
}
// 如果启用了日志记录,则计算解析和缓存更新的时间,并根据设定的阈值(LOG_PARSE_TIMINGS_THRESHOLD_MS)决定是否打印日志信息。
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");
}
}
// 返回解析得到的ParsedPackage对象。
return parsed;
}
上面的代码中调用 parsingUtils.parsePackage 函数,parsingUtils 是 ParsingPackageUtils 类对象。
8,在frameworks/base/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java中
public ParseResult<ParsingPackage> parsePackage(ParseInput input, File packageFile, int flags,
List<File> frameworkSplits) {
if (((flags & PARSE_FRAMEWORK_RES_SPLITS) != 0)
&& frameworkSplits.size() > 0
&& packageFile.getAbsolutePath().endsWith("/framework-res.apk")) {
return parseClusterPackage(input, packageFile, frameworkSplits, flags);
} else if (packageFile.isDirectory()) {
return parseClusterPackage(input, packageFile, /* frameworkSplits= */null, flags);
} else {
return parseMonolithicPackage(input, packageFile, flags);
}
}
上面的代码中,函数 parsePackage 接收四个参数:
一个ParseInput对象input用于存储解析过程中需要的数据,一个File对象packageFile指向要解析的包文件或目录,一个整数flags用于提供解析时的选项或标志,以及一个List对象frameworkSplits包含框架资源分割文件的列表。
在if条件中,如果flags中包含了PARSE_FRAMEWORK_RES_SPLITS标志,且frameworkSplits列表不为空,并且packageFile的路径以/framework-res.apk结尾,那么调用parseClusterPackage方法来解析一个包含框架资源分割的包。
在else if 条件中,如果packageFile是一个目录(而不是文件),则也调用parseClusterPackage方法来解析,但此时不传入框架资源分割文件列表(传入null)。
如果上述条件都不满足,那么调用parseMonolithicPackage方法来解析一个单体包(即,不是包含框架资源分割的包,也不是一个目录)。 是一个单独的一个apk就使用 parseMonolithicPackage 进行解析。
下面看下 parseMonolithicPackage 函数。
private ParseResult<ParsingPackage> parseMonolithicPackage(ParseInput input, File apkFile,
int flags) {
final ParseResult<PackageLite> liteResult =
ApkLiteParseUtils.parseMonolithicPackageLite(input, apkFile, flags); // 先通过parseMonolithicPackageLite初步解析这个apk,会解析出minSdkVersion,versionCode等基本信息。
if (liteResult.isError()) {
return input.error(liteResult);
}
final PackageLite lite = liteResult.getResult();
// 如果mOnlyCoreApps标志为true且APK不是核心应用(lite.isCoreApp()返回false),则返回一个包含错误信息的ParseResult<ParsingPackage>对象,错误信息指出只允许核心应用。
if (mOnlyCoreApps && !lite.isCoreApp()) {
return input.error(INSTALL_PARSE_FAILED_ONLY_COREAPP_ALLOWED,
"Not a coreApp: " + apkFile);
}
// 创建一个SplitAssetLoader对象,用于加载APK中的资源。这里使用的是DefaultSplitAssetLoader实现,并传入了PackageLite对象和解析标志。
final SplitAssetLoader assetLoader = new DefaultSplitAssetLoader(lite, flags);
try {
// 调用parseBaseApk方法解析APK文件,该方法接收ParseInput、APK文件、APK文件的规范路径、资源加载器和解析标志作为参数,并返回一个ParseResult<ParsingPackage>对象。
final ParseResult<ParsingPackage> result = parseBaseApk(input,
apkFile,
apkFile.getCanonicalPath(),
assetLoader, flags);
// 如果解析过程中发生错误,则使用input.error(result)方法返回一个包含错误信息的ParseResult<ParsingPackage>对象。
if (result.isError()) {
return input.error(result);
}
// 如果解析成功,则使用input.success(result.getResult().setUse32BitAbi(lite.isUse32bitAbi()))方法返回一个包含解析成功的ParsingPackage对象的ParseResult<ParsingPackage>对象,并设置32位ABI的使用情况。
return input.success(result.getResult()
.setUse32BitAbi(lite.isUse32bitAbi()));
} catch (IOException e) {
return input.error(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
"Failed to get path: " + apkFile, e);
} finally {
IoUtils.closeQuietly(assetLoader);
}
}
上面的代码中先通过 parseMonolithicPackageLite 初步解析这个apk,得到PackageLite,然后调用 parseBaseApk 进一步解析apk.
首先分析下 parseMonolithicPackageLite 函数。
9,在frameworks/base/core/java/android/content/pm/parsing/ApkLiteParseUtils.java中
/**
* Parse lightweight details about a single APK files.
*/
public static ParseResult<PackageLite> parseMonolithicPackageLite(ParseInput input,
File packageFile, int flags) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parseApkLite");
try {
// 调用parseApkLite方法解析APK文件,该方法返回一个ParseResult<ApkLite>对象。ApkLite是一个包含APK基本信息(如路径、目标SDK版本等)的简化对象。
final ParseResult<ApkLite> result = parseApkLite(input, packageFile, flags);
// 检查parseApkLite方法的返回结果是否为错误。如果是,使用input.error(result)方法将错误信息封装在ParseResult<PackageLite>对象中并返回。
if (result.isError()) {
return input.error(result);
}
final ApkLite baseApk = result.getResult(); // 获取ApkLite对象
final String packagePath = packageFile.getAbsolutePath(); // 获取APK文件的绝对路径。
// 使用input.success方法返回一个包含成功解析结果的ParseResult<PackageLite>对象。
// 这个PackageLite对象通过构造器初始化,包含了APK文件的路径、ApkLite对象的路径、ApkLite对象本身,以及一些关于APK分割(splits)的信息(这里全部为null,表示这是一个整体APK,没有分割)。此外,还包含了目标SDK版本。
return input.success(
new PackageLite(packagePath, baseApk.getPath(), baseApk, null /* splitNames */,
null /* isFeatureSplits */, null /* usesSplitNames */,
null /* configForSplit */, null /* splitApkPaths */,
null /* splitRevisionCodes */, baseApk.getTargetSdkVersion(),
null /* requiredSplitTypes */, null /* splitTypes */));
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}
上面的代码中,静态方法 parseMonolithicPackageLite 它接受三个参数:
ParseInput input(解析输入),File packageFile(APK文件),和int flags(解析标志)。
该方法返回一个ParseResult类型的对象,这个对象包含了解析结果,可能是成功的结果(PackageLite对象)或错误信息。
整体而言,上面的函数解析一个整体的APK文件,并将其基本信息封装在一个PackageLite对象中返回。如果解析过程中出现错误,将返回错误信息。此外,代码还包含了性能追踪的逻辑,帮助开发者监控方法的执行效率。
下面分析下 parseBaseApk 函数。
10,在frameworks/base/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java中
private ParseResult<ParsingPackage> parseBaseApk(ParseInput input, File apkFile,
String codePath, SplitAssetLoader assetLoader, int flags) {
// 通过apkFile.getAbsolutePath()获取APK文件的绝对路径。
final String apkPath = apkFile.getAbsolutePath();
String volumeUuid = null;
// 检查是否位于扩展存储:如果APK路径以MNT_EXPAND(一个表示扩展存储挂载点的常量)开头,则提取并保存卷UUID。
// MNT_EXPAND 是一个常量,即 public static final String MNT_EXPAND = "/mnt/expand/";
if (apkPath.startsWith(MNT_EXPAND)) {
final int end = apkPath.indexOf('/', MNT_EXPAND.length());
volumeUuid = apkPath.substring(MNT_EXPAND.length(), end);
}
if (DEBUG_JAR) Slog.d(TAG, "Scanning base APK: " + apkPath);
final AssetManager assets;
try {
// 从assetLoader获取基础资源管理器。如果失败,则根据异常类型返回相应的错误码和错误信息。
assets = assetLoader.getBaseAssetManager();
} catch (IllegalArgumentException e) {
return input.error(e.getCause() instanceof IOException ? INSTALL_FAILED_INVALID_APK
: INSTALL_PARSE_FAILED_NOT_APK, e.getMessage(), e);
}
// 使用资源管理器查找APK路径对应的cookie。如果未找到,则返回解析失败错误。
final int cookie = assets.findCookieForPath(apkPath);
if (cookie == 0) {
return input.error(INSTALL_PARSE_FAILED_BAD_MANIFEST,
"Failed adding asset path: " + apkPath);
}
// 使用找到的cookie打开APK中的AndroidManifest.xml文件,并创建一个资源对象用于解析。
// ANDROID_MANIFEST_FILENAME 是一个常量,即 public static final String ANDROID_MANIFEST_FILENAME = "AndroidManifest.xml";
try (XmlResourceParser parser = assets.openXmlResourceParser(cookie,
ANDROID_MANIFEST_FILENAME)) {
final Resources res = new Resources(assets, mDisplayMetrics, null);
// 调用重载函数 parseBaseApk 解析APK文件,包括其manifest文件和其他资源。
ParseResult<ParsingPackage> result = parseBaseApk(input, apkPath, codePath, res,
parser, flags);
// 如果解析结果包含错误,则返回错误信息和错误位置。
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());
}
}
// 从assetLoader获取APK资源对象,并检查是否定义了可覆盖的资源。
ApkAssets apkAssets = assetLoader.getBaseApkAssets();
boolean definesOverlayable = false;
try {
definesOverlayable = apkAssets.definesOverlayable();
} catch (IOException ignored) {
// Will fail if there's no packages in the ApkAssets, which can be treated as false
}
// 如果APK定义了可覆盖的资源,则遍历所有包名和对应的可覆盖资源映射,将这些信息添加到解析的包对象中。
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);
}
// 返回包含解析后的包对象的成功结果
return input.success(pkg);
} catch (Exception e) {
return input.error(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
"Failed to read manifest from " + apkPath, e);
}
}
上面的函数代码用于解析APK文件的基础信息,是Android应用安装过程中解析APK文件的关键部分,它负责读取APK的manifest文件和其他资源,并构建一个ParsingPackage对象,该对象包含了应用的基本信息和资源。
上面的函数 parseBaseApk 调用另一个重载的 parseBaseApk 函数。
private ParseResult<ParsingPackage> parseBaseApk(ParseInput input, String apkPath,
String codePath, Resources res, XmlResourceParser parser, int flags)
throws XmlPullParserException, IOException {
final String splitName;
final String pkgName;
// 调用ApkLiteParseUtils.parsePackageSplitNames 方法解析APK的分割名称和包名。如果解析出错,则通过input.error方法返回错误结果。
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;
// 检查分割名称是否不为空。如果是,说明期望的是一个基础APK,但找到的是一个分割APK,因此通过input.error方法返回错误结果,错误码为INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME。
if (!TextUtils.isEmpty(splitName)) {
return input.error(
PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME,
"Expected base APK, but found split " + splitName
);
}
// 通过资源对象res和解析器parser获取AndroidManifest.xml文件中定义的属性数组TypedArray。
final TypedArray manifestArray = res.obtainAttributes(parser, R.styleable.AndroidManifest);
try {
// 从AndroidManifest.xml中读取coreApp属性,该属性标记应用是否为系统核心应用。
final boolean isCoreApp = parser.getAttributeBooleanValue(null /*namespace*/,
"coreApp",false);
// 调用回调方法mCallback.startParsingPackage 开始解析包,该方法接收包名、APK路径、代码路径、属性数组和是否为核心应用等参数,返回一个ParsingPackage对象。
final ParsingPackage pkg = mCallback.startParsingPackage(
pkgName, apkPath, codePath, manifestArray, isCoreApp);
// 调用parseBaseApkTags方法继续解析APK的基本标签,如应用权限、组件声明等。如果解析出错,则返回错误结果。
final ParseResult<ParsingPackage> result =
parseBaseApkTags(input, pkg, manifestArray, res, parser, flags);
if (result.isError()) {
return result;
}
// 如果解析成功,则通过input.success方法返回成功结果,包含解析后的ParsingPackage对象。
return input.success(pkg);
} finally {
manifestArray.recycle();
}
}
上面的代码函数 parseBaseApk 负责解析一个APK文件的基础信息,包括包名、是否为分割APK、是否为系统核心应用等,并继续解析APK中的基本标签。如果在解析过程中遇到任何错误,会返回相应的错误结果。
上面的函数调用了 parseBaseApkTags 函数进一步解析AndroidManifest中的各个标签。
private ParseResult<ParsingPackage> parseBaseApkTags(ParseInput input, ParsingPackage pkg,
TypedArray sa, Resources res, XmlResourceParser parser, int flags)
throws XmlPullParserException, IOException {
// 调用parseSharedUser方法解析<manifest>标签中的<shared-user>子标签。如果解析出错,则直接返回错误结果。
ParseResult<ParsingPackage> sharedUserResult = parseSharedUser(input, pkg, sa);
if (sharedUserResult.isError()) {
return sharedUserResult;
}
// 从TypedArray sa中读取并设置APK包的安装位置、目标沙箱版本,以及是否支持外部存储等属性。
// PARSE_DEFAULT_INSTALL_LOCATION是一个整型常量,即 public static final int PARSE_DEFAULT_INSTALL_LOCATION = PackageInfo.INSTALL_LOCATION_UNSPECIFIED;
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);
boolean foundApp = false;
final int depth = parser.getDepth();
int type;
// 使用 XmlResourceParser parser遍历AndroidManifest.xml文档。在遍历过程中,忽略非START_TAG类型的节点,并对每个START_TAG节点进行处理。
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG
|| parser.getDepth() > depth)) {
if (type != XmlPullParser.START_TAG) {
continue;
}
String tagName = parser.getName();
final ParseResult result;
// <application> has special logic, so it's handled outside the general method
if (TAG_APPLICATION.equals(tagName)) {// 处理 application 标签
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 { // 尚未处理过<application>标签,则调用 parseBaseApplication 方法解析<application>标签。
foundApp = true;
result = parseBaseApplication(input, pkg, res, parser, flags);
}
} else { // 对于非<application>标签,调用 parseBaseApkTag 方法进行处理。
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);
}
}
// 检查APK包中的<attribution>标签组合是否有效。如果无效,则报告错误。
if (!ParsedAttributionUtils.isCombinationValid(pkg.getAttributions())) {
return input.error(
INSTALL_PARSE_FAILED_BAD_MANIFEST,
"Combination <attribution> tags are not valid"
);
}
// 检查APK包中是否声明了重复的权限,但具有不同的属性值。如果是,则报告错误。
if (ParsedPermissionUtils.declareDuplicatePermission(pkg)) {
return input.error(
INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
"Found duplicate permission with a different attribute value."
);
}
// 调用convertCompatPermissions和convertSplitPermissions方法处理权限的兼容性转换和分割权限的处理。
convertCompatPermissions(pkg);
convertSplitPermissions(pkg);
// At this point we can check if an application is not supporting densities and hence
// cannot be windowed / resized. Note that an SDK version of 0 is common for
// pre-Doughnut applications.
if (pkg.getTargetSdkVersion() < DONUT
|| (!pkg.isSupportsSmallScreens()
&& !pkg.isSupportsNormalScreens()
&& !pkg.isSupportsLargeScreens()
&& !pkg.isSupportsExtraLargeScreens()
&& !pkg.isResizeable()
&& !pkg.isAnyDensity())) {
adjustPackageToBeUnresizeableAndUnpipable(pkg);
}
return input.success(pkg);
}
上面的代码中 parseBaseApkTags 接收一个ParseInput对象(用于报告解析过程中的错误和成功结果)、一个ParsingPackage对象(代表正在解析的APK包)、一个TypedArray对象(包含从AndroidManifest.xml中解析出的属性)、
一个Resources对象(用于访问资源)、一个XmlResourceParser对象(用于解析XML文档),以及一个int类型的flags(用于控制解析行为)。该方法可能抛出XmlPullParserException和IOException。
上面的函数代码中,如果是 application 标签,则调用 parseBaseApplication 函数,因为四大组件都在这个application标签下面。
下面分析下 parseBaseApplication 函数。
private ParseResult<ParsingPackage> parseBaseApplication(ParseInput input,
ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags)
throws XmlPullParserException, IOException {
final String pkgName = pkg.getPackageName();
int targetSdk = pkg.getTargetSdkVersion();
// 使用res.obtainAttributes(parser, R.styleable.AndroidManifestApplication)获取<application>标签的属性数组(TypedArray sa)。
TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestApplication);
try {
// TODO(b/135203078): Remove this and force unit tests to mock an empty manifest
// This case can only happen in unit tests where we sometimes need to create fakes
// of various package parser data structures.
if (sa == null) { // 如果sa为空,表示<application>标签不包含任何属性,此时会返回一个错误信息。
return input.error("<application> does not contain any attributes");
}
// 从属性数组中获取应用名称(name)。
String name = sa.getNonConfigurationString(R.styleable.AndroidManifestApplication_name,
0);
if (name != null) {
// 如果名称不为空,构建完整的类名(outInfoName),并检查这个名称是否有效。
String packageName = pkg.getPackageName();
String outInfoName = ParsingUtils.buildClassName(packageName, name);
if (PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals(outInfoName)) {
return input.error("<application> invalid android:name");
} else if (outInfoName == null) {
return input.error("Empty class name in package " + packageName);
}
// 将解析出的类名设置到pkg对象中。
pkg.setClassName(outInfoName);
}
// 从属性数组中获取标签(label)的值。
TypedValue labelValue = sa.peekValue(R.styleable.AndroidManifestApplication_label);
if (labelValue != null) {
pkg.setLabelRes(labelValue.resourceId); // 如果标签值不为空,首先设置标签的资源ID(labelValue.resourceId)。
if (labelValue.resourceId == 0) { // 如果资源ID为0(表示没有指定资源ID),则使用标签的字符串值(labelValue.coerceToString()),并标记为非本地化标签。
pkg.setNonLocalizedLabel(labelValue.coerceToString());
}
}
// 调用parseBaseAppBasicFlags(pkg, sa)方法,进一步解析<application>标签中的基本标志(如是否允许调试等)。
parseBaseAppBasicFlags(pkg, sa);
// 使用nonConfigString方法从属性数组sa中获取manageSpaceActivity的值。
String manageSpaceActivity = nonConfigString(Configuration.NATIVE_CONFIG_VERSION,
R.styleable.AndroidManifestApplication_manageSpaceActivity, sa);
// 如果manageSpaceActivity不为空,使用ParsingUtils.buildClassName方法结合包名pkgName和manageSpaceActivity的值来构建完整的类名manageSpaceActivityName。
if (manageSpaceActivity != null) {
String manageSpaceActivityName = ParsingUtils.buildClassName(pkgName,
manageSpaceActivity);
if (manageSpaceActivityName == null) { // 如果构建的类名为空,则返回一个错误信息,指出包名中缺少类名。
return input.error("Empty class name in package " + pkgName);
}
// 如果类名有效,将其设置到pkg对象的manageSpaceActivityName属性中。
pkg.setManageSpaceActivityName(manageSpaceActivityName);
}
// 使用ComponentParseUtils.buildProcessName方法构建应用的进程名称。
// 这个方法接受多个参数,包括包名pkgName、一个可能为空的默认进程名defProc(在这里传入null)、应用名pname、一些标志位flags、一个表示是否分离进程的布尔值mSeparateProcesses,以及解析输入input。
ParseResult<String> processNameResult = ComponentParseUtils.buildProcessName(
pkgName, null /*defProc*/, pname, flags, mSeparateProcesses, input);
if (processNameResult.isError()) {
return input.error(processNameResult);
}
// 调用processNameResult.getResult()获取进程名称。
String processName = processNameResult.getResult();
// 将获取到的进程名称设置到pkg对象的processName属性中。
pkg.setProcessName(processName);
if (pkg.isCantSaveState()) {
// A heavy-weight application can not be in a custom process.
// We can do direct compare because we intern all strings.
if (processName != null && !processName.equals(pkgName)) {
return input.error(
"cantSaveState applications can not use custom processes");
}
}
String classLoaderName = pkg.getClassLoaderName();
if (classLoaderName != null
&& !ClassLoaderFactory.isValidClassLoaderName(classLoaderName)) {
return input.error("Invalid class loader name: " + classLoaderName);
}
} finally {
sa.recycle();
}
boolean hasActivityOrder = false;
boolean hasReceiverOrder = false;
boolean hasServiceOrder = false;
final int depth = parser.getDepth();
int type;
// 使用XmlPullParser解析AndroidManifest.xml文件。循环遍历XML文档,直到到达文档末尾或当前标签的深度小于初始深度(表示已经退出了<manifest>标签)。
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG
|| parser.getDepth() > depth)) {
if (type != XmlPullParser.START_TAG) {
continue;
}
final ParseResult result;
String tagName = parser.getName();
boolean isActivity = false;
// 对于每个开始标签(START_TAG),根据标签名(tagName)执行不同的操作。
switch (tagName) {
// 当遇到<activity>标签时,解析为ParsedActivity对象,并根据isActivity标志将其添加到活动的列表或接收器的列表中
// (注意,由于代码中的fall-through,<activity>标签实际上也会进入<receiver>的代码块,但isActivity会被设置为true,因此它会被正确地添加到活动的列表中)。
case "activity":
isActivity = true;
// fall-through
// 当遇到<receiver>标签时(由于fall-through,这里也处理<activity>之后的情况),解析为ParsedActivity对象(这里使用相同的解析器是因为接收器和活动在AndroidManifest.xml中有许多共同的属性),但将其添加到接收器的列表中。
case "receiver":
ParseResult<ParsedActivity> activityResult =
ParsedActivityUtils.parseActivityOrReceiver(mSeparateProcesses, pkg,
res, parser, flags, sUseRoundIcon, null /*defaultSplitName*/,
input);
if (activityResult.isSuccess()) {
ParsedActivity activity = activityResult.getResult();
if (isActivity) {
hasActivityOrder |= (activity.getOrder() != 0);
pkg.addActivity(activity);
} else {
hasReceiverOrder |= (activity.getOrder() != 0);
pkg.addReceiver(activity);
}
}
result = activityResult;
break;
// 当遇到<service>标签时,解析为ParsedService对象,并添加到服务的列表中。
case "service":
ParseResult<ParsedService> serviceResult =
ParsedServiceUtils.parseService(mSeparateProcesses, pkg, res, parser,
flags, sUseRoundIcon, null /*defaultSplitName*/,
input);
if (serviceResult.isSuccess()) {
ParsedService service = serviceResult.getResult();
hasServiceOrder |= (service.getOrder() != 0);
pkg.addService(service);
}
result = serviceResult;
break;
// 当遇到<provider>标签时,解析为ParsedProvider对象,并添加到内容提供者的列表中。
case "provider":
ParseResult<ParsedProvider> providerResult =
ParsedProviderUtils.parseProvider(mSeparateProcesses, pkg, res, parser,
flags, sUseRoundIcon, null /*defaultSplitName*/,
input);
if (providerResult.isSuccess()) {
pkg.addProvider(providerResult.getResult());
}
result = providerResult;
break;
// 当遇到<activity-alias>标签时,解析为ParsedActivity对象(因为活动别名在功能上类似于活动),并添加到活动的列表中。
case "activity-alias":
activityResult = ParsedActivityUtils.parseActivityAlias(pkg, res,
parser, sUseRoundIcon, null /*defaultSplitName*/,
input);
if (activityResult.isSuccess()) {
ParsedActivity activity = activityResult.getResult();
hasActivityOrder |= (activity.getOrder() != 0);
pkg.addActivity(activity);
}
result = activityResult;
break;
// 当遇到<apex-system-service>标签时,解析为ParsedApexSystemService对象,并添加到Apex系统服务的列表中。
case "apex-system-service":
ParseResult<ParsedApexSystemService> systemServiceResult =
ParsedApexSystemServiceUtils.parseApexSystemService(res,
parser, input);
if (systemServiceResult.isSuccess()) {
ParsedApexSystemService systemService =
systemServiceResult.getResult();
pkg.addApexSystemService(systemService);
}
result = systemServiceResult;
break;
default:
result = parseBaseAppChildTag(input, tagName, pkg, res, parser, flags);
break;
}
if (result.isError()) {
return input.error(result);
}
// 如果应用包含的组件数量超过了最大限制(通过hasTooManyComponents(pkg)检查),则返回错误信息。
if (hasTooManyComponents(pkg)) {
return input.error(MAX_NUM_COMPONENTS_ERR_MSG);
}
} // 当XML文档解析完毕时,循环结束。此时,pkg对象已经包含了应用的所有组件信息。
// 检查应用程序包的静态共享库名称和SDK库名称是否都为空
if (TextUtils.isEmpty(pkg.getStaticSharedLibName()) && TextUtils.isEmpty(
pkg.getSdkLibName())) {
// Add a hidden app detail activity to normal apps which forwards user to App Details
// page.
ParseResult<ParsedActivity> a = generateAppDetailsHiddenActivity(input, pkg);
if (a.isError()) {
// Error should be impossible here, as the only failure case as of SDK R is a
// string validation error on a constant ":app_details" string passed in by the
// parsing code itself. For this reason, this is just a hard failure instead of
// deferred.
return input.error(a);
}
pkg.addActivity(a.getResult());
}
// 根据hasActivityOrder、hasReceiverOrder和hasServiceOrder的值,分别调用pkg.sortActivities()、pkg.sortReceivers()和pkg.sortServices()对活动、接收器和服务进行排序。
if (hasActivityOrder) {
pkg.sortActivities();
}
if (hasReceiverOrder) {
pkg.sortReceivers();
}
if (hasServiceOrder) {
pkg.sortServices();
}
// Must be run after the entire {@link ApplicationInfo} has been fully processed and after
// every activity info has had a chance to set it from its attributes.
setMaxAspectRatio(pkg);
setMinAspectRatio(pkg);
setSupportsSizeChanges(pkg);
pkg.setHasDomainUrls(hasDomainURLs(pkg));
return input.success(pkg);
}
上面的代码解析标签,根据标签的不同调用不同的解析函数。
上面分析了InitAppHelper类中的 initSystemApps 函数,下面看下 initNonSystemApps 函数。
11,在frameworks/base/services/core/java/com/android/server/pm/InitAppsHelper.java中
@GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
public void initNonSystemApps(PackageParser2 packageParser, @NonNull int[] userIds,
long startTime) {
if (!mIsOnlyCoreApps) { // 这行代码检查一个布尔变量mIsOnlyCoreApps。如果它的值为false(即不是仅核心应用),则执行大括号内的代码。
// 记录一个事件日志,表示开始扫描PMS(包管理器服务)数据。
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START,
SystemClock.uptimeMillis());
// 调用scanDirTracedLI方法来扫描应用安装目录。这个方法可能涉及到并发执行,因为它使用了mExecutorService。
scanDirTracedLI(mPm.getAppInstallDir(), /* frameworkSplits= */ null, 0,
mScanFlags | SCAN_REQUIRE_KNOWN,
packageParser, mExecutorService);
}
// 关闭执行服务mExecutorService,并返回等待执行的任务列表。
List<Runnable> unfinishedTasks = mExecutorService.shutdownNow();
// 检查是否有未完成的任务
if (!unfinishedTasks.isEmpty()) {
throw new IllegalStateException("Not all tasks finished before calling close: "
+ unfinishedTasks);
}
if (!mIsOnlyCoreApps) {
// 调用fixSystemPackages方法来修复系统包。
fixSystemPackages(userIds);
// 记录非系统应用扫描的耗时。
logNonSystemAppScanningTime(startTime);
}
mExpectingBetter.clear();
// 调用pruneRenamedPackagesLPw方法来修剪重命名的包。
mPm.mSettings.pruneRenamedPackagesLPw();
}
在上面的代码中,有一个 BOOT_PROGRESS_PMS_DATA_SCAN_START 的事件日志记录。
上面分析了BOOT_PROGRESS_PMS_SYSTEM_SCAN_START 和 BOOT_PROGRESS_PMS_DATA_SCAN_START,在PKMS的构造函数中比较复杂的部分已经分析过了,对于下面的 BOOT_PROGRESS_PMS_SCAN_END 和 BOOT_PROGRESS_PMS_READY 在第二步已做了基本分析。 至此,PKMS的构造函数的执行分析完毕。