1.前言

1.1 Android6.0以前系统

结合以前G2项目Android系统版本是4.4(Android 6.0以前),权限系统只会在安装的时候询问一次(安装时权限),用户可以选择性的授予应用相关权限。一旦授权,除非卸载应用重新安装,被授权的权限不能撤销。

1.2 Android6.0及以后系统

Android 6.0 及更高版本中的 Android 应用权限模式需要危险权限的 Android 应用从安装时权限模式转移至运行时权限模式,用户可以在程序运行时授予/撤销危险权限。

因此,关于应用权限管理,针对4.4以前的系统,我们可以在PackageInstaller安装应用的时候,针对项目的权限管理策略,对被安装应用进行授权或撤销;针对Android6.0及以后的系统版本,可以启动一个系统服务,动态的修改应用权限

2.运行时权限

2.1 权限分类

Android权限分级具体体现是在PermissionInfo类中protectionLevel成员变量来定义权限的级别。

Android把应用权限分为四类:Normal, Dangerous, Signature, SigatureOrSystem。

  • Normal Permission指的是那些 app获取它所在的sandbox(每个进程都有独立的一个沙箱)以外的数据和资源所对应的权限,这些权限一般不会对用户的隐私信息造成风险. 比如,设置时区的权限(SET_TIME_ZONE)。对于此类权限,app申请后系统会自动赋予。
  • Dangerous Permission指的是那些可能对用户的隐私信息造成风险,或者可能影响用户数据的行为权限。比如读取用户的联系人。对于Dangerous Permission,app必须显示的获取用户的允许才可以正常使用。Runtime Permission机制针对的即是此类 dangerous permission。
  • Signature permission:权限请求者只有使用和[权限声明者]相同的证书来签名的情况下,才可以使用的权限。如果证书匹配,系统会自动赋予这些权限,不需要通知或请求用户。
  • SignatureOrSystem: SDK版本23之前叫“signature|privileged”,该类权限除了上述的 Signature Permission以外,还包括那些只赋予Android System Image内的应用的权限。Android并不建议app使用这类,因为Signature Permission已经能满足大部分的需求,不管这些app是否是build在System Image里。
2.2 特殊权限

上面说明了permission 中属性android:protectionLevel 使用意义,其中存在一些特殊的权限,这些权限比较敏感,在使用的时候必须特殊处理。SYSTEM_ALERT_WINDOW和WRITE_SETTINGS就是此类比较敏感的权限。

例如 WRITE_SETTINGS:

1
2
3
4
<permission android:name="android.permission.WRITE_SETTINGS"
android:label="@string/permlab_writeSettings"
android:description="@string/permdesc_writeSettings"
android:protectionLevel="signature|preinstalled|appop|pre23" />

官方解释如下:

​ Note: If the app targets API level 23 or higher, the app user must explicitly grant this
permission to the app through a permission management screen.
​ The app requests the user’s approval by sending an intent with action ACTION_MANAGE_WRITE_ SETTINGS.
​ The app can check whether it has this authorization by calling Settings.System.canWrite().

2.3 危险权限

Android 6.0 及更高版本要求危险权限必须使用运行时权限模式。危险权限是具有更高风险的权限(例如READ_CALENDAR),此类权限允许寻求授权的应用访问用户私人数据或获取可对用户造成不利影响的设备控制权。要查看危险权限列表,请运行以下命令:

1
adb shell pm list permissions -g -d
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
msmnile_gvmq:/ $ pm list permissions -g -d
pm list permissions -g -d
Dangerous Permissions:

group:android.permission-group.CONTACTS
permission:android.permission.WRITE_CONTACTS
permission:android.permission.GET_ACCOUNTS
permission:android.permission.READ_CONTACTS

group:android.permission-group.PHONE
permission:android.permission.ANSWER_PHONE_CALLS
permission:android.permission.READ_PHONE_NUMBERS
permission:android.permission.READ_PHONE_STATE
permission:android.permission.CALL_PHONE
permission:android.permission.ACCEPT_HANDOVER
permission:android.permission.USE_SIP
permission:com.android.voicemail.permission.ADD_VOICEMAIL

