PKMS 钻研(1) - 启动流程

Updated on 2020.03.30

核心源码(Android 10)

关键类 路径
Process.java frameworks/base/core/java/android/os/Process.java
Settings.java frameworks/base/services/core/java/com/android/server/pm/Settings.java
SettingBase.java frameworks/base/services/core/java/com/android/server/pm/SettingBase.java
SystemConfig.java frameworks/base/core/java/com/android/server/SystemConfig.java
SystemServer.java frameworks/base/services/java/com/android/server/SystemServer.java
SharedUserSetting.java frameworks/base/services/core/java/com/android/server/pm/SharedUserSetting.java
PackageManagerService.java frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java

一、SystemServer

PackageManagerService (PKMS)SystemServer 启动后的 第一个核心服务,也是 Android 系统中最常用、最为核心的服务之一。它负责系统中 Package 的管理,比如常见的:应用程序的安装卸载信息查询等。

SystemServer 创建,SystemServer 调用了 PackageManagerServicemain() 方法创建 PackageManagerService 实例

1.1 SystemServer.main()

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
// frameworks/base/services/java/com/android/server/SystemServer.java

public final class SystemServer {

/**
* The main entry point from zygote.
*/
public static void main(String[] args) {
new SystemServer().run();
}

private void run() {
... ...

// Start services.
try {
startBootstrapServices();
startCoreServices();
startOtherServices();
SystemServerInitThreadPool.shutdown();
}
... ...
}

}

1.2 SystemServer.startBootstrapServices()

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
// frameworks/base/services/java/com/android/server/SystemServer.java

public final class SystemServer {

private PackageManagerService mPackageManagerService;
private Context mSystemContext;
private boolean mOnlyCore;

private void startBootstrapServices() {
// 启动 Watchdog
final Watchdog watchdog = Watchdog.getInstance();
watchdog.start();
... ...

// 启动 installer 服务
Installer installer = mSystemServiceManager.startService(Installer.class);
... ...

// 处于加密状态则仅仅解析核心应用
String cryptState = VoldProperties.decrypt().orElse("");
if (ENCRYPTING_STATE.equals(cryptState)) {
Slog.w(TAG, "Detected encryption in progress - only parsing core apps");
mOnlyCore = true; // ENCRYPTING_STATE = "trigger_restart_min_framework"
} else if (ENCRYPTED_STATE.equals(cryptState)) {
Slog.w(TAG, "Device encrypted - only parsing core apps");
mOnlyCore = true; // ENCRYPTED_STATE = "1"
}
... ...

traceBeginAndSlog("StartPackageManagerService");
try {
Watchdog.getInstance().pauseWatchingCurrentThread("packagemanagermain");
// 调用 PKMS 的 main 函数,创建 PKMS 服务,并注册到 ServiceManager
mPackageManagerService = PackageManagerService.main(mSystemContext, installer,
mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);
} finally {
Watchdog.getInstance().resumeWatchingCurrentThread("packagemanagermain");
}

mFirstBoot = mPackageManagerService.isFirstBoot(); // PKMS 是否首次启动
mPackageManager = mSystemContext.getPackageManager(); // 获取 PackageManager
traceEnd();
... ...
}

}

1.3 SystemServer.startOtherServices()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// frameworks/base/services/java/com/android/server/SystemServer.java

public final class SystemServer {

private void startOtherServices() {
... ...

traceBeginAndSlog("MakePackageManagerServiceReady");
mPackageManagerService.systemReady();
traceEnd();
... ...

}

}

SystemServer 启动 PKMS 的流程到此为止,接下来的分析重点就是 PKMSmain() 方法!

二、PackageManagerService

2.1 PackageManagerService.main()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java

public static PackageManagerService main(Context context, Installer installer,
boolean factoryTest, boolean onlyCore) {
// 检查系统属性
PackageManagerServiceCompilerMapping.checkProperties();

// 初始化 PMKS 对象
PackageManagerService m = new PackageManagerService(context, installer, factoryTest, onlyCore);

m.enableSystemUserPackages();

// 利用 Binder 通信,将自己注册到 ServiceManager 进程中(这是 Binder 服务的常规注册流程)
ServiceManager.addService("package", m);
final PackageManagerNative pmn = m.new PackageManagerNative();
ServiceManager.addService("package_native", pmn);
return m;
}

该方法主要 创建 PKMS 对象,并将其注册到 ServiceManager 中,内部是一个 HashMap 集合,存储了很多相关的 Binder 服务,缓存起来,我们在使用的时候,会通过 getService(key) 的方式去 Map 中获取。

往下分析之前,我们先简单了解一下 PKMS 构造函数的主要功能:扫描 Android 系统中几个目标文件夹中的 APK,从而建立合适的数据结构来管理各种信息,如:Package 信息、四大组件信息、权限信息等。

我们抽象地来看,PKMS 像一个加工厂,它解析实际的物理文件(APK文件)以生成符合自己要求的产品。(例如:PKMS 将解析 APK 包中的 AndroidManifest.xml,并根据其中声明的 Activity 标签 来创建与此对应的对象并加以保管。)

从源码角度来看,PKMS 的工作流程相对简单。但深入研究后,发现其很复杂!

