[toc]
基于 Android 7.1.1 源码分析 PackageManagerService 的架构和逻辑实现,本文是作者原创,转载请说明出处!
0 综述
我们进入第一阶段来分析,代码比较长,我们先来个总体的阶段回顾:
1 | public PackageManagerService(Context context, Installer installer, |
下面我们来逐一分析这个阶段的过程:
1 Settings
首先,创建了 Settings 对象:!
参数传递:
- Object lock:mPackage 对象!
1 | Settings(Object lock) { |
接着调用第二个构造函数: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
29Settings(File dataDir, Object lock) {
mLock = lock;
// 用于处理运行时权限相关的操作!
mRuntimePermissionsPersistence = new RuntimePermissionPersistence(mLock);
// 创建 /data/system 目录,并设置该目录的权限!
mSystemDir = new File(dataDir, "system");
mSystemDir.mkdirs();
FileUtils.setPermissions(mSystemDir.toString(),
FileUtils.S_IRWXU|FileUtils.S_IRWXG
|FileUtils.S_IROTH|FileUtils.S_IXOTH,
-1, -1);
// 创建 packages.xml 和 packages-backup.xml 文件对象
mSettingsFilename = new File(mSystemDir, "packages.xml");
mBackupSettingsFilename = new File(mSystemDir, "packages-backup.xml");
// 创建 packages.list 文件对象,并设置权限信息!
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;
// 创建 packages-stopped.xml 和 packages-stopped-backup.xml 文件对象!
mStoppedPackagesFilename = new File(mSystemDir, "packages-stopped.xml");
mBackupStoppedPackagesFilename = new File(mSystemDir, "packages-stopped-backup.xml");
}
Settings 对象的构造器很简单,这里要重点说明一下;
- packages.xml 和 packages-backup.xml 文件是一组,用于描述系统中所安装的所有 Package 信息,PMS 会先把数据写入 packages-backup.xml,信息写成功后,再改名为 packages.xml !
- packages.list 文件同样保存了系统中所有的应用的信息!
- packages-stopped.xml 和 packages-stopped-backup.xml 文件是一组,用于描述被强行停止运行的 package 信息!
1 | com.android.settings 1000 0 /data/user_de/0/com.android.settings platform:privapp 2001,3009,3002,1023,1015,3003,3001,1004,2002,1007,3006,3188 |
1.1 new RuntimePermissionPersistence
创建运行时权限管理对象!
1.2 Settings.addSharedUserLPw
接着调用了 addSharedUserLPw 方法,添加了几个系统共享用户,参数传递:
- String name:共享用户名,下面是传入的用户名:
- android.uid.system
- android.uid.phone
- android.uid.log
- android.uid.nfc
- android.uid.bluetooth
- android.uid.shell
- int uid:共享用户 id,下面的用户名和上面的 id 一一对应!
- Process.SYSTEM_UID
- RADIO_UID
- LOG_UID
- NFC_UID
- BLUETOOTH_UID
- SHELL_UID
- int pkgFlags:
- ApplicationInfo.FLAG_SYSTEM
- int pkgPrivateFlags:
- ApplicationInfo.PRIVATE_FLAG_PRIVILEGED
1 | SharedUserSetting addSharedUserLPw(String name, int uid, int pkgFlags, int pkgPrivateFlags) { |
1.2.1 SharedUserSetting
我们来看看 SharedUserSetting 这个对象:
1 | final class SharedUserSetting extends SettingBase { |
代码很简单,这里就不多说了!
1.2.2 Settings.addUserIdLPw
addUserIdLPw 用于将创建的 sharedUserId 对象根据其 uid 是系统 uid 还是非系统 uid ,添加到指定的集合中!
1 | private boolean addUserIdLPw(int uid, Object obj, Object name) { |
这里来说明一下:
涉及的常量
- Process.FIRST_APPLICATION_UID:取值 10000,用来区分系统应用 uid 和非系统应用的 uid。非系统应用的 uid 大于等于 10000,小于等于 19999,系统应用的 uid 小于 10000;
- Process.LAST_APPLICATION_UID:取值 19999,表示应用程序的 uid 最大为 19999!
总结
- mSharedUsers 集合用来保存所有的共享用户id!
- mUserIds 集合中会保存非系统应用的 uid,包括共享和非共享!
- mOtherUserIds 集合中会保存系统应用的 uid,包括共享和非共享!
2 PackageDexOptimizer
PackageDexOptimizer 用于进行 odex 优化,我们来先简单的看看代码,参数传递:
- Installer installer:installer 对象,用于安装程序;
- Object installLock:Object() 锁对象!
- Context context:系统进程上下文实例!
- String wakeLockTag:传入”dexopt“
1 | class PackageDexOptimizer { |
这里创建了 PackageDexOptimizer 对象,后续执行 odex 优化会用到!
3 MoveCallbacks
用于监听 package 的 move 操作!!
1 | private static class MoveCallbacks extends Handler { |
4 OnPermissionChangeListeners
创建 OnPermissionChangeListeners 对象!1
2
3// 创建 OnPermissionChangeListeners 对象,用于监听权限改变!
mOnPermissionChangeListeners = new OnPermissionChangeListeners(
FgThread.get().getLooper());
OnPermissionChangeListeners 用于监听权限的改变:
1 | private final static class OnPermissionChangeListeners extends Handler { |
当指定 uid 的权限发生变化后,会调用 IOnPermissionsChangeListener 的 onPermissionsChanged 方法,通过 Binder 机制,最终会调用 ApplicationPackageManager 的 OnPermissionsChangeListenerDelegate 中的 onPermissionsChanged 方法!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
32public class OnPermissionsChangeListenerDelegate extends IOnPermissionsChangeListener.Stub
implements Handler.Callback{
private static final int MSG_PERMISSIONS_CHANGED = 1;
private final OnPermissionsChangedListener mListener;
private final Handler mHandler;
public OnPermissionsChangeListenerDelegate(OnPermissionsChangedListener listener,
Looper looper) {
mListener = listener;
mHandler = new Handler(looper, this);
}
public void onPermissionsChanged(int uid) {
mHandler.obtainMessage(MSG_PERMISSIONS_CHANGED, uid, 0).sendToTarget();
}
public boolean handleMessage(Message msg) {
switch (msg.what) {
case MSG_PERMISSIONS_CHANGED: {
final int uid = msg.arg1;
// 最后调用 OnPermissionsChangedListener 的 onPermissionsChanged 方法!
mListener.onPermissionsChanged(uid);
return true;
}
}
return false;
}
}
OnPermissionsChangedListener 是一个接口,定义在 PackageManager 中:1
2
3
4
5
6
7
8
9
public interface OnPermissionsChangedListener {
/**
* Called when the permissions for a UID change.
* @param uid The UID with a change.
*/
public void onPermissionsChanged(int uid);
}
具体的实现是在 LocationManagerService 中:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21public void systemRunning() {
synchronized (mLock) {
if (D) Log.d(TAG, "systemRunning()");
... ... ... ...
// 这里创建了一个 OnPermissionsChangedListener 对象,并添加到 PackageManagerervice 中!
PackageManager.OnPermissionsChangedListener permissionListener
= new PackageManager.OnPermissionsChangedListener() {
public void onPermissionsChanged(final int uid) {
synchronized (mLock) {
applyAllProviderRequirementsLocked();
}
}
};
mPackageManager.addOnPermissionsChangeListener(permissionListener);
... ... ...
}
进入 PackageManagerService 中:1
2
3
4
5
6
7
8
9
10
11
12
public void addOnPermissionsChangeListener(IOnPermissionsChangeListener listener) {
mContext.enforceCallingOrSelfPermission(
Manifest.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS,
"addOnPermissionsChangeListener");
synchronized (mPackages) {
// 添加到 mOnPermissionChangeListeners 内部的集合中!
mOnPermissionChangeListeners.addListenerLocked(listener);
}
}
这里先看到这里吧!
5 SystemConfig
SystemConfig 是用来存储系统全局的配置信息的数据结构,有系统配置信息,权限信息,Gid 等等!1
2
3
4
5
6
7
8
9// 单例模式!
public static SystemConfig getInstance() {
synchronized (SystemConfig.class) {
if (sInstance == null) {
sInstance = new SystemConfig();
}
return sInstance;
}
}
下面我们来看看 SystemConfig 构造器中的相关方法:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23// 其构造器会调用方法读取系统配置信息!
SystemConfig() {
// 读取配置信息:/etc/sysconfig
readPermissions(Environment.buildPath(
Environment.getRootDirectory(), "etc", "sysconfig"), ALLOW_ALL);
// 读取配置信息:/etc/permissions
readPermissions(Environment.buildPath(
Environment.getRootDirectory(), "etc", "permissions"), ALLOW_ALL);
// 读取配置信息:/odm/etc/sysconfig,/odm/etc/permissions
int odmPermissionFlag = ALLOW_LIBS | ALLOW_FEATURES | ALLOW_APP_CONFIGS;
readPermissions(Environment.buildPath(
Environment.getOdmDirectory(), "etc", "sysconfig"), odmPermissionFlag);
readPermissions(Environment.buildPath(
Environment.getOdmDirectory(), "etc", "permissions"), odmPermissionFlag);
// 读取配置信息:/oem/etc/sysconfig,/oem/etc/permissions
readPermissions(Environment.buildPath(
Environment.getOemDirectory(), "etc", "sysconfig"), ALLOW_FEATURES);
readPermissions(Environment.buildPath(
Environment.getOemDirectory(), "etc", "permissions"), ALLOW_FEATURES);
}
第一个参数,表示可以访问的目录,这些目录包括:
- /etc/sysconfig:ALLOW_ALL
- /etc/permissions:ALLOW_ALL
- /odm/etc/sysconfig:ALLOW_LIBS | ALLOW_FEATURES | ALLOW_APP_CONFIGS
- /odm/etc/permissions:ALLOW_LIBS | ALLOW_FEATURES | ALLOW_APP_CONFIGS
- /oem/etc/sysconfig:ALLOW_FEATURES
- /oem/etc/permissions:ALLOW_FEATURES
第二个参数,表示这个目录可以设置的配置信息的类型,同样的,PMS 在解析时,也只会解析和读取参数指定的配置属性:
- oem:只能设置 features!
- odm:只能设置 libs,features 和 app configs!
- /system/etc/sysconfig,/system/etc/permissions:可以设置所有的配置,libs,permissions,features,app configs 等等!
下面是 flag 的取值:1
2
3
4
5private static final int ALLOW_FEATURES = 0x01;
private static final int ALLOW_LIBS = 0x02;
private static final int ALLOW_PERMISSIONS = 0x04;
private static final int ALLOW_APP_CONFIGS = 0x08;
private static final int ALLOW_ALL = ~0;
继续看!
2.1 SystemConfig.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
35
36
37
38
39
40void readPermissions(File libraryDir, int permissionFlag) {
// 校验目录可读性!
if (!libraryDir.exists() || !libraryDir.isDirectory()) {
if (permissionFlag == ALLOW_ALL) {
Slog.w(TAG, "No directory " + libraryDir + ", skipping");
}
return;
}
if (!libraryDir.canRead()) {
Slog.w(TAG, "Directory " + libraryDir + " cannot be read");
return;
}
// 遍历解析目录下的所有 .xml 文件!
File platformFile = null;
for (File f : libraryDir.listFiles()) {
//【1】对于 etc/permissions/platform.xml 文件,最后再处理!
if (f.getPath().endsWith("etc/permissions/platform.xml")) {
platformFile = f;
continue;
}
if (!f.getPath().endsWith(".xml")) {
Slog.i(TAG, "Non-xml file " + f + " in " + libraryDir + " directory, ignoring");
continue;
}
if (!f.canRead()) {
Slog.w(TAG, "Permissions library file " + f + " cannot be read");
continue;
}
//【2】进一步解析文件!
readPermissionsFromXml(f, permissionFlag);
}
//【3】读取和解析 platform.xml 文件!
if (platformFile != null) {
readPermissionsFromXml(platformFile, permissionFlag);
}
}
这个方法的主要过程是:
- 先解析该目录下的其他的 xml 文件;
- 最后解析 etc/permissions/platform.xml 文件(如果有);
2.2 SystemConfig.readPermissionsFromXml
继续调用 readPermissionsFromXml 方法,这里我们重点看一些 tag: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
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321private 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;
}
//【1】判读设备是否是低内存!
final boolean lowRam = ActivityManager.isLowRamDeviceStatic();
try {
XmlPullParser parser = Xml.newPullParser();
parser.setInput(permReader);
int type;
while ((type=parser.next()) != parser.START_TAG
&& type != parser.END_DOCUMENT) {
;
}
if (type != parser.START_TAG) {
throw new XmlPullParserException("No start tag found");
}
if (!parser.getName().equals("permissions") && !parser.getName().equals("config")) {
throw new XmlPullParserException("Unexpected start tag in " + permFile
+ ": found " + parser.getName() + ", expected 'permissions' or 'config'");
}
//【2】解析 Flag,指定二进制位为 1 ,即表示该条件满足,如果 flag 是 ALLOW_ALL,
// 那么其他的条件均满足!
boolean allowAll = permissionFlag == ALLOW_ALL;
boolean allowLibs = (permissionFlag & ALLOW_LIBS) != 0;
boolean allowFeatures = (permissionFlag & ALLOW_FEATURES) != 0;
boolean allowPermissions = (permissionFlag & ALLOW_PERMISSIONS) != 0;
boolean allowAppConfigs = (permissionFlag & ALLOW_APP_CONFIGS) != 0;
while (true) {
// 解析下一个标签!
XmlUtils.nextElement(parser);
if (parser.getEventType() == XmlPullParser.END_DOCUMENT) {
break;
}
String name = parser.getName();
if ("group".equals(name) && allowAll) {
//【1】allowAll 为 true,解析 "group" 标签;
// 解析系统中所有的 Gid 信息!
String gidStr = parser.getAttributeValue(null, "gid");
if (gidStr != null) {
int gid = android.os.Process.getGidForName(gidStr);
mGlobalGids = appendInt(mGlobalGids, gid);
} else {
Slog.w(TAG, "<group> without gid in " + permFile + " at "
+ parser.getPositionDescription());
}
XmlUtils.skipCurrentTag(parser);
continue;
} else if ("permission".equals(name) && allowPermissions) {
//【2】allowPermissions 为 true,解析 "permission" 标签
// 解析系统中所有权限和其所属 gid 的信息!
String perm = parser.getAttributeValue(null, "name");
if (perm == null) {
Slog.w(TAG, "<permission> without name in " + permFile + " at "
+ parser.getPositionDescription());
XmlUtils.skipCurrentTag(parser);
continue;
}
perm = perm.intern();
readPermission(parser, perm); // 调用 readPermission 继续解析权限
} else if ("assign-permission".equals(name) && allowPermissions) {
//【3】如果 allowPermissions 为 true,解析 "assign-permission" 标签
// 解析系统中 uid 和其所持有的权限的关系!
String perm = parser.getAttributeValue(null, "name"); // 权限名
if (perm == null) {
Slog.w(TAG, "<assign-permission> without name in " + permFile + " at "
+ parser.getPositionDescription());
XmlUtils.skipCurrentTag(parser);
continue;
}
String uidStr = parser.getAttributeValue(null, "uid"); // 拥有该权限的 uid
if (uidStr == null) {
Slog.w(TAG, "<assign-permission> without uid in " + permFile + " at "
+ parser.getPositionDescription());
XmlUtils.skipCurrentTag(parser);
continue;
}
int uid = Process.getUidForName(uidStr); // 将 uid 转为 int 值!
if (uid < 0) {
Slog.w(TAG, "<assign-permission> with unknown uid \""
+ uidStr + " in " + permFile + " at "
+ parser.getPositionDescription());
XmlUtils.skipCurrentTag(parser);
continue;
}
perm = perm.intern();
ArraySet<String> perms = mSystemPermissions.get(uid);
if (perms == null) {
perms = new ArraySet<String>();
mSystemPermissions.put(uid, perms);
}
perms.add(perm);
XmlUtils.skipCurrentTag(parser);
} else if ("library".equals(name) && allowLibs) {
//【4】如果 allowLibs 为 true,解析 "library" 标签
// 获得系统中所有共享库的信息!
String lname = parser.getAttributeValue(null, "name");
String lfile = parser.getAttributeValue(null, "file");
if (lname == null) {
Slog.w(TAG, "<library> without name in " + permFile + " at "
+ parser.getPositionDescription());
} else if (lfile == null) {
Slog.w(TAG, "<library> without file in " + permFile + " at "
+ parser.getPositionDescription());
} else {
//Log.i(TAG, "Got library " + lname + " in " + lfile);
mSharedLibraries.put(lname, lfile);
}
XmlUtils.skipCurrentTag(parser);
continue;
} else if ("feature".equals(name) && allowFeatures) {
//【5】如果 allowFeatures 为 true,解析 "feature" 标签
// 获得系统中所有可用特性的信息!
String fname = parser.getAttributeValue(null, "name");
int fversion = XmlUtils.readIntAttribute(parser, "version", 0);
boolean allowed;
if (!lowRam) {
allowed = true;
} else {
// 内存不足时,配置了 notLowRam 的 feature 不会在加载!
String notLowRam = parser.getAttributeValue(null, "notLowRam");
allowed = !"true".equals(notLowRam);
}
if (fname == null) {
Slog.w(TAG, "<feature> without name in " + permFile + " at "
+ parser.getPositionDescription());
} else if (allowed) {
// 将 feature 构造成 featureInfo,加入到 mAvailableFeatures 对象中!
addFeature(fname, fversion);
}
XmlUtils.skipCurrentTag(parser);
continue;
} else if ("unavailable-feature".equals(name) && allowFeatures) {
//【6】如果 allowFeatures 为 true,解析 "unavailable-feature" 标签
// 获得系统中所有不可用特性的信息!
String fname = parser.getAttributeValue(null, "name");
if (fname == null) {
Slog.w(TAG, "<unavailable-feature> without name in " + permFile + " at "
+ parser.getPositionDescription());
} else {
mUnavailableFeatures.add(fname);
}
XmlUtils.skipCurrentTag(parser);
continue;
} else if ("allow-in-power-save-except-idle".equals(name) && allowAll) {
//【7】解析 "allow-in-power-save-except-idle" 标签
// 用于获得系统中处于省电模式白名单,而没有在 idle 模式白名单中的应用信息!
// 这些应用可以在后台运行!
String pkgname = parser.getAttributeValue(null, "package");
if (pkgname == null) {
Slog.w(TAG, "<allow-in-power-save-except-idle> without package in "
+ permFile + " at " + parser.getPositionDescription());
} else {
mAllowInPowerSaveExceptIdle.add(pkgname);
}
XmlUtils.skipCurrentTag(parser);
continue;
} else if ("allow-in-power-save".equals(name) && allowAll) {
//【8】解析 "allow-in-power-save" 标签
// 获得哪些处于 doze 模式白名单中的应用信息!
String pkgname = parser.getAttributeValue(null, "package");
if (pkgname == null) {
Slog.w(TAG, "<allow-in-power-save> without package in " + permFile + " at "
+ parser.getPositionDescription());
} else {
mAllowInPowerSave.add(pkgname);
}
XmlUtils.skipCurrentTag(parser);
continue;
} else if ("allow-in-data-usage-save".equals(name) && allowAll) {
//【9】解析 "allow-in-data-usage-save" 标签,
// 获得哪些处于流量节省模式白名单中的应用的信息,在白名单中的应用,当系统处于
// 流量节省模式时,可以在后台运行!
String pkgname = parser.getAttributeValue(null, "package");
if (pkgname == null) {
Slog.w(TAG, "<allow-in-data-usage-save> without package in " + permFile
+ " at " + parser.getPositionDescription());
} else {
mAllowInDataUsageSave.add(pkgname);
}
XmlUtils.skipCurrentTag(parser);
continue;
} else if ("app-link".equals(name) && allowAppConfigs) {
//【10】解析 "app-link" 标签
String pkgname = parser.getAttributeValue(null, "package");
if (pkgname == null) {
Slog.w(TAG, "<app-link> without package in " + permFile + " at "
+ parser.getPositionDescription());
} else {
mLinkedApps.add(pkgname);
}
XmlUtils.skipCurrentTag(parser);
} else if ("system-user-whitelisted-app".equals(name) && allowAppConfigs) {
//【11】解析 "system-user-whitelisted-app" 标签
// 获得哪些在 system user 下可以运行的应用信息!
String pkgname = parser.getAttributeValue(null, "package");
if (pkgname == null) {
Slog.w(TAG, "<system-user-whitelisted-app> without package in "
+ permFile + " at " + parser.getPositionDescription());
} else {
mSystemUserWhitelistedApps.add(pkgname);
}
XmlUtils.skipCurrentTag(parser);
} else if ("system-user-blacklisted-app".equals(name) && allowAppConfigs) {
//【12】解析 "system-user-blacklisted-app" 标签
// 获得哪些在 system user 下不可以运行的应用信息!
String pkgname = parser.getAttributeValue(null, "package");
if (pkgname == null) {
Slog.w(TAG, "<system-user-blacklisted-app without package in " + permFile
+ " at " + parser.getPositionDescription());
} else {
mSystemUserBlacklistedApps.add(pkgname);
}
XmlUtils.skipCurrentTag(parser);
} else if ("default-enabled-vr-app".equals(name) && allowAppConfigs) {
//【13】解析 "default-enabled-vr-app" 标签
String pkgname = parser.getAttributeValue(null, "package");
String clsname = parser.getAttributeValue(null, "class");
if (pkgname == null) {
Slog.w(TAG, "<default-enabled-vr-app without package in " + permFile
+ " at " + parser.getPositionDescription());
} else if (clsname == null) {
Slog.w(TAG, "<default-enabled-vr-app without class in " + permFile
+ " at " + parser.getPositionDescription());
} else {
mDefaultVrComponents.add(new ComponentName(pkgname, clsname));
}
XmlUtils.skipCurrentTag(parser);
} else if ("backup-transport-whitelisted-service".equals(name) && allowFeatures) {
//【14】解析 "backup-transport-whitelisted-service" 标签
String serviceName = parser.getAttributeValue(null, "service");
if (serviceName == null) {
Slog.w(TAG, "<backup-transport-whitelisted-service> without service in "
+ permFile + " at " + parser.getPositionDescription());
} else {
ComponentName cn = ComponentName.unflattenFromString(serviceName);
if (cn == null) {
Slog.w(TAG,
"<backup-transport-whitelisted-service> with invalid service name "
+ serviceName + " in "+ permFile
+ " at " + parser.getPositionDescription());
} else {
mBackupTransportWhitelist.add(cn);
}
}
XmlUtils.skipCurrentTag(parser);
} else if ("disabled-until-used-preinstalled-carrier-associated-app".equals(name)
&& allowAppConfigs) {
//【15】解析 "disabled-until-used-preinstalled-carrier-associated-app" 标签
String pkgname = parser.getAttributeValue(null, "package");
String carrierPkgname = parser.getAttributeValue(null, "carrierAppPackage");
if (pkgname == null || carrierPkgname == null) {
Slog.w(TAG, "<disabled-until-used-preinstalled-carrier-associated-app"
+ " without package or carrierAppPackage in " + permFile + " at "
+ parser.getPositionDescription());
} else {
List<String> associatedPkgs =
mDisabledUntilUsedPreinstalledCarrierAssociatedApps.get(
carrierPkgname);
if (associatedPkgs == null) {
associatedPkgs = new ArrayList<>();
mDisabledUntilUsedPreinstalledCarrierAssociatedApps.put(
carrierPkgname, associatedPkgs);
}
associatedPkgs.add(pkgname);
}
XmlUtils.skipCurrentTag(parser);
} else {
XmlUtils.skipCurrentTag(parser);
continue;
}
}
} 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);
}
// Some devices can be field-converted to FBE, so offer to splice in
// those features if not already defined by the static config
// 加密相关的 feature!
if (StorageManager.isFileEncryptedNativeOnly()) {
addFeature(PackageManager.FEATURE_FILE_BASED_ENCRYPTION, 0);
addFeature(PackageManager.FEATURE_SECURELY_REMOVES_USERS, 0);
}
// 从 mAvailableFeatures 移除不支持的 feature!
for (String featureName : mUnavailableFeatures) {
removeFeature(featureName);
}
}
这个方法的流程如下:
- 解析 “group” 标签;
- 解析 “permission” 标签;
- 解析 “assign-permission” 标签;
- 解析 “library” 标签
- 解析 “feature” 标签
- 解析 “unavailable-feature” 标签
- 解析 “allow-in-power-save-except-idle” 标签
- 解析 “allow-in-power-save” 标签
- 解析 “allow-in-data-usage-save” 标签
- 解析 “app-link” 标签
- 解析 “system-user-whitelisted-app” 标签
- 解析 “system-user-blacklisted-app” 标签
- 解析 “default-enabled-vr-app” 标签
- 解析 “backup-transport-whitelisted-service” 标签
- 解析 “disabled-until-used-preinstalled-carrier-associated-app” 标签
这些解析后的数据都会保存到下面的集合中:
1 | public class SystemConfig { |
下面,我们重点分析几个配置解析,以 /system/etc/permissions 为例,该目录所有的 xml 文件的根节点都是:1
2
3<permissions>
</permissions>
下面我们继续来看:
2.2.1 解析 “group” - 获得系统中的所有 gid
和 “group” 相关的 xml 内容如下,可以看到 “group” 并不是单独存在的,而是和 “permission” 一起配合使用!
1 | <permissions> |
下面是解析过程:1
2
3
4
5
6
7
8
9
10
11
12
13
14if ("group".equals(name) && allowAll) {
//【1】解析 "group" 标签
String gidStr = parser.getAttributeValue(null, "gid");
if (gidStr != null) {
// 将 Gid 从字符串形式转为对应的 int 形式!
int gid = android.os.Process.getGidForName(gidStr);
mGlobalGids = appendInt(mGlobalGids, gid);
} else {
Slog.w(TAG, "<group> without gid in " + permFile + " at "
+ parser.getPositionDescription());
}
XmlUtils.skipCurrentTag(parser);
continue;
}
根据组名称,调用 android.os.Process.getGidForName 转为 gid,保存到 mGlobalGids!
2.2.2 解析 “permission” - 获得系统权限和所属的 gid
xml 信息如下:1
2
3
4
5
6
7
8
9
10<permissions>
<!--解析 permission 标签-->
<permission name="android.permission.BLUETOOTH_ADMIN" >
<group gid="net_bt_admin" />
</permission>
<permission name="android.permission.BLUETOOTH" >
<group gid="net_bt" />
</permission>
</permissions>
下面是解析过程:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 } else if ("permission".equals(name) && allowPermissions) {
//【1】解析 "permission" 标签,获得权限的名称 perm!
String perm = parser.getAttributeValue(null, "name");
if (perm == null) {
Slog.w(TAG, "<permission> without name in " + permFile + " at "
+ parser.getPositionDescription());
XmlUtils.skipCurrentTag(parser);
continue;
}
perm = perm.intern();
//【2】调用 readPermission 继续解析权限
readPermission(parser, perm);
}
进一步调用 readPermission 方法: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
40void readPermission(XmlPullParser parser, String name)
throws IOException, XmlPullParserException {
//【1】如果 mPermissions 已经包含这个权限,解析冲突!
if (mPermissions.containsKey(name)) {
throw new IllegalStateException("Duplicate permission definition for " + name);
}
//【2】perUser 表示该权限的 gid 是否针对设备用户 id 进行调整,默认为 false!
final boolean perUser = XmlUtils.readBooleanAttribute(parser, "perUser", false);
//【3】创建 PermissionEntry,并添加到 mPermissions 中!
final PermissionEntry perm = new PermissionEntry(name, perUser);
mPermissions.put(name, perm);
int outerDepth = parser.getDepth();
int type;
while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG
|| parser.getDepth() > outerDepth)) {
if (type == XmlPullParser.END_TAG
|| type == XmlPullParser.TEXT) {
continue;
}
String tagName = parser.getName();
//【4】解析内部 "group" 标签,获得权限所属的 gid,将其添加到 PermissionEntry 的 gids 数组中!
if ("group".equals(tagName)) {
String gidStr = parser.getAttributeValue(null, "gid");
if (gidStr != null) {
int gid = Process.getGidForName(gidStr);
perm.gids = appendInt(perm.gids, gid);
} else {
Slog.w(TAG, "<group> without gid at "
+ parser.getPositionDescription());
}
}
XmlUtils.skipCurrentTag(parser);
}
}
可以看到,解析 “permission” 标签以及其内部的标签 “group”,获得了 gid 和权限的映射关系,保存到了 mPermissions 中,一个权限可以对应多个 gid!
2.2.3 解析 “assign-permission” - 给指定的 uid 分配权限
“assign-permission” 的 xml 内容如下:1
2
3<assign-permission name="android.permission.MODIFY_AUDIO_SETTINGS" uid="media" />
<assign-permission name="android.permission.ACCESS_SURFACE_FLINGER" uid="media" />
<assign-permission name="android.permission.WAKE_LOCK" uid="media" />
下面是解析过程: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} else if ("assign-permission".equals(name) && allowPermissions) {
//【1】解析 "name" 属性,获得权限的名称!
String perm = parser.getAttributeValue(null, "name");
if (perm == null) {
Slog.w(TAG, "<assign-permission> without name in " + permFile + " at "
+ parser.getPositionDescription());
XmlUtils.skipCurrentTag(parser);
continue;
}
//【2】获得权限所属用户的名称!
String uidStr = parser.getAttributeValue(null, "uid");
if (uidStr == null) {
Slog.w(TAG, "<assign-permission> without uid in " + permFile + " at "
+ parser.getPositionDescription());
XmlUtils.skipCurrentTag(parser);
continue;
}
//【3】获得用户 uid!
int uid = Process.getUidForName(uidStr);
if (uid < 0) {
Slog.w(TAG, "<assign-permission> with unknown uid \""
+ uidStr + " in " + permFile + " at "
+ parser.getPositionDescription());
XmlUtils.skipCurrentTag(parser);
continue;
}
perm = perm.intern();
ArraySet<String> perms = mSystemPermissions.get(uid);
if (perms == null) {
perms = new ArraySet<String>();
mSystemPermissions.put(uid, perms);
}
perms.add(perm);
XmlUtils.skipCurrentTag(parser);
}
可以看到,解析 “assign-permission” 标签,获得了 uid 和权限的映射关系,保存到了 mSystemPermissions 中,一个 uid 可以对应多个权限!
2.2.4 解析 “library” - 获得共享库信息
xml 信息如下:1
2
3
4
5
6<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" />
下面是解析过程:
1 | } else if ("library".equals(name) && allowLibs) { |
解析共享库信息,保存到 mSharedLibraries 中,key 为 共享库名称,value 为共享库路径!
2.2.5 解析 “feature” - 获得系统特性配置信息
“feature” 的 xml 内容如下:1
2
3<permissions>
<feature name="android.hardware.bluetooth" />
</permissions>
下面是解析过程: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} else if ("feature".equals(name) && allowFeatures) {
//【1】解析 "feature" 标签
// 获得 feature 的名称和版本号
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, "<feature> without name in " + permFile + " at "
+ parser.getPositionDescription());
} else if (allowed) {
addFeature(fname, fversion);
}
XmlUtils.skipCurrentTag(parser);
continue;
} else if ("unavailable-feature".equals(name) && allowFeatures) {
//【2】解析 "unavailable-feature" 标签
String fname = parser.getAttributeValue(null, "name");
if (fname == null) {
Slog.w(TAG, "<unavailable-feature> without name in " + permFile + " at "
+ parser.getPositionDescription());
} else {
mUnavailableFeatures.add(fname);
}
XmlUtils.skipCurrentTag(parser);
continue;
}
上面会把配置文件中的 feature 解析出来,可用 feature 会被添加到 mAvailableFeatures 中,不可用 feature 会被添加到 mUnavailableFeatures 中!
2.2.6 解析其他的标签
其他的标签有如下几个:
- 解析 “allow-in-power-save-except-idle”,这个是省电模式的白名单,该白名单不包括 idle 白名单!
- mAllowInPowerSaveExceptIdle.add(pkgname);
解析 “allow-in-power-save”,这个是省电模式的白名单!
- mAllowInPowerSave.add(pkgname);
解析 “allow-in-data-usage-save”
- mAllowInDataUsageSave.add(pkgname);
解析 “app-link”
- mLinkedApps.add(pkgname);
解析 “system-user-whitelisted-app”
- mSystemUserWhitelistedApps.add(pkgname);
解析 “default-enabled-vr-app”
- mDefaultVrComponents.add(new ComponentName(pkgname, clsname));
解析 “backup-transport-whitelisted-service”
- ComponentName cn = ComponentName.unflattenFromString(serviceName);
- mBackupTransportWhitelist.add(cn);
解析 “disabled-until-used-preinstalled-carrier-associated-app”
- associatedPkgs = new ArrayList<>();
- mDisabledUntilUsedPreinstalledCarrierAssociatedApps.put(carrierPkgname, associatedPkgs);
- associatedPkgs.add(pkgname);
SystemConfig 内部的数据关系图:
2.3 PMS 对于 SystemConfig 的后续处理
然后,回到 PMS 的构造器中:
将 SystemConfig 中的 mGlobalGids,mSystemPermissions 和 mAvailableFeatures 拷贝一份保存到 PMS 中!1
2
3
4//【1】保存到 PMS 中!
mGlobalGids = systemConfig.getGlobalGids();
mSystemPermissions = systemConfig.getSystemPermissions();
mAvailableFeatures = systemConfig.getAvailableFeatures();
接着,处理权限信息和共享库信息,将解析出来的 mPermissions 信息保存到 Setting 的 mPermissions 中,权限所属的包名为 android !
因为 SystemConfig.mPermissions 保存的是系统中定义的权限和对应的 Gid 的关系!!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//【2】从获得 SystemConfig 加载的系统权限集合 mPermissions!
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);
//【2】加入到 Settings 的 mPermissions 中,SystemConfig 解析的权限的包名都为 “android”!
if (bp == null) {
// BasePermission.TYPE_BUILTIN 表示的是在编译时期就确定好的,系统要提供的权限!
bp = new BasePermission(perm.name, "android", BasePermission.TYPE_BUILTIN);
mSettings.mPermissions.put(perm.name, bp);
}
// 如果系统权限有所属的 gids,将其添加到 BasePermission 对象中!
if (perm.gids != null) {
bp.setGids(perm.gids, perm.perUser);
}
}
//【3】处理共享库,将共享库信息也保存一份在 PMS 中!
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));
}
创建了一个 BasePermission 对象,封装系统权限!!1
2
3
4
5
6
7BasePermission(String _name, String _sourcePackage, int _type) {
name = _name; // 权限名;
sourcePackage = _sourcePackage; // 定义权限的包名;
type = _type; // 权限类型!
// Default to most conservative protection level.
protectionLevel = PermissionInfo.PROTECTION_SIGNATURE; // 权限界别!
}
同时设置系统权限的 gid:1
2
3
4public void setGids(int[] gids, boolean perUser) {
this.gids = gids; // 设置 gids
this.perUser = perUser;
}
可以看到,SystemConfig 保存的均是和系统相关的属性信息,比如系统权限,系统特性, Gid 等等,而 Settings 中则保存的系统和和应用所有的信息,所以,这里需要将 SystemConfig 中的权限信息保存过来!
这里就先分析到这里,后面会继续补充!
2.4 阶段总结
我们来总结一下,SystemConfig、Settings 以及 PackageManagerService 之间的数据关系:
通过 SystemConfig 解析,我们可以系统定义的一些配置信息:
- 系统定义的一些 gid;
- 系统定义的权限和 uid 的映射关系;
- 系统定义的权限和 gid 的映射关系;
- 系统共享库和feature;
我们继续看!
6 PackageHandler
1 | // 建立并启动一个名为 “PackageManager” 的 ServiceThread,用于处理一些耗时的操作! |
这里的 ServiceThread 继承了 HandlerThread,专门为系统服务定义的!
PackageHandler 处理的 msg 有如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18static final int SEND_PENDING_BROADCAST = 1;
static final int MCS_BOUND = 3;
static final int END_COPY = 4;
static final int INIT_COPY = 5;
static final int MCS_UNBIND = 6;
static final int START_CLEANING_PACKAGE = 7; // 清楚 package
static final int FIND_INSTALL_LOC = 8;
static final int POST_INSTALL = 9; // 安装 package
static final int MCS_RECONNECT = 10;
static final int MCS_GIVE_UP = 11;
static final int UPDATED_MEDIA_STATUS = 12;
static final int WRITE_SETTINGS = 13;
static final int WRITE_PACKAGE_RESTRICTIONS = 14;
static final int PACKAGE_VERIFIED = 15; // 校验 package
static final int CHECK_PENDING_VERIFICATION = 16;
static final int START_INTENT_FILTER_VERIFICATIONS = 17;
static final int INTENT_FILTER_VERIFIED = 18;
static final int WRITE_PACKAGE_LIST = 19;
一些耗时的操作,pms 都会交给 PackageHandler 去做,比如安装应用,卸载应用等等的操作,这个以后再分析!
7 SELinuxMMAC.readInstallPolicy
调用 SELinuxMMAC.readInstallPolicy 读取 /etc/security/mac_permissions.xml 文件中的 selinux 配置,设置应用程序的安全上下文!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
59public static boolean readInstallPolicy() {
// Temp structure to hold the rules while we parse the xml file
List<Policy> policies = new ArrayList<>();
FileReader policyFile = null;
XmlPullParser parser = Xml.newPullParser();
try {
//【1】获得 /etc/security/mac_permissions.xml 文件引用!
policyFile = new FileReader(MAC_PERMISSIONS);
Slog.d(TAG, "Using policy file " + MAC_PERMISSIONS);
parser.setInput(policyFile);
parser.nextTag();
parser.require(XmlPullParser.START_TAG, null, "policy");
while (parser.next() != XmlPullParser.END_TAG) {
if (parser.getEventType() != XmlPullParser.START_TAG) {
continue;
}
switch (parser.getName()) {
case "signer": //【7.1】解析 "signer" 标签的内容,返回一个 Policy 对象!!
policies.add(readSignerOrThrow(parser));
break;
default:
skip(parser);
}
}
} catch (IllegalStateException | IllegalArgumentException |
XmlPullParserException ex) {
... ... ... ...
return false;
} catch (IOException ioe) {
Slog.w(TAG, "Exception parsing " + MAC_PERMISSIONS, ioe);
return false;
} finally {
IoUtils.closeQuietly(policyFile);
}
//【3】使用比较器对其进行排序!
PolicyComparator policySort = new PolicyComparator();
Collections.sort(policies, policySort);
if (policySort.foundDuplicate()) {
Slog.w(TAG, "ERROR! Duplicate entries found parsing " + MAC_PERMISSIONS);
return false;
}
synchronized (sPolicies) {
sPolicies = policies;
if (DEBUG_POLICY_ORDER) {
for (Policy policy : sPolicies) {
Slog.d(TAG, "Policy: " + policy.toString());
}
}
}
return true;
}
这里用到了一个文件对象!1
2private static final File MAC_PERMISSIONS = new File(Environment.getRootDirectory(),
"/etc/security/mac_permissions.xml");
我们来看下该文件的内容;1
2
3
4
5
6
7
8
9
10
11<policy>
<!-- Platform dev key in AOSP -->
<signer signature="@PLATFORM" >
<seinfo value="platform" />
</signer>
<!-- Media key in AOSP -->
<signer signature="@MEDIA" >
<seinfo value="media" />
</signer>
</policy>
在 Android 引入 SeLinux 以后,Android 会为每个文件打上 SE Label,对于 APK 而言,打 SE Label 的准则就是签名,即根据签名信息打上不同的 SE Label。
Android 将签名分类成为 platform,testkey,media等,签名与类别的映射关系就存在一个叫 mac_permission.xml 的文件中。
所以,需要读取该文件的内容。
根据 mac_permissions.xml 的定义,如果 App 是在 Android 源码编译环境下,其 Android.mk 中指定了 LOCAL_CERTIFICATE : = platform 的话,它的 seinfo 就是 platform。如果 Android.mk 中不进行对应的设置,setinfo 为默认值 default。
对于第三方 APK,其 seinfo 值通常为 default。
当 mac_permissions.xml 编译进 system/etc 目录时,@PLATFORM 将被实际的签名信息替换:
1 | <?xml version="1.0" encoding="iso-8859-1"?> |
这里不多说了。
7.1 SELinuxMMAC.readSignerOrThrow
1 | private static Policy readSignerOrThrow(XmlPullParser parser) throws IOException, |
可以看到,readSignerOrThrow 方法会解析 signer 标签和其子标签,然后将结果通过 buidler 模式,封装成一个 Policy 对象返回!
8 Settings.readLPw
继续看:1
mFirstBoot = !mSettings.readLPw(sUserManager.getUsers(false));
这个方法用来恢复上一次安装的信息,参数传递:
- List
users:当前是设备用户 id!
1 | boolean readLPw(@NonNull List<UserInfo> users) { |
这个方法的主要流程是:
- 读取 packages-backup.xml 或者 packages.xml 文件中的数据并解析,获得上一次应用的安装和使用信息!
- 校验共享用户id 的应用程序包的 uid 的有效性!
- 读取 packages-stopped-backup.xml 和 packages_stopped.xml 文件中的数据并解析!
- 处理被替换的系统应用的共享用户 id 和自身的关系!
下面我们来看下重要的属性的解析:
8.1 读取 pacakges.xml 文件
这个流程主要会解析如下的标签:
标签 | 标签解释 |
---|---|
“package” | 系统中所有应用程序包的信息 |
“permissions” | 系统中定义的所有的权限 |
“permission-trees” | 系统中定义的权限树 |
“shared-user” | 系统中定义的共享用户 |
“preferred-activities” | 系统默认应用相关 |
“persistent-preferred-activities” | 系统默认应用相关 |
“crossProfile-intent-filters” | |
“default-browser” | 默认的浏览器 |
“updated-package” | 系统中被更新的应用程序包 |
“cleaning-package” | 系统中被清除掉的应用程序包 |
“renamed-package” | 系统中被重命名的应用程序包 |
“restored-ivi” | |
“last-platform-version” | 系统升级之前的版本 |
“database-version” | 数据库版本 |
“verifier” | |
“read-external-storage” | |
“keyset-settings” | |
“version” | 当前系统的版本 |
我们重点分析和 package、permission,shared-user 相关的内容,其他的内容我们后续接触到在分析!
1 | <packages> |
上面是 pacakges.xml 的主要内容!
8.1.1 解析 “package” 标签
和 “package” 相关内容如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15<package name="com.qualcomm.qti.haven.telemetry.service" codePath="/system/app/TelemetryService"
nativeLibraryPath="/system/app/TelemetryService/lib"
publicFlags="940097093" privateFlags="0" ft="11e8dc5d800" it="11e8dc5d800" ut="11e8dc5d800"
version="25" userId="10091" isOrphaned="true">
<sigs count="1">
<cert index="0" />
</sigs>
<perms>
<item name="android.permission.REAL_GET_TASKS" granted="true" flags="0" />
<item name="android.permission.RECEIVE_BOOT_COMPLETED" granted="true" flags="0" />
<item name="android.permission.INTERNET" granted="true" flags="0" />
<item name="android.permission.ACCESS_NETWORK_STATE" granted="true" flags="0" />
</perms>
<proper-signing-keyset identifier="1" />
</package>
接下来,看看具体的解析过程:
1 | if (tagName.equals("package")) { |
8.1.1.1 Settings.readPackageLPw
调用 readPackageLPw 方法!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
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358private void readPackageLPw(XmlPullParser parser) throws XmlPullParserException, IOException {
// 需要解析的属性
String name = null;
String realName = null;
String idStr = null;
String sharedIdStr = null;
String codePathStr = null;
String resourcePathStr = null;
String legacyCpuAbiString = null;
String legacyNativeLibraryPathStr = null;
String primaryCpuAbiString = null;
String secondaryCpuAbiString = null;
String cpuAbiOverrideString = null;
String systemStr = null;
String installerPackageName = null;
String isOrphaned = null;
String volumeUuid = null;
String uidError = null;
int pkgFlags = 0;
int pkgPrivateFlags = 0;
long timeStamp = 0;
long firstInstallTime = 0;
long lastUpdateTime = 0;
PackageSettingBase packageSetting = null;
String version = null;
int versionCode = 0;
String parentPackageName;
try {
//【1】获得应用的 name!
name = parser.getAttributeValue(null, ATTR_NAME);
realName = parser.getAttributeValue(null, "realName");
//【2】获得 userId 和 sharedId 的名称, userId 和 sharedIdStr 不能同时存在!
idStr = parser.getAttributeValue(null, "userId");
uidError = parser.getAttributeValue(null, "uidError");
sharedIdStr = parser.getAttributeValue(null, "sharedUserId");
//【3】获得应用程序包的路径,例如:/system/priv-app/CalendarProvider!
codePathStr = parser.getAttributeValue(null, "codePath");
resourcePathStr = parser.getAttributeValue(null, "resourcePath");
legacyCpuAbiString = parser.getAttributeValue(null, "requiredCpuAbi");
parentPackageName = parser.getAttributeValue(null, "parentPackageName");
legacyNativeLibraryPathStr = parser.getAttributeValue(null, "nativeLibraryPath");
primaryCpuAbiString = parser.getAttributeValue(null, "primaryCpuAbi");
secondaryCpuAbiString = parser.getAttributeValue(null, "secondaryCpuAbi");
cpuAbiOverrideString = parser.getAttributeValue(null, "cpuAbiOverride");
if (primaryCpuAbiString == null && legacyCpuAbiString != null) {
primaryCpuAbiString = legacyCpuAbiString;
}
//【4】获得版本号!
version = parser.getAttributeValue(null, "version");
if (version != null) {
try {
versionCode = Integer.parseInt(version);
} catch (NumberFormatException e) {
}
}
// 获得安装器的名称!
installerPackageName = parser.getAttributeValue(null, "installer");
isOrphaned = parser.getAttributeValue(null, "isOrphaned");
volumeUuid = parser.getAttributeValue(null, "volumeUuid");
//【5】获得flags相关信息,读取顺序为:publicFlags 和 privateFlags、flags、system;
systemStr = parser.getAttributeValue(null, "publicFlags");
if (systemStr != null) {
try {
pkgFlags = Integer.parseInt(systemStr);
} catch (NumberFormatException e) {
}
systemStr = parser.getAttributeValue(null, "privateFlags");
if (systemStr != null) {
try {
pkgPrivateFlags = Integer.parseInt(systemStr);
} catch (NumberFormatException e) {
}
}
} else {
// 在 Android M 之前,是没有 publicFlags 和 privateFlags 之分的,所有的标志位都存放在 flags 中!
systemStr = parser.getAttributeValue(null, "flags");
if (systemStr != null) {
try {
pkgFlags = Integer.parseInt(systemStr);
} catch (NumberFormatException e) {
}
if ((pkgFlags & PRE_M_APP_INFO_FLAG_HIDDEN) != 0) {
pkgPrivateFlags |= ApplicationInfo.PRIVATE_FLAG_HIDDEN;
}
if ((pkgFlags & PRE_M_APP_INFO_FLAG_CANT_SAVE_STATE) != 0) {
pkgPrivateFlags |= ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE;
}
if ((pkgFlags & PRE_M_APP_INFO_FLAG_FORWARD_LOCK) != 0) {
pkgPrivateFlags |= ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK;
}
if ((pkgFlags & PRE_M_APP_INFO_FLAG_PRIVILEGED) != 0) {
pkgPrivateFlags |= ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;
}
pkgFlags &= ~(PRE_M_APP_INFO_FLAG_HIDDEN
| PRE_M_APP_INFO_FLAG_CANT_SAVE_STATE
| PRE_M_APP_INFO_FLAG_FORWARD_LOCK
| PRE_M_APP_INFO_FLAG_PRIVILEGED);
} else {
// For backward compatibility
systemStr = parser.getAttributeValue(null, "system");
if (systemStr != null) {
pkgFlags |= ("true".equalsIgnoreCase(systemStr)) ? ApplicationInfo.FLAG_SYSTEM
: 0;
} else {
// Old settings that don't specify system... just treat
// them as system, good enough.
pkgFlags |= ApplicationInfo.FLAG_SYSTEM;
}
}
}
// 获得时间戳!
String timeStampStr = parser.getAttributeValue(null, "ft");
if (timeStampStr != null) {
try {
timeStamp = Long.parseLong(timeStampStr, 16);
} catch (NumberFormatException e) {
}
} else {
timeStampStr = parser.getAttributeValue(null, "ts");
if (timeStampStr != null) {
try {
timeStamp = Long.parseLong(timeStampStr);
} catch (NumberFormatException e) {
}
}
}
//【6】获得第一次安装时间
timeStampStr = parser.getAttributeValue(null, "it");
if (timeStampStr != null) {
try {
firstInstallTime = Long.parseLong(timeStampStr, 16);
} catch (NumberFormatException e) {
}
}
//【7】获得最近更新时间!
timeStampStr = parser.getAttributeValue(null, "ut");
if (timeStampStr != null) {
try {
lastUpdateTime = Long.parseLong(timeStampStr, 16);
} catch (NumberFormatException e) {
}
}
if (PackageManagerService.DEBUG_SETTINGS)
Log.v(PackageManagerService.TAG, "Reading package: " + name + " userId=" + idStr
+ " sharedUserId=" + sharedIdStr);
//【8】获得 userId 的 int 值,如果 AndroidManifest.xml 有设置 android:sharedUserId 属性,
// 那么应用的 userId 就为 0!!
int userId = idStr != null ? Integer.parseInt(idStr) : 0;
if (resourcePathStr == null) {
resourcePathStr = codePathStr;
}
if (realName != null) {
realName = realName.intern();
}
if (name == null) {
PackageManagerService.reportSettingsProblem(Log.WARN,
"Error in package manager settings: <package> has no name at "
+ parser.getPositionDescription());
} else if (codePathStr == null) {
PackageManagerService.reportSettingsProblem(Log.WARN,
"Error in package manager settings: <package> has no codePath at "
+ parser.getPositionDescription());
} else if (userId > 0) {
//【8.1】如果 userId 大于0,说明 package 是独立用户 id
// 调用 addPackageLPw 方法保存这个有独立的 Linux 用户 ID 的 Package!
packageSetting = addPackageLPw(name.intern(), realName, new File(codePathStr),
new File(resourcePathStr), legacyNativeLibraryPathStr, primaryCpuAbiString,
secondaryCpuAbiString, cpuAbiOverrideString, userId, versionCode, pkgFlags,
pkgPrivateFlags, parentPackageName, null);
if (PackageManagerService.DEBUG_SETTINGS)
Log.i(PackageManagerService.TAG, "Reading package " + name + ": userId="
+ userId + " pkg=" + packageSetting);
if (packageSetting == null) {
PackageManagerService.reportSettingsProblem(Log.ERROR, "Failure adding uid "
+ userId + " while parsing settings at "
+ parser.getPositionDescription());
} else {
// 设置时间戳,第一次安装时间,最近更新时间
packageSetting.setTimeStamp(timeStamp);
packageSetting.firstInstallTime = firstInstallTime;
packageSetting.lastUpdateTime = lastUpdateTime;
}
} else if (sharedIdStr != null) {
//【8.2】sharedIdStr 不为 null,说明 package 设置了共享用户 id!
userId = sharedIdStr != null ? Integer.parseInt(sharedIdStr) : 0;
if (userId > 0) {
// 对于共享用户 ID 这种情况,还需要验证其有效性!
// 创建一个 PendingPackage 对象,来封装这个有共享用户 ID 的 package 的信息!
packageSetting = new PendingPackage(name.intern(), realName, new File(
codePathStr), new File(resourcePathStr), legacyNativeLibraryPathStr,
primaryCpuAbiString, secondaryCpuAbiString, cpuAbiOverrideString,
userId, versionCode, pkgFlags, pkgPrivateFlags, parentPackageName,
null);
// 设置时间戳,第一次安装时间,最近更新时间!
packageSetting.setTimeStamp(timeStamp);
packageSetting.firstInstallTime = firstInstallTime;
packageSetting.lastUpdateTime = lastUpdateTime;
//【8.2.1】添加到 mPendingPackages 中,因为后续需要确定 shareUserId 的有效性!
mPendingPackages.add((PendingPackage) packageSetting);
if (PackageManagerService.DEBUG_SETTINGS)
Log.i(PackageManagerService.TAG, "Reading package " + name
+ ": sharedUserId=" + userId + " pkg=" + packageSetting);
} else {
PackageManagerService.reportSettingsProblem(Log.WARN,
"Error in package manager settings: package " + name
+ " has bad sharedId " + sharedIdStr + " at "
+ parser.getPositionDescription());
}
} else {
PackageManagerService.reportSettingsProblem(Log.WARN,
"Error in package manager settings: package " + name + " has bad userId "
+ idStr + " at " + parser.getPositionDescription());
}
} catch (NumberFormatException e) {
PackageManagerService.reportSettingsProblem(Log.WARN,
"Error in package manager settings: package " + name + " has bad userId "
+ idStr + " at " + parser.getPositionDescription());
}
//【9】接下来设置其他的属性值。
if (packageSetting != null) {
packageSetting.uidError = "true".equals(uidError);
packageSetting.installerPackageName = installerPackageName;
packageSetting.isOrphaned = "true".equals(isOrphaned);
packageSetting.volumeUuid = volumeUuid;
packageSetting.legacyNativeLibraryPathString = legacyNativeLibraryPathStr;
packageSetting.primaryCpuAbiString = primaryCpuAbiString;
packageSetting.secondaryCpuAbiString = secondaryCpuAbiString;
// Handle legacy string here for single-user mode
final String enabledStr = parser.getAttributeValue(null, ATTR_ENABLED);
if (enabledStr != null) {
try {
packageSetting.setEnabled(Integer.parseInt(enabledStr), 0 /* userId */, null);
} catch (NumberFormatException e) {
if (enabledStr.equalsIgnoreCase("true")) {
packageSetting.setEnabled(COMPONENT_ENABLED_STATE_ENABLED, 0, null);
} else if (enabledStr.equalsIgnoreCase("false")) {
packageSetting.setEnabled(COMPONENT_ENABLED_STATE_DISABLED, 0, null);
} else if (enabledStr.equalsIgnoreCase("default")) {
packageSetting.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT, 0, null);
} else {
PackageManagerService.reportSettingsProblem(Log.WARN,
"Error in package manager settings: package " + name
+ " has bad enabled value: " + idStr + " at "
+ parser.getPositionDescription());
}
}
} else {
packageSetting.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT, 0, null);
}
if (installerPackageName != null) {
mInstallerPackages.add(installerPackageName);
}
// package 安装状态!
final String installStatusStr = parser.getAttributeValue(null, "installStatus");
if (installStatusStr != null) {
if (installStatusStr.equalsIgnoreCase("false")) {
packageSetting.installStatus = PackageSettingBase.PKG_INSTALL_INCOMPLETE;
} else {
packageSetting.installStatus = PackageSettingBase.PKG_INSTALL_COMPLETE;
}
}
int outerDepth = parser.getDepth();
int type;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
continue;
}
String tagName = parser.getName();
// Legacy
if (tagName.equals(TAG_DISABLED_COMPONENTS)) {
//【8.1.1.3】解析 "disabled-components"
readDisabledComponentsLPw(packageSetting, parser, 0);
} else if (tagName.equals(TAG_ENABLED_COMPONENTS)) {
//【8.1.1.3】解析 "enabled-components"
readEnabledComponentsLPw(packageSetting, parser, 0);
} else if (tagName.equals("sigs")) { // 解析 "sigs"
packageSetting.signatures.readXml(parser, mPastSignatures);
} else if (tagName.equals(TAG_PERMISSIONS)) { // 解析 perms
//【8.1.1.4】解析 "perms" 获得这个 package 的权限管理对象!
readInstallPermissionsLPr(parser, packageSetting.getPermissionsState());
packageSetting.installPermissionsFixed = true;
} else if (tagName.equals("proper-signing-keyset")) {
long id = Long.parseLong(parser.getAttributeValue(null, "identifier"));
Integer refCt = mKeySetRefs.get(id);
if (refCt != null) {
mKeySetRefs.put(id, refCt + 1);
} else {
mKeySetRefs.put(id, 1);
}
packageSetting.keySetData.setProperSigningKeySet(id);
} else if (tagName.equals("signing-keyset")) {
// from v1 of keysetmanagerservice - no longer used
} else if (tagName.equals("upgrade-keyset")) {
long id = Long.parseLong(parser.getAttributeValue(null, "identifier"));
packageSetting.keySetData.addUpgradeKeySetById(id);
} else if (tagName.equals("defined-keyset")) {
long id = Long.parseLong(parser.getAttributeValue(null, "identifier"));
String alias = parser.getAttributeValue(null, "alias");
Integer refCt = mKeySetRefs.get(id);
if (refCt != null) {
mKeySetRefs.put(id, refCt + 1);
} else {
mKeySetRefs.put(id, 1);
}
packageSetting.keySetData.addDefinedKeySet(id, alias);
} else if (tagName.equals(TAG_DOMAIN_VERIFICATION)) { // 解析 "domain-verification"
readDomainVerificationLPw(parser, packageSetting);
} else if (tagName.equals(TAG_CHILD_PACKAGE)) { // 解析 "child-package"
String childPackageName = parser.getAttributeValue(null, ATTR_NAME);
if (packageSetting.childPackageNames == null) {
packageSetting.childPackageNames = new ArrayList<>();
}
packageSetting.childPackageNames.add(childPackageName);
} else {
PackageManagerService.reportSettingsProblem(Log.WARN,
"Unknown element under <package>: " + parser.getName());
XmlUtils.skipCurrentTag(parser);
}
}
} else {
XmlUtils.skipCurrentTag(parser);
}
}
流程总结:
- 对于分配了独立的 uid 的 package,创建 PackageSetting 对象,添加到 mPackage 中,并且将自身和 uid 的引用关心保存到 mUsersId 或者 mOthersId 中;
- 对于分配了共享的 uid 的 package,创建 PendingPackage 对象,添加到 mPendingPackages 中,后续会对其共享 uid 的有效性进行校验!
8.1.1.2 Settings.addPackageLPw
1 | PackageSetting addPackageLPw(String name, String realName, File codePath, File resourcePath, |
这里会调用 addUserIdLPw 方法,将 package 的 uid 进行封装,根据其范围,将其添加到 mUserIds 或者 mOtherUserIds 中!
8.1.1.2.1 Settings.addUserIdLPw
1 | private boolean addUserIdLPw(int uid, Object obj, Object name) { |
到这里,我们解析 /data/system/packages.xml 里 “package” 标签对应的 xml 元素,对于 “package” 标签的子标签 “perms”,我们最后在再分析!
8.1.1.3 解析 “disabled-components” / “enabled-component 子标签
下面是解析可用和不可用组件的过程:
8.1.1.3.1 Settings.readDisabledComponentsLPw
1 | private void readDisabledComponentsLPw(PackageSettingBase packageSetting, XmlPullParser parser, |
这里调用了 PackageSettingBase 的 addDisabledComponent 方法!1
2
3void addDisabledComponent(String componentClassName, int userId) {
modifyUserStateComponents(userId, true, false).disabledComponents.add(componentClassName);
}
这里要看下 PackageSettingBase 的 modifyUserStateComponents 方法!
1 | PackageUserState modifyUserStateComponents(int userId, boolean disabled, boolean enabled) { |
PackageSettingBase 有一个 userState 成员变量,用于保存该 package 在不同 userId 下的使用情况!1
private final SparseArray<PackageUserState> userState = new SparseArray<PackageUserState>();
PackageSettingBase.modifyUserState 返回 userId 下该 package 的 PackageUserState 对象,若没还有就会创建新的!1
2
3
4
5
6
7
8private PackageUserState modifyUserState(int userId) {
PackageUserState state = userState.get(userId);
if (state == null) {
state = new PackageUserState();
userState.put(userId, state);
}
return state;
}
PackageUserState 的属性如下,调用了默认的构造器,这里先不多说!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
26public class PackageUserState {
public long ceDataInode;
public boolean installed;
public boolean stopped;
public boolean notLaunched;
public boolean hidden; // Is the app restricted by owner / admin
public boolean suspended;
public boolean blockUninstall;
public int enabled;
public String lastDisableAppCaller;
public int domainVerificationStatus;
public int appLinkGeneration;
public ArraySet<String> disabledComponents; // 保存不可用的组件
public ArraySet<String> enabledComponents; // 保存可用组件!
public PackageUserState() {
installed = true;
hidden = false;
suspended = false;
enabled = COMPONENT_ENABLED_STATE_DEFAULT;
domainVerificationStatus =
PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
}
... ... ... ...
}
解析不可用组件的过程到这里就结束了!
8.1.1.3.2 Settings.readEnabledComponentsLPw
1 | private void readEnabledComponentsLPw(PackageSettingBase packageSetting, XmlPullParser parser, |
同样的,这里调用了 PackageSettingBase 的 addEnabledComponent 方法:
1 | void addEnabledComponent(String componentClassName, int userId) { |
这里就不在分析了!
8.1.1.4 解析 “perms” 子标签 - 解析安装时权限授予情况
1 | <perms> |
对于 PackageSetting 和 SharedUserSetting 都有自己的权限管理对象 PermissionsState,用于保存和管理 package 和 SharedUser 所有的权限,其权限通过解析子标签 “perms” 获得,具体的解析方法是 readInstallPermissionsLPr!
这里会涉及到一个对象,每一个 PackageSetting 对象都有一个 PermissionsState 对象,用于管理 package 的权限!1
2
3
4
5
6
7
8
9
10
11public class PermissionsState {
public static final int PERMISSION_OPERATION_FAILURE = -1; // 返回值:权限操作失败
public static final int PERMISSION_OPERATION_SUCCESS = 0; // 返回值:权限操作成功,gid 没有改变!
public static final int PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED = 1; // 返回值:权限操作成功,gid 改变!
private ArrayMap<String, PermissionData> mPermissions; // 用于封装这个应用程序的所有权限!
private int[] mGlobalGids = NO_GIDS;
... ... ...
}
我们继续分析:
8.1.1.4.1 Setting.readInstallPermissionsLPr
参数传递:
- XmlPullParser parser:xml解析类对象,指向子标签 “perms”
- PermissionsState permissionsState:PackageSetting.getPermissionsState 或者 SharedUserSetting.getPermissionsState!
接着,调用了 readInstallPermissionsLPr 来解析 package 所使用的权限和其授予情况!
1 | void readInstallPermissionsLPr(XmlPullParser parser, |
Settings.mPermissions 中保存的权限来自两部分,一个是 SystemConfig.mPermissions 中解析到了系统定义的权限,还有一部分,来自继续 packages.xml 中获得的权限信息!
这里要简答的说下 flags 属性,其可以取一下的几个或者多个值:
1 |
|
这里简单的解释下这个这些 flags 的作用!
8.1.2 解析 “permissions” “permission-trees” 标签
我们先来看看 “permissions” 标签的内容,可以看到该标签的内容是:权限和定义权限的包名1
2
3
4
5
6
7
8
9
10<permissions>
<item name="android.permission.REAL_GET_TASKS" package="android" protection="18" />
<item name="android.permission.SEND_RECEIVE_STK_INTENT" package="com.android.stk" protection="2" />
<item name="android.permission.ACCESS_CACHE_FILESYSTEM" package="android" protection="18" />
<item name="android.permission.REMOTE_AUDIO_PLAYBACK" package="android" protection="2" />
<item name="android.permission.DOWNLOAD_WITHOUT_NOTIFICATION" package="com.android.providers.downloads" />
<item name="android.permission.REGISTER_WINDOW_MANAGER_LISTENERS" package="android" protection="2" />
<!---->
</permissions>
下面是具体的解析过程,注意 “permissions” 的内容是最先解析的!
对于 “permission-trees” 和 “permissions” 解析的方法都是 readPermissionsLPw,但是 permissions 的解析结果,会保存到 mPermissions 中,而 permission-trees 的解析结果,会保存到 mPermissionTrees 中;
8.1.2.1 Settings.readPermissionsLPw
这里的 ArrayMap<String, BasePermission> out 分别是 Settings.permissions 和 Settings.mPermissionTrees:
1 | final ArrayMap<String, BasePermission> mPermissions = new ArrayMap<String, BasePermission>(); |
下面我们来看看 readPermissionsLPw 方法的逻辑: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
57private void readPermissionsLPw(ArrayMap<String, BasePermission> out, XmlPullParser parser)
throws IOException, XmlPullParserException {
int outerDepth = parser.getDepth();
int type;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
continue;
}
final String tagName = parser.getName();
if (tagName.equals(TAG_ITEM)) { // 解析 item 标签
final String name = parser.getAttributeValue(null, ATTR_NAME); // 获得 permission 的名称!
final String sourcePackage = parser.getAttributeValue(null, "package"); // 获得定义 permission 包名!
final String ptype = parser.getAttributeValue(null, "type"); // 解析 type 类型!
if (name != null && sourcePackage != null) {
final boolean dynamic = "dynamic".equals(ptype);
//【1】尝试从 Settings 对应集合中获得权限 bp!
BasePermission bp = out.get(name);
//【2】如果 bp 为 null 或者
// bp 不为 null 且其类型不是 BasePermission.TYPE_BUILTIN,就创建一个新的!
// BasePermission.TYPE_BUILTIN 类型的权限是系统权限,前面已经解析过!
// BasePermission.TYPE_DYNAMIC 类型是针对于权限树的类型的!
if (bp == null || bp.type != BasePermission.TYPE_BUILTIN) {
bp = new BasePermission(name.intern(), sourcePackage,
dynamic ? BasePermission.TYPE_DYNAMIC : BasePermission.TYPE_NORMAL);
}
//【3】获得定义 permission 的级别,默认为 PROTECTION_NORMAL,然后对保护级别修正!
bp.protectionLevel = readInt(parser, null, "protection", PermissionInfo.PROTECTION_NORMAL);
bp.protectionLevel = PermissionInfo.fixProtectionLevel(bp.protectionLevel);
if (dynamic) {
//【3.1】如果是动态权限,进一步处理,动态权限会封装为 PermissionInfo!
// 动态权限是可以动态添加和移除的权限!通过权限树指定!
PermissionInfo pi = new PermissionInfo();
pi.packageName = sourcePackage.intern();
pi.name = name.intern();
pi.icon = readInt(parser, null, "icon", 0);
pi.nonLocalizedLabel = parser.getAttributeValue(null, "label");
pi.protectionLevel = bp.protectionLevel;
bp.pendingInfo = pi;
}
//【4】封装成 BasePermission 保存到 mPermissions 集合中!
out.put(bp.name, bp);
} else {
... ... ... ...
}
} else {
... ... ... ...
}
XmlUtils.skipCurrentTag(parser);
}
}
所有的非动态权限信息最终都会被解析保存到 Setting.mPermissions 中了!!
而动态权限信息会被保存在 Setting.mPermissionTrees 中了!!
8.1.3 解析 “shared-user” 标签
共享用户相关数据如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20<shared-user name="android.media" userId="10014">
<sigs count="1">
<cert index="3" />
</sigs>
<perms>
<item name="android.permission.WRITE_SETTINGS" granted="true" flags="0" />
<item name="android.permission.RECEIVE_BOOT_COMPLETED" granted="true" flags="0" />
<item name="android.permission.WRITE_MEDIA_STORAGE" granted="true" flags="0" />
<item name="android.permission.INTERNET" granted="true" flags="0" />
<item name="android.permission.MANAGE_USB" granted="true" flags="0" />
<item name="android.permission.MANAGE_USERS" granted="true" flags="0" />
<item name="android.permission.ACCESS_NETWORK_STATE" granted="true" flags="0" />
<item name="android.permission.ACCESS_MTP" granted="true" flags="0" />
<item name="android.permission.READ_LOGS" granted="true" flags="0" />
<item name="android.permission.INTERACT_ACROSS_USERS" granted="true" flags="0" />
<item name="android.permission.ACCESS_WIFI_STATE" granted="true" flags="0" />
<item name="oppo.permission.OPPO_COMPONENT_SAFE" granted="true" flags="0" />
<item name="android.permission.WAKE_LOCK" granted="true" flags="0" />
</perms>
</shared-user>
8.1.3.1 Settings.readSharedUserLPw
解析系统定义的共享用户 id: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
70private void readSharedUserLPw(XmlPullParser parser) throws XmlPullParserException,IOException {
String name = null;
String idStr = null;
int pkgFlags = 0;
int pkgPrivateFlags = 0;
SharedUserSetting su = null;
try {
name = parser.getAttributeValue(null, ATTR_NAME); // 获得共享用户的名称
idStr = parser.getAttributeValue(null, "userId"); // 获得共享用户的id
int userId = idStr != null ? Integer.parseInt(idStr) : 0;
if ("true".equals(parser.getAttributeValue(null, "system"))) { // 是否是系统的用户 id!
pkgFlags |= ApplicationInfo.FLAG_SYSTEM;
}
if (name == null) {
PackageManagerService.reportSettingsProblem(Log.WARN,
"Error in package manager settings: <shared-user> has no name at "
+ parser.getPositionDescription());
} else if (userId == 0) {
PackageManagerService.reportSettingsProblem(Log.WARN,
"Error in package manager settings: shared-user " + name
+ " has bad userId " + idStr + " at "
+ parser.getPositionDescription());
} else {
//【8.1.3.2】调用 addSharedUserLPw 方法,将这个共享用户和对应的 uid 保存下来!
if ((su = addSharedUserLPw(name.intern(), userId, pkgFlags, pkgPrivateFlags))
== null) {
PackageManagerService
.reportSettingsProblem(Log.ERROR, "Occurred while parsing settings at "
+ parser.getPositionDescription());
}
}
} catch (NumberFormatException e) {
PackageManagerService.reportSettingsProblem(Log.WARN,
"Error in package manager settings: package " + name + " has bad userId "
+ idStr + " at " + parser.getPositionDescription());
}
if (su != null) { // 解析子标签!
int outerDepth = parser.getDepth();
int type;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
continue;
}
String tagName = parser.getName();
if (tagName.equals("sigs")) {
su.signatures.readXml(parser, mPastSignatures);
} else if (tagName.equals("perms")) { // 解析 "perms" 标签
//【8.1.1.3.1】解析共享用户的权限信息!
readInstallPermissionsLPr(parser, su.getPermissionsState());
} else {
PackageManagerService.reportSettingsProblem(Log.WARN,
"Unknown element under <shared-user>: " + parser.getName());
XmlUtils.skipCurrentTag(parser);
}
}
} else {
XmlUtils.skipCurrentTag(parser);
}
}
8.1.3.2 Settings.addSharedUserLPw
1 | SharedUserSetting addSharedUserLPw(String name, int uid, int pkgFlags, int pkgPrivateFlags) { |
流程总结:
- 解析 “shared-user” ,将其分装成 SharedUserSetting 对象,保存到 mSharedUsers,并根据 uid 的取值将其保存到 mUserIds 或者 mOtherIds 中!
- 解析 “shared-user” 子标签 “perm” 等等,解析共享用户的权限,每个权限对应一个 PermissionData 对象,保存进入 PermisssionState 中,用于管理共享用户的权限!
8.1.4 解析 “preferred-activities” 相关标签
8.1.4.1 Settings.readPreferredActivitiesLPw
1 | void readPreferredActivitiesLPw(XmlPullParser parser, int userId) |
8.1.4.2 Settings.readPersistentPreferredActivitiesLPw
1 | private void readPersistentPreferredActivitiesLPw(XmlPullParser parser, int userId) |
不多说了!!
8.1.5 解析 “updated-package” 标签
我们来看看这个标签的内容,进行对比:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20<package name="com.android.xxxx"
codePath="/data/app/com.android.xxxx-1"
nativeLibraryPath="/data/app/com.android.xxxx-2/lib"
publicFlags="944258757"
privateFlags="0"
ft="15920cea638" it="15920cea638" ut="15920cea638" version="1000" userId="10053"
installer="com.android.packageinstaller">
<perms>
<!---->
</perms>
</package>
<updated-package name="com.android.xxxx"
codePath="/data/app/com.android.xxxx-1"
ft="158ff005268" it="158ff005268" ut="158ff005268" version="845"
nativeLibraryPath="/data/app/com.android.xxxx-1/lib" userId="10053">
<perms>
<!---->
</perms>
</updated-package>
接下里,我们去看看解析过程:
8.1.5.1 Settings.readDisabledSysPackageLPw
1 | private void readDisabledSysPackageLPw(XmlPullParser parser) throws XmlPullParserException, |
8.1.6 解析 “cleaning-package” 标签
1 | } else if (tagName.equals("cleaning-package")) { |
调用 addPackageToCleanLPw 方法;1
2
3
4
5
6
7void addPackageToCleanLPw(PackageCleanItem pkg) {
if (!mPackagesToBeCleaned.contains(pkg)) {
//【1】添加到 mPackagesToBeCleaned!
mPackagesToBeCleaned.add(pkg);
}
}
这个方法很简单!
8.1.7 解析 “renamed-package” 标签
1 | else if (tagName.equals("renamed-package")) { |
代码很简单,将重新命名的应用的数据,key 为 新名字。value 为旧名字,添加到 mRenamedPackages 中!
8.1.8 阶段总结
我们来用一张图总结一下,PermissionsState 和 PackageSetting,SharedUserSetting 的关系!
![未命名文件.png-59.5kB][3]
- 如果 pacakge 是共享用户 id,那么所有 uid 为共享用户 id 的 package,其权限是一样的,都是共享用户的权限;
- 如果 pacakge 是独立用户 id,那么这个 package 有自己独立的权限!
8.2 确定共享 uid 有效性
主要逻辑如下: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//【1】对 mPendingPackages 集合中的需要验证共享用户 id 有效性的 package,进行共享用户 id 有效性的验证!
final int N = mPendingPackages.size();
for (int i = 0; i < N; i++) {
final PendingPackage pp = mPendingPackages.get(i);
//【8.2.1】看 sharedId 是否能对应找到一个 ShardUserSetting 对象!
Object idObj = getUserIdLPr(pp.sharedId);
if (idObj != null && idObj instanceof SharedUserSetting) { // 能找到,说明共享用户 ID 是有效的!
//【8.2.2】创建 PackageSetting!
PackageSetting p = getPackageLPw(pp.name, null, pp.realName,
(SharedUserSetting) idObj, pp.codePath, pp.resourcePath,
pp.legacyNativeLibraryPathString, pp.primaryCpuAbiString,
pp.secondaryCpuAbiString, pp.versionCode, pp.pkgFlags, pp.pkgPrivateFlags,
null, true /* add */, false /* allowInstall */, pp.parentPackageName,
pp.childPackageNames);
if (p == null) {
PackageManagerService.reportSettingsProblem(Log.WARN,
"Unable to create application package for " + pp.name);
continue;
}
// 将 PendingPackage 的其他数据拷贝到 PackageSetting 中!
p.copyFrom(pp);
} else if (idObj != null) {
String msg = "Bad package setting: package " + pp.name + " has shared uid "
+ pp.sharedId + " that is not a shared uid\n";
mReadMessages.append(msg);
PackageManagerService.reportSettingsProblem(Log.ERROR, msg);
} else {
String msg = "Bad package setting: package " + pp.name + " has shared uid "
+ pp.sharedId + " that is not defined\n";
mReadMessages.append(msg);
PackageManagerService.reportSettingsProblem(Log.ERROR, msg);
}
}
mPendingPackages.clear(); // 清空 mPendingPackages
这里调用 getUserIdLPr 来从 mUserIds 或 mOtherUserIds 中获得一个共享用户对象!
8.2.1 Settings.getUserIdLPr
获得指定 uid 对应的 Object,可能是一个 PackageSetting 或者是 SharedUserSetting!1
2
3
4
5
6
7
8
9
10
11public Object getUserIdLPr(int uid) {
if (uid >= Process.FIRST_APPLICATION_UID) {
final int N = mUserIds.size();
final int index = uid - Process.FIRST_APPLICATION_UID;
return index < N ? mUserIds.get(index) : null;
} else {
return mOtherUserIds.get(uid);
}
}
之前在分析解析 “shared-user” 的时候,就知道 SharedUserSetting 会被保存到这两个几个当中的,这里就不多说了!
接着,调用 getPackageLPw 方法来创建这个 PendingPackage 对应的 PackageSetting 对象!
8.2.2 Settings.getPackageLPw
参数传递:
- PackageSetting origPackage:表示源包,传入 null;
- SharedUserSetting sharedUser:共享 uid 对象,这里是不为 null!
- UserHandle installUser:为 null;
- add:true;
1 | private PackageSetting getPackageLPw(String name, PackageSetting origPackage, |
继续看:
8.2.2.1 Settings.addPackageSettingLPw
将确定了共享用户 id 的 PackageSetting 对象,添加到 mPackages 中!
1 | private void addPackageSettingLPw(PackageSetting p, String name, SharedUserSetting sharedUser) { |
下面是 replaceUserIdLPw 方法:1
2
3
4
5
6
7
8
9
10
11 private void replaceUserIdLPw(int uid, Object obj) {
if (uid >= Process.FIRST_APPLICATION_UID) {
final int N = mUserIds.size();
final int index = uid - Process.FIRST_APPLICATION_UID;
if (index < N) mUserIds.set(index, obj);
} else {
mOtherUserIds.put(uid, obj);
}
}
8.3 读取 packages_stopped.xml 文件,偏好设置
代码段如下:
1 | if (mBackupStoppedPackagesFilename.exists() |
读取 packages_stopped.xml 文件调用的是 readStoppedLPw 方法:
8.3.1 Settings.readStoppedLPw
1 | void readStoppedLPw() { |
这个过程很简单,解析 packages-stopped-backup.xml 或 packages-stopped.xml 文件,设置 package 的 stopped 和 notLaunched 属性!
8.3.2 Settings.writePackageRestrictionsLPr
writePackageRestrictionsLPr 方法用户保存用户对于系统应用的一些偏好设置,通过获得 package 在指定用户下的状态信息,我们就知道那些应用被 stop,那些应用被 hidden 等等!
1 | void writePackageRestrictionsLPr(int userId) { |
我们来看下偏好设置的 xml 的内容!
1 | <?xml version='1.0' encoding='utf-8' standalone='yes' ?> |
这里只列出了一部分的内容,其他的很类似,不再多说!
8.3.3 Settings.readPackageRestrictionsLPr
该方法用于读取已有的偏好设置,更新 package 安装的信息!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
179
180void readPackageRestrictionsLPr(int userId) {
if (DEBUG_MU) {
Log.i(TAG, "Reading package restrictions for user=" + userId);
}
FileInputStream str = null;
// 同样的,获得 data/system/users/0/package-restrictions.xml 和
// data/system/users/0/package-restrictions-backup.xml 文件对象!
File userPackagesStateFile = getUserPackagesStateFile(userId);
File backupFile = getUserPackagesStateBackupFile(userId);
// 优先从 backupFile 读取,如果两个文件都存在,那就会删除非备份文件!
if (backupFile.exists()) {
try {
str = new FileInputStream(backupFile);
mReadMessages.append("Reading from backup stopped packages file\n");
PackageManagerService.reportSettingsProblem(Log.INFO,
"Need to read from backup stopped packages file");
if (userPackagesStateFile.exists()) {
Slog.w(PackageManagerService.TAG, "Cleaning up stopped packages file "
+ userPackagesStateFile);
userPackagesStateFile.delete();
}
} catch (java.io.IOException e) {
// We'll try for the normal settings file.
}
}
try {
if (str == null) {
if (!userPackagesStateFile.exists()) {
mReadMessages.append("No stopped packages file found\n");
PackageManagerService.reportSettingsProblem(Log.INFO,
"No stopped packages file; "
+ "assuming all started");
// 对于第一次启动,这里会初始化操作,并返回!!
for (PackageSetting pkg : mPackages.values()) {
pkg.setUserState(userId, 0, COMPONENT_ENABLED_STATE_DEFAULT,
true, // installed
false, // stopped
false, // notLaunched
false, // hidden
false, // suspended
null, null, null,
false, // blockUninstall
INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED, 0);
}
return;
}
str = new FileInputStream(userPackagesStateFile);
}
final XmlPullParser parser = Xml.newPullParser();
parser.setInput(str, StandardCharsets.UTF_8.name());
int type;
while ((type=parser.next()) != XmlPullParser.START_TAG
&& type != XmlPullParser.END_DOCUMENT) {
;
}
if (type != XmlPullParser.START_TAG) {
mReadMessages.append("No start tag found in package restrictions file\n");
PackageManagerService.reportSettingsProblem(Log.WARN,
"No start tag found in package manager stopped packages");
return;
}
int maxAppLinkGeneration = 0;
int outerDepth = parser.getDepth();
PackageSetting ps = null;
while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG
|| parser.getDepth() > outerDepth)) {
if (type == XmlPullParser.END_TAG
|| type == XmlPullParser.TEXT) {
continue;
}
//【1】解析 pkg 标签的属性!
String tagName = parser.getName();
if (tagName.equals(TAG_PACKAGE)) {
String name = parser.getAttributeValue(null, ATTR_NAME);
ps = mPackages.get(name);
if (ps == null) {
Slog.w(PackageManagerService.TAG, "No package known for stopped package "
+ name);
XmlUtils.skipCurrentTag(parser);
continue;
}
final long ceDataInode = XmlUtils.readLongAttribute(parser, ATTR_CE_DATA_INODE,
0);
final boolean installed = XmlUtils.readBooleanAttribute(parser, ATTR_INSTALLED,
true);
final boolean stopped = XmlUtils.readBooleanAttribute(parser, ATTR_STOPPED,
false);
final boolean notLaunched = XmlUtils.readBooleanAttribute(parser,
ATTR_NOT_LAUNCHED, false);
// For backwards compatibility with the previous name of "blocked", which
// now means hidden, read the old attribute as well.
final String blockedStr = parser.getAttributeValue(null, ATTR_BLOCKED);
boolean hidden = blockedStr == null
? false : Boolean.parseBoolean(blockedStr);
final String hiddenStr = parser.getAttributeValue(null, ATTR_HIDDEN);
hidden = hiddenStr == null
? hidden : Boolean.parseBoolean(hiddenStr);
final boolean suspended = XmlUtils.readBooleanAttribute(parser, ATTR_SUSPENDED,
false);
final boolean blockUninstall = XmlUtils.readBooleanAttribute(parser,
ATTR_BLOCK_UNINSTALL, false);
final int enabled = XmlUtils.readIntAttribute(parser, ATTR_ENABLED,
COMPONENT_ENABLED_STATE_DEFAULT);
final String enabledCaller = parser.getAttributeValue(null,
ATTR_ENABLED_CALLER);
final int verifState = XmlUtils.readIntAttribute(parser,
ATTR_DOMAIN_VERIFICATON_STATE,
PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED);
final int linkGeneration = XmlUtils.readIntAttribute(parser,
ATTR_APP_LINK_GENERATION, 0);
if (linkGeneration > maxAppLinkGeneration) {
maxAppLinkGeneration = linkGeneration;
}
ArraySet<String> enabledComponents = null;
ArraySet<String> disabledComponents = null;
int packageDepth = parser.getDepth();
while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG
|| parser.getDepth() > packageDepth)) {
if (type == XmlPullParser.END_TAG
|| type == XmlPullParser.TEXT) {
continue;
}
tagName = parser.getName();
if (tagName.equals(TAG_ENABLED_COMPONENTS)) {
enabledComponents = readComponentsLPr(parser);
} else if (tagName.equals(TAG_DISABLED_COMPONENTS)) {
disabledComponents = readComponentsLPr(parser);
}
}
//【2】通过解析到的偏好设置,来更新安装的 package 的信息!
ps.setUserState(userId, ceDataInode, enabled, installed, stopped, notLaunched,
hidden, suspended, enabledCaller, enabledComponents, disabledComponents,
blockUninstall, verifState, linkGeneration);
} else if (tagName.equals("preferred-activities")) { // 解析默认应用的属性!
readPreferredActivitiesLPw(parser, userId);
} else if (tagName.equals(TAG_PERSISTENT_PREFERRED_ACTIVITIES)) {
readPersistentPreferredActivitiesLPw(parser, userId);
} else if (tagName.equals(TAG_CROSS_PROFILE_INTENT_FILTERS)) {
readCrossProfileIntentFiltersLPw(parser, userId);
} else if (tagName.equals(TAG_DEFAULT_APPS)) {
readDefaultAppsLPw(parser, userId);
} else {
Slog.w(PackageManagerService.TAG, "Unknown element under <stopped-packages>: "
+ parser.getName());
XmlUtils.skipCurrentTag(parser);
}
}
str.close();
mNextAppLinkGeneration.put(userId, maxAppLinkGeneration + 1);
} catch (XmlPullParserException e) {
mReadMessages.append("Error reading: " + e.toString());
PackageManagerService.reportSettingsProblem(Log.ERROR,
"Error reading stopped packages: " + e);
Slog.wtf(PackageManagerService.TAG, "Error reading package manager stopped packages",
e);
} catch (java.io.IOException e) {
mReadMessages.append("Error reading: " + e.toString());
PackageManagerService.reportSettingsProblem(Log.ERROR, "Error reading settings: " + e);
Slog.wtf(PackageManagerService.TAG, "Error reading package manager stopped packages",
e);
}
}
逻辑很简单,我们就不多说了!
8.4 读取运行时权限信息,并处理授予情况!
1 | //【8.4】读取每个 user 下的运行是权限信息! |
mRuntimePermissionsPersistence 对象我们前面介绍过,其专门用于处理运行时权限!
8.4.1 RuntimePermissionsPersistence.readStateForUserSyncLPr
该方法会读取指定的 userId 下的运行时权限信息!
1 | public void readStateForUserSyncLPr(int userId) { |
首先,会获得保存了运行时权限的文件对象!1
2
3
4private File getUserRuntimePermissionsFile(int userId) {
File userDir = new File(new File(mSystemDir, "users"), Integer.toString(userId));
return new File(userDir, RUNTIME_PERMISSIONS_FILE_NAME);
}
该文件位于 /data/system/users/0/runtime-permissions.xml,和偏好设置的文件位于同一个目录下,我们来简单的看下该文件的具体内容:
1 | <?xml version='1.0' encoding='UTF-8' standalone='yes' ?> |
8.4.2 Settings.parseRuntimePermissionsLPr
用于解析运行时权限文件,获得运行时权限相关的信息!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
52private void parseRuntimePermissionsLPr(XmlPullParser parser, int userId)
throws IOException, XmlPullParserException {
final int outerDepth = parser.getDepth();
int type;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
continue;
}
switch (parser.getName()) {
case TAG_RUNTIME_PERMISSIONS: { // runtime-permissions 标签
String fingerprint = parser.getAttributeValue(null, ATTR_FINGERPRINT); // fingerprint 属性
mFingerprints.put(userId, fingerprint); // 保存 fingerprint 到 mFingerprints 中!
// 然后判断是否对该设备用户授予默认的权限,添加到 mDefaultPermissionsGranted 中!
final boolean defaultsGranted = Build.FINGERPRINT.equals(fingerprint);
mDefaultPermissionsGranted.put(userId, defaultsGranted);
} break;
case TAG_PACKAGE: { //【1】pkg 标签
String name = parser.getAttributeValue(null, ATTR_NAME);
PackageSetting ps = mPackages.get(name);
if (ps == null) {
Slog.w(PackageManagerService.TAG, "Unknown package:" + name);
XmlUtils.skipCurrentTag(parser);
continue;
}
//【8.4.2.1】解析并处理 package 的运行时权限授予情况!
parsePermissionsLPr(parser, ps.getPermissionsState(), userId);
} break;
case TAG_SHARED_USER: { //【2】shared-user 标签
String name = parser.getAttributeValue(null, ATTR_NAME);
SharedUserSetting sus = mSharedUsers.get(name);
if (sus == null) {
Slog.w(PackageManagerService.TAG, "Unknown shared user:" + name);
XmlUtils.skipCurrentTag(parser);
continue;
}
//【8.4.2.1】解析并处理 shared-user 的运行时权限授予情况!
parsePermissionsLPr(parser, sus.getPermissionsState(), userId);
} break;
case TAG_RESTORED_RUNTIME_PERMISSIONS: { //【3】restored-perms 标签
//【3.1】解析要被恢复的权限所属的应用包名!
final String pkgName = parser.getAttributeValue(null, ATTR_PACKAGE_NAME);
//【8.4.2.2】解析 restored-perms!
parseRestoredRuntimePermissionsLPr(parser, pkgName, userId);
} break;
}
}
}
这个过程主要工作:
- 解析并处理 package 的运行时权限授予情况;
- 解析并处理 shared-user 的运行时权限授予情况;
8.4.2.1 Settings.parsePermissionsLPr
我们来看下 parsePermissionsLPr 是如何解析和处理运行时权限的!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
44private void parsePermissionsLPr(XmlPullParser parser, PermissionsState permissionsState,
int userId) throws IOException, XmlPullParserException {
final int outerDepth = parser.getDepth();
int type;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
continue;
}
switch (parser.getName()) {
case TAG_ITEM: {
String name = parser.getAttributeValue(null, ATTR_NAME); // name 属性!
BasePermission bp = mPermissions.get(name);
if (bp == null) {
Slog.w(PackageManagerService.TAG, "Unknown permission:" + name);
XmlUtils.skipCurrentTag(parser);
continue;
}
String grantedStr = parser.getAttributeValue(null, ATTR_GRANTED); // granted 属性!
final boolean granted = grantedStr == null
|| Boolean.parseBoolean(grantedStr);
String flagsStr = parser.getAttributeValue(null, ATTR_FLAGS); // flags 属性!
final int flags = (flagsStr != null)
? Integer.parseInt(flagsStr, 16) : 0;
//【8.4.2.1.1】处理运行时权限的授予情况,这里和处理安装时权限很类似!
if (granted) {
// 如果上次安装时,该运行时权限处于授予状态,接着更新 flags!
permissionsState.grantRuntimePermission(bp, userId);
permissionsState.updatePermissionFlags(bp, userId,
PackageManager.MASK_PERMISSION_FLAGS, flags);
} else {
// 如果上次安装时,该运行时权限处于未授予状态,只更新 flags!
permissionsState.updatePermissionFlags(bp, userId,
PackageManager.MASK_PERMISSION_FLAGS, flags);
}
} break;
}
}
}
我们看到,对于运行时权限已授予的情况,我们会先进行一次运行时权限授予,然后更新权限的 flags;对于运行时权限未授予的情况,只是更新 flags 即可!
8.4.2.1.1 Permissions.grantRuntimePermission
处理运行时权限的授予!
1 | public int grantRuntimePermission(BasePermission permission, int userId) { |
这里不多说!
8.4.2.2 Settings.parseRestoredRuntimePermissionsLPr
解析那些需要恢复的权限信息:
1 | private void parseRestoredRuntimePermissionsLPr(XmlPullParser parser, |
如果权限是被授予的状态,或者其 flags 不为 null,那么会调用 rememberRestoredUserGrantLPr 方法,保存到 Settings.mRestoredUserGrants 中!
#####8.4.2.2.1 Settings.rememberRestoredUserGrantLPr1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21public void rememberRestoredUserGrantLPr(String pkgName, String permission,
boolean isGranted, int restoredFlagSet, int userId) {
//【1】获得该 userId 下的 restore perms 数据集合,是一个 Map!
// 如果为 null,就初始化!
ArrayMap<String, ArraySet<RestoredPermissionGrant>> grantsByPackage =
mRestoredUserGrants.get(userId);
if (grantsByPackage == null) {
grantsByPackage = new ArrayMap<String, ArraySet<RestoredPermissionGrant>>();
mRestoredUserGrants.put(userId, grantsByPackage);
}
//【2】获得该 package 的 restore perms 数据集合,如果为 null,就初始化!
ArraySet<RestoredPermissionGrant> grants = grantsByPackage.get(pkgName);
if (grants == null) {
grants = new ArraySet<RestoredPermissionGrant>();
grantsByPackage.put(pkgName, grants);
}
//【3】将 retore 的权限封装成一个 RestoredPermissionGrant,添加到集合中!
RestoredPermissionGrant grant = new RestoredPermissionGrant(permission,
isGranted, restoredFlagSet);
grants.add(grant);
}
Settings 内部有一个 SparseArray:1
2
3private final SparseArray<ArrayMap<String, ArraySet<RestoredPermissionGrant>>>
mRestoredUserGrants =
new SparseArray<ArrayMap<String, ArraySet<RestoredPermissionGrant>>>();
下标是 userId,值是一个 ArrayMap<String, ArraySet
1 | final class RestoredPermissionGrant { |
RestoredPermissionGrant 内部的结构很简单,不多说了!!
9 安装时权限处理过程分析 - 接 8.1.1.4
markdown 只支持 6 级标题,所以这里接 8.1.1.4 继续分析!
下面来较为深入的分析下解析 “perms” 标签,并通过解析结果授予安装时权限的过程:
1 | <perms> |
回顾一下,Settings.readInstallPermissionsLPr 的方法:
1 | void readInstallPermissionsLPr(XmlPullParser parser, PermissionsState permissionsState) |
接下来,我们看一看,这个流程是如何处理权限的:
这里有涉及到 permissionsState 对象,每一个 PackageSetting 和 SharedUserSetting 都有一个 permissionsState 对象,用来保存其权限的状态信息!
在创建 PackageSetting 和 SharedUserSetting 的时候,都会默认创建一个 PermissionsState 对象!
PermissionsState 内部有一个 ArrayMap<String, PermissionData> mPermissions 用来保存权限名和其对应权限信息数据!
下面为了简化分析过程,PermissionsState 统一化为 PermissionsS
9.1 PermissionsS.grantInstallPermission - 处理授予情况
1 | // 授予一个安装时权限,可以看到,安装时权限是对所有用户都默认授予的! |
我们继续看,参数传入:
- BasePermission permission:权限封装对象
- int userId:设备用户,因为是安装时权限,所以对所有设备用户都是一样,这里传入 UserHandle.USER_ALL!
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
31private int grantPermission(BasePermission permission, int userId) {
//【9.1.1】如果之前有权限,本次授予失败!
if (hasPermission(permission.name, userId)) {
return PERMISSION_OPERATION_FAILURE;
}
//【9.1.2】判断权限 BasePermission 在 userId 下是否有映射 gid,如果有,那 hasGids 为 true!
final boolean hasGids = !ArrayUtils.isEmpty(permission.computeGids(userId));
//【9.1.3】如果权限 BasePermission 有映射的 gid,那就计算下该 package 的所属的 Gid 组!
final int[] oldGids = hasGids ? computeGids(userId) : NO_GIDS;
//【9.1.4】创建权限的 permissionData 对象,并添加到 PermissionState.mPermissions 中!
PermissionData permissionData = ensurePermissionData(permission);
//【9.1.5】授予权限!
if (!permissionData.grant(userId)) { // 授权失败,就返回 PERMISSION_OPERATION_FAILURE!
return PERMISSION_OPERATION_FAILURE;
}
// 如果权限有映射的 gid,在授予权限后,再次计算该 package 的所有映射的 gid!
// 比较授权前后,gid 组是否发生了变化,如果有,返回 PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED!
if (hasGids) {
final int[] newGids = computeGids(userId);
if (oldGids.length != newGids.length) {
return PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED;
}
}
return PERMISSION_OPERATION_SUCCESS; // 返回 PERMISSION_OPERATION_SUCCESS!
}
方法逻辑如下:
- 如果已经授予权限,那就不处理,返回 PERMISSION_OPERATION_FAILURE;
- 如果授予权限失败,那就不处理,返回 PERMISSION_OPERATION_FAILURE;
- 如果授予权限成功,但是发现权限所属的 gid 发生了变化,返回 PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED;
- 如果授予权限成功,但是发现权限所属的 gid 没有发生变化,返回 PERMISSION_OPERATION_SUCCESS;
9.1.1 PermissionsS.hasPermission
hasPermission 用来判断是否已经授予的该权限!1
2
3
4
5
6
7
8
9
10
11public boolean hasPermission(String name, int userId) {
// 校验 uid 的有效性!
enforceValidUserId(userId);
if (mPermissions == null) {
return false;
}
//【1】获得该权限对应的 permissionData 对象!
PermissionData permissionData = mPermissions.get(name);
//【9.1.1.1】判断是否已经授予了该权限!
return permissionData != null && permissionData.isGranted(userId);
}
授予该权限的条件是,其对应的 PermissionData 不为 null,同时该权限属于已经授予的状态;
9.1.1.1 PermissionData.isGranted
PermissionData 的 isGranted 用于判断该权限在 userId 所在的设备用户下是否是授予状态!1
2
3
4
5
6
7
8
9
10
11
12public boolean isGranted(int userId) {
if (isInstallPermission()) { // 如果是安装时权限,userId 即 key 为 USER_ALL!
userId = UserHandle.USER_ALL;
}
PermissionState userState = mUserStates.get(userId);
if (userState == null) {
return false;
}
return userState.mGranted; // 返回是否授予!
}
isInstallPermission 方法用来判断,该权限是否是一个安装时的权限:1
2
3
4private boolean isInstallPermission() {
return mUserStates.size() == 1
&& mUserStates.get(UserHandle.USER_ALL) != null;
}
判断依据很简单,对于安装时权限,所有的设备用户都一样,所以 mUserStates 大小为 1,且 key 为 UserHandle.USER_ALL!
9.1.2 BasePermission.computeGids
BasePermission 的 computeGids 方法用于通过 userId 来调整该权限所映射的 gids!
1 | public int[] computeGids(int userId) { |
如果 perUser 为 true,表示需要根据当前应用所在的 userId,调整映射的 gid,那么会通过 UserHandle.getUid 方法计算出当前 userId 下的该权限所映射的 gid!
如果 perUser 为 false,那么该权限在所有的设备用户下映射的 gid 是一样的!
9.1.3 PermissionsS.computeGids
返回指定设备用户 id 下,该 package 当前被授予的所有权限映射的 gid 数组!!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
26public int[] computeGids(int userId) {
enforceValidUserId(userId);
int[] gids = mGlobalGids;
if (mPermissions != null) {
final int permissionCount = mPermissions.size();
for (int i = 0; i < permissionCount; i++) {
//【1】遍历该 package 的已经被授予的所有权限!
// 没有授予就跳过!
String permission = mPermissions.keyAt(i);
if (!hasPermission(permission, userId)) {
continue;
}
PermissionData permissionData = mPermissions.valueAt(i);
//【9.1.3.1】计算该权限在当前设备用户下的 gid,将其添加到 gids!
final int[] permGids = permissionData.computeGids(userId);
if (permGids != NO_GIDS) {
gids = appendInts(gids, permGids);
}
}
}
// 并返回 gids!!
return gids;
}
mGlobalGids 是 PermissionsState 的成员变量,默认取值为:1
2private static final int[] NO_GIDS = {};
private int[] mGlobalGids = NO_GIDS;
9.1.3.1 PermissionData.computeGids
1 | private static final class PermissionData { |
PermissionData.computeGids 方法最后调用的是 BasePermission.computeGids 方法,这里不多说了。
9.1.4 PermissionsS.ensurePermissionData
创建 PermissionData,并添加到 PermissionsState 内部的 mPermissions 集合中!1
2
3
4
5
6
7
8
9
10
11
12private PermissionData ensurePermissionData(BasePermission permission) {
if (mPermissions == null) {
mPermissions = new ArrayMap<>();
}
//【9.1.2.1】创建权限对应的 PermissionData 对象!
PermissionData permissionData = mPermissions.get(permission.name);
if (permissionData == null) {
permissionData = new PermissionData(permission);
mPermissions.put(permission.name, permissionData);
}
return permissionData;
}
可以看到,在 PermissionsState 中有一个 mPermissions 表,保存了 permission.name 和 PermissionData 的映射关系,如果是第一次创建某个权限的 PermissionData,会创建 PermissionData 实例!
9.1.4.1 new PermissionData
创建 PermissionData 对象!
1 | private static final class PermissionData { |
创建 PermissionData 对象!
mUserStates 是以 userId 为下标,存储了每个设备用户下该应用的这个权限的状态信息!
9.1.5 PermissionData.grant
下面我们来看看具体的权限授予过程!1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20public boolean grant(int userId) {
if (!isCompatibleUserId(userId)) {
return false;
}
if (isGranted(userId)) { // 如果已经授予,那就返回 false!
return false;
}
// 获得该权限对应在 userId 下对应的 PermissionState 对象,没有的话就创建新的!
PermissionState userState = mUserStates.get(userId);
if (userState == null) {
userState = new PermissionState(mPerm.name);
mUserStates.put(userId, userState);
}
// 设置在该 userId 下的权限状态为 true!
userState.mGranted = true;
return true;
}
首先,判断 userId 的兼容性!
1 | private boolean isCompatibleUserId(int userId) { |
userId 要满足兼容性要求,需要至少满足其中之一条件:
- PermissionData.mUserStates 的大小 <= 0,意味着该权限还没有初始化在该 userId 下的状态!
1 | public boolean isDefault() { |
- 要么,该权限是安装时权限,同时 userId 只能为 UserHandle.USER_ALL; 要么,该权限不是安装时权限,同时 userId 不能为 UserHandle.USER_ALL;
1 | public static boolean isInstallPermissionKey(int userId) { |
对于 isInstallPermission 方法,前面已经分析了,不多说了!
9.2 PermissionsS.revokeInstallPermission - 处理不授予情况
1 | public int revokeInstallPermission(BasePermission permission) { |
我们继续看,参数传入;
- BasePermission permission:权限封装对象
- int userId:设备用户,因为是安装时权限,所以对所有设备用户都是一样,这里传入:UserHandle.USER_ALL
1 | private int revokePermission(BasePermission permission, int userId) { |
撤销安装时权限的过程,和授予有很多类似的地方,我们重点看那些不同的地方!
9.2.1 PermissionData.revoke
接下来看看安装时权限撤销的过程!1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19public boolean revoke(int userId) {
if (!isCompatibleUserId(userId)) {
return false;
}
// 如果权限还没有被授予,无法撤销!
if (!isGranted(userId)) {
return false;
}
//【1】将权限在该 userId 下的状态设置为非授予的情况!
PermissionState userState = mUserStates.get(userId);
userState.mGranted = false;
// 如果该权限恢复了默认状态,从 mUserStates 中移除掉它!
if (userState.isDefault()) {
mUserStates.remove(userId);
}
return true;
}
9.2.2 PermissionsS.ensureNoPermissionData
1 | private void ensureNoPermissionData(String name) { |
ensureNoPermissionData 用于从当前的 package 中移除该权限数据!
9.3 PermissionsS.updatePermissionFlags - 更新权限标志位
当 revokeInstallPermission 或者 grantInstallPermission 方法返回的是 PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED 或者 PERMISSION_OPERATION_SUCCESS 的时候,我们需要更新权限的标志位!
参数传递:
- userId 传入的是 UserHandle.USER_ALL;
- flagMask 传入的是 PackageManager.MASK_PERMISSION_FLAGS,取值为 0xFF,转为二进制就是 11111
- flagValues 则是解析上次安装信息时,解析该 package 的 perms 标签时,每一个权限的 flags!
1 | public boolean updatePermissionFlags(BasePermission permission, int userId, |
对于 mayChangeFlags,由于我们 flagMask 传入的是 PackageManager.MASK_PERMISSION_FLAGS,那就意味了需要更新 flags!
可以看到,如果 mayChangeFlags 为 true 的话,即使没有权限对应的 PermissionData 也会创建,因为这次会更新 flags!
PermissionData.getFlags 方法会获得权限旧的 flags!
重点的操作在 PermissionData.updateFlag 方法中:
9.3.1 PermissionData.updateFlags
更新指定权限的 flags,flagValues 是本次解析获得的 flags!!
1 | public boolean updateFlags(int userId, int flagMask, int flagValues) { |
这里有一个操作:
- 首先,进行 & 操作,保留了 flagValues 和 flagMask 相同的位,为 newFlags;
- 接着,userState.mFlags 表示旧的 flags,这里在 userState.mFlags 和 ~flagMask 中做了 & 操作,等价于取了 userState.mFlags 和 flagMask 不同的位,然后加上 newFlags,作为新的 flags!
10 阶段总结
在这个阶段,PMS 主要完成了如下了几个过程:
一、解析 SystemConfig 系统配置,主要会解析一下系统属性:
1、group:系统中定义的 gid,保存到了 SystemConfig.mGlobalGids 中!
2、permission:系统权限,每一个权限都创建一个 PermissionEntry,保存到 SystemConfig.mPermissions 中;
PermissionEntry 有如下属性:name 权限名:perUser:表示该权限的 gid 是否针对不同的 userId 做调整;gids:该权限所属 gid
3、assign-permission:系统 uid 和其具有的权限,保存到了 SystemConfig.mSystemPermissions 中!每一个 uid 都对应一个或多个权限;
4、library:共享库信息,保存到了 SystemConfig.mSharedLibraries 中!
5、feature:特性信息,保存到了 SystemConfig.mAvailableFeatures 和 SystemConfig.mUnavailableFeatures 中!
这里我们重点看一些属性的解析,其他的属性我们先不关注!
然后将 SystemConfig 中的一些解析结果保存到 Settings 中:
Settings.mGlobalGids = systemConfig.getGlobalGids();
Settings.mSystemPermissions = systemConfig.getSystemPermissions();
Settings.mAvailableFeatures = systemConfig.getAvailableFeatures();
接着处理 SystemConfig.mPermissions 中解析到的系统权限!
对于每一个 PermissionEntry 创建一个 BasePermission 对象:
在创建时会设置 BasePermission.name 为权限名;
设置 BasePermission.sourcePackage 为定义权限的源包,因为是系统权限,所以为 android;
设置 BasePermission.type 权限类型为 BasePermission.TYPE_BUILTIN;
设置 BasePermission.protectionLevel 权限级别默认为 PermissionInfo.PROTECTION_SIGNATURE;
同时会设置 BasePermission.perUser 为 PermissionEntry.perUser;
同时会设置 BasePermission.gids 为 PermissionEntry.gids;
最后新创建的 BasePermission,会添加到 Settings.mPermissions 集合中!
二、读取上一次的安装信息!