group:android.permission-group.CALENDAR
permission:android.permission.READ_CALENDAR
permission:android.permission.WRITE_CALENDAR

group:android.permission-group.CALL_LOG
permission:android.permission.READ_CALL_LOG
permission:android.permission.WRITE_CALL_LOG
permission:android.permission.PROCESS_OUTGOING_CALLS

group:android.permission-group.CAMERA
permission:android.permission.CAMERA

group:android.permission-group.SENSORS
permission:android.permission.BODY_SENSORS

group:android.car.permission-group.CAR_MONITORING
permission:android.car.permission.CAR_ENERGY

group:android.permission-group.LOCATION
permission:android.permission.ACCESS_FINE_LOCATION
permission:android.car.permission.CAR_SPEED
permission:android.permission.ACCESS_COARSE_LOCATION

group:android.permission-group.STORAGE
permission:android.permission.READ_EXTERNAL_STORAGE
permission:android.permission.WRITE_EXTERNAL_STORAGE

group:android.permission-group.MICROPHONE
permission:android.permission.RECORD_AUDIO

group:android.permission-group.SMS
permission:android.permission.READ_SMS
permission:android.permission.RECEIVE_WAP_PUSH
permission:android.permission.RECEIVE_MMS
permission:android.permission.RECEIVE_SMS
permission:android.permission.SEND_SMS
permission:android.permission.READ_CELL_BROADCASTS

ungrouped:
permission:org.codeaurora.permission.POWER_OFF_ALARM
2.4 权限组

所有危险的 Android 系统权限都属于权限组。如果设备运行的是 Android 6.0(API 级别 23),并且应用的 targetSdkVersion 是 23 或更高版本,则当用户请求危险权限时系统会发生以下行为:

  • 如果应用请求其清单中列出的危险权限,而应用目前在权限组中没有任何权限,则系统会向用户显示一个对话框,描述应用要访问的权限组。对话框不描述该组内的具体权限。例如,如果应用请求 READ_CONTACTS 权限,系统对话框只说明该应用需要访问设备的联系信息。如果用户批准,系统将向应用授予其请求的权限。

  • 如果应用请求其清单中列出的危险权限,而应用在同一权限组中已有另一项危险权限,则系统会立即授予该权限,而无需与用户进行任何交互。例如,如果某应用已经请求并且被授予了 READ_CONTACTS 权限,然后它又请WRITE_CONTACTS,系统将立即授予该权限。

任何权限都可属于一个权限组,包括正常权限和应用定义的权限。但权限组仅当权限危险时才影响用户体验。可以忽略正常权限的权限组。

2.5 权限校验方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
//检查某个 uid 和 pid是否有permission
//如果返回PackageManager#PERMISSION_GRANTED,说明该权限已经被allowed
//如果返回PackageManager#PERMISSION_DENIED,说明该权限不被allowed
public int checkPermission(String permission, int pid, int uid)
//同checkPermission,这里提供给当前进程调用
public int checkCallingPermission(String permission)
//同checkPermission,这里跟checkCallingPermission区别是,不需要判断调用者是否是当前进程
public int checkCallingOrSelfPermission(String permission)
//同checkPermission,主要是当返回不是PackageManager.PERMISSION_GRANTED会抛出SecurityException
public void enforcePermission(String permission, int pid, int uid, String message)
//同enforcePermission
public void enforceCallingPermission(String permission, String message)
//同enforcePermission
public void enforceCallingOrSelfPermission(String permission, String message)
2.6 permission flags
  • PackageManager.FLAG_PERMISSION_USER_SET: 权限被用户设置,应用还可以在runtime 的时候request
  • PackageManager.FLAG_PERMISSION_USER_FIXED:权限被用户设置,但是应用不能再request此权限(用户勾选了“never ask again”)。
  • PackageManager.FLAG_PERMISSION_POLICY_FIXED:device policy设定的权限,用户和app都不能修改。
  • PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE:如果permission被标记了这个flag,那么表示,app升级后被deny的permission,会依然是deny的状态。这个flag会在下面的情况中用到。适用于L以前版本的app,安装得到M的device上,如果它的dangerous permission被撤销了,比如通过settings里面的permission管理撤销或者device policy中设定,那么该APP升级到适用于M新的permission模式后,那么升级后这个permission依然是撤销的状态。也就是dangerous permission如果在升级之前被撤销过,升级后依然是撤销的状态。
  • PackageManager.FLAG_PERMISSION_SYSTEM_FIXED: 系统app获得的自动授权的permission。
  • PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT: 默认的系统基本功能app获得的自动授权的permission.
  • PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED:在app 运行之前必须要进行permission review