复杂的是其中用于保存各种信息的数据结构它们之间的关系,以及影响最终结果的策略控制

如果你自行研究过 PKMS,你会发现代码中存在大量不同的数据结构,它们之间的关系会让人大为头疼。所以,在这篇文章中我们除了分析 PKMS 的工作流程以外,会重点关注重要的数据结构以及它们的作用。

接下来开始重点分析 PKMS 的构造函数,如果放在一篇文章中去分析是不可能梳理清楚的!所以,我们分两部分研究,如下:

           ✂  构造函数(1) - 扫描 Package 前 准备工作 (本篇博文要讨论的内容)
           ✂  构造函数(2) - 扫描 Package 和 扫尾工作

构造函数的分析,我们也会围绕着以下五个阶段,每个阶段都会输出清晰的 EventLog,大致如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public PackageManagerService(Context context, Installer installer, boolean factoryTest, boolean onlyCore) {

阶段 1:BOOT_PROGRESS_PMS_START
...
synchronized (mInstallLock) {
synchronized (mPackages) {
...
阶段 2:BOOT_PROGRESS_PMS_SYSTEM_SCAN_START
阶段 3:BOOT_PROGRESS_PMS_DATA_SCAN_START
阶段 4:BOOT_PROGRESS_PMS_SCAN_END
阶段 5:BOOT_PROGRESS_PMS_READY
...
}
}
}

2.2 构造函数

接下来我们具体看看 PackageManagerService 做了哪些工作:

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
// frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java

public PackageManagerService(Context context, Installer installer, boolean factoryTest, boolean onlyCore) {

Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "create package manager");
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_START, SystemClock.uptimeMillis()); // 第一阶段

if (mSdkVersion <= 0) { // 其值取自系统属性 ro.build.version.sdk
Slog.w(TAG, "**** ro.build.version.sdk not set!");
}

mContext = context;

mFactoryTest = factoryTest; // 是否工厂模式
mOnlyCore = onlyCore; // 是否只加载核心服务
mMetrics = new DisplayMetrics(); // 存储与显示屏相关的一些属性,例如屏幕的宽/高尺寸,分辨率等信息
mInstaller = installer; // 创建 Installer 对象,该对象和 Native 进程 installd 交互

// Create sub-components that provide services / data. Order here is important.
synchronized (mInstallLock) {
synchronized (mPackages) {
// 暴露私有服务,用于系统组件的使用
LocalServices.addService(PackageManagerInternal.class, new PackageManagerInternalImpl());
// 创建用户管理服务
sUserManager = new UserManagerService(context, this,
new UserDataPreparer(mInstaller, mInstallLock, mContext, mOnlyCore), mPackages);
mComponentResolver = new ComponentResolver(sUserManager,
LocalServices.getService(PackageManagerInternal.class), mPackages);
mPermissionManager = PermissionManagerService.create(context, mPackages /*externalLock*/);
mDefaultPermissionPolicy = mPermissionManager.getDefaultPermissionGrantPolicy();

// Settings 是一个非常重要的类,该类用于存储系统运行过程中的一些设置,我们后面会重点分析这个类!
mSettings = new Settings(Environment.getDataDirectory(),
mPermissionManager.getPermissionSettings(), mPackages);
}
}

// 添加 system、phone、log、nfc、bluetooth、shell 这六种 shareUserId 到 mSettings
// addSharedUserLPw 做了什么?我们留到下面分析
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);
... ...
}

刚进入构造函数,我们就遇到了第一个较为复杂的数据结构 Settings,以及它的 addSharedUserLPw 方法。

2.2.1 Settings

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
// frameworks/base/services/core/java/com/android/server/pm/Settings.java

public final class Settings {

Settings(File dataDir, PermissionSettings permission, Object lock) {
mLock = lock;
mPermissions = permission;
mRuntimePermissionsPersistence = new RuntimePermissionPersistence(mLock);

mSystemDir = new File(dataDir, "system"); // 创建指向 /data/system/ 目录的 File
mSystemDir.mkdirs(); // 创建目录

FileUtils.setPermissions(mSystemDir.toString(),
FileUtils.S_IRWXU|FileUtils.S_IRWXG
|FileUtils.S_IROTH|FileUtils.S_IXOTH,
-1, -1);

// 用于描述系统所安装的 Package 信息
mSettingsFilename = new File(mSystemDir, "packages.xml");

// packages.xml的备份信息
mBackupSettingsFilename = new File(mSystemDir, "packages-backup.xml");

// 保存系统中存在的所有非系统自带的 APK 信息,即 UID 大于 10000 的 apk
mPackageListFilename = new File(mSystemDir, "packages.list");
FileUtils.setPermissions(mPackageListFilename, 0640, SYSTEM_UID, PACKAGE_INFO_GID);

// sdcardfs 相关的文件
final File kernelDir = new File("/config/sdcardfs");
mKernelMappingFilename = kernelDir.exists() ? kernelDir : null;

// 记录系统中被强制停止运行的 App 信息,如有 App 被强制停止运行,会将一些信息记录到该文件中
mStoppedPackagesFilename = new File(mSystemDir, "packages-stopped.xml");

// packages-stopped.xml 的备份信息
mBackupStoppedPackagesFilename = new File(mSystemDir, "packages-stopped-backup.xml");
}
}

