PMS的使用
通过以下代码可以获取安卓手机中已安装程序的 PackageInfo:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
PackageManager packageManager = getPackageManager();
// 获取所有已安装程序的 PackageInfo
List<PackageInfo> packages = packageManager.getInstalledPackages(0);
for(PackageInfo packageInfo : packages){
Log.d("MainActivity", "package name:" + packageInfo.packageName);
}
}
}
在模拟器上运行后,打印如下:
MainActivity com.example.test D package name:com.google.android.networkstack.tethering
MainActivity com.example.test D package name:com.google.android.ext.services
MainActivity com.example.test D package name:com.android.providers.telephony
MainActivity com.example.test D package name:com.android.dynsystem
MainActivity com.example.test D package name:com.google.android.cellbroadcastservice
MainActivity com.example.test D package name:com.android.providers.calendar
...
里面究竟是怎么获取的呢?我们来看看源码,本文源码基于 Android 11.0。
由于 Activity 继承自 ContextWrapper,最终会委托给 ContextImpl 调用其 getPackageManager() 方法:
class ContextImpl extends Context {
public PackageManager getPackageManager() {
if (mPackageManager != null) {
return mPackageManager;
}
final IPackageManager pm = ActivityThread.getPackageManager();
final IPermissionManager permissionManager = ActivityThread.getPermissionManager();
if (pm != null && permissionManager != null) {
// Doesn't matter if we make more than one instance.
return (mPackageManager = new ApplicationPackageManager(this, pm, permissionManager));
}
return null;
}
}
可以看到 getPackageManager() 实际返回的是 ApplicationPackageManager 的实例,这里 ActivityThread.getPackageManager() 拿到的 IPackageManager 对象传给了 ApplicationPackageManager 实例。ApplicationPackageManager 代码如下:
public class ApplicationPackageManager extends PackageManager {
private final ContextImpl mContext;
private final IPackageManager mPM;
private final IPermissionManager mPermissionManager;
protected ApplicationPackageManager(ContextImpl context, IPackageManager pm,
IPermissionManager permissionManager) {
mContext = context;
mPM = pm;
mPermissionManager = permissionManager;
}
public List<PackageInfo> getInstalledPackages(int flags) {
return getInstalledPackagesAsUser(flags, getUserId());
}
public List<PackageInfo> getInstalledPackagesAsUser(int flags, int userId) {
try {
// 调用 mPM 的 getInstalledPackages() 方法
ParceledListSlice<PackageInfo> parceledList =
mPM.getInstalledPackages(updateFlagsForPackage(flags, userId), userId);
if (parceledList == null) {
return Collections.emptyList();
}
return parceledList.getList();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
}
里面调用了 mPM 的 getInstalledPackages() 方法,mPM 是通过 ActivityThread 的 getPackageManager() 获取的, 代码如下:
public final class ActivityThread extends ClientTransactionHandler {
public static IPackageManager getPackageManager() {
if (sPackageManager != null) {
return sPackageManager;
}
final IBinder b = ServiceManager.getService("package");
sPackageManager = IPackageManager.Stub.asInterface(b);
return sPackageManager;
}
}
这里出现了 IBinder,很明显是跨进程通信,实际调用的是 SystemServer 进程的 PackageManagerService 的 getInstalledPackages() 方法。类图如下:
PackageManagerService 的 getInstalledPackages() 方法代码如下:
public class PackageManagerService extends IPackageManager.Stub implements PackageSender {
// Keys are String (package name), values are Package.
final ArrayMap<String, AndroidPackage> mPackages = new ArrayMap<>();
final Settings mSettings;
@Override
public ParceledListSlice<PackageInfo> getInstalledPackages(int flags, int userId) {
...
flags = updateFlagsForPackage(flags, userId);
// listUninstalled 包含所有系统已知的包(包括已卸载但有记录的)
final boolean listUninstalled = (flags & MATCH_KNOWN_PACKAGES) != 0;
final boolean listApex = (flags & MATCH_APEX) != 0;
final boolean listFactory = (flags & MATCH_FACTORY_ONLY) != 0;
...
synchronized (mLock) {
ArrayList<PackageInfo> list;
if (listUninstalled) {
// mSettings.mPackages 包含所有曾经安装过,且在 packages.xml 中有记录的包
list = new ArrayList<>(mSettings.mPackages.size());
for (PackageSetting ps : mSettings.mPackages.values()) {
if (filterSharedLibPackageLPr(ps, callingUid, userId, flags)) {
continue;
}
if (shouldFilterApplicationLocked(ps, callingUid, userId)) {
continue;
}
final PackageInfo pi = generatePackageInfo(ps, flags, userId);
if (pi != null) {
list.add(pi);
}
}
} else {
// 否则,从 mPackages 中获取
list = new ArrayList<>(mPackages.size());
for (AndroidPackage p : mPackages.values()) {
final PackageSetting ps = getPackageSetting(p.getPackageName());
if (filterSharedLibPackageLPr(ps, callingUid, userId, flags)) {
continue;
}
if (shouldFilterApplicationLocked(ps, callingUid, userId)) {
continue;
}
final PackageInfo pi = generatePackageInfo(ps, flags, userId);
if (pi != null) {
list.add(pi);
}
}
}
if (listApex) {
if (listFactory) {
list.addAll(mApexManager.getFactoryPackages());
} else {
list.addAll(mApexManager.getActivePackages());
}
if (listUninstalled) {
list.addAll(mApexManager.getInactivePackages());
}
}
return new ParceledListSlice<>(list);
}
}
}
这里根据传入的 flags 参数来判断 listUninstalled 是否为 true,前面我们传的 flags 是 0 ,所以这里 listUninstalled 是 false 。代码中判断如果 listUninstalled 是 true,从 mSettings.mPackages 中获取 PackageInfo 列表,否则从 mPackages 中获取。mSettings 和 mPackages 都是 PMS 中的成员。
mSettings.mPackages 和 mPackages 中的数据又是从哪里来的呢?我们来看看 PMS 的初始化过程。
PMS的初始化
PackageManagerService 的初始化在 SystemServer 的 startBootstrapServices() 方法中:
private void startBootstrapServices(@NonNull TimingsTraceAndSlog t) {
...
// Installer
Installer installer = mSystemServiceManager.startService(Installer.class);
...
// AMS
mActivityManagerService = ActivityManagerService.Lifecycle.startService(mSystemServiceManager, atm);
...
mActivityManagerService.setInstaller(installer);
...
// PMS
mPackageManagerService = PackageManagerService.main(mSystemContext, installer,
mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);
...
mFirstBoot = mPackageManagerService.isFirstBoot();
mPackageManager = mSystemContext.getPackageManager();
}
在这里启动了 AMS、PMS,并且都传入了 Installer 的实例。mFirstBoot 表示 PMS 是否首次被启动,mFirstBoot 是后续 WMS 创建时所需的参数,由此可以看出系统服务之间是有依赖关系的,启动顺序不能随意更改。PMS 的 main() 方法代码如下:
public static PackageManagerService main(Context context, Installer installer,
boolean factoryTest, boolean onlyCore) {
...
PackageManagerService m = new PackageManagerService(injector, onlyCore, factoryTest);
...
ServiceManager.addService("package", m);
final PackageManagerNative pmn = m.new PackageManagerNative();
ServiceManager.addService("package_native", pmn);
return m;
}
这里新建了 PackageManagerService 的实例,并将 PMS 注册到 ServiceManager 中。
下面来看看 PMS 的构造方法,PMS 的构造方法大概有 800 多行代码,分为 5 个阶段,每个阶段会打印出相应的 EventLog(EventLog 用于打印 Android 系统的事件日志),如下所示。
- BOOT_PROGRESS_PMS_START(开始阶段)。
- BOOT_PROGRESS_PMS_SYSTEM_SCAN_START(扫描系统分区阶段)。
- BOOT_PROGRESS_PMS_DATA_SCAN_START(扫描 Data 分区阶段)。
- BOOT_PROGRESS_PMS_SCAN_END(扫描结束阶段)。
- BOOT_PROGRESS_PMS_READY(准备就绪阶段)。
下面对每个阶段进行分析。
开始阶段
public PackageManagerService(Injector injector, boolean onlyCore, boolean factoryTest) {
...
// 打印开始阶段日志
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_START,
SystemClock.uptimeMillis());
...
// 屏幕的相关信息
mMetrics = new DisplayMetrics();
mInstaller = injector.getInstaller();
// PackageManagerInternalImpl 是 PackageManager 的本地服务
mPmInternal = new PackageManagerInternalImpl();
// 加入 LocalServices
LocalServices.addService(PackageManagerInternal.class, mPmInternal);
...
// 初始化 mSettings
mSettings = injector.getSettings();
...
// 在 mSettings 中添加多个默认 sharedUserId
mSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED); // 注释 1
mSettings.addSharedUserLPw("android.uid.phone", RADIO_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.log", LOG_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
...
// 创建 Dex 优化工具类
mPackageDexOptimizer = new PackageDexOptimizer(mInstaller, mInstallLock, mContext,
"*dexopt*");
mDexManager =
new DexManager(mContext, this, mPackageDexOptimizer, mInstaller, mInstallLock);
...
// 创建后台线程 ServiceThread
mHandlerThread = new ServiceThread(TAG,
Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/);
mHandlerThread.start();
// 创建 PackageHandler,绑定到 ServiceThread 的消息队列
mHandler = new PackageHandler(mHandlerThread.getLooper());
// 添加到 Watchdog 中检测
Watchdog.getInstance().addThread(mHandler, WATCHDOG_TIMEOUT);
...
// 读取 /data/system/packages.xml 中存储的包信息,
// 解析后将数据存放到 mSettings 中
mFirstBoot = !mSettings.readLPw(mInjector.getUserManagerInternal().getUsers(
/* excludePartial= */ true,
/* excludeDying= */ false,
/* excludePreCreated= */ false));
...
}
在开始阶段创建了多个对象,下面来简单介绍一下其中几个比较关键的对象:
- mSettings:作用是持久化并管理所有已安装应用的运行时状态、权限授予、启用/禁用状态、sharedUserId 映射等元数据,确保系统重启后能准确恢复应用的安装与配置状态。注释 1 处添加默认 sharedUserId(如 android.uid.system)是为了让系统核心 APK(如 framework-res.apk、SystemUI.apk)能以 SYSTEM_UID 运行,并共享同一个 Linux UID,从而共享进程或访问彼此的数据。这些 sharedUserId 必须在读取 packages.xml 之前注册,否则解析到使用该 sharedUserId 的包时会失败。
- mInstaller:Installer 继承自 SystemService,和 PMS、AMS 一样是系统服务,PMS 的很多操作都是由 Installer 来完成的,比如 apk 的安装和卸载。Installer 是 Java 层的客户端,通过 IInstalld AIDL 接口与 native 层的 installd 守护进程进行通信,由 installd 来完成具体的操作。
- mPackageDexOptimizer:Dex 优化的工具类。
- mHandler(PackageHandler类型):PackageHandler 继承自 Handler,它绑定了后台线程 ServiceThread 的消息队列。PMS 通过 PackageHandler 驱动 apk 的复制和安装工作。
- Watchdog:用于检测系统关键线程是否发生死锁或长时间无响应。将 PackageHandler 加入 Watchdog,是为了监控其消息处理是否被阻塞(例如因 I/O 卡住)。若超时未响应,Watchdog 会记录日志并在必要时重启 SystemServer,防止系统僵死。
上面的代码最后调用了 mSettings 的 readLPw() 方法读取 packages.xml(或其备份)文件,里面记录了每个包的权限、签名、组件、sharedUserId、权限授予等信息,对里面的信息进行解析后,添加到 mSettings 中(将 <package> 标签中的信息转换为内存中的 PackageSetting 对象)。
如果成功地从磁盘上的配置文件(packages.xml 或其备份)中完整读取并解析了 PackageManager 的持久化状态,则 mFirstBoot 为 false;如果系统是首次启动,则 packages.xml 和 packages-backup.xml 都不存在,此时 mFirstBoot 将为 true。
packages.xml 在设备的 /data/system/ 目录下,内容如下所示:
...
<package name="com.google.android.calendar" codePath="/product/app/CalendarGooglePrebuilt" nativeLibraryPath="/product/app/CalendarGooglePrebuilt/lib" publicFlags="810270277" privateFlags="-1945628656" ft="187e77be448" it="187e77be448" ut="187e77be448" version="2016607548" sharedUserId="10131" isOrphaned="true">
<sigs count="1" schemeVersion="3">
<cert index="6" />
</sigs>
<perms>
<item name="com.google.android.c2dm.permission.RECEIVE" granted="true" flags="0" />
<item name="android.permission.ACCESS_NETWORK_STATE" granted="true" flags="0" />
</perms>
<proper-signing-keyset identifier="8" />
<domain-verification packageName="com.google.android.calendar" status="0">
<domain name="www.google.com" />
<domain name="calendar.google.com" />
</domain-verification>
</package>
<package name="com.google.android.deskclock" codePath="/product/app/PrebuiltDeskClockGoogle" nativeLibraryPath="/product/app/PrebuiltDeskClockGoogle/lib" publicFlags="810270277" privateFlags="-1945628416" ft="187e77bb568" it="187e77bb568" ut="187e77bb568" version="62000712" userId="10144" isOrphaned="true">
<sigs count="1" schemeVersion="3">
<cert index="6" />
</sigs>
<perms>
<item name="android.permission.FOREGROUND_SERVICE" granted="true" flags="0" />
<item name="android.permission.VIBRATE" granted="true" flags="0" />
<item name="android.permission.WAKE_LOCK" granted="true" flags="0" />
</perms>
<proper-signing-keyset identifier="8" />
</package>
...
其中, package 表示包信息
- name 表示包名
- codePath 表示的是 APK 文件的路径
- nativeLibraryPath 表示应用的 native 库的存储路径
- it 表示首次安装时间戳(十六进制)
- ut 表示最后更新时间
- version 表示应用的版本号
sign 表示应用的签名 perms 表示应用声明使用的权限,每一个子标签代表一项权限
扫描系统分区阶段
// 打印扫描系统分区阶段日志
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SYSTEM_SCAN_START,
startTime);
...
// /sytem/framework
File frameworkDir = new File(Environment.getRootDirectory(), "framework");
...
// 创建包解析器
PackageParser2 packageParser = new PackageParser2(mSeparateProcesses, mOnlyCore,
mMetrics, mCacheDir, mPackageParserCallback);
// 线程池
ExecutorService executorService = ParallelPackageParser.makeExecutorService();
// 扫描 apks 之前准备 apex 的包信息,在 apex 中扫描 apk 的时候需要这些信息
mApexManager.scanApexPackagesTraced(packageParser, executorService);
// 扫描 sytem、vendor、odm、oem、product、system_ext 这些目录下的 overlay 目录
for (int i = mDirsToScanAsSystem.size() - 1; i >= 0; i--) {
final ScanPartition partition = mDirsToScanAsSystem.get(i);
if (partition.getOverlayFolder() == null) {
continue;
}
scanDirTracedLI(partition.getOverlayFolder(), systemParseFlags,
systemScanFlags | partition.scanFlag, 0,
packageParser, executorService);
}
// 扫描 /sytem/framework 目录
scanDirTracedLI(frameworkDir, systemParseFlags,
systemScanFlags | SCAN_NO_DEX | SCAN_AS_PRIVILEGED, 0,
packageParser, executorService);
if (!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) {
// 扫描 sytem、vendor、odm、oem、product、system_ext 这些目录下的 priv-app 目录
scanDirTracedLI(partition.getPrivAppFolder(), systemParseFlags,
systemScanFlags | SCAN_AS_PRIVILEGED | partition.scanFlag, 0,
packageParser, executorService);
}
// 扫描 sytem、vendor、odm、oem、product、system_ext 这些目录下的 app 目录
scanDirTracedLI(partition.getAppFolder(), systemParseFlags,
systemScanFlags | partition.scanFlag, 0,
packageParser, executorService);
}
...
// 用于删除不存在的系统 App
final List<String> possiblyDeletedUpdatedSystemApps = new ArrayList<>();
...
if (!mOnlyCore) {
...
final Iterator<PackageSetting> psit = mSettings.mPackages.values().iterator();
while (psit.hasNext()) {
PackageSetting ps = psit.next();
// 如果不是系统 App,跳过
if ((ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) == 0) {
continue;
}
final AndroidPackage scannedPkg = mPackages.get(ps.name);
if (scannedPkg != null) {
// 如果这个 App 在 mSettings.mDisabledSysPackages 中
if (mSettings.isDisabledSystemPackageLPr(ps.name)) { // 注释 1
// 从 mPackages 中移除
removePackageLI(scannedPkg, true);
// 把 apk 路径添加到 mExpectingBetter 列表中
mExpectingBetter.put(ps.name, ps.codePath);
}
continue;
}
if (!mSettings.isDisabledSystemPackageLPr(ps.name)) {
...
} else {
// 如果这个 App 还在 mSettings.mDisabledSysPackages 中
final PackageSetting disabledPs =
mSettings.getDisabledSystemPkgLPr(ps.name);
// 没有找到该 apk
if (disabledPs.codePath == null || !disabledPs.codePath.exists()
|| disabledPs.pkg == null) { // 注释 2
possiblyDeletedUpdatedSystemApps.add(ps.name);
} else {
// 添加到 mExpectingBetter 列表中,如果扫描 Data 分区的时候找不到这个应用,
// 后面可以从 mExpectingBetter 中恢复这个应用
mExpectingBetter.put(disabledPs.name, disabledPs.codePath);
}
}
}
}
这里调用了 scanDirTracedLI() 方法扫描系统分区(/vendor/app、/sytem/framework、 /sytem/priv-app、/sytem/app 等目录)下的 apk 文件。
- /vendor/app:保存设备厂商提供的 App
- /sytem/framework:保存的是资源型的 App,它们用来打包资源文件;
- /sytem/priv-app:用来存放特权 App;
- /sytem/app:用于存放系统 App;
有可能出现这种情况,设备上有一个非系统应用,现在进行 OTA 升级,发现升级的是同一个应用,但是是系统应用。这时候需要判断两个应用的版本,如果非系统应用的版本更加新,系统应用需要被隐藏。这时候 PMS 会将该系统应用的 PackageSetting 存储到 mSettings 的 mDisabledSysPackages 列表中,见 PMS 的 addForInitLI()方法(scanDirTracedLI() 中会调用 addForInitLI()):
private AndroidPackage addForInitLI(ParsedPackage parsedPackage,
@ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime,
@Nullable UserHandle user) throws PackageManagerException {
...
// 是否要隐藏系统应用
if (shouldHideSystemApp) {
synchronized (mLock) {
// 添加到 mSettings 的 mDisabledSysPackages 列表中
mSettings.disableSystemPackageLPw(parsedPackage.getPackageName(), true);
}
}
}
mDisabledSysPackages 中的信息会被 PMS 保存到 packages.xml 中的 <updated-package 标签下,具体见 Settings 的 writeDisabledSysPackageLPr() 方法。
扫描系统分区阶段代码的注释 1 处就是上面说的这种情况,需要将该 App 从 mPackages 中移除,并将 apk 路径添加到 mExpectingBetter 列表中,等待后续处理。mExpectingBetter 的作用是:如果后续扫描 Data 分区的时候找不到这个应用,可以从 mExpectingBetter 中恢复这个应用。在注释 2 处,如果找不到这个系统应用的 apk 的路径,会将它加入 possiblyDeletedUpdatedSystemApps 列表中,意为 “可能已经删除的升级的系统App” ,之所以是 “可能”,是因为系统还没有扫描 Data 分区。
扫描 Data 分区阶段
if (!mOnlyCore) {
// 打印扫描 Data 分区阶段日志
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START,
SystemClock.uptimeMillis());
// 扫描 /data/app/ 目录
scanDirTracedLI(sAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0,
packageParser, executorService);
}
// 关闭解析器
packageParser.close();
// 关闭线程池
List<Runnable> unfinishedTasks = executorService.shutdownNow();
...
if (!mOnlyCore) {
// 扫描完 Data 分区后,处理 possiblyDeletedUpdatedSystemApps 列表
for (int i = possiblyDeletedUpdatedSystemApps.size() - 1; i >= 0; --i) {
final String packageName = possiblyDeletedUpdatedSystemApps.get(i);
final AndroidPackage pkg = mPackages.get(packageName);
final String msg;
// 从 mSetting.mDisabledSysPackages 中移除此应用
mSettings.removeDisabledSystemPackageLPw(packageName);
// 如果这个系统 App 的包信息不在 mPackages 中,
// 则说明其是残留的 App 信息,后续会在 reconciliation 这一步中删除它的数据
if (pkg == null) {
msg = "Updated system package " + packageName
+ " no longer exists; removing its data";
} else {
// 如果这个系统 App 在 mPackages 中,则说明其存在于 Data 分区中,
// 不属于系统 App, 那么移除其系统权限
msg = "Updated system package " + packageName
+ " no longer exists; rescanning package on data";
removePackageLI(pkg, true);
try {
final File codePath = new File(pkg.getCodePath());
scanPackageTracedLI(codePath, 0, scanFlags, 0, null);
} catch (PackageManagerException e) {
Slog.e(TAG, "Failed to parse updated, ex-system package: "
+ e.getMessage());
}
}
// 如果这个系统 App 的包信息不在 mPackages 中,但是它在 mSettings
// 的 mPackages 中,可能是之前系统扫描拿到的,但是不存在于 Data 分区中,需要
// 移除包数据
final PackageSetting ps = mSettings.mPackages.get(packageName);
if (ps != null && mPackages.get(packageName) == null) {
removePackageDataLIF(ps, null, null, 0, false);
}
logCriticalInfo(Log.WARN, msg);
}
// 遍历 mExpectingBetter 列表
for (int i = 0; i < mExpectingBetter.size(); i++) {
final String packageName = mExpectingBetter.keyAt(i);
// 如果在 Data 分区没有扫描到这个应用
if (!mPackages.containsKey(packageName)) {
// 拿到系统 App 的升级包路径
final File scanFile = mExpectingBetter.valueAt(i);
logCriticalInfo(Log.WARN, "Expected better " + packageName
+ " but never showed up; reverting to system");
@ParseFlags int reparseFlags = 0;
@ScanFlags int rescanFlags = 0;
// 根据系统 App 所在的目录设置扫描的解析参数
for (int i1 = mDirsToScanAsSystem.size() - 1; i1 >= 0; i1--) {
final ScanPartition partition = mDirsToScanAsSystem.get(i1);
if (partition.containsPrivApp(scanFile)) {
reparseFlags = systemParseFlags;
rescanFlags = systemScanFlags | SCAN_AS_PRIVILEGED
| partition.scanFlag;
break;
}
if (partition.containsApp(scanFile)) {
reparseFlags = systemParseFlags;
rescanFlags = systemScanFlags | partition.scanFlag;
break;
}
}
// 将 packageName 对应的包配置(PackageSetting)添加
// 到 mSettings.mPackages 中
mSettings.enableSystemPackageLPw(packageName);
// 重新扫描该系统 App
scanPackageTracedLI(scanFile, reparseFlags, rescanFlags, 0, null);
}
}
...
}
// 清空 mExpectingBetter
mExpectingBetter.clear();
...
扫描 Data 分区阶段的主要工作有以下3点:
- 调用 scanDirTracedLI() 方法扫描 /data/app 目录下的 apk 文件;
- 遍历 possiblyDeletedUpdatedSystemApps 列表,如果这个系统 App 的包信息不在 mPackages 中,则说明其是残留的 App 信息,后续会删除它的数据。如果这个系统 App 的包信息在 mPackages 中,则说明其存在于 Data 分区中,不属于系统 App,那么移除其系统权限。
- 遍历 mExpectingBetter 列表,如果在 Data 分区没有扫描到这个系统 App,根据系统 App 所在的目录设置扫描的解析参数,将 packageName 对应的包配置添加到 mSettings.mPackages 中。调用 scanPackageTracedLI() 扫描该系统 App,最后清理 mExpectingBetter 列表。
扫描结束阶段
// 打印扫描结束阶段日志
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SCAN_END,
SystemClock.uptimeMillis());
// 如果当前平台的 SDK 版本跟上次启动时的 SDK 版本不同,则更新 apk 的授权
final boolean sdkUpdated = (ver.sdkVersion != mSdkVersion);
mPermissionManager.updateAllPermissions(
StorageManager.UUID_PRIVATE_INTERNAL, sdkUpdated);
ver.sdkVersion = mSdkVersion;
// 如果是第一次启动或者是 Android M 升级后的第一次启动,
// 则需要初始化所有用户定义的默认首选 App
if (!mOnlyCore && (mPromoteSystemApps || mFirstBoot)) {
for (UserInfo user : mInjector.getUserManagerInternal().getUsers(true)) {
mSettings.applyDefaultPreferredAppsLPw(user.id);
primeDomainVerificationsLPw(user.id);
}
}
...
// OTA 升级后的第一次启动,且非“仅核心模式”启动时,
// 清除所有内部存储上已安装应用的代码缓存(如 dexopt 生成的 oat/art 文件),
// 以确保新系统环境下运行的是与当前系统兼容的优化代码。
if (mIsUpgrade && !mOnlyCore) {
Slog.i(TAG, "Build fingerprint changed; clearing code caches");
for (int i = 0; i < mSettings.mPackages.size(); i++) {
final PackageSetting ps = mSettings.mPackages.valueAt(i);
if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, ps.volumeUuid)) {
// No apps are running this early, so no need to freeze
clearAppDataLIF(ps.pkg, UserHandle.USER_ALL,
FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL
| Installer.FLAG_CLEAR_CODE_CACHE_ONLY
| Installer.FLAG_CLEAR_APP_DATA_KEEP_ART_PROFILES);
}
}
ver.fingerprint = Build.FINGERPRINT;
}
...
// 把 mSettings 的内容保存到 packages.xml 中
mSettings.writeLPr();
扫描结束阶段的主要工作有以下几点。
- 如果当前平台的 SDK 版本和上次启动时的 SDK 版本不同,则更新 apk 的授权。
- 如果是第一次启动或者是 Android M 升级后的第一次启动,则需要初始化所有用户定义的默认首选 App。
- OTA 升级后的第一次启动,且非“仅核心模式”启动时,清除所有内部存储上已安装应用的代码缓存(如 dexopt 生成的 oat/art 文件),以确保新系统环境下运行的是与当前系统兼容的优化代码。
- 调用 mSettings 的 writeLPr() 方法把 mSettings 中的内容保存到 packages.xml 中,这样以后 PMS 再创建时会读到此前保存的内容。
准备就绪阶段
// 打印准备就绪阶段的日志
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_READY,
SystemClock.uptimeMillis());
...
// 创建 PackageInstallerService
mInstallerService = new PackageInstallerService(mContext, this, apexParserSupplier);
...
// 垃圾回收
Runtime.getRuntime().gc();
...
// The initial scanning above does many calls into installd while
// holding the mPackages lock, but we're mostly interested in yelling
// once we have a booted system.
mInstaller.setWarnIfHeld(mLock);
...
这里创建了 PackageInstallerService,PackageInstallerService 是用于管理安装会话的服务,它会为每次的安装过程分配一个 Sessionld,这里还进行了一次垃圾收集。
apk的解析过程
扫描系统分区和扫描 Data 分区都会调用 scanDirTracedLI() 方法扫描对应的目录,代码如下:
private void scanDirTracedLI(File scanDir, final int parseFlags, int scanFlags,
long currentTime, PackageParser2 packageParser, ExecutorService executorService) {
...
scanDirLI(scanDir, parseFlags, scanFlags, currentTime, packageParser, executorService);
...
}
里面调用了scanDirLI():
private void scanDirLI(File scanDir, int parseFlags, int scanFlags, long currentTime,
PackageParser2 packageParser, ExecutorService executorService) {
final File[] files = scanDir.listFiles();
...
// ParallelPackageParser 中传入了 executorService
ParallelPackageParser parallelPackageParser =
new ParallelPackageParser(packageParser, executorService);
int fileCount = 0;
for (File file : files) {
// 把解析任务提交给线程池处理
parallelPackageParser.submit(file, parseFlags);
fileCount++;
}
...
// 处理解析结果
for (; fileCount > 0; fileCount--) {
ParallelPackageParser.ParseResult parseResult = parallelPackageParser.take();
...
addForInitLI(parseResult.parsedPackage, parseFlags, scanFlags,
currentTime, null);
}
...
}
在这里调用了 ParallelPackageParser 的 submit() 方法,然后使用 addForInitLI() 处理解析结果。ParallelPackageParser 的 submit() 方法代码如下:
class ParallelPackageParser {
private final PackageParser2 mPackageParser;
private final ExecutorService mExecutorService;
ParallelPackageParser(PackageParser2 packageParser, ExecutorService executorService) {
mPackageParser = packageParser;
mExecutorService = executorService;
}
/**
* Submits the file for parsing
*/
public void submit(File scanFile, int parseFlags) {
mExecutorService.submit(() -> {
ParseResult pr = new ParseResult();
...
pr.scanFile = scanFile;
pr.parsedPackage = parsePackage(scanFile, parseFlags);
...
mQueue.put(pr);
...
});
}
protected ParsedPackage parsePackage(File scanFile, int parseFlags)
throws PackageParser.PackageParserException {
return mPackageParser.parsePackage(scanFile, parseFlags, true);
}
}
在 ParallelPackageParser 中使用了线程池来处理解析任务,调用了 PackageParser2 的 parsePackage() 方法:
public class PackageParser2 implements AutoCloseable {
private ParsingPackageUtils parsingUtils;
public PackageParser2(String[] separateProcesses, boolean onlyCoreApps,
DisplayMetrics displayMetrics, @Nullable File cacheDir, @NonNull Callback callback) {
...
parsingUtils = new ParsingPackageUtils(onlyCoreApps, separateProcesses, displayMetrics,
callback);
...
}
public ParsedPackage parsePackage(File packageFile, int flags, boolean useCaches)
throws PackageParserException {
...
ParseInput input = mSharedResult.get().reset();
ParseResult<ParsingPackage> result = parsingUtils.parsePackage(input, packageFile, flags);
...
ParsedPackage parsed = (ParsedPackage) result.getResult().hideAsParsed();
...
return parsed;
}
}
parsingUtils 是 ParsingPackageUtils 的实例,继续看 ParsingPackageUtils 的 parsePackage() 方法:
public ParseResult<ParsingPackage> parsePackage(ParseInput input, File packageFile,
int flags)
throws PackageParserException {
if (packageFile.isDirectory()) {
return parseClusterPackage(input, packageFile, flags);
} else {
return parseMonolithicPackage(input, packageFile, flags);
}
}
上面的代码通过判断 packageFile 是否是目录走向了两个分支。
Android 5.0 引人了 SplitAPK 机制其目的是解决 65536 上限及 apk 安装包越来越大的问题。SplitAPK 机制可以将一个 apk 拆分成多个独立的 apk。在引入了 SplitAPK 机制后,APK 有以下两种分类。
- SingleAPK:一个完整的 apk 安装文件,即 baseAPK。Android 称其为 Monolithic;
- MutipleAPK:在一个文件目录中安装文件,其内部有多个被拆分的 apk,这些 apk 由一个 baseAPK 和一个或多个 splitAPK 组成。Android 称其为 Cluster;
如果 packageFile 是一个目录,说明 apk 的种类是 MutipleAPK,需要调用 parseClusterPackage() 方法来解析,否则调用 parseMonolithicPackage() 方法。parseClusterPackage() 方法更加复杂,我们来看看这个方法:
private ParseResult<ParsingPackage> parseClusterPackage(ParseInput input, File packageDir,
int flags) {
// 轻量级解析目录文件
ParseResult<PackageParser.PackageLite> liteResult =
ApkLiteParseUtils.parseClusterPackageLite(input, packageDir, 0);
if (liteResult.isError()) {
return input.error(liteResult);
}
final PackageParser.PackageLite lite = liteResult.getResult();
// mOnlyCoreApps 用于指示是否只解析核心应用
// lite.coreApp 表示当前包是否包含核心应用
if (mOnlyCoreApps && !lite.coreApp) {
return input.error(INSTALL_PARSE_FAILED_ONLY_COREAPP_ALLOWED,
"Not a coreApp: " + packageDir);
}
...
try {
final AssetManager assets = assetLoader.getBaseAssetManager();
final File baseApk = new File(lite.baseCodePath);
// 解析 baseApk
ParseResult<ParsingPackage> result = parseBaseApk(input, baseApk,
lite.codePath, assets, flags);
if (result.isError()) {
return input.error(result);
}
ParsingPackage pkg = result.getResult();
if (!ArrayUtils.isEmpty(lite.splitNames)) {
pkg.asSplit(
lite.splitNames,
lite.splitCodePaths,
lite.splitRevisionCodes,
splitDependencies
);
final int num = lite.splitNames.length;
for (int i = 0; i < num; i++) {
final AssetManager splitAssets = assetLoader.getSplitAssetManager(i);
parseSplitApk(input, pkg, i, splitAssets, flags);
}
}
...
return input.success(pkg);
}
}
这里先调用了 parseClusterPackageLite() 方法,用于轻量级解析目录文件,之所以要轻量级解析,是因为解析 apk 是一个复杂且耗时的工作,这里的逻辑并不需要 apk 的所有信息。parseClusterPackageLite() 方法内部会通过 parseApkLite() 方法解析每个 MutipleAPK,得到每个 MutipleAPK 对应的 ApkLite(轻量级apk信息),然后将这些 ApkLite 封装为一个 PackageLite(轻量级包信息)并返回。
mOnlyCoreApps 用于指示是否只解析“核心”应用,“核心”应用指的是 AndroidManifest.xml 中属性 coreApp 的值为 true,只解析“核心”应用是为了创建一个极简的启动环境。它是在创建 PMS 时 mOnlyCoreApps 一路传递过来,如果我们加密了设备,mOnlyCoreApps的值就为 true,另外可以通过 PackageParser 的 setOnlyCoreApps() 方法来设置 mOnlyCoreApps 的值。lite.coreApp 表示当前包是否包含“核心”应用。
然后调用了 parseBaseApk() 方法用于解析 baseApk,最后获取了 splitAPK 的数量,根据这个数量遍历调用 parseSplitApk() 方法来解析每个 splitAPK。这里主要查看 parseBaseApk() 方法,parseMonolithicPackage() 也会走到 parseBaseApk() 方法,其代码如下:
private ParseResult<ParsingPackage> parseBaseApk(ParseInput input, File apkFile,
String codePath, AssetManager assets, int flags) {
final String apkPath = apkFile.getAbsolutePath();
String volumeUuid = null;
if (apkPath.startsWith(PackageParser.MNT_EXPAND)) { // 注释 1
final int end = apkPath.indexOf('/', PackageParser.MNT_EXPAND.length());
volumeUuid = apkPath.substring(PackageParser.MNT_EXPAND.length(), end);
}
...
XmlResourceParser parser = assets.openXmlResourceParser(cookie,
PackageParser.ANDROID_MANIFEST_FILENAME);
final Resources res = new Resources(assets, mDisplayMetrics, null);
ParseResult<ParsingPackage> result = parseBaseApk(input, apkPath, codePath, res, parser, flags);
final ParsingPackage pkg = result.getResult();
...
pkg.setVolumeUuid(volumeUuid); // 注释 2
...
return input.success(pkg);
}
在注释 1 处,如果 apk 的路径以 /mnt/expand/ 开头,就截取该路径来获取 volumeUuid。注释 2 处的代码用于以后标识这个解析后的 Package。这里创建了 XmlResourceParser 实例,用于解析 apk 中的 AndroidManifest.xml 文件,然后调用了重载的 parseBaseApk() 方法:
private ParseResult<ParsingPackage> parseBaseApk(ParseInput input, String apkPath,
String codePath, Resources res, XmlResourceParser parser, int flags)
throws XmlPullParserException, IOException, PackageParserException {
...
// 从资源中提取自定义属性集 com.android.internal.R.styleable.AndroidManifest
// 并得到 TypedArray
final TypedArray manifestArray = res.obtainAttributes(parser, R.styleable.AndroidManifest);
// 创建 ParsingPackage 实例用于承载解析结果
final ParsingPackage pkg = mCallback.startParsingPackage(
pkgName, apkPath, codePath, manifestArray, isCoreApp);
final ParseResult<ParsingPackage> result =
parseBaseApkTags(input, pkg, manifestArray, res, parser, flags);
...
return input.success(pkg);
}
这里先从资源中提取了自定义属性集 com.android.internal.R.styleable.AndroidManifest 并得到 TypedArray,这个属性集所在的源码位置为:frameworks/base/core/res/res/values/ attrs_manifest.xml。然后调用了 parseBaseApkTags() :
private ParseResult<ParsingPackage> parseBaseApkTags(ParseInput input, ParsingPackage pkg,
TypedArray sa, Resources res, XmlResourceParser parser, int flags)
throws XmlPullParserException, IOException {
...
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> 标签
if (PackageParser.TAG_APPLICATION.equals(tagName)) {
if (foundApp) {
if (PackageParser.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);
}
} else {
result = parseBaseApkTag(tagName, input, pkg, res, parser, flags);
}
}
...
return input.success(pkg);
}
这里使用 XmlResourceParser 查找 AndroidManifest.xml 中的 <application> 标签,然后调用 parseBaseApplication() 来解析 apk 中的基础 Application 节点树:
private ParseResult<ParsingPackage> parseBaseApplication(ParseInput input,
ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags)
throws XmlPullParserException, IOException {
...
// 解析 <application> 标签中的属性:theme 这些
parseBaseAppBasicFlags(pkg, sa);
...
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG
|| parser.getDepth() > depth)) {
...
final ParseResult result;
String tagName = parser.getName();
boolean isActivity = false;
switch (tagName) {
case "activity":
isActivity = true;
// fall-through
case "receiver":
ParseResult<ParsedActivity> activityResult =
ParsedActivityUtils.parseActivityOrReceiver(mSeparateProcesses, pkg,
res, parser, flags, PackageParser.sUseRoundIcon, input);
if (activityResult.isSuccess()) {
ParsedActivity activity = activityResult.getResult();
// 判断是 Activity
if (isActivity) {
hasActivityOrder |= (activity.getOrder() != 0);
pkg.addActivity(activity); // 添加 Activity
} else {
hasReceiverOrder |= (activity.getOrder() != 0);
pkg.addReceiver(activity); // 添加 Receiver
}
}
result = activityResult;
break;
case "service":
ParseResult<ParsedService> serviceResult =
ParsedServiceUtils.parseService(mSeparateProcesses, pkg, res, parser,
flags, PackageParser.sUseRoundIcon, input);
if (serviceResult.isSuccess()) {
ParsedService service = serviceResult.getResult();
hasServiceOrder |= (service.getOrder() != 0);
pkg.addService(service); // 添加Service
}
result = serviceResult;
break;
case "provider":
ParseResult<ParsedProvider> providerResult =
ParsedProviderUtils.parseProvider(mSeparateProcesses, pkg, res, parser,
flags, PackageParser.sUseRoundIcon, input);
if (providerResult.isSuccess()) {
pkg.addProvider(providerResult.getResult()); // 添加Provider
}
result = providerResult;
break;
case "activity-alias":
activityResult = ParsedActivityUtils.parseActivityAlias(pkg, res,
parser, PackageParser.sUseRoundIcon, input);
if (activityResult.isSuccess()) {
ParsedActivity activity = activityResult.getResult();
hasActivityOrder |= (activity.getOrder() != 0);
pkg.addActivity(activity);
}
result = activityResult;
break;
default:
result = parseBaseAppChildTag(input, tagName, pkg, res, parser, flags);
break;
}
if (result.isError()) {
return input.error(result);
}
}
...
return input.success(pkg);
}
这里就是解析 Application 的子节点——四大组件,并把信息存入 ParsingPackage 实例 pkg 中,最后转成 ParallelPackageParser.ParseResult。然后调用了 addForInitLI() 方法,这个方法逻辑比较复杂,有很多对系统应用的校验(包名修改、版本升级、签名收集和校验),但我们只关注下面内容:
// 在系统启动期间,将新安装的包添加到其内部的数据结构中。
// 添加后,该包对于系统就是可见的,可以用于查询。
private AndroidPackage addForInitLI(ParsedPackage parsedPackage,
@ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime,
@Nullable UserHandle user)
throws PackageManagerException {
...
// 继续扫描
final ScanResult scanResult = scanPackageNewLI(parsedPackage, parseFlags, scanFlags
| SCAN_UPDATE_SIGNATURE, currentTime, user, null);
if (scanResult.success) {
synchronized (mLock) {
boolean appIdCreated = false;
final String pkgName = scanResult.pkgSetting.name;
final Map<String, ReconciledPackage> reconcileResult = reconcilePackagesLocked(
new ReconcileRequest(
Collections.singletonMap(pkgName, scanResult),
mSharedLibraries,
mPackages,
Collections.singletonMap(pkgName, getSettingsVersionForPackage(parsedPackage)),
Collections.singletonMap(pkgName, getSharedLibLatestVersionSetting(scanResult))),
mSettings.mKeySetManagerService);
appIdCreated = optimisticallyRegisterAppId(scanResult);
// 提交扫描结果,更新系统状态
commitReconciledScanResultLocked(
reconcileResult.get(pkgName), mUserManager.getUserIds());
}
}
...
return scanResult.pkgSetting.pkg;
}
里面调用 scanPackageNewLI() 继续扫描,主要是更新 mSettings 中的成员变量 mPackages。然后调用 reconcilePackagesLocked() 进行一致化处理,再调用 commitReconciledScanResultLocked() 提交包扫描结果、更新系统状态,内部又调用了commitPackageSettings():
// 添加扫描完成的包到系统,这个方法执行完后,包可以用于查询、解析
private void commitPackageSettings(AndroidPackage pkg,
@Nullable AndroidPackage oldPkg, PackageSetting pkgSetting,
final @ScanFlags int scanFlags, boolean chatty, ReconciledPackage reconciledPkg) {
...
synchronized (mLock) {
// 添加到 mSettings.mPackages 中
mSettings.insertPackageSettingLPw(pkgSetting, pkg);
// 添加到 mPackages 中
mPackages.put(pkg.getPackageName(), pkg);
...
// 添加组件到 mComponentResolver:所有的四大组件信息添加到内部数据结构中
mComponentResolver.addAllComponents(pkg, chatty);
...
// 添加权限到 mPermissionManager 中
mPermissionManager.addAllPermissionGroups(pkg, chatty);
...
for (i = 0; i < collectionSize; i++) {
ParsedInstrumentation a = pkg.getInstrumentations().get(i);
a.setPackageName(pkg.getPackageName());
mInstrumentation.put(a.getComponentName(), a);
}
}
}
在这里,把 PackageSetting 添加到 mSettings.mPackages 中,把 AndroidPackage 添加到 mPackages 中,四大组件信息添加到 mComponentResolver 中,应用的权限添加到 mPermissionManager 中。这样就把包信息记录在了 PMS 管理的内存中。
总结一下:
- 系统启动后会启动 SystemServer,在 SystemServer 的 startBootstrapServices() 中会调用 PMS 的构造方法,PMS 的构造方法共分为 5 个阶段:开始阶段、扫描系统分区阶段、扫描 Data 分区阶段、扫描结束阶段、准备就绪阶段。
- 在开始阶段,初始化关键对象(如 mSettings、mInstaller、PackageHandler),并读取 /data/system/packages.xml 文件,将其内容解析为 PackageSetting 对象存入 mSettings.mPackages,但尚未构建运行时包对象 AndroidPackage。
- 在扫描系统分区阶段,扫描 /system、/vendor 等目录下的 APK,解析后构建 AndroidPackage 对象,并创建或更新其对应的 PackageSetting,确保其存在于 mSettings.mPackages 中,并将 AndroidPackage 加入 PMS 的 mPackages,同时注册四大组件和权限。
- 在扫描 Data 分区阶段,扫描 /data/app 目录,执行类似的解析与注册流程(使用不同扫描标志),并处理 OTA 升级后可能被 disable 的系统应用(如从 mExpectingBetter 恢复)。
- 在扫描结束阶段,将 mSettings 中的最新包状态(包括新安装、权限变更等)写回 packages.xml,实现持久化。
- 在准备就绪阶段,创建 PackageInstallerService(用于管理安装会话),执行 GC,并完成 PMS 初始化。