2.7 授权/撤销权限

在PackageManager中提供了两个方法来给某个应用赋予/撤销某些权限

1
2
3
4
5
6
7
8
9
10
11
// 允许权限
@override
public void grantRuntimePermission(String packageName, String name, final int userId) {
grantRuntimePermission(packageName, name, userId, false /* Only if not fixed by policy */);
}

// 撤销权限
@Override
public void revokeRuntimePermission(String packageName, String name, int userId) {
revokeRuntimePermission(packageName, name, userId, false /* Only if not fixed by policy */);
}

3. GrantPermissionsActivity授权界面

该界面是Android 6.0之后对Runtime Permission控制,与用户的交互界面。

GrantPermissionsActivity的注册信息如下:

1
2
3
4
5
6
7
8
9
10
11
// 在包安装管理器 packages/apps/PackageInstaller/AndroidManifest.xml
<activity android:name=".permission.ui.GrantPermissionsActivity"
android:configChanges="orientation|keyboardHidden|screenSize"
android:excludeFromRecents="true"
android:theme="@style/GrantPermissions"
android:visibleToInstantApps="true">
<intent-filter android:priority="1">
<action android:name="android.content.pm.action.REQUEST_PERMISSIONS" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>

在Activity.java基类中请求权限

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public final void requestPermissions(@NonNull String[] permissions, int requestCode) {
if (requestCode < 0) {
throw new IllegalArgumentException("requestCode should be >= 0");
}
if (mHasCurrentPermissionsRequest) {
Log.w(TAG, "Can request only one set of permissions at a time");
// Dispatch the callback with empty arrays which means a cancellation.
onRequestPermissionsResult(requestCode, new String[0], new int[0]);
return;
}
// 在PackageManager中去构建intent,主要是pm能够拿到action和intent的包名
Intent intent = getPackageManager().buildRequestPermissionsIntent(permissions);
startActivityForResult(REQUEST_PERMISSIONS_WHO_PREFIX, intent, requestCode, null);
mHasCurrentPermissionsRequest = true;
}
1
2
3
4
5
6
7
8
9
10
11
12
public Intent buildRequestPermissionsIntent(@NonNull String[] permissions) {
if (ArrayUtils.isEmpty(permissions)) {
throw new IllegalArgumentException("permission cannot be null or empty");
}
// GrantPermissionsActivity的action
Intent intent = new Intent(ACTION_REQUEST_PERMISSIONS);
// 声请的权限
intent.putExtra(EXTRA_REQUEST_PERMISSIONS_NAMES, permissions);
// GrantPermissionsActivity所在的包,即PackageInstaller
intent.setPackage(getPermissionControllerPackageName());
return intent;
}

至此,当某个应用的Activity需要申请权限,都会走到PackageInstaller的GrantPermissionsActivity 里来和用户交互申请权限。

3.1 GrantPermissionsActivity 交互过程

该Activity主要偏向UI逻辑控制,有以下几个部分:

1.mRequestedPermissions