Settings 的构造函数的主要工作:建立与某些系统配置文件、目录之间的关联。

首先,它会创建指向 /data/system/ 目录的 File 实例,这个目录下会保存很多系统文件。其次,就是创建 /data/system/ 目录下的某些 .xml 文件 或其他文件的 File 实例。

上面源码中涉及到 5 个文件:

           ✒ packages.xml: PKMS 扫描完目标文件夹后,会创建 packages.xml。当系统进行程序安装、卸载和更新等操作时,均会更新该文件;

           ✒ packages-backup.xml:packages.xml 文件的备份;

           ✒ packages.list:用于描述系统中存在的所有非系统自带的 APK 信息。当这些 APK 有变化时,PKMS 就会更新该文件;

           ✒ packages-stopped.xml:记录被用户强行停止的应用的 Package 信息;

           ✒ packages-stopped-back.xml:packages-stopped.xml 文件的备份。

我们注意到,上面的介绍中涉及到了两个 back-up 文件,它们是做什么的?

其实 Android 系统在修改 packages.xmlpackages-stopped.xml 之前,会先对它们进行备份。当对它们的修改操作正常完成,则会删掉备份的文件。如果在修改过程中系统出现问题重启了,会再次去读取这两个文件;如果此时发现它们的备份文件还存在,则说明上一次对两份文件的修改操作发生了异常,这两份文件的内容可能已经不准确了,这时系统会去使用之前备份的文件的内容。

创建完相关系统文件 File 实例后,Settings 的构造工作也就结束了。

之前我们提出了一个问题:addSharedUserLPw 方法做了什么? 从上面截取一段代码回顾一下:

1
2
mSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);

addSharedUserLPw 传递了 4 个参数:

           ✒  android.uid.system:字符串

           ✒  Process.SYSTEM_UID:值为 1000 // public static final int SYSTEM_UID = 1000;

           ✒  ApplicationInfo.FLAG_SYSTEM:标志 // public static final int FLAG_SYSTEM = 1<<0;

           ✒  ApplicationInfo.PRIVATE_FLAG_PRIVILEGED:特权 Apk // public static final int PRIVATE_FLAG_PRIVILEGED = 1<<3;

addSharedUserLPw 函数分析之前,我们有必要先了解一下 SYSTEM_UID 的相关知识。

2.2.2 UID/GID

UID 为 用户 ID 的缩写,GID 为 用户组 ID 的缩写。一般来说,每一个进程都会有一个对应的 UID(即标示该进程属于哪个用户,不同用户拥有不同权限)。一个进程也可分属不用的用户组。

UID/GID 和进程的权限有关

在 Android 平台中,系统定义的 UID/GID 在 Process.java 文件中,如下所示(列举部分):

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
// frameworks/base/core/java/android/os/Process.java

public class Process {

/**
* Defines the UID/GID under which system code runs.
*/
public static final int SYSTEM_UID = 1000; // 系统进程的 UID
/**
* Defines the UID/GID under which the telephony code runs.
*/
public static final int PHONE_UID = 1001; // Phone 进程的 UID
/**
* Defines the UID/GID for the user shell.
*/
public static final int SHELL_UID = 2000; // shell 进程的 UID
/**
* Defines the UID/GID for the log group.
*/
public static final int LOG_UID = 1007; // LOG 进程的 UID
/**
* Defines the UID/GID for the WIFI supplicant process.
*/
public static final int WIFI_UID = 1010; // WIFI 进程的 UID
/**
* Defines the UID/GID for the mediaserver process.
*/
public static final int MEDIA_UID = 1013; // mediaserver 进程的 UID
/**
* Defines the UID/GID for the NFC service process.
*/
public static final int NFC_UID = 1027; // NFC 进程的 UID
... ...

/**
* Defines the start of a range of UIDs (and GIDs), going from this
* number to {@link #LAST_APPLICATION_UID} that are reserved for assigning
* to applications.
*/
public static final int FIRST_APPLICATION_UID = 10000; // 第一个应用 Package 的起始 UID
/**
* Last of application-specific UIDs starting at
* {@link #FIRST_APPLICATION_UID}.
*/
public static final int LAST_APPLICATION_UID = 19999; // 系统所支持的最大的应用 Package 的 UID
... ...

}

2.2.3 Settings.addSharedUserLPw()

现在我们开始分析 addSharedUserLPw

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
// frameworks/base/services/core/java/com/android/server/pm/Settings.java

public final class Settings {

SharedUserSetting addSharedUserLPw(String name, int uid, int pkgFlags, int pkgPrivateFlags) {
// 根据 key 从 map 中获取值
SharedUserSetting s = mSharedUsers.get(name);
// 如果值不为 null 并且保存的 uid 和传递过来的一致,就直接返回结果,uid 不一致则返回 null
if (s != null) {
if (s.userId == uid) {
return s;
}
PackageManagerService.reportSettingsProblem(Log.ERROR,
"Adding duplicate shared user, keeping first: " + name);
return null;
}

// 若 s 为 null,则根据传递过来的参数新创建对象
s = new SharedUserSetting(name, pkgFlags, pkgPrivateFlags);
s.userId = uid;
// 在系统中保存值为 uid 的 用户 id,成功返回 true
if (registerExistingAppIdLPw(uid, s, name)) {
mSharedUsers.put(name, s); // 将 name 与 s 键值对添加到 mSharedUsers 中保存
return s;
}
return null;
}

}

