Android中PackageManagerService机制分析

概要

每一个社会群落都有管理机制,其中有三个要素:被管理者、管理者以及管理机制的运转。在Android的世界中,有一处群落叫“包管理”,要研究Android的包管理机制,同样可以从以下几个角度来思考:

  1. 被管理的对象是什么?
  2. 管理者的职能是什么?
  3. 管理机制是如何运转的?
    所谓包,其实就是一种文件格式,譬如APK包、JAR包等。在Android中存活着很多包,所有的应用程序都是APK包,很多构成Android运行环境的都是JAR包,还有一些以so为后缀的库文件,包管理者很重要的一个职能就是识别不同的包,统一维护这些包的信息。当有一个包进入或离开Android世界,都需要向包管理者申报,其他管理部门要获取包的具体信息,也都需要向包管理者申请。

如同社会是由人与人的协作形成,不同的包之间也需要进行协作。既然有协作,自然就有协作的规范,一个包可以干什么,不可以干什么,都需要有一个明确的范围界定,这就是包管理中的权限设计。涉及到的内容非常广泛,Linux的UGO(User Group Other)和ACL(Access Control List,访问控制列表)权限管理、数字签名与验证、Android授权机制、Selinux,都是包管理中权限设计的组成部分。

Android的世界就如同一个井然有序的人类社会,除了包管理部门,还有其他各种管理部门,譬如电源管理、窗口管理、活动管理等等,大家不仅各司其职,而且也有交流往来。从APK的安装到Activity的显示这么一个看似简单的过程,却需要大量管理部门参与进来,不断地进行数据解析、封装、传递、呈现,内部机理十分复杂。

PackageManagerService是包管理中最重要的服务,为了描述方便,本文会简写成PMS。
PMS的部分函数带有LI后缀,表示需要获取mInstalllock这个锁时才能执行;部分函数带有LP后缀,表示需要获取mPackages这个锁才能执行。

PackageManagerService相关的代码路径如下:
frameworks/base/core/java/android/content/pm/
frameworks/base/services/core/java/com/android/server/pm

被管理对象的形态

Android中的APK和JAR包都以静态文件的形式分布在不同的硬件分区,包管理者面临的第一个任务就是将这些静态的文件转化成内存的数据结构,这样才能将其管理起来。Android中最重要的包管理对象就是APK,APK可以包含so文件,负责将静态文件转换内存中数据结构的工具就是PackageParser,包解析器。
PackageParser

Android L(5.0)以后,支持APK拆分,即一个APK可以分割成很多部分,位于相同的目录下,每一个部分都是一个单独的APK文件,所有的APK文件具备相同的签名,在APK解析过程中,会将拆分的APK重新组合成内存中的一个Package。对于一个完整的APK,Android称其为Monolithic;对于拆分后的APK,Android称其为Cluster。

在Android L(5.0)以前,APK文件都是直接位于app或priv-app目录下,譬如短彩信APK的目录就是/system/priv-app/Mms.apk;到了Android L(5.0)之后,多了一级目录结构,譬如短彩信APK的目录是/system/priv-app/Mms/Mms.apk,这是Android为了支持APK拆分而做的改动,如果要将短彩信APK进行拆分,那所有被拆出来的APK都位于/system/priv-app/Mms/即可,这样在包解析时,就会变成以Cluster的方式解析目录。

一个包在内存中的数据结构就是Package,那么,Package有一些什么属性?是怎么从APK文件中获取数据的呢? 这就涉及到包解析器的工作原理。

包解析器

为了先让读者对被管理对象有一个初步的认识,我们先把一个包最终在内存中的数据结构拎出来。其实生成这个数据结构,需要包管理者进行大量的调度工作,调度中心是PMS,包解析的过程也都是由PMS驱动的。在分析包解析过程之前,我们先上包解析的结果:
包解析结果
这个类图,示意了一个包最终在内存中的数据结构Package,它包含很多属性,部分属性还是包解析器中的子数据结构。我们可以从设计的角度来理解这个类图:

  • 一个包中有很多组件,为此设计了一个高层的基类Component,所有具体的组件都是Component的子类。什么是组件呢?AndroidManifest.xml文件中所定义的的一些标签,就是组件,譬如等,这些标签分别对应到包解析器中的一个数据结构,它们各自有自身的属性。

  • 诸如标签,都可以配置,来过滤其可以接收的Intent,这些信息也需要在包解析器中体现出来,为此组件Component依赖于IntentInfo这个数据结构。每一个具体的组件所依赖的IntentInfo不同,所以Component和IntentInfo之间的依赖关系采用了桥接(Bridge)这种设计模式,通过泛型的手段实现。

  • 各种组件最终聚合到Package这个数据结构中,形成了最终包解析器的输出。当然,在解析的过程中,还有利用了一些数据结构来优化设计,PackageLite和ApkLite就是一些很简单的数据封装。

