Android 系统基于 Linux 系统开发,而 Android 系统基于 Linux 系统的用户机制,为应用程序分配一个用户ID,应用程序还可以拥有若干个 Linux 用户组ID,通过用户ID和用户组ID,实现了对应用程序权限访问的机制;Android 系统的用户则是单独的一套机制,与 Linux 的用户机制不同,这里需要注意;
Android 系统启动时,system_server 进程会启动 PMS 服务,PMS 服务是应用程序的管理服务,PMS 的构造方法内会扫描预置 apk 文件的目录,对保存的应用程序进行安装,应用程序的安装过程主要有两个操作:
- 扫描 apk 的 AndroidManifest.xml 文件,获取应用程序的组件、权限等信息;
- 应用程序分配 Linux 用户ID和用户组ID,控制应用程序的权限访问;
由于 Android 系统每次启动时,都会重新安装一遍系统中的应用程序,但是有些应用程序的信息每次安装都需要保持一致,eg:应用程序的Linux用户ID,否则每次系统重启之后,表现会不一致,因此PMS在应用安装完成之后,需要将应用的安装信息保存下来,以便系统启动时可以恢复,在 PMS 内通过 Settings 类型的成员 mSettings 变量来实现安装信息的存储和恢复。
1、PMS 安装应用程序
PMS 的构造方法的主要进行三个操作:
- 恢复系统安装的应用程序信息;
- 系统预置的 apk 文件的进行安装和授权;
- 保存应用程序的安装信息;
在开始分析之前,先回答两个问题。
为什么要保存和恢复应用程序安装的信息?
- 因为在 Android 系统中,应用程序拥有一个 Linux 用户ID,该信息在系统中是唯一的,为了应用程序表现的一致性,所以在系统重启后,需要保证应用程序的用户ID等信息稳定。
什么是共享用户?
- 包含一系列权限的用户,当应用程序申请某个共享用户,相当于拥有了该共享用户所拥有的权限,eg:android.uid.system 共享用户,包含一系列的系统权限,应用在 AndroidMainfest.xml 文件的 manifest 标签内添加如下配置
android:sharedUserId="android.uid.system"
, 即可申请该共享用户的所拥有的权限信息。
下面分析下 PMS 构造函数的代码
// PackageManagerService.java
public PackageManagerService(Injector injector ...) {
...
// 管理应用程序的安装信息
mSettings = injector.getSettings();
...
// 添加系统默认的共享用户
mSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
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);
mSettings.addSharedUserLPw("android.uid.nfc", NFC_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.bluetooth", BLUETOOTH_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.shell", SHELL_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.se", SE_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.networkstack", NETWORKSTACK_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.uwb", UWB_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
...
// 目录:/data/app
mAppInstallDir = new File(Environment.getDataDirectory(), "app");
...
synchronized (mLock) {
// 添加超时任务,超时时间 10 分钟
Watchdog.getInstance().addThread(mHandler, WATCHDOG_TIMEOUT);
// 1、从 packages.xml 文件中恢复上次应用程序的安装信息
mFirstBoot = !mSettings.readLPw(mInjector.getUserManagerInternal().getUsers());
// 目录:/system/framework
File frameworkDir = new File(Environment.getRootDirectory(), "framework");
...
// 解析apk文件的解析器
PackageParser2 packageParser = injector.getScanningCachingPackageParser();
// 解析apk文件的线程池
ExecutorService executorService = ParallelPackageParser.makeExecutorService();
// 2、安装应用程序
// 2.1、安装系统预置目录下 overlay 文件内的apk文件
for (int i = mDirsToScanAsSystem.size() - 1; i >= 0; i--) {
final ScanPartition partition = mDirsToScanAsSystem.get(i);
if (partition.getOverlayFolder() == null) {
continue;
}
// 安装 /oem、/odm、/vendor、/product、/system_ext 等目录下 overlay 文件内的apk文件
scanDirTracedLI(partition.getOverlayFolder() ...);
}
// 2.2、安装 /system/framework 目录下的apk文件
scanDirTracedLI(frameworkDir ...);
// 2.3、安装系统预置目录下 priv-app 文件内的apk文件
for (int i = 0, size = mDirsToScanAsSystem.size(); i < size; i++) {
final ScanPartition partition = mDirsToScanAsSystem.get(i);
if (partition.getPrivAppFolder() != null) {
// 安装 /system、/odm、/vendor、/product、/system_ext 等目录下 priv-app 文件内的apk文件
scanDirTracedLI(partition.getPrivAppFolder() ... packageParser, executorService);
}
// 安装 /system、/oem、/odm、/vendor、/product、/system_ext 等目录下 app 文件内的apk文件
scanDirTracedLI(partition.getAppFolder() ...);
}
// 2.4、安装非系统预置目录下 /data/app 文件内的apk文件(通过AS/adb等方式安装的apk会在该目录下)
if (!mOnlyCore) {
// 安装 /data/app 目录下的apk文件
scanDirTracedLI(mAppInstallDir ...);
}
...
// 3、删除未被应用程序使用的共享用户
mSettings.pruneSharedUsersLPw();
...
// 4、记录应用程序安装信息
writeSettingsLPrTEMP();
...
}
}
Settings 是用于维护应用程序安装信息的类,主要用于保存和恢复应用程序的安装信息,该类将数据保存到 /data/system/packages.xml文件内,文件内容请参考附录。
在 PMS 的构造方法内,主要任务分为4个部分:
- Settings.addSharedUserLPw() 方法,系统会先记录一些默认的共享用户,如果共享用户没有应用程序使用,则会被删除;
- Settings.readLPw() 方法,该方法内部对 packages.xml 文件进行解析处理,将恢复的应用程序、共享用户等信息放入相应的集合内,用于后续的应用程序更新和保存;
- scanDirLI 方法扫描系统预置APK文件的路径,安装/更新/删除应用程序;
- Settings.writeLPr 方法,将应用程序、共享用户等信息从集合写入 packages.xml 文件内,用于系统重启时,安装信息的恢复操作;
2、恢复应用程序
Settings 恢复应用程序的安装信息时序图
解析已安装的信息的过程通过 Settings 类进行,主要分为3个步骤:
- readPackageLPw 方法,读取已安装的应用程序信息;
- readeSharedUserLPw 方法,读取已安装的共享用户信息;
- loop 循环更新 mPendingPackages 变量,记录了使用共享用户ID作为用户ID的应用程序,设置共享用户信息和 Linux 用户ID;
2.1、解析已安装的信息
// Settings.java
// 记录应用程序配置信息的Map,Key:用户包名;Value:应用程序配置信息
final WatchedArrayMap<String, PackageSetting> mPackages;
// 记录使用共享用户ID作为用户ID的应用程序,因为共享用户ID有可能发生变化,所以在解析完成之后再设置共享应用程序的用户ID
private final WatchedArrayList<PackageSetting> mPendingPackages = new WatchedArrayList<>();
// 构造方法,初始化存放应用程序配置信息的文件和记录应用配置信息的集合
Settings(File dataDir ...) {
// 记录应用程序配置信息的Map
mPackages = new WatchedArrayMap<>();
// 记录应用程序用户ID
mAppIds = new WatchedArrayList<>();
// 记录shared-user用户ID
mOtherAppIds = new WatchedSparseArray<>();
// 路径:/data/system
mSystemDir = new File(dataDir, "system");
mSystemDir.mkdirs();
// 应用程序配置信息文件路径:/data/system/packages.xml
mSettingsFilename = new File(mSystemDir, "packages.xml");
// 应用程序配置信息备份文件路径:/data/system/packages-backup.xml
mBackupSettingsFilename = new File(mSystemDir, "packages-backup.xml");
}
// readLPw 方恢复已安装的信息
// users 参数表示 Android 系统的用户,该信息记录在 /data/system/users/userslist.xml 文件,用户信息会根据用户id保存到对应的/data/system/users/(id).xml文件,此处的User表明应用程序属于哪个Android系统用户,用于账号间的数据隔离。
boolean readLPw(List<UserInfo> users) {
FileInputStream str = null;
// 如果上次写入已安装的信息出错,则备份文件会存在,系统从备份文件恢复已安装的信息
// 文件路径:/data/system/packages-backup.xml
if (mBackupSettingsFilename.exists()) {
try {
str = new FileInputStream(mBackupSettingsFilename);
// 正常存储的场景,备份文件应该不存在,所以删除已损坏的 packages.xml 文件
if (mSettingsFilename.exists()) mSettingsFilename.delete();
} catch (java.io.IOException e) {
...
}
}
...
try {
// 如果备份文件不存在,说明上次存储正常,从 packages.xml 文件中恢复已安装的信息
if (str == null) {
...
str = new FileInputStream(mSettingsFilename);
}
// 解析 xml 文件
final TypedXmlPullParser parser = Xml.resolvePullParser(str);
...
// 开始循环解析各个 attr 内容,这里主要关注 pacakge 和 shared-user 属性
int outerDepth = parser.getDepth();
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
// 过滤不需要检测的内容
...
String tagName = parser.getName();
if (tagName.equals("package")) {
// 解析 package 属性,主要是应用程序信息
readPackageLPw(parser, users);
} else if (tagName.equals("shared-user")) {
// 解析 shared-user 属性,主要是共享用户信息
readSharedUserLPw(parser, users);
}
...
}
str.close();
} catch (IOException | XmlPullParserException e) {
...
}
// 设置使用共享用户作为用户信息的应用程序,在解析package属性时,如果应用程序是共享用户,则会存储 mPendingPackages 集合内
final int N = mPendingPackages.size();
for (int i = 0; i < N; i++) {
final PackageSetting p = mPendingPackages.get(i);
final int sharedUserId = p.getSharedUserId();
final Object idObj = getSettingLPr(sharedUserId);
if (idObj instanceof SharedUserSetting) {
final SharedUserSetting sharedUser = (SharedUserSetting) idObj;
// 设置共享用户信息
p.sharedUser = sharedUser;
// 设置应用程序用户ID为共享用户ID,eg:framework-res.apk 等应用程序即属于共享用户应用程序
p.appId = sharedUser.userId;
// 存入已安装的应用程序集合 mPackages
addPackageSettingLPw(p, sharedUser);
}
...
}
mPendingPackages.clear();
...
return true;
}
恢复已安装的信息,主要分为4个步骤:
- PMS构造方法内,创建 Settings 对象,初始化记录数据的文件和集合,用于管理应用程序等安装信息;
- 解析 package 属性,表示应用程序的安装信息;
- 解析 shared-user 属性,表示共享用户的安装信息;
- 更新应用程序依赖的共享用户,因为共享用户信息有可能会发生变化,所以在共享用户信息恢复之后,再设置应用程序的共享用户信息;
2.2、解析 package 属性
- package 属性主要记录了应用程序的安装信息,具体内容请参考附录;
- updated-pacakge 属性同样记录了应用程序的安装信息,但主要是系统预置的较低版本的应用程序;
// 解析 pacakge 属性的内容
private void readPackageLPw(TypedXmlPullParser parser, List<UserInfo> users) {
try {
// 获取对应属性的值,eg:name、userId、sharedUserId等
String name = parser.getAttributeValue(null, "name");
int userId = parser.getAttributeInt(null, "userId", 0);
int sharedUserId = parser.getAttributeInt(null, "sharedUserId", 0);
String codePathStr = parser.getAttributeValue(null, "codePath");
long versionCode = parser.getAttributeLong(null, "version", 0);
String volumeUuid = parser.getAttributeValue(null, "volumeUuid");
...
// name 和 codePathStr 字段不能为null,否则记录异常信息
if (name == null) {
// 记录异常信息
...
} else if (codePathStr == null) {
// 记录异常信息
...
} else if (userId > 0) {
// 创建应用程序对象,存储到集合 mPackages
packageSetting = addPackageLPw(name.intern(), ... userId, ...);
...
} else if (sharedUserId != 0) {
if (sharedUserId > 0) {
// 创建使用共享用户的应用程序信息对象,eg: framework-res.apk 无userId,有sharedUserId,记录到集合 mPendingPackages
packageSetting = new PackageSetting(name.intern(), ... sharedUserId ...);
mPendingPackages.add(packageSetting);
} else {
// sharedUserId 小于0非法,记录异常信息
...
}
}
} catch (NumberFormatException e) {
// 记录异常信息,解析文件出错
...
}
if (packageSetting != null) {
...
// 解析enabled属性,判断应用是否启用,默认是启用状态
final String enabledStr = parser.getAttributeValue(null, "enabled");
if (enabledStr != null) {
try {
packageSetting.setEnabled(Integer.parseInt(enabledStr), 0 /* userId */, null);
} catch (NumberFormatException e) {
// 通过字符内容判断启用标识,默认 "enabled"
}
} else {
packageSetting.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT, 0, null);
}
...
} else {
XmlUtils.skipCurrentTag(parser);
}
}
// 创建/缓存应用程序信息(PackageSetting),PackageSetting 用于维护应用程序的安装信息
PackageSetting addPackageLPw(String name, String realName, File codePath ... int uid ...) {
// 先从缓存中加载,如果已经解析,则直接返回
PackageSetting p = mPackages.get(name);
if (p != null) {
// 如果userId相同,则直接返回;否则返回null;
if (p.appId == uid) return p;
return null;
}
// 创建 PackageSetting 对象
p = new PackageSetting(name, realName, codePath ... 0(sharedUserId) ...);
// 设置用户ID
p.appId = uid;
// 判断userId是否合法,如果合法,则添加到集合 mPackages
if (registerExistingAppIdLPw(uid, p, name)) {
// 将应用程序放入缓存
mPackages.put(name, p);
return p;
}
return null;
}
// 判断 userId/sharedUserId 的合法性,并记录共享用户和应用程序的ID信息
// mAppIds:记录应用程序的ID信息
// mOtherAppIds:记录共享用户的ID信息
private boolean registerExistingAppIdLPw(int appId, SettingBase obj, Object name) {
// 用户 ID 有效范围在 19999 内(含19999)
if (appId > Process.LAST_APPLICATION_UID) {
return false;
}
// 普通用户ID范围(10000~19999);共享用户ID范围(10000内)
if (appId >= Process.FIRST_APPLICATION_UID) {
int size = mAppIds.size();
final int index = appId - Process.FIRST_APPLICATION_UID;
// 将数组填充到该索引所在的位置
while (index >= size) {
mAppIds.add(null);
size++;
}
if (mAppIds.get(index) != null) {
// 应用程序已经存在,不需要重复添加
return false;
}
// 将应用程序信息放入集合
mAppIds.set(index, obj);
} else {
// 共享用户已经存在,不需要重复添加
if (mOtherAppIds.get(appId) != null) {
return false;
}
// 将共享用户信息放入集合
mOtherAppIds.put(appId, obj);
}
return true;
}
解析 package 属性,主要分为4个步骤:
- 解析 package 属性内各 attr 的值;
- 如果有 userId,则使用普通的用户ID,创建 PackageSetting 对象,记录到应用程序集合 mPackages;
- 如果有 sharedUserId,则使用共享用户ID,创建 PackageSetting 对象,记录到待确认应用程序集合 mPendingPackages;
- 存入集合 mPackages 时,判断用户ID的合法性;
2.3、解析 shread-user 属性
- shared-user 属性主要记录共享用户的安装信息,具体内容请参考附录;
// 解析 shared-user 属性的内容
private void readSharedUserLPw(TypedXmlPullParser parser, List<UserInfo> users) {
...
SharedUserSetting su = null;
{
// 获取对应属性的值,eg:name、userId等
String name = parser.getAttributeValue(null, "name");
int userId = parser.getAttributeInt(null, "userId", 0);
...
// name 为 null 或 userId 为0,属于无效信息,需要记录异常信息
if (name == null) {
...
} else if (userId == 0) {
...
} else {
// 创建共享用户
if ((su = addSharedUserLPw(name.intern(), userId, ...)) == null) {
// 创建失败,记录异常信息
}
}
}
if (su != null) {
int outerDepth = parser.getDepth();
int type;
// 解析共享用户内的属性
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
...
String tagName = parser.getName();
if (tagName.equals("sigs")) {
su.signatures.readXml(parser, mPastSignatures.untrackedStorage());
} else if (tagName.equals("perms")) {
// 解析共享用户拥有的权限
readInstallPermissionsLPr(parser, su.getLegacyPermissionState(), users);
} else {
...
}
}
} else {
XmlUtils.skipCurrentTag(parser);
}
}
// 创建/缓存共享用户信息(SharedUserSetting),SharedUserSetting 用于维护共享用户的安装信息
SharedUserSetting addSharedUserLPw(String name, int uid, int pkgFlags, int pkgPrivateFlags) {
SharedUserSetting s = mSharedUsers.get(name);
if (s != null) {
// 共享用户信息已存在,直接返回
if (s.userId == uid) {
return s;
}
return null;
}
// 创建 SharedUserSetting 对象
s = new SharedUserSetting(name, pkgFlags, pkgPrivateFlags);
// 设置用户ID
s.userId = uid;
if (registerExistingAppIdLPw(uid, s, name)) {
// 将共享用户记录到集合 mSharedUsers
mSharedUsers.put(name, s);
return s;
}
return null;
}
解析 shared-user 属性,主要分为4个步骤:
- 解析 shared-user 属性内各 attr 的值;
- 如果有 name 且 userId 合法,创建 SharedUserSetting 对象,记录到共享用户集合 mSharedUsers;
- 解析共享用户拥有的权限、签名等信息,并存到 SharedUserSetting 对象内,拥有该共享用的应用程序同时拥有了相关权限和签名等信息;
- 存入集合 mSharedUsers 时,判断用户ID的合法性;
2.4、更新应用程序的共享用户
在遍历集合 mPendingPackags 时,通过 addPackageSettingLPw 方法,更新应用程序的共享用户信息;
// 更新使用共享用户的应用程序信息
private void addPackageSettingLPw(PackageSetting p, SharedUserSetting sharedUser) {
// 将应用程序信息记录到集合 mPackages
mPackages.put(p.name, p);
if (sharedUser != null) {
// 清理旧的共享用户记录的应用程序信息
if (p.sharedUser != null && p.sharedUser != sharedUser) {
p.sharedUser.removePackage(p);
} else if (p.appId != sharedUser.userId) {
// 记录异常信息
...
}
// 共享用户记录使用该用户的应用程序
sharedUser.addPackage(p);
// 设置应用程序新的的共享用户
p.sharedUser = sharedUser;
// 设置应用程序的用户ID为共享用户ID
p.appId = sharedUser.userId;
}
...
}
更新应用程序的共享用户信息过程,主要分为4个步骤:
- 将使用共享用户的应用程放入集合 mPackages;
- 清理旧的共享用户信息;
- 共享用户记录使用该用户的应用程序;
- 更新应用程序的用户信息;
3、安装应用程序
通过时序图分析,安装应用程序,主要是解析apk文件,设置用户ID,并将信息存入 Settings,主要分为3个步骤:
- scanDirLI 方法,扫描预置apk文件的目录,逐个解析内部的apk文件或目录;
- 将解析文件的任务提交到线程池内,解析apk文件;
- 将解析的apk数据,创建 PackageSetting 对象(复用或生成用户ID)存入 Settings 内;
3.1、安装预置目录下的应用程序
在 PMS 的构造方法内,通过 scanDirTracedLI 方法开始扫描预置 apk 文件的目录;
// PackageManagerService.java
// 扫描预置apk文件的目录
private void scanDirTracedLI(File scanDir, final int parseFlags, int scanFlags,
long currentTime, PackageParser2 packageParser, ExecutorService executorService) {
...
// 真正的实现扫描的方法 scanDirLI
scanDirLI(scanDir, parseFlags, scanFlags, currentTime, packageParser, executorService);
...
}
// 开始扫描预置apk文件的目录,将解析apk文件或目录的任务提交到线程池,解析完成之后安装应用程序
private void scanDirLI(File scanDir, int parseFlags, int scanFlags, long currentTime,
PackageParser2 packageParser, ExecutorService executorService) {
final File[] files = scanDir.listFiles();
...
// 将解析器和线程池封装
ParallelPackageParser parallelPackageParser =
new ParallelPackageParser(packageParser, executorService);
int fileCount = 0;
for (File file : files) {
// 将apk文件或目录提交到线程池解析
parallelPackageParser.submit(file, parseFlags);
// 记录解析任务的数量,用于解析任务的获取
fileCount++;
}
// 逐个处理解析任务的结果
for (; fileCount > 0; fileCount--) {
// 解析完成后,处理apk文件解析结果,当任务解析完成会放入阻塞队列,通知主线程处理,这里主线程需要等待因为PMS构造方法执行完成后,需要确保所有的应用程序已经被正确安装;
ParallelPackageParser.ParseResult parseResult = parallelPackageParser.take();
Throwable throwable = parseResult.throwable;
if (throwable == null) {
...
// 解析完成,开始安装应用程序
addForInitLI(parseResult.parsedPackage, parseFlags, scanFlags,
currentTime, null);
}
...
}
}
安装应用程序的过程,主要分为3个步骤:
- 预置应用程序目录内的 apk 文件或目录,封装到解析任务内,然后提交到解析线程;
- 解析 apk 文件,并将解析结果放入完成队列 mQueue;
- 处理 apk 文件解析结果,完成应用程序的安装;
3.2、解析apk文件
apk 文件实际是一个 zip 压缩文件,压缩包内包含多个 class.dex、Androidmanifest.xml、resources.arsc 等类型的文件;
解析 apk 文件过程,主要是针对 apk 文件内的 AndroidManifest.xml 文件进行解析。
// ParallelPackageParser.java
// 将解析文件的任务提交到线程池
public void submit(File scanFile, int parseFlags) {
mExecutorService.submit(() -> {
ParseResult pr = new ParseResult();
pr.scanFile = scanFile;
// 解析处理,最终是调用 PackageParser2.parsePackage 方法
pr.parsedPackage = parsePackage(scanFile, parseFlags);
// 将解析结果放入队列内
mQueue.put(pr);
});
}
// 提取解析结果,mQueue 是阻塞队列(BlockingQueue),当队列内有元素是take返回,如果队列内无元素时,阻塞获取线程;
public ParseResult take() {
...
return mQueue.take();
...
}
// PackageParser2.java
// 解析文件
public ParsedPackage parsePackage(File packageFile, int flags, boolean useCaches) {
// 从缓存中加载解析数据
if (useCaches && mCacher != null) {
ParsedPackage parsed = mCacher.getCachedResult(packageFile, flags);
if (parsed != null) {
return parsed;
}
}
ParseInput input = mSharedResult.get().reset();
// 通过 ParsingPackageUtils.parsePackage 方法进行解析
ParseResult<ParsingPackage> result = parsingUtils.parsePackage(input, packageFile, flags);
// 获取解析数据,解析数据保存到 ParsedPackage 对象
ParsedPackage parsed = (ParsedPackage) result.getResult().hideAsParsed();
// 将解析数据放入缓存
if (mCacher != null) {
mCacher.cacheResult(packageFile, flags, parsed);
}
return parsed;
}
// ParsingPackageUtils.java
public ParseResult<ParsingPackage> parsePackage(ParseInput input, File packageFile,
int flags)
throws PackageParserException {
// 区分文件夹和文件,最终内部都是通过 parseBaseApk 方法对 apk 文件进行解析
if (packageFile.isDirectory()) {
// 解析文件夹内的apk文件
return parseClusterPackage(input, packageFile, flags);
} else {
// 解析apk文件
return parseMonolithicPackage(input, packageFile, flags);
}
}
// 解析操作主要是针对 apk 内的 AndroidManifest.xml 文件进行解析
private ParseResult<ParsingPackage> parseBaseApk(ParseInput input, File apkFile,
String codePath, SplitAssetLoader assetLoader, int flags)
throws PackageParserException {
...
// 打开 AndroidManifest.xml 文件
try (XmlResourceParser parser = assets.openXmlResourceParser(cookie, "AndroidManifest.xml")) {
final Resources res = new Resources(assets, mDisplayMetrics, null);
// pareseBaseApk 重载方法,内部通过 parseBaseApkTags 方法解析 APK 文件
ParseResult<ParsingPackage> result = parseBaseApk(input, apkPath, codePath, res, parser, flags);
...
return input.success(pkg);
} catch (Exception e) {
}
}
// 开始解析xml文件,解析不同的tag内容
private ParseResult<ParsingPackage> parseBaseApkTags(ParseInput input, ParsingPackage pkg ...) {
// 解析应用程序申请的共享用户,即属性 android:sharedUserId 的内容
ParseResult<ParsingPackage> sharedUserResult = parseSharedUser(input, pkg, sa);
if (sharedUserResult.isError()) {
return sharedUserResult;
}
...
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT ...) {
...
if (TAG_APPLICATION.equals(tagName)) {
// 解析 applitaion item 属性
result = parseBaseApplication(input, pkg, res, parser, flags);
} else {
// 解析其他 item 属性
result = parseBaseApkTag(tagName, input, pkg, res, parser, flags);
}
...
}
return input.success(pkg);
}
// 解析 application 属性内的元素,主要是解析4大组件信息
private ParseResult<ParsingPackage> parseBaseApplication(ParseInput input ...) {
...
// 循环解析 application 属性内的各组件内容
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) {
// 解析 Activity/Receiver 组件
case "activity":
isActivity = true;
// fall-through
case "receiver":
ParseResult<ParsedActivity> activityResult =
ParsedActivityUtils.parseActivityOrReceiver(... parser ...);
if (activityResult.isSuccess()) {
ParsedActivity activity = activityResult.getResult();
if (isActivity) {
pkg.addActivity(activity);
} else {
pkg.addReceiver(activity);
}
}
result = activityResult;
break;
// 解析 Service 组件
case "service":
ParseResult<ParsedService> serviceResult =
ParsedServiceUtils.parseService(... parser ...);
if (serviceResult.isSuccess()) {
ParsedService service = serviceResult.getResult();
pkg.addService(service);
}
result = serviceResult;
break;
// 解析 Provider 组件
case "provider":
ParseResult<ParsedProvider> providerResult =
ParsedProviderUtils.parseProvider(... parser ...);
if (providerResult.isSuccess()) {
pkg.addProvider(providerResult.getResult());
}
result = providerResult;
break;
default:
result = parseBaseAppChildTag(input, tagName, pkg, res, parser, flags);
break;
}
if (result.isError()) {
return input.error(result);
}
}
...
return input.success(pkg);
}
解析apk文件的过程,主要分为4个步骤:
- 解析apk文件,主要是针对 AndroidManifest.xml 文件,由 ParsingPackageUtils 类管理解析操作;
- 解析 application 和其他类型属性的内容;
- 解析 application 内的组件元素内容,将解析的组件内容放入 ParsingPackage 类型的对象内,内部通过组件集合管理不同的组件信息;
- 解析其他属性,eg:permission等,并将其放入相应集合内;
3.3、添加或更新已安装的应用程序信息
parallelPackageParser.take() 返回解析结果,PMS开始进行应用程序的安装过程;
// PackageManagerServices.java
private AndroidPackage addForInitLI(ParsedPackage parsedPackage ...) {
...
synchronized (mLock) {
...
// 获取已安装的应用程序安装信息
final PackageSetting installedPkgSetting = mSettings.getPackageLPr(
parsedPackage.getPackageName());
pkgSetting = originalPkgSetting == null ? installedPkgSetting : originalPkgSetting;
...
// 对解析的apk文件内容进行转换,更新或创建应用程序安装信息
final ScanResult scanResult = scanPackageNewLI(parsedPackage ...);
if (scanResult.success) {
synchronized (mLock) {
...
// 创建应用程序的用户ID,并记录到 Settings 内
appIdCreated = optimisticallyRegisterAppId(scanResult);
// 如果有更新,则记录新的应用程序安装信息
commitReconciledScanResultLocked(reconcileResult.get(pkgName), mUserManager.getUserIds());
...
}
}
...
return scanResult.pkgSetting.pkg;
}
// 更新/创建应用程序的安装信息
private ScanResult scanPackageNewLI(ParsedPackage parsedPackage ...) {
// 获取更新的应用程序信息
final PackageSetting originalPkgSetting = getOriginalPackageLocked(parsedPackage,
renamedPkgName);
// 获取已安装的应用程序信息
final PackageSetting pkgSetting = mSettings.getPackageLPr(parsedPackage.getPackageName());
scanFlags = adjustScanFlags(scanFlags, pkgSetting, disabledPkgSetting, user, parsedPackage);
synchronized (mLock) {
final ScanRequest request = new ScanRequest(parsedPackage, sharedUserSetting ... );
// 返回一个最新的应用程序安装信息结果
return scanPackageOnlyLI(request, mInjector, mFactoryTest, currentTime);
}
}
// 根据解析的应用程序数据或已安装的应用程序信息,创建或更新应用程序安装信息
static ScanResult scanPackageOnlyLI(ScanRequest request ...) {
...
final boolean createNewPackage = (pkgSetting == null);
// 应用程序首次安装,创建应用程序信息
if (createNewPackage) {
pkgSetting = Settings.createNewSetting(parsedPackage.getPackageName() ...);
} else {
// 已安装过应用程序信息,则通过已安装的应用程序信息更新解析的应用程序信息
Settings.updatePackageSetting(pkgSetting ...);
}
...
return new ScanResult(request, true, pkgSetting ...);
}
// 更新应用程序安装信息
private AndroidPackage commitReconciledScanResultLocked(ReconciledPackage reconciledPkg, int[] allUsers) {
...
if (result.existingSettingCopied) {
// 如果存在已安装的应用程序,则使用已安装的应用程序更新解析的应用程序信息
pkgSetting = request.pkgSetting;
pkgSetting.updateFrom(result.pkgSetting);
} else {
// 如果不存在已安装的应用程序,则使用解析的应用程序信息
pkgSetting = result.pkgSetting;
}
...
parsedPackage.setUid(pkgSetting.appId);
final AndroidPackage pkg = parsedPackage.hideAsFinal();
...
commitPackageSettings(pkg, oldPkg, pkgSetting, oldPkgSetting ...);
...
return pkg;
}
// 应用程序安装信息更新覆盖旧的数据
private void commitPackageSettings(AndroidPackage pkg, AndroidPackage oldPkg ...) {
...
synchronized (mLock) {
// 将新的应用程序数据存入Settings配置管理器
mSettings.insertPackageSettingLPw(pkgSetting, pkg);
// 记录安装的应用程序
mPackages.put(pkg.getPackageName(), pkg);
// 由权限管理模块,更新应用程序的权限信息
mPermissionManager.onPackageAdded(pkg, (scanFlags & SCAN_AS_INSTANT_APP) != 0, oldPkg);
}
}
安装应用程序过程,主要分为4个步骤:
- 获取到apk文件的解析结果,将其转换为应用程序的安装信息;
- 根据已安装信息的状态决定,更新或创建应用程序的安装信息;
- 更新应用程序,将已安装的应用程序版本存入 Settings 的集合 mDisabledSysPackages,存储到 packages.xml 文件内对应的属性是 "updated-package";
- 将应用程序信息更新到 Settings 的集合 mPackages,存储到 packages.xml 文件内对应的属性是 "package";
4、记录应用程序的安装信息
应用程序安装结束之后,需要将安装/覆盖/权限/共享用户等信息写入packages.xml 文件内;
// Settings.java
void writeLPr() {
// 备份 pacakges.xml 文件到 packages-backup.xml
if (mSettingsFilename.exists()) {
if (!mBackupSettingsFilename.exists()) {
// 直接重命名文件的方式进行备份
if (!mSettingsFilename.renameTo(mBackupSettingsFilename)) {
return;
}
} else {
mSettingsFilename.delete();
}
}
...
final FileOutputStream fstr = new FileOutputStream(mSettingsFilename);
final TypedXmlSerializer serializer = Xml.resolveSerializer(fstr);
...
serializer.startTag(null, "packages");
// 将 Settings 内记录的信息写入 xml 文件
...
serializer.endTag(null, "packages");
...
mBackupSettingsFilename.delete();
...
return;
}
写入数据的过程,主要分为3个步骤:
- 将 packages.xml 文件重命名为 packages-backup.xml 文件,作为备份文件;
- 创建 xml 文件生成器,遍历 Settings 内的各个集合数据,写入到对应的标签内;
- 成功记录安装的信息之后,将备份文件删除;
至此,应用程序的安装流程结束,后续 AMS 启动应用程序,或 Launcher 显示应用的图标,即可从 PMS 查询应用程序的相关数据。
总结
PMS 在安装应用程序时,对应用程序的 AndroidManifes.xml 配置文件进行解析,配置文件内有很多信息,其中最重要的是 Android 四大组件信息,组件在配置文件内被正确的使用,才可以被 AMS 服务正常启动。
应用申请的权限或申请共享用户信息,实际是分配用户组ID或用户ID的过程;PMS 根据权限为应用程序分配 Linux 用户组ID,eg:读取联系人、使用摄像头等权限;而分配共享用户,则系统直接将共享用户设置给应用程序,所以应用程序拥有了共享用户的一系列权限;
Android 中的权限会有对应的用户组,在 packages.xml 对应的 permission 属性内有记录,当应用程序申请权限,相当于将应用程序授予了某个用户组的权限。这里其实借助的是 Linux 的用户管理机制,为A用户分配一个用户组,则该组所拥有的访问权限,A用户即可享有。
最后,分析 Android 应用程序的安装流程,当理解了应用程序安装的内容,再分析应用程序安装的具体过程就会更清晰,希望本篇文章可以帮助你更好的掌握应用程序的安装流程。
附录
Settings 保存的应用程序配置信息文件(存放路径:/data/system/packages.xml)
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<packages>
<version sdkVersion="30" databaseVersion="3" fingerprint="qti/admin_jaymz/admin_jaymz:11/REWQ.20109.001/eng.demohh.20240711.090822:userdebug/test-keys" />
<version volumeUuid="primary_physical" sdkVersion="30" databaseVersion="3" fingerprint="qti/admin_jaymz/admin_jaymz:11/REWQ.20109.001/eng.demohh.20240711.090822:userdebug/test-keys" />
<!-- 权限集合 -->
<permission-trees />
<!-- 权限 -->
<permissions>
<item name="android.permission.REAL_GET_TASKS" package="android" protection="18" />
<item name="android.permission.ACCESS_CACHE_FILESYSTEM" package="android" protection="18" />
...
</permissions>
<!-- 应用程序 -->
<package name="com.android.providers.media" codePath="/system/priv-app/MediaProviderLegacy" nativeLibraryPath="/system/priv-app/MediaProviderLegacy/lib" publicFlags="944258629" privateFlags="-1946152952" ft="11e8f7d4c00" it="11e8f7d4c00" ut="11e8f7d4c00" version="1024" sharedUserId="10010" isOrphaned="true">
<sigs count="1" schemeVersion="3">
<cert index="4" key="390kajkgnjkgdjahthkjgh" />
</sigs>
<perms>
<item name="android.permission.ACCESS_CACHE_FILESYSTEM" granted="true" flags="0" />
<item name="android.permission.MANAGE_EXTERNAL_STORAGE" granted="true" flags="0" />
<item name="android.permission.WRITE_MEDIA_STORAGE" granted="true" flags="0" />
...
</perms>
<proper-signing-keyset identifier="3" />
</package>
<package name="android" codePath="/system/framework/framework-res.apk" nativeLibraryPath="/system/lib64/framework-res" primaryCpuAbi="arm64-v8a" publicFlags="810073609" privateFlags="-1945104280" ft="11e8f7d4c00" it="11e8f7d4c00" ut="11e8f7d4c00" version="30" sharedUserId="1000" isOrphaned="true">
<sigs count="1" schemeVersion="3">
<cert index="2" />
</sigs>
<perms>
<item name="android.permission.REAL_GET_TASKS" granted="true" flags="0" />
...
</perms>
<proper-signing-keyset identifier="2" />
</package>
<package name="com.example.test.demo" codePath="/data/app/~~9wVilhPbAuqqaPB8DdXDKw==/com.example.test.demo-o19apShZNSh7CKSkJ5gZig==" nativeLibraryPath="/data/app/~~9wVilhPbAuqqaPB8DdXDKw==/com.example.test.demo-o19apShZNSh7CKSkJ5gZig==/lib" primaryCpuAbi="arm64-v8a" publicFlags="541605831" privateFlags="-1936715520" ft="190b9a8f868" it="11e8f7d4c00" ut="190b9a8fc14" version="601" userId="10199">
<sigs count="1" schemeVersion="2">
<cert index="2" />
</sigs>
<perms>
<item name="android.permission.SYSTEM_ALERT_WINDOW" granted="true" flags="0" />
...
</perms>
<proper-signing-keyset identifier="2" />
</package>
<!-- 过期的应用程序 -->
<updated-package name="com.example.test.demo" codePath="/system/app/TestDemo" ft="11e8f7d4c00" it="11e8f7d4c00" ut="11e8f7d4c00" version="596" nativeLibraryPath="/system/app/TestDemo/lib" primaryCpuAbi="arm64-v8a" userId="10199">
<perms>
<item name="android.permission.SYSTEM_ALERT_WINDOW" granted="true" flags="0" />
...
</perms>
</updated-package>
<!-- shared-user 信息 -->
<shared-user name="android.uid.systemui" userId="10058">
<sigs count="1" schemeVersion="3">
<cert index="2" />
</sigs>
<perms>
<item name="android.permission.REAL_GET_TASKS" granted="true" flags="0" />
<item name="android.permission.REMOTE_AUDIO_PLAYBACK" granted="true" flags="0" />
...
</perms>
</shared-user>
...
<shared-user name="android.uid.system" userId="1000">
<sigs count="1" schemeVersion="3">
<cert index="2" />
</sigs>
<perms>
<item name="android.permission.REAL_GET_TASKS" granted="true" flags="0" />
...
</perms>
</shared-user>
</packages>