从源码中我们发现,Settings 中有一个 mSharedUsers 成员,该成员存储的是 【“字符串” 与 “SharedUserSetting” 键值对】,也就是说可以通过 字符串key 得到对应的 SharedUserSetting 对象。

那么 SharedUserSetting 是什么?创建它的目的是什么?接下来我们继续分析!

2.2.4 SharedUserSetting()

为了解释 SharedUserSetting,我们拿 SystemUI 作为例子来讨论这个问题。

我们看下 SystemUI 的 AndroidManifest.xml

1
2
3
4
5
6
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
package="com.android.systemui"
android:sharedUserId="android.uid.systemui"
xmlns:tools="http://schemas.android.com/tools"
coreApp="true">

AndroidManifest.xml 中,声明了一个 android:sharedUserId 的属性:android.uid.systemui

有必要聊聊这个 “sharedUserId” 的作用!

1、两个或多个声明了同一种 sharedUserIdAPK 可共享彼此的数据,并且可运行在同一进程中。

2、通过声明特定的 sharedUserId,该 APK 所在 进程 将被赋予指定的 UID(比如本例中的 SystemUI 声明了 system 的 uid,运行 SystemUI 的进程就可享受 system 用户所对应的权限)。

除了在 AndroidManifest.xml 中声明 sharedUserId 外,APK 在编译时还必须使用对应的证书进行签名。例如本例的 SystemUI,在其 Android.mk 中需要额外申明 LOCAL_CERTIFICATE := platform,如此才可以获得指定的 UID(当然这个不是我们分析的重点,在项目开发的过程中,我们会了解到这一点)。

通过以上分析,我们知道了如何组织一种数据结构来包括上面的内容。

有 3 个关键点需要注意:

     ♣  XML 中 sharedUserId 属性指定了一个字符串,它是 UID 的字符串描述,故对应数据结构中也应该有这样一个字符串,这样就把代码和 XML 中的属性联系起来了。
     ♣  在 LINUX 系统中,真正的 uid 是一个整数,所以该数据结构中必然有一个整型变量。
     ♣  多个 Package 可声明同一个 sharedUserId,因此该数据结构必然会保存那些声明了相同 sharedUserId 的 Package 的某些信息。

我们对 SharedUserSetting 做个总结:

1、Settings 类定义了一个 mSharedUsers 成员,它是一个 ArrayMap,以 字符串(如:android.uid.system)为 key,对应的 Value 是一个 SharedUserSetting 对象。

1
final ArrayMap<String, SharedUserSetting> mSharedUsers = new ArrayMap<String, SharedUserSetting>();

2、SharedUserSetting 定义了一个成员变量 packages,类型为 ArraySet,用于保存声明了相同 sharedUserIdPackage 的权限设置信息(这一点我们之前提到过)。

1
2
3
4
5
6
7
8
9
10
11
12
final class SharedUserSetting extends SettingBase {
final String name;

int userId;

// flags that are associated with this uid, regardless of any package flags
int uidFlags;
int uidPrivateFlags;

final ArraySet<PackageSetting> packages = new ArraySet<PackageSetting>();
... ...
}

3、每个 Package 有自己的权限设置。权限的概念由 PackageSeting 类表达。该类继承自 PackageSettingBase 类,PackageSettingBase 又继承自 SettingBase

1
2
public final class PackageSetting extends PackageSettingBase {}
public abstract class PackageSettingBase extends SettingBase {}

SettingBase 对象持有 PermissionsState 对象,用于表示可用的权限。

1
2
3
abstract class SettingBase {
protected final PermissionsState mPermissionsState;
}

4、Settings 中还有两个成员,一个是 mAppIds,另一个是 mOtherUserIds,这两位成员的类型分别是 ArrayListSparseArray。其目的是以 UID 为索引,得到对应的 SharedUserSeting 对象。在一般情况下,以 索引 获取数组元素的速度,比以 Key 获取 ArrayMap元素 的速度要快很多。

1
2
private final ArrayList<SettingBase> mAppIds = new ArrayList<>();
private final SparseArray<Object> mOtherUserIds = new SparseArray<Object>();

2.2.5 Settings.registerExistingAppIdLPw

我们回忆一下 registerExistingAppIdLPw 方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// frameworks/base/services/core/java/com/android/server/pm/Settings.java

public final class Settings {

SharedUserSetting addSharedUserLPw(String name, int uid, int pkgFlags, int pkgPrivateFlags) {
SharedUserSetting s = mSharedUsers.get(name);
... ...

s = new SharedUserSetting(name, pkgFlags, pkgPrivateFlags);
s.userId = uid;
if (registerExistingAppIdLPw(uid, s, name)) {
mSharedUsers.put(name, s);
return s;
}
return null;
}

}

它的作用是将 SharedUserSettings 对象保存到对应的数组中,代码如下:

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
// frameworks/base/services/core/java/com/android/server/pm/Settings.java