要得到以上的数据结构,包解析器PackageParser功不可没,从接收一个静态的文件(File类型)开始,会经过一个漫长的包解析过程,直到生成最终的Package:

1
2
3
4
5
6
7
8
9
10
11
12
parsePackages(File file...)
└── parseClusterPackage(File packageDir...)
└── parseClusterPackageLite(File packageDir...)
| └── parseApkLite(File apkFile...)
| └── parseApkLite(String codePath...)
└── parseBaseApk()
└── parseBaseApplication()
| └── parseActivity()
| └── parseService()
| └── ...
└── parseInstrumentation()
└── ...

这些函数的具体逻辑本文不予分析,仅把关键的流程捋出来:

  1. PackageParser.parsePackages()是包解析器的入口函数,它首先会判定给定的输入是否为一个目录,如果是目录,则以为着目录下可能存在多个拆分后的APK,这就需要以Cluster的方式进行解析;如果仅仅是一个APK文件,就以Monolithic的方式解析;

  2. 解析APK,需要先得到一个中间数据结构PacakgeLite,包名、版本、拆分包等信息都会保存在这个数据结构中;由于一个包可能有多个拆分的APK,所以PackageLite可能关联到多个APK,每一个APK都对应到ApkLite这个数据结构,也是一些基本信息的封装。之所以以Lite为后缀命名,是因为这两个数据结构都比较轻量,只保存APK中很少信息;

  3. 一个APK真正的信息都写在AndroidManifest.xml这个文件中,PackageParser.parseBaseApk()这个函数就是用来解析该文件。其解析过程与AndroidManifest.xml的文件结构一一对应,譬如先解析标签的内容,然后解析其下的,等标签。由于AndroidManifest.xml文件的结构非常复杂,所以该函数逻辑也非常庞大,读者们可以自行分析源码。
    至此,包解析器PackageParser就将一个静态的文件,转换成了内存中的数据结构Package,它包含了一个包的所有信息,如包名、包路径、权限、四大组件等,其数据来源主要就是AndroidManifest.xml文件。

包信息体

包解析器从静态文件中获取的数据,很多都是需要用于跨进程传递的,譬如初次启动Activity时,就需要把包信息从系统进程传递到应用进程,先完成应用进程的启动。在包解析器的类图中,我们看到Activity、Service、Provider、Permission、Instrumentaion这些类都有一个共同的特征:都具备info这个属性,其实这些类的结构非常简单,就是对info的一次封装,info这个结构体才是真正的包数据,笔者暂且称之为“包信息体”:
包信息体
所有的info都实现了Parcelable接口,意图很明显,info是可以进行跨进程传递的。不同组件的info类型是不同的,除了实现了Parcelable接口,它们之间又构成了一个庞大的数据结构,把这些具体的info类型展开,就是以下的类图:
Info展开图
可以看到,这个类图与PackageParser中的类图在结构上很相似,我们依旧是从设计的角度来理解这个类图:

  • PackageItemInfo作为包每一项信息的高层基类:
    • 针对permission,permission,instrumentation等,分别为其设计了一个类,都继承自PackageItemInfo
    • 针对activity,service,provider等四大组件,在PackageItemInfo之下又多设计了一层:ComponentInfo作为四大组件的基类
  • ApplicationInfo也是包信息中的一项,但与四大组件紧密相连,四大组件肯定都属于某个Application,所以ComponentInfo与Application存在依赖关系,继而,具体到每个组件都与Application存在依赖关系
  • 所有的包信息都聚合到PackageInfo这个类中,PackageInfo就是一个包向外提供的所有信息。其实除了上图列出来的类,还有一些类没有示意出来,譬如ConfigurationInfo,FeatureInfo,它们都可以对应到AndroidManifest.xml中的标签。
    这些结构体中的数据,都是在包解析器时初始化的,譬如Activity依赖于ActivityInfo,在解析Activity时,就会创建一个ActivityInfo对象,把所定义的数据全都填充到ActivityInfo中。读者可以思考一下PackageParser中的Activity与此处的ActivityInfo的分开设计的目的和好处是什么?

    在分析包的形态时,我们见到了很多类,类的命名方式还有点相似,初读代码的时候,很容易陷入各个类之间复杂的关系网之中。不得不说,包在内存中的数据结构是比较庞大的,因为它蕴含的信息大多了。