这个是通过intent 的extra 传过来的,extra 的name 是PackageManager.EXTRA_REQUEST_PERMISSIONS_ NAMES

1
mRequestedPermissions = getIntent().getStringArrayExtra(PackageManager. EXTRA_REQUEST_PERMISSIONS_NAMES);

2、GrantPermissionsViewHandlerImpl
这个是用来更新activity UI的重要类

1
2
3
mViewHandler = new com.android.packageinstaller.permission.ui.handheld
.GrantPermissionsViewHandlerImpl(this, getCallingPackage())
.setResultListener(this);

3、setContentView(mViewHandler.createView());

acitivity 将GrantPermissionsViewHandlerImpl 中的createView 出来的View 显示出来。

4、mAppPermissions

1
2
3
4
5
6
7
mAppPermissions = new AppPermissions(this, callingPackageInfo, null, false,
new Runnable() {
@Override
public void run() {
setResultAndFinish();
}
});

这个AppPermissions 其实就是单个应用所拥有的所有的group permission 的统计,详细看
private final ArrayList mGroups = new ArrayList<>();

5、showNextPermissionGroupGrantRequest

1
2
3
mViewHandler.updateUi(groupState.mGroup.getName(), groupCount, currentIndex,
Icon.createWithResource(resources, icon), message,
groupState.mGroup.isUserSet());

6、点击界面按钮

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
public void onClick(View view) {
switch (view.getId()) {
case R.id.permission_allow_button:
if (mResultListener != null) {
view.performAccessibilityAction(
AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS, null);
mResultListener.onPermissionGrantResult(mGroupName, true, false);
}
break;
case R.id.permission_deny_button:
mAllowButton.setEnabled(true);
if (mResultListener != null) {
view.performAccessibilityAction(
AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS, null);
mResultListener.onPermissionGrantResult(mGroupName, false,
AppPermissionGroup.isStrictOpEnable() ? false : mShowDonNotAsk
&& mDoNotAskCheckbox.isChecked());
}
break;
case R.id.permission_more_info_button:
Intent intent = new Intent(Intent.ACTION_MANAGE_APP_PERMISSIONS);
intent.putExtra(Intent.EXTRA_PACKAGE_NAME, mAppPackageName);
intent.putExtra(ManagePermissionsActivity.EXTRA_ALL_PERMISSIONS, true);
mActivity.startActivity(intent);
break;
case R.id.do_not_ask_checkbox:
mAllowButton.setEnabled(!mDoNotAskCheckbox.isChecked());
break;
}
}

7、onPermissionGrantResult

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
public void onPermissionGrantResult(String name, boolean granted, boolean doNotAskAgain) {
GroupState groupState = mRequestGrantPermissionGroups.get(name);
if (groupState.mGroup != null) {
if (granted) {
groupState.mGroup.grantRuntimePermissions(doNotAskAgain,
groupState.affectedPermissions);
groupState.mState = GroupState.STATE_ALLOWED;
} else {
if (!AppPermissionGroup.isStrictOpEnable()) {
groupState.mGroup.revokeRuntimePermissions(doNotAskAgain,
groupState.affectedPermissions);
}
groupState.mState = GroupState.STATE_DENIED;

int numRequestedPermissions = mRequestedPermissions.length;
for (int i = 0; i < numRequestedPermissions; i++) {
String permission = mRequestedPermissions[i];

if (groupState.mGroup.hasPermission(permission)) {
EventLogger.logPermissionDenied(this, permission,
mAppPermissions.getPackageInfo().packageName);
}
}
}
updateGrantResults(groupState.mGroup);
}
if (!showNextPermissionGroupGrantRequest()) {
setResultAndFinish();
}
}

这里的callback 是从GrantPermissionsViewHandlerImpl 回来,确定执行grant 还是revoke。

4.grantRuntimePermission 授权流程

在PackageManager提供了grantRuntimePermission给某个应用授权。该API是@SystemApi,需要有平台签名的应用才能调用。