public final class Settings {

/** Returns true if the requested AppID was valid and not already registered. */
private boolean registerExistingAppIdLPw(int appId, SettingBase obj, Object name) {
// 系统所支持的最大的应用 Package 的 UID,不能超出限制 19999
if (appId > Process.LAST_APPLICATION_UID) {
return false;
}

// 第一个应用 Package(非系统安装应用)的起始 UID
if (appId >= Process.FIRST_APPLICATION_UID) {
// 获取数组的长度
int size = mAppIds.size();
// 计算索引,其值是 uid 和 FIRST_APPLICATION_UID 的差
final int index = appId - Process.FIRST_APPLICATION_UID;
while (index >= size) {
mAppIds.add(null);
size++;
}
// 如果数组的目标索引值位置有不为 null 的值,说明已经添加过
if (mAppIds.get(index) != null) {
PackageManagerService.reportSettingsProblem(Log.ERROR,
"Adding duplicate app id: " + appId
+ " name=" + name);
return false;
}
// 应用 Package 的 uid 由 mAppIds 保存
mAppIds.set(index, obj);
} else {
if (mOtherAppIds.get(appId) != null) {
PackageManagerService.reportSettingsProblem(Log.ERROR,
"Adding duplicate shared id: " + appId
+ " name=" + name);
return false;
}
// 系统 Package 的 uid 由 mOtherUserIds 保存
mOtherAppIds.put(appId, obj);
}
return true;
}

}

至此对 Settings 的分析我们暂时告一段落。

2.3 XML 文件扫描

分析完 PKMS 构造函数前期工作的第一阶段后,接下来就要继续回到构造函数中分析剩下的代码:

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
// frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java

public PackageManagerService(Context context, Installer installer,
boolean factoryTest, boolean onlyCore) {
... ... // 第一阶段

// 该值和调试有关,一般不设置该属性
String separateProcesses = SystemProperties.get("debug.separate_processes");

if (separateProcesses != null && separateProcesses.length() > 0) {
if ("*".equals(separateProcesses)) {
mDefParseFlags = PackageParser.PARSE_IGNORE_PROCESSES;
mSeparateProcesses = null;
Slog.w(TAG, "Running with debug.separate_processes: * (ALL)");
} else {
mDefParseFlags = 0;
mSeparateProcesses = separateProcesses.split(",");
Slog.w(TAG, "Running with debug.separate_processes: " + separateProcesses);
}
} else {
mDefParseFlags = 0;
mSeparateProcesses = null;
}

// 对应用进行 dexopt 优化的辅助类
mPackageDexOptimizer = new PackageDexOptimizer(installer, mInstallLock, context, "*dexopt*");
mDexManager = new DexManager(mContext, this, mPackageDexOptimizer, installer, mInstallLock);
mArtManagerService = new ArtManagerService(mContext, this, installer, mInstallLock);
mMoveCallbacks = new MoveCallbacks(FgThread.get().getLooper());

mViewCompiler = new ViewCompiler(mInstallLock, mInstaller);

mOnPermissionChangeListeners = new OnPermissionChangeListeners(FgThread.get().getLooper());

// 获取当前设备的显示屏信息
getDefaultDisplayMetrics(context, mMetrics);

// 通过 SystemConfig 读取系统的 feature、permession 等配置,并初始化 mGlobalGids/mAvailableFeatures 成员
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "get system config");
SystemConfig systemConfig = SystemConfig.getInstance(); // 接下来重点讨论
mAvailableFeatures = systemConfig.getAvailableFeatures();
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);

mProtectedPackages = new ProtectedPackages(mContext);
... ...

}

以上代码除了创建了几个对象以外,还有一个重要的需要关注的类:SystemConfig,这就是我们接下来分析的重点!!!

2.3.1 SystemConfig

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
// frameworks/base/core/java/com/android/server/SystemConfig.java

/**
* Loads global system configuration info.
* Note: Initializing this class hits the disk and is slow. This class should generally only be
* accessed by the system_server process.
*/
public class SystemConfig {
static SystemConfig sInstance;
... ...

public static SystemConfig getInstance() { // 单例模式
if (!isSystemProcess()) {
Slog.wtf(TAG, "SystemConfig is being accessed by a process other than "
+ "system_server.");
}

synchronized (SystemConfig.class) {
if (sInstance == null) {
sInstance = new SystemConfig();
}
return sInstance;
}
}
... ...

}

查看它的构造函数:

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
// frameworks/base/core/java/com/android/server/SystemConfig.java