PMS的启动过程

frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java

对象创建

PMS部分属性初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
// 代码片段1:PMS部分属性的初始化
public PackageManagerService(Context context, Installer installer,
boolean factoryTest, boolean onlyCore) {
// 关键的Event日志,PMS开始启动了
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_START,
SystemClock.uptimeMillis());

mContext = context;
mFactoryTest = factoryTest;
mOnlyCore = onlyCore;
// 如果是eng版,则延迟做DexOpt
mLazyDexOpt = "eng".equals(SystemProperties.get("ro.build.type"));
mMetrics = new DisplayMetrics();
// Settings是包管理中一个很重要的数据结构,用于维护所有包的信息
mSettings = new Settings(mPackages);
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);

long dexOptLRUThresholdInMinutes;
if (mLazyDexOpt) {
dexOptLRUThresholdInMinutes = 30; // only last 30 minutes of apps for eng builds.
} else {
dexOptLRUThresholdInMinutes = 7 * 24 * 60; // apps used in the 7 days for users.
}
mDexOptLRUThresholdInMills = dexOptLRUThresholdInMinutes * 60 * 1000;
...
mInstaller = installer;
// DexOpt工具类
mPackageDexOptimizer = new PackageDexOptimizer(this);
mMoveCallbacks = new MoveCallbacks(FgThread.get().getLooper());
mOnPermissionChangeListeners = new OnPermissionChangeListeners(
FgThread.get().getLooper());
getDefaultDisplayMetrics(context, mMetrics);
// 读取系统默认的权限
SystemConfig systemConfig = SystemConfig.getInstance();
mGlobalGids = systemConfig.getGlobalGids();
mSystemPermissions = systemConfig.getSystemPermissions();
mAvailableFeatures = systemConfig.getAvailableFeatures();