1
2
3
4
@SystemApi
@RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS)
public abstract void grantRuntimePermission(@NonNull String packageName,
@NonNull String permissionName, @NonNull UserHandle user);

最终会调到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
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
@Override
public void grantRuntimePermission(String packageName, String name, final int userId) {
grantRuntimePermission(packageName, name, userId, false /* Only if not fixed by policy */);
}

private void grantRuntimePermission(String packageName, String name, final int userId,
boolean overridePolicy) {
if (!sUserManager.exists(userId)) {//确认user id存在
Log.e(TAG, "No such user:" + userId);
return;
}
final int callingUid = Binder.getCallingUid();//确定调用端app 的uid

//需要app端注册GRANT_RUNTIME_PERMISSIONS 权限,不然会抛出exception
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS,
"grantRuntimePermission");

//需要INTERACT_ACROSS_USERS_FULL 权限,也会抛出exception
enforceCrossUserPermission(callingUid, userId,
true /* requireFullPermission */, true /* checkShell */,
"grantRuntimePermission");

final int uid;
final PackageSetting ps;

synchronized (mPackages) {
// //packageName 正确性
final PackageParser.Package pkg = mPackages.get(packageName);
if (pkg == null) {
throw new IllegalArgumentException("Unknown package: " + packageName);
}
//permission name 正确性
final BasePermission bp = mSettings.mPermissions.get(name);
if (bp == null) {
throw new IllegalArgumentException("Unknown permission: " + name);
}
// 获取ps,可以从里面拿到PermissionsState
ps = (PackageSetting) pkg.mExtras;
if (ps == null
|| filterAppAccessLPr(ps, callingUid, userId)) {
throw new IllegalArgumentException("Unknown package: " + packageName);
}

enforceDeclaredAsUsedAndRuntimeOrDevelopmentPermission(pkg, bp);
//app将该permission 注册到AndroidManifest中
//并且该permission 是Runtime 或者是development permission
// If a permission review is required for legacy apps we represent
// their permissions as always granted runtime ones since we need
// to keep the review required permission flag per user while an
// install permission's state is shared across all users.
if (mPermissionReviewRequired //对于legacy apps不做处理
&& pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M
&& bp.isRuntime()) {
return;
}

uid = UserHandle.getUid(userId, pkg.applicationInfo.uid);

final PermissionsState permissionsState = ps.getPermissionsState();

final int flags = permissionsState.getPermissionFlags(name, userId);
if ((flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0) {
throw new SecurityException("Cannot grant system fixed permission "
+ name + " for package " + packageName);
}
if (!overridePolicy && (flags & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0) {
throw new SecurityException("Cannot grant policy fixed permission "
+ name + " for package " + packageName);
}

if (bp.isDevelopment()) {
// Development permissions must be handled specially, since they are not
// normal runtime permissions. For now they apply to all users.
if (permissionsState.grantInstallPermission(bp) !=
PermissionsState.PERMISSION_OPERATION_FAILURE) {
scheduleWriteSettingsLocked();
}
return;
}

if (ps.getInstantApp(userId) && !bp.isInstant()) {
throw new SecurityException("Cannot grant non-ephemeral permission"
+ name + " for package " + packageName);
}

if (pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M) {
Slog.w(TAG, "Cannot grant runtime permission to a legacy app");
return;
}

// 对当前用户授权
final int result = permissionsState.grantRuntimePermission(bp, userId);
switch (result) {
case PermissionsState.PERMISSION_OPERATION_FAILURE: {
return;
}

case PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED: {
final int appId = UserHandle.getAppId(pkg.applicationInfo.uid);
mHandler.post(new Runnable() {
@Override
public void run() {
killUid(appId, userId, KILL_APP_REASON_GIDS_CHANGED);
}
});
}
break;
}

if (bp.isRuntime()) {
logPermissionGranted(mContext, name, packageName);
}

mOnPermissionChangeListeners.onPermissionsChanged(uid);

// Not critical if that is lost - app has to request again.
mSettings.writeRuntimePermissionsForUserLPr(userId, false);
}

// Only need to do this if user is initialized. Otherwise it's a new user
// and there are no processes running as the user yet and there's no need
// to make an expensive call to remount processes for the changed permissions.
if (READ_EXTERNAL_STORAGE.equals(name)
|| WRITE_EXTERNAL_STORAGE.equals(name)) {
final long token = Binder.clearCallingIdentity();
try {
if (sUserManager.isInitialized(userId)) {
StorageManagerInternal storageManagerInternal = LocalServices.getService(
StorageManagerInternal.class);
storageManagerInternal.onExternalStoragePolicyChanged(uid, packageName);
}
} finally {
Binder.restoreCallingIdentity(token);
}
}
}

主要注意 3 个地方:

1、permissionsState.grantRuntimePermission(bp, userId);

1
2
3
4
5
6
7
public int grantRuntimePermission(BasePermission permission, int userId) {
enforceValidUserId(userId);
if (userId == UserHandle.USER_ALL) {//这个操作不能针对所有用户,只能针对当前用户
return PERMISSION_OPERATION_FAILURE;
}
return grantPermission(permission, userId);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
private int grantPermission(BasePermission permission, int userId) {
if (hasPermission(permission.name, userId)) {//确认是否已经grant
return PERMISSION_OPERATION_FAILURE;
}

final boolean hasGids = !ArrayUtils.isEmpty(permission.computeGids(userId));
final int[] oldGids = hasGids ? computeGids(userId) : NO_GIDS;

PermissionData permissionData = ensurePermissionData(permission);
//这里就是终点,修改PermissionData 中PermissionState 的 mGranted属性
if (!permissionData.grant(userId)) {
return PERMISSION_OPERATION_FAILURE;
}

if (hasGids) {
final int[] newGids = computeGids(userId);
if (oldGids.length != newGids.length) {
return PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED;
}
}

return PERMISSION_OPERATION_SUCCESS;
}

2、mOnPermissionChangeListeners.onPermissionsChanged(uid);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private void handleOnPermissionsChanged(int uid) {
final int count = mPermissionListeners.beginBroadcast();
try {
for (int i = 0; i < count; i++) {
IOnPermissionsChangeListener callback = mPermissionListeners
.getBroadcastItem(i);
try {
callback.onPermissionsChanged(uid);
} catch (RemoteException e) {
Log.e(TAG, "Permission listener is dead", e);
}
}
} finally {
mPermissionListeners.finishBroadcast();
}
}

给客户端注册I OnPermissionsChangeListener 的callback 用,提示grant 成功。

3、mSettings.writeRuntimePermissionsForUserLPr(userId, false);

1
2
3
4
5
6
7
public void writeRuntimePermissionsForUserLPr(int userId, boolean sync) {
if (sync) {
mRuntimePermissionsPersistence.writePermissionsForUserSyncLPr(userId);
} else {
mRuntimePermissionsPersistence.writePermissionsForUserAsyncLPr(userId);
}
}
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
public void writePermissionsForUserAsyncLPr(int userId) {
final long currentTimeMillis = SystemClock.uptimeMillis();

if (mWriteScheduled.get(userId)) {
mHandler.removeMessages(userId);

// If enough time passed, write without holding off anymore.
final long lastNotWrittenMutationTimeMillis = mLastNotWrittenMutationTimesMillis
.get(userId);
final long timeSinceLastNotWrittenMutationMillis = currentTimeMillis
- lastNotWrittenMutationTimeMillis;
if (timeSinceLastNotWrittenMutationMillis >= MAX_WRITE_PERMISSIONS_DELAY_MILLIS) {
mHandler.obtainMessage(userId).sendToTarget();
return;
}

// Hold off a bit more as settings are frequently changing.
final long maxDelayMillis = Math.max(lastNotWrittenMutationTimeMillis
+ MAX_WRITE_PERMISSIONS_DELAY_MILLIS - currentTimeMillis, 0);
final long writeDelayMillis = Math.min(WRITE_PERMISSIONS_DELAY_MILLIS,
maxDelayMillis);

Message message = mHandler.obtainMessage(userId);
mHandler.sendMessageDelayed(message, writeDelayMillis);
} else {
mLastNotWrittenMutationTimesMillis.put(userId, currentTimeMillis);
Message message = mHandler.obtainMessage(userId);
mHandler.sendMessageDelayed(message, WRITE_PERMISSIONS_DELAY_MILLIS);
mWriteScheduled.put(userId, true);
}
}

这个函数主要异步处理,首先确认mWriteScheduled 是否在处理userId,如果没有就添加进去,200ms 后发出消息开始处理;如果已经在处理,计算出最合适的delay 处理,可以看出系统给定的一次操作最长是2秒。

最后会在mHandler 中的handleMessage 中处理:

1
2
3
4
5
6
7
8
9
10
@Override
public void handleMessage(Message message) {
final int userId = message.what;
Runnable callback = (Runnable) message.obj;
// 写各个应用的运行时权限到文件中
writePermissionsSync(userId);
if (callback != null) {
callback.run();
}
}

writePermissionsSync方法中从permissionsForPackage和permissionsForSharedUser 中将各个应用运行时权限获取出来,调用writePermissions()将对应的runtime permission 存放在runtime-permissions.xml 中,主要就是将之前保存的PermissionStates中的mGranted属性和mFlags属性存放在这里:

1
2
3
4
5
6
7
8
9
10
11
12
private void writePermissions(XmlSerializer serializer,
List<PermissionState> permissionStates) throws IOException {
for (PermissionState permissionState : permissionStates) {
serializer.startTag(null, TAG_ITEM);
serializer.attribute(null, ATTR_NAME,permissionState.getName());
serializer.attribute(null, ATTR_GRANTED,
String.valueOf(permissionState.isGranted()));
serializer.attribute(null, ATTR_FLAGS,
Integer.toHexString(permissionState.getFlags()));
serializer.endTag(null, TAG_ITEM);
}
}

最终的xml 如下(详见/data/system/users/0/runtime-permissions.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
<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
<runtime-permissions fingerprint="qti/msmnile_gvmq/msmnile_gvmq:9/PQ1A.190105.004/jieou05250205:userdebug/test-keys">
<pkg name="com.android.car.messenger">
<item name="android.permission.READ_SMS" granted="true" flags="20" />
<item name="android.permission.SEND_SMS" granted="true" flags="20" />
<item name="android.permission.READ_CONTACTS" granted="true" flags="20" />
</pkg>
<pkg name="com.ts.appservice.weather">
<item name="android.permission.ACCESS_FINE_LOCATION" granted="true" flags="0" />
<item name="android.permission.ACCESS_COARSE_LOCATION" granted="true" flags="0" />
</pkg>
<pkg name="com.android.car.trust">
<item name="android.permission.ACCESS_FINE_LOCATION" granted="true" flags="0" />
<item name="android.permission.ACCESS_COARSE_LOCATION" granted="true" flags="0" />
</pkg>
<pkg name="com.ts.app.settings">
<item name="android.permission.READ_EXTERNAL_STORAGE" granted="true" flags="0" />
<item name="android.permission.WRITE_EXTERNAL_STORAGE" granted="true" flags="0" />
</pkg>
<pkg name="com.ts.app.music">
<item name="android.permission.READ_EXTERNAL_STORAGE" granted="true" flags="0" />
<item name="android.permission.WRITE_EXTERNAL_STORAGE" granted="true" flags="0" />
</pkg>
<pkg name="com.ts.app.raido">
<item name="android.permission.READ_EXTERNAL_STORAGE" granted="true" flags="0" />
<item name="android.permission.WRITE_EXTERNAL_STORAGE" granted="true" flags="0" />
</pkg>

.......