public class SystemConfig {

/**
* 通过 readPermissions() 读取并解析 /system/etc/ 等目录下的 sysconfig.xml、permission.xml 文件
*/
SystemConfig() {
// Read configuration from system
readPermissions(Environment.buildPath(
Environment.getRootDirectory(), "etc", "sysconfig"), ALLOW_ALL);

// Read configuration from the old permissions dir
readPermissions(Environment.buildPath(
Environment.getRootDirectory(), "etc", "permissions"), ALLOW_ALL);

// Vendors are only allowed to customize these
int vendorPermissionFlag = ALLOW_LIBS | ALLOW_FEATURES | ALLOW_PRIVAPP_PERMISSIONS;
if (Build.VERSION.FIRST_SDK_INT <= Build.VERSION_CODES.O_MR1) {
// For backward compatibility
vendorPermissionFlag |= (ALLOW_PERMISSIONS | ALLOW_APP_CONFIGS);
}
readPermissions(Environment.buildPath(
Environment.getVendorDirectory(), "etc", "sysconfig"), vendorPermissionFlag);
readPermissions(Environment.buildPath(
Environment.getVendorDirectory(), "etc", "permissions"), vendorPermissionFlag);

// Allow ODM to customize system configs as much as Vendor, because /odm is another
// vendor partition other than /vendor.
int odmPermissionFlag = vendorPermissionFlag;
readPermissions(Environment.buildPath(
Environment.getOdmDirectory(), "etc", "sysconfig"), odmPermissionFlag);
readPermissions(Environment.buildPath(
Environment.getOdmDirectory(), "etc", "permissions"), odmPermissionFlag);

// Allow OEM to customize these
int oemPermissionFlag = ALLOW_FEATURES | ALLOW_OEM_PERMISSIONS;
readPermissions(Environment.buildPath(
Environment.getOemDirectory(), "etc", "sysconfig"), oemPermissionFlag);
readPermissions(Environment.buildPath(
Environment.getOemDirectory(), "etc", "permissions"), oemPermissionFlag);

// Allow Product to customize all system configs
readPermissions(Environment.buildPath(
Environment.getProductDirectory(), "etc", "sysconfig"), ALLOW_ALL);
readPermissions(Environment.buildPath(
Environment.getProductDirectory(), "etc", "permissions"), ALLOW_ALL);

// Allow /system_ext to customize all system configs
readPermissions(Environment.buildPath(
Environment.getSystemExtDirectory(), "etc", "sysconfig"), ALLOW_ALL);
readPermissions(Environment.buildPath(
Environment.getSystemExtDirectory(), "etc", "permissions"), ALLOW_ALL);
}

}

我们发现 SystemConfig 的构造函数所做的工作就是:readPermissions(),即从文件中读取权限

2.3.2 SystemConfig.readPermissions()

接下来我们看看 readPermissions 的源码:

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
// frameworks/base/core/java/com/android/server/SystemConfig.java

public class SystemConfig {

void readPermissions(File libraryDir, int permissionFlag) {
... ...

// Iterate over the files in the directory and scan .xml files
File platformFile = null;
for (File f : libraryDir.listFiles()) {
if (!f.isFile()) {
continue;
}

// We'll read platform.xml last
// 处理该目录下的非 platform.xml 文件
if (f.getPath().endsWith("etc/permissions/platform.xml")) {
platformFile = f;
continue;
}
... ...

// 调用 readPermissionsFromXml 解析此 XML 文件
readPermissionsFromXml(f, permissionFlag);
}

// Read platform permissions last so it will take precedence
if (platformFile != null) {
// 不知道你有没有发现,platform.xml文件的解析优先级最高!
readPermissionsFromXml(platformFile, permissionFlag);
}
}

}

从源码中,我们发现 readPermissions 函数不就是调用 readPermissionFromXml 函数解析 "/xxx/etc/permissions/" 目录下的文件吗?

这些文件似乎都是 XML 文件。你也许有个疑问?该目录下都有哪些 XML 文件?这些 XML 文件中有些什么内容呢?以我手中的 pixel 为例

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
sailfish:/system/etc/permissions $ ls -al
ls -al
total 168
drwxr-xr-x 2 root root 4096 2009-01-01 16:00 .
drwxr-xr-x 14 root root 4096 2009-01-01 16:00 ..
-rw-r--r-- 1 root root 1050 2009-01-01 16:00 android.software.live_wallpaper.xml
-rw-r--r-- 1 root root 748 2009-01-01 16:00 android.software.webview.xml
-rw-r--r-- 1 root root 1778 2009-01-01 16:00 com.android.ims.rcsmanager.xml
-rw-r--r-- 1 root root 828 2009-01-01 16:00 com.android.location.provider.xml
-rw-r--r-- 1 root root 828 2009-01-01 16:00 com.android.media.remotedisplay.xml
-rw-r--r-- 1 root root 820 2009-01-01 16:00 com.android.mediadrm.signer.xml
-rw-r--r-- 1 root root 158 2009-01-01 16:00 com.android.omadm.service.xml
-rw-r--r-- 1 root root 435 2009-01-01 16:00 com.android.sdm.plugins.connmo.xml
-rw-r--r-- 1 root root 701 2009-01-01 16:00 com.android.sdm.plugins.sprintdm.xml
-rw-r--r-- 1 root root 234 2009-01-01 16:00 com.android.vzwomatrigger.xml
-rw-r--r-- 1 root root 1079 2009-01-01 16:00 com.customermobile.preload.vzw.xml
-rw-r--r-- 1 root root 850 2009-01-01 16:00 com.google.android.camera.experimental2016.xml
-rw-r--r-- 1 root root 563 2009-01-01 16:00 com.google.android.dialer.support.xml
-rw-r--r-- 1 root root 816 2009-01-01 16:00 com.google.android.maps.xml
-rw-r--r-- 1 root root 835 2009-01-01 16:00 com.google.android.media.effects.xml
-rw-r--r-- 1 root root 811 2009-01-01 16:00 com.google.vr.platform.xml
-rw-r--r-- 1 root root 160 2009-01-01 16:00 com.verizon.apn.xml
-rw-r--r-- 1 root root 158 2009-01-01 16:00 com.verizon.embms.xml
-rw-r--r-- 1 root root 288 2009-01-01 16:00 com.verizon.llkagent.xml
-rw-r--r-- 1 root root 174 2009-01-01 16:00 com.verizon.provider.xml
-rw-r--r-- 1 root root 220 2009-01-01 16:00 com.verizon.services.xml
-rw-r--r-- 1 root root 239 2009-01-01 16:00 features-verizon.xml
-rw-r--r-- 1 root root 811 2009-01-01 16:00 obdm_permissions.xml
-rw-r--r-- 1 root root 8916 2009-01-01 16:00 platform.xml
-rw-r--r-- 1 root root 23092 2009-01-01 16:00 privapp-permissions-google.xml
-rw-r--r-- 1 root root 1346 2009-01-01 16:00 privapp-permissions-marlin.xml
-rw-r--r-- 1 root root 20848 2009-01-01 16:00 privapp-permissions-platform.xml
-rw-r--r-- 1 root root 1587 2009-01-01 16:00 vzw_mvs_permissions.xml
sailfish:/system/etc/permissions $