// 这里上了两把锁: mInstallLock是安装APK时需要用到的锁;mPackage是更新APK信息时需要的锁
synchronized (mInstallLock) {
synchronized (mPackages) {
// 构建一个后台线程,并将线程的消息队列绑定到Handler
mHandlerThread = new ServiceThread(TAG,
Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/);
mHandlerThread.start();
mHandler = new PackageHandler(mHandlerThread.getLooper());
// 将PMS加入Watchdog的监控列表
Watchdog.getInstance().addThread(mHandler, WATCHDOG_TIMEOUT);

// 初始化一些文件目录
File dataDir = Environment.getDataDirectory();
mAppDataDir = new File(dataDir, "data");
mAppInstallDir = new File(dataDir, "app");
mAppLib32InstallDir = new File(dataDir, "app-lib");
mAsecInternalPath = new File(dataDir, "app-asec").getPath();
mUserAppDataDir = new File(dataDir, "user");
mDrmAppPrivateInstallDir = new File(dataDir, "app-private");

// 初始化系统权限
ArrayMap<String, SystemConfig.PermissionEntry> permConfig
= systemConfig.getPermissions();
for (int i=0; i<permConfig.size(); i++) {
SystemConfig.PermissionEntry perm = permConfig.valueAt(i);
BasePermission bp = mSettings.mPermissions.get(perm.name);
if (bp == null) {
bp = new BasePermission(perm.name, "android", BasePermission.TYPE_BUILTIN);
mSettings.mPermissions.put(perm.name, bp);
}
if (perm.gids != null) {
bp.setGids(perm.gids, perm.perUser);
}
}

// 初始化PMS中的ShareLibraries
ArrayMap<String, String> libConfig = systemConfig.getSharedLibraries();
for (int i=0; i<libConfig.size(); i++) {
mSharedLibraries.put(libConfig.keyAt(i),
new SharedLibraryEntry(libConfig.valueAt(i), null));
}

// 读取mac_permission.xml文件的内容
mFoundPolicyFile = SELinuxMMAC.readInstallPolicy();

mRestoredSettings = mSettings.readLPw(this, sUserManager.getUsers(false),
mSdkVersion, mOnlyCore);
String customResolverActivity = Resources.getSystem().getString(
R.string.config_customResolverActivity);

// 未完接代码片段2

【代码片段1】完成了很多PMS的属性初始化操作,几个重要的属性如下:

  • mSettings:PMS内部有一个Settings数据结构,用于维护所有包的信息。写过Android应用程序的朋友可能知道两个APK可以运行在相同的进程中,前提是两个APK具有相同的签名和ShareUid。Android系统中定义了一些默认的ShareUid,譬如android.uid.system表示系统进程的UID,如果有一个APK想要运行在系统进程中,则其需要在AndroidManifest.xml文件中声明ShareUid为android.uid.system,并且该APK的签名必须与framework-res.apk的签名一致,即platform签名。

    PMS在创建完Settings对象之后,便把很多系统默认的ShareUid加入其中。

mInstaller:Installer是一个系统服务,它封装了很多PMS进行包管理需要用到的函数,譬如install()、 dexopt()、rename()等。在Installer内部,其实是通过Socket连接installd,将执行指令发送到installd完成具体的操作。

mPackageDexOptimizer: 进行Dex优化的工具类。对于一个APK而言,编译后其APK包中可执行文件的格式dex,安装到了手机上以后,需要经过文件格式转化才能运行,譬如APK需要转换成oat格式才能在ART虚拟机上运行,文件格式转换的过程就叫DexOpt。

DalvikVM的时代,Android可执行文件的格式是dex,有一种进一步优化的格式叫odex;ART虚拟机的时代,Android可执行文件的格式是oat。虽然都叫做DexOpt,但在DalvikVM和ART两种不同虚拟机的时代分别有不同的内涵。

SystemConfig: 系统全局的配置信息的数据结构。原始的数据来源于/system/etc/sysconfig和/system/etc/permissions目录下的XML文件,XML文件的详细信息见 Android中PackageManagerService相关的文件 这篇文章,在SystemConfig对象构建时,会读取这两个目录下所有XML文件的内容,主要有以下几个维度:

  • 权限与GID的映射关系,譬如,以下内容表示属于inet这个组的用户都拥有android.permission.INTERNET权限:

    1
    2
    3
    <permission name="android.permission.INTERNET" >
    <group git="inet">
    </permission>
  • 权限与UID的映射关系,譬如,以下内容表示UID为meida用户拥有android.permission.CAMERA这个权限:

    1
    <assign-permission name="android.permission.CAMERA" uid="media" />
  • 公共库的定义。Android中有很多公共库,除了BOOTCLASSPATH中定义的,框架层还支持额外的扩展,譬如,以下内容表示公共库的包名和其路径的关系:

    1
    2
    <library name="android.test.runner"
    file="/system/framework/android.test.runner.jar" />

mHandler: 创建PackageHandler对象,将其绑定到一个后台线程的消息队列。可想而知,一些厚重的活,譬如安装APK,就交由这个后台线程完成了。由于PMS是一个重要的系统服务,这个后台线程的消息队列如果过于忙碌,则会导致系统一直卡住,所以需要将这个消息队列加入Watchdog的监控列表,以便在这种情况下,Watchdog可以做出一些应急操作。

初始化一些/data文件目录:应用程序的安装和运行都需要用到Data分区,PMS会在Data分区新建一些子目录。

初始化系统权限:在SystemConfig初始化的时候,从/system/etc/permissions和/system/etc/sysconfig目录下读取了XML文件,这些信息要添加到Settings这个数据结构中。Android设计了一个BasePermission的数据结构,主要用于保存权限与包名之间的映射关系,此处,添加的权限是从SystemConfig中取出,包名是android,也就是先将系统权限添加到Settings中。

mFoundPolicyFile: 有了SeLinux以后,Android会为每个文件打上SE Label,对于APK而言,打SE Label的准则就是签名,即根据签名信息打上不同的SE Label。Android将签名分类成为platform,testkey, media等,签名与类别的映射关系就存在一个叫mac_permission.xml的文件中。此处,需要读取该文件的内容。

PMS 系统文件Dexopt扫描

在完成部分属性的初始化之后,PMS要进入扫描安装阶段了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
//代码片段2:DexOpt处理,扫描系统文件
// 关键日志,开始扫描系统APP
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SYSTEM_SCAN_START,
startTime);

// 设置扫描参数
final int scanFlags = SCAN_NO_PATHS | SCAN_DEFER_DEX | SCAN_BOOTING | SCAN_INITIAL;
// 1. 构建数组变量用于保存已经做过DexOpt的文件,后文中,会往这个数组变量中添加元素
final ArraySet<String> alreadyDexOpted = new ArraySet<String>();
final String bootClassPath = System.getenv("BOOTCLASSPATH");
final String systemServerClassPath = System.getenv("SYSTEMSERVERCLASSPATH");

// BOOTCLASSPATH环境变量所定义的文件已经做过DexOpt
if (bootClassPath != null) {
String[] bootClassPathElements = splitString(bootClassPath, ':');
for (String element : bootClassPathElements) {
alreadyDexOpted.add(element);
}
}

// SYSTEMSERVERCLASSPATH环境变量所定义的文件已经做过了DexOpt
if (systemServerClassPath != null) {
String[] systemServerClassPathElements = splitString(systemServerClassPath, ':');
for (String element : systemServerClassPathElements) {
alreadyDexOpted.add(element);
}
}

// 获取指令集,以便后续进行DexOpt
final List<String> allInstructionSets = InstructionSets.getAllInstructionSets();
final String[] dexCodeInstructionSets =
getDexCodeInstructionSets(
allInstructionSets.toArray(new String[allInstructionSets.size()]));

// 公共库是定义在 etc/sysconfig 和 etc/permissions 文件夹下的XML文件中
// 需要这些公共库进行DexOpt处理
if (mSharedLibraries.size() > 0) {
for (String dexCodeInstructionSet : dexCodeInstructionSets) {
for (SharedLibraryEntry libEntry : mSharedLibraries.values()) {
final String lib = libEntry.path;
if (lib == null) {
continue;
}
try {
int dexoptNeeded = DexFile.getDexOptNeeded(lib, null, dexCodeInstructionSet, false);
if (dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) {
alreadyDexOpted.add(lib);
mInstaller.dexopt(lib, Process.SYSTEM_UID, true, dexCodeInstructionSet, dexoptNeeded);
}
} catch (...)
}
}
}

File frameworkDir = new File(Environment.getRootDirectory(), "framework");
alreadyDexOpted.add(frameworkDir.getPath() + "/framework-res.apk");
alreadyDexOpted.add(frameworkDir.getPath() + "/core-libart.jar");

// system/framework目录下,除了framework-res.apk和core-libart.jar这两个文件外
// 其他的APK和JAR文件都需要进行DexOpt处理
String[] frameworkFiles = frameworkDir.list();
if (frameworkFiles != null) {
for (String dexCodeInstructionSet : dexCodeInstructionSets) {
for (int i=0; i<frameworkFiles.length; i++) {
File libPath = new File(frameworkDir, frameworkFiles[i]);
String path = libPath.getPath();
if (alreadyDexOpted.contains(path)) {
continue;
}
if (!path.endsWith(".apk") && !path.endsWith(".jar")) {
continue;
}
try {
int dexoptNeeded = DexFile.getDexOptNeeded(path, null, dexCodeInstructionSet, false);
if (dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) {
mInstaller.dexopt(path, Process.SYSTEM_UID, true, dexCodeInstructionSet, dexoptNeeded);
}
} catch(...)
}
}
}

// 2. Android M的APK授权机制有了变化,此处是与授权相关的版本兼容处理
final VersionInfo ver = mSettings.getInternalVersion();
mIsUpgrade = !Build.FINGERPRINT.equals(ver.fingerprint);
mPromoteSystemApps =
mIsUpgrade && ver.sdkVersion <= Build.VERSION_CODES.LOLLIPOP_MR1;
if (mPromoteSystemApps) {
Iterator<PackageSetting> pkgSettingIter = mSettings.mPackages.values().iterator();
while (pkgSettingIter.hasNext()) {
PackageSetting ps = pkgSettingIter.next();
if (isSystemApp(ps)) {
mExistingSystemPackages.add(ps.name);
}
}
}

// 3. 扫描系统文件
File vendorOverlayDir = new File(VENDOR_OVERLAY_DIR);
// 扫描 /vendor/overlay 目录下的文件
scanDirLI(vendorOverlayDir, PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags | SCAN_TRUSTED_OVERLAY, 0);
// 扫描 /system/framework 目录下的文件
scanDirLI(frameworkDir, PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR
| PackageParser.PARSE_IS_PRIVILEGED,
scanFlags | SCAN_NO_DEX, 0);
// 扫描 /system/priv-app 目录下的文件
final File privilegedAppDir = new File(Environment.getRootDirectory(), "priv-app");
scanDirLI(privilegedAppDir, PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR
| PackageParser.PARSE_IS_PRIVILEGED, scanFlags, 0);
// 扫描 /system/app 目录下的文件
final File systemAppDir = new File(Environment.getRootDirectory(), "app");
scanDirLI(systemAppDir, PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);
// 扫描 /vendor/app 目录下的文件
File vendorAppDir = new File("/vendor/app");
try {
vendorAppDir = vendorAppDir.getCanonicalFile();
} catch (IOException e) {}
scanDirLI(vendorAppDir, PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);
// 扫描 /oem/app 目录下的文件
final File oemAppDir = new File(Environment.getOemDirectory(), "app");
scanDirLI(oemAppDir, PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);
mInstaller.moveFiles();

// 4. 对扫描到的系统文件善后处理
final List<String> possiblyDeletedUpdatedSystemApps = new ArrayList<String>();
if (!mOnlyCore) {
Iterator<PackageSetting> psit = mSettings.mPackages.values().iterator();
while (psit.hasNext()) {
PackageSetting ps = psit.next();
if ((ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) == 0) {
continue;
}
final PackageParser.Package scannedPkg = mPackages.get(ps.name);
if (scannedPkg != null) {
if (mSettings.isDisabledSystemPackageLPr(ps.name)) {
removePackageLI(ps, true);
mExpectingBetter.put(ps.name, ps.codePath);
}
continue;
}

if (!mSettings.isDisabledSystemPackageLPr(ps.name)) {
psit.remove();
removeDataDirsLI(null, ps.name);
} else {
final PackageSetting disabledPs = mSettings.getDisabledSystemPkgLPr(ps.name);
if (disabledPs.codePath == null || !disabledPs.codePath.exists()) {
possiblyDeletedUpdatedSystemApps.add(ps.name);
}
}
}
}

ArrayList<PackageSetting> deletePkgsList = mSettings.getListOfIncompleteInstallPackagesLPr();
for(int i = 0; i < deletePkgsList.size(); i++) {
cleanupInstallFailedPackage(deletePkgsList.get(i));
}
deleteTempPackageFiles();
mSettings.pruneSharedUsersLPw();
// 未完接代码片段3

【代码片段2】的主体逻辑如下:

  1. 通过不断往alreadyDexOpted数组中填充元素,来略过不需要做DexOpt的文件:BOOTCLASSPATH和SYSTEMSERVERPATH这两个环境变量中定义的文件、system/framework-res.apk、system/core-libart.jar。除略过的文件外,其他APK和JAR文件都是需要做DexOpt处理的,通过调用Installer.dexopt()函数完成,这个函数只是将dexopt命令发送给installd。

  2. 由于Android M的APK授权机制发生了变化,在扫描系统文件之前,做了一些简单的记录,以便后续的授权处理:

    mIsUpgrade:如果当前版本的指纹与历史版本的指纹信息不一致,表示当前版本是一次OTA升级上来更新版本
    mPromoteSystemApps:如果历史版本是Android M之前的版本(ver.sdkVersion <= Build.VERSION_CODES.LOLLIPOP_MR1),当前又有版本升级,则需要用一个布尔变量,表示当前需要对系统应用的授权做特殊处理,此时会先把已有的系统应用都保存在mExistingSystemPackages这个数组中

  3. 扫描系统文件,PMS中所有的文件扫描都是调用scanDirLI()函数,扫描系统文件重要的参数就是 PackageParser.PARSE_IS_SYSTEM和PackageParser.PARSE_IS_SYSTEM_DIR,在后文中我们会剖析这个函数。此处,需要注意的是被扫描目录的顺序,这个顺序意味着:先被扫描到的文件,就是最终被用到的文件。

    1
    /vendor/overlay >> /system/framework >> /system/priv-app >> /system/app >> /vendor/app >> /oem/app
  4. possiblyDeletedUpdatedSystemApps这个变量表示“可能被删除的系统APP”,这是一个什么概念呢?除了 possiblyDeletedUpdatedSystemApps ,还有mExpectingBetter,表示当前这个APK有更好的选择,这又是什么概念呢?对于一个系统APP而言,在一次OTA升级的过程中,有三种可能:

    • 保持原状。即这个系统APP没有任何更新。
    • 更新版本。即新的OTA版本中,这个系统APP有更新。
      -不复存在。在新的OTA版本中已经删除了这个系统APP。
      当系统APP升级过后,PMS的Settings中会将原来的系统APP标识为Disable状态,这时候通过Settings.isDisabledSystemPackageLPr()函数调用便返回了false。因此,如果系统APP有更新版本,则属于mExpectingBetter这一类,接下来会扫描Data分区的文件,更新的系统APP就安装在Data分区。

如果一个系统APP不复存在,而且也没有被标记为Disable状态,说明这个系统APP已经彻底不存在了,需要把其在Data分区下的数据删除;如果不复存在的系统APP被标记为Disable状态,那还不能确定该系统APP是否已经被删除,因为还没有扫描Data分区的文件,所以,只能暂时将其放到possiblyDeletedUpdatedSystemApps变量中,表示“可能被删除”,在扫描Data分区之前,这是不能确定的。

PMS Data分区扫描

扫描完系统文件之后,接下来会扫描Data分区的文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
// 代码片段3:扫描Data分区文件,更新公共库信息
if (!mOnlyCore) {
// 关键日志,PMS对Data分区的文件扫描开始了
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START,
SystemClock.uptimeMillis());
// 1. 扫描Data分区的文件目录
scanDirLI(mAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0);
scanDirLI(mDrmAppPrivateInstallDir, PackageParser.PARSE_FORWARD_LOCK,
scanFlags | SCAN_REQUIRE_KNOWN, 0);
// 2. 扫描完Data分区后,处理“可能被删除的系统应用”
for (String deletedAppName : possiblyDeletedUpdatedSystemApps) {
PackageParser.Package deletedPkg = mPackages.get(deletedAppName);
mSettings.removeDisabledSystemPackageLPw(deletedAppName);
String msg;
if (deletedPkg == null) {
removeDataDirsLI(null, deletedAppName);
} else {
deletedPkg.applicationInfo.flags &= ~ApplicationInfo.FLAG_SYSTEM;
PackageSetting deletedPs = mSettings.mPackages.get(deletedAppName);
deletedPs.pkgFlags &= ~ApplicationInfo.FLAG_SYSTEM;
}
}
// 3. 处理有版本更新的系统应用
for (int i = 0; i < mExpectingBetter.size(); i++) {
final String packageName = mExpectingBetter.keyAt(i);
if (!mPackages.containsKey(packageName)) {
final File scanFile = mExpectingBetter.valueAt(i);
final int reparseFlags;
// 设置重新扫描的解析参数
if (FileUtils.contains(privilegedAppDir, scanFile)) {
reparseFlags = PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR
| PackageParser.PARSE_IS_PRIVILEGED;
} else {...}

// 将原来的系统应用重新置为Enable状态
mSettings.enableSystemPackageLPw(packageName);
try {
scanPackageLI(scanFile, reparseFlags, scanFlags, 0, null);
} catch (PackageManagerException e) { ... }
}
}
} // end of "if (!mOnlyCore)"
mExpectingBetter.clear();

// 4. 处理公共库
updateAllSharedLibrariesLPw();
for (SharedUserSetting setting : mSettings.getAllSharedUsersLPw()) {
adjustCpuAbisForSharedUserLPw(setting.packages, null /* scanned package */,
false /* force dexopt */, false /* defer dexopt */);
}

mPackageUsage.readLP();
// 关键日志,PMS扫描结束了
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SCAN_END,
SystemClock.uptimeMillis());
// 未完接代码片段4

Data分区文件的扫描都被mOnlyCore这个布尔变量笼罩,当其为true时,表示只需要扫描系统文件;当其为false时,才会扫描Data分区文件。【代码片段3】的主体逻辑如下:

  1. 调用PMS.scanDirLI()函数扫描 /data/app 和 /data/app-private两个目录的文件。后文会详细剖析该函数。

  2. 扫描完Data分区的文件后,需要对之前系统文件的余孽做一些处理,第一类是possiblyDeletedUpdatedSystemApps,因为在扫描Data分区文件之前,不能确定系统应用有没有被彻底删除,如果在Data分区也无法找到了不复存在的系统应用,则需要彻底删除该系统应用;如果在Data分区找到了不复存在的系统应用,则需要去除其系统应用的标识。

  3. 另外一类系统应用的余孽是mExpectingBetter,表示系统应用已经升级过。如果在Data分区无法找到这些升级过的系统应用,那很可能是用户在OTA升级时,清除了Data分区的数据,对于这种场景,需要重新扫描一下该应用原来位于系统分区的文件。

  4. SystemConfig中定义了公共库,在APK的AndroidManifest.xml文件中,会通过标签标记该APK动态依赖的公共库,此处的逻辑就是将APK与SystemConfig中的公共库关联起来。如果APK使用的公共库并不存在,则会抛出异常(INSTALL_FAILED_MISSING_SHARED_LIBRARY)。