既然我们上面一直在说 platform.xml 这个文件,那就看下 platform.xml 有什么:

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
<permissions>

<!-- 建立权限名与 gid 的映射关系。如下面声明的 BLUETOOTH_ADMIN 权限,
它对应的用户组是 net_bt_admin。注意,该文件中的 permission 标签只对
那些需要通过读写设备(蓝牙/cameta)/创建 socket 等进程划分了 gid。
因为这些权限涉及和 Linux 内核交互,所以需要在底层权限(由不用的用户组界定)
和 Android 层权限(由不同的字符串界定)之间建立映射关系。 -->

<permission name="android.permission.BLUETOOTH_ADMIN" >
<group gid="net_bt_admin" />
</permission>

<permission name="android.permission.BLUETOOTH" >
<group gid="net_bt" />
</permission>

<permission name="android.permission.BLUETOOTH_STACK" >
<group gid="bluetooth" />
<group gid="wakelock" />
<group gid="uhid" />
</permission>

... ...

<!-- 赋予对应 uid 相应的权限。如果下面一行表示 uid 为 audioserver,那么就
赋予它 WAKE_LOCK 的权限,其实就是把它加到对应的用户组中 -->

... ...

<assign-permission name="android.permission.MODIFY_AUDIO_SETTINGS" uid="audioserver" />
<assign-permission name="android.permission.ACCESS_SURFACE_FLINGER" uid="audioserver" />
<assign-permission name="android.permission.WAKE_LOCK" uid="audioserver" />
<assign-permission name="android.permission.UPDATE_DEVICE_STATS" uid="audioserver" />
<assign-permission name="android.permission.UPDATE_APP_OPS_STATS" uid="audioserver" />

... ...

<!-- This is a list of all the libraries available for application
code to link against. -->

<!-- 系统提供的 Java 库,应用程序运行时必须要链接这些库,该工作由系统自动完成 -->

<library name="android.test.mock"
file="/system/framework/android.test.mock.jar" />
<library name="android.test.runner"
file="/system/framework/android.test.runner.jar" />
<library name="javax.obex"
file="/system/framework/javax.obex.jar" />
<library name="org.apache.http.legacy"
file="/system/framework/org.apache.http.legacy.jar" />

... ...
</permissions>

platform.xml 文件中主要使用了如下 4 个标签

         ♣ permissiongroup 用于建立 Linux 层 gid 和 Android 层 permission 之间的映射关系。

         ♣ assign-permission 用于向指定的 uid 赋予相应的权限。这个权限由 Android 定义,用于字符串表示。

         ♣ library 用于指定系统库。当应用程序运行时,系统会自动为这些进程加载这些库。

不知道你是否已经产生了疑问?设备上的 /system/etc/permission 目录中的文件是从哪里来的?我直接告诉你答案:在编译阶段由不用硬件平台根据自己的配置信息复制相关文件到目标目录中的来的。(这个具体细节我们不讨论,有兴趣的读者可以自行查阅)

2.3.3 readPermissionFromXML

前面我们说过:readPermissions 函数其实就是调用 readPermissionFromXml 函数解析 "/xxx/etc/permissions/" 目录下的文件!

readPermissionFromXml 又做了哪些工作?其实它的作用就是将 XML 文件中的标签以及它们之间的关系转换成代码中的相应数据结构:

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
167
168
169
170
171
172
173
174
175
176
177
178
// frameworks/base/core/java/com/android/server/SystemConfig.java