收尾工作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
// 代码片段4:收尾工作
// 授权
int updateFlags = UPDATE_PERMISSIONS_ALL;
if (ver.sdkVersion != mSdkVersion) {
updateFlags |= UPDATE_PERMISSIONS_REPLACE_PKG | UPDATE_PERMISSIONS_REPLACE_ALL;
}
updatePermissionsLPw(null, null, updateFlags);
ver.sdkVersion = mSdkVersion;

// 多用户场景下的版本兼容处理
if (!onlyCore && (mPromoteSystemApps || !mRestoredSettings)) {
for (UserInfo user : sUserManager.getUsers(true)) {
mSettings.applyDefaultPreferredAppsLPw(this, user.id);
applyFactoryDefaultBrowserLPw(user.id);
primeDomainVerificationsLPw(user.id);
}
}

// 如果是升级新版本,则需要清除已有的Code cache目录
if (mIsUpgrade && !onlyCore) {
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)) {
deleteCodeCacheDirsLI(ps.volumeUuid, ps.name);
}
}
ver.fingerprint = Build.FINGERPRINT;
}
checkDefaultBrowser();
mExistingSystemPackages.clear();
mPromoteSystemApps = false;
ver.databaseVersion = Settings.CURRENT_DATABASE_VERSION;
mSettings.writeLPr();

// 关键日志,PMS已经启动完毕了
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_READY,
SystemClock.uptimeMillis());

mRequiredVerifierPackage = getRequiredVerifierLPr();
mRequiredInstallerPackage = getRequiredInstallerLPr();
// 初始化包安装服务
mInstallerService = new PackageInstallerService(context, this);
mIntentFilterVerifierComponent = getIntentFilterVerifierComponentNameLPr();
mIntentFilterVerifier = new IntentVerifierProxy(mContext,
mIntentFilterVerifierComponent);
} // synchronized (mPackages)
} // synchronized (mInstallLock)

Runtime.getRuntime().gc();
LocalServices.addService(PackageManagerInternal.class, new PackageManagerInternalImpl());
} // PMS构造函数完结

【代码片段4】主要进行一些收尾工作,有几个关键点:

  • 授权,通过调用PMS.updatePermissionsLPw()函数,后文会详细分析
  • 版本兼容处理,Android M引入了多用户,需要更新每个APK关联到的userid
  • 将PMS的Settings信息写入/system/packages.xml文件中,Settings是PMS中所有包信息的汇总的数据结构,PMS对包的管理极其依赖于这个数据结构。
  • 初始化包安装服务PackageInstallerService。
    至此,PMS对象的构建过程已经分析完毕,整个逻辑还是较为清晰的,但其实这是一个非常耗时的过程,开机时间大部分都耗在文件扫描上。