public class SystemConfig {

private void readPermissionsFromXml(File permFile, int permissionFlag) {
FileReader permReader = null;
try {
permReader = new FileReader(permFile);
} catch (FileNotFoundException e) {
Slog.w(TAG, "Couldn't find or open permissions file " + permFile);
return;
}

final boolean lowRam = ActivityManager.isLowRamDeviceStatic();

try {
XmlPullParser parser = Xml.newPullParser();
parser.setInput(permReader);
... ...

while (true) {
XmlUtils.nextElement(parser);
if (parser.getEventType() == XmlPullParser.END_DOCUMENT) {
break;
}

String name = parser.getName();
if (name == null) {
XmlUtils.skipCurrentTag(parser);
continue;
}

switch (name) {
// 解析 group 标签
case "group": {
if (allowAll) {
String gidStr = parser.getAttributeValue(null, "gid");
if (gidStr != null) {
int gid = android.os.Process.getGidForName(gidStr);
// 转换 XML 中的 gid 字符串为整型,并保存到 mGlobalGids中
mGlobalGids = appendInt(mGlobalGids, gid);
} else {
Slog.w(TAG, "<" + name + "> without gid in " + permFile + " at "
+ parser.getPositionDescription());
}
} else {
logNotAllowedInPartition(name, permFile, parser);
}
XmlUtils.skipCurrentTag(parser);
} break;
// 解析 permission标签
case "permission": {
if (allowPermissions) {
String perm = parser.getAttributeValue(null, "name");
if (perm == null) {
Slog.w(TAG, "<" + name + "> without name in " + permFile + " at "
+ parser.getPositionDescription());
XmlUtils.skipCurrentTag(parser);
break;
}
perm = perm.intern();
readPermission(parser, perm); // 调用 readPermission 处理
} else {
logNotAllowedInPartition(name, permFile, parser);
XmlUtils.skipCurrentTag(parser);
}
} break;
/ 解析 assign-permission 标签
case "assign-permission": {
if (allowPermissions) {
String perm = parser.getAttributeValue(null, "name");
if (perm == null) {
Slog.w(TAG, "<" + name + "> without name in " + permFile
+ " at " + parser.getPositionDescription());
XmlUtils.skipCurrentTag(parser);
break;
}
String uidStr = parser.getAttributeValue(null, "uid");
if (uidStr == null) {
Slog.w(TAG, "<" + name + "> without uid in " + permFile
+ " at " + parser.getPositionDescription());
XmlUtils.skipCurrentTag(parser);
break;
}
// 如果是 assign-permission,则取出 uid 字符串,然后获得 Linux 平台上的整型 uid 值
int uid = Process.getUidForName(uidStr);
if (uid < 0) {
Slog.w(TAG, "<" + name + "> with unknown uid \""
+ uidStr + " in " + permFile + " at "
+ parser.getPositionDescription());
XmlUtils.skipCurrentTag(parser);
break;
}
perm = perm.intern();
// 和 assign 相关的信息保存在 mSystemPermissions 中
ArraySet<String> perms = mSystemPermissions.get(uid);
if (perms == null) {
perms = new ArraySet<String>();
mSystemPermissions.put(uid, perms);
}
perms.add(perm);
} else {
logNotAllowedInPartition(name, permFile, parser);
}
XmlUtils.skipCurrentTag(parser);
} break;
// 解析 split-permission 标签
case "split-permission": {
if (allowPermissions) {
readSplitPermission(parser, permFile);
} else {
logNotAllowedInPartition(name, permFile, parser);
XmlUtils.skipCurrentTag(parser);
}
} break;
// 解析 library 标签
case "library": {
if (allowLibs) {
String lname = parser.getAttributeValue(null, "name");
String lfile = parser.getAttributeValue(null, "file");
String ldependency = parser.getAttributeValue(null, "dependency");
if (lname == null) {
Slog.w(TAG, "<" + name + "> without name in " + permFile + " at "
+ parser.getPositionDescription());
} else if (lfile == null) {
Slog.w(TAG, "<" + name + "> without file in " + permFile + " at "
+ parser.getPositionDescription());
} else {
//Log.i(TAG, "Got library " + lname + " in " + lfile);
SharedLibraryEntry entry = new SharedLibraryEntry(lname, lfile,
ldependency == null ? new String[0] : ldependency.split(":"));
// 将 XML 中的 lname 和 entry 属性值存储到 mSharedLibraries 中
mSharedLibraries.put(lname, entry);
}
} else {
logNotAllowedInPartition(name, permFile, parser);
}
XmlUtils.skipCurrentTag(parser);
} break;
// 解析 feature标签
case "feature": {
if (allowFeatures) {
String fname = parser.getAttributeValue(null, "name");
int fversion = XmlUtils.readIntAttribute(parser, "version", 0);
boolean allowed;
if (!lowRam) {
allowed = true;
} else {
String notLowRam = parser.getAttributeValue(null, "notLowRam");
allowed = !"true".equals(notLowRam);
}
if (fname == null) {
Slog.w(TAG, "<" + name + "> without name in " + permFile + " at "
+ parser.getPositionDescription());
} else if (allowed) {
addFeature(fname, fversion);
}
} else {
logNotAllowedInPartition(name, permFile, parser);
}
XmlUtils.skipCurrentTag(parser);
} break;
// 解析其它标签
case "xxx": {
... ...
} break;
}
}
} catch (XmlPullParserException e) {
Slog.w(TAG, "Got exception parsing permissions.", e);
} catch (IOException e) {
Slog.w(TAG, "Got exception parsing permissions.", e);
} finally {
IoUtils.closeQuietly(permReader);
}
}

}

至此,我们看到 readPermission 函数确实是将 XML 中的标签转换成对应的数据结构!