文件扫描

scanDirLI()只是文件扫描的起点,由此引发出一串的与文件扫描相关的函数。
文件扫描
接下来,笔者会按照调用时序,对关键函数进行分析。PMS对象构建时,待扫描的目录有很多,不同目录的文件扫描,只是在扫描参数上略有区别,整体逻辑上并无不同。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
private void scanDirLI(File dir, int parseFlags, int scanFlags, long currentTime) {
final File[] files = dir.listFiles();
if (ArrayUtils.isEmpty(files)) {
return;
}

for (File file : files) {
final boolean isPackage = (isApkFile(file) || file.isDirectory())
&& !PackageInstallerService.isStageName(file.getName());
if (!isPackage) {
continue;
}
try {
scanPackageLI(file, parseFlags | PackageParser.PARSE_MUST_BE_APK,
scanFlags, currentTime, null);
} catch (PackageManagerException e) {
if ((parseFlags & PackageParser.PARSE_IS_SYSTEM) == 0 &&
e.error == PackageManager.INSTALL_FAILED_INVALID_APK) {
// 如果扫描Data分区的APK失败,则删除Data分区扫描失败的文件
if (file.isDirectory()) {
mInstaller.rmPackageDir(file.getAbsolutePath());
} else {
file.delete();
}
}
}
}
}

Ongoing—

##

参考资料

Android包管理机制
Android 包管理(PackageManagerService)
第4章 深入理解PackageManagerService
深入理解 PackageManagerService
Android7.0 PackageManagerService (1) 通信结构、启动和main函数
Android PackageManagerService详细分析
Android8.0 PackageManagerService相关 – APK安装和install 的变更和源码浅析