本文基于 Android 7.1.1 源码分析,如有错误,欢迎指正,谢谢!
[toc]
0 综述
前面分析了 AppOpsManager 中的一些接口,最终都会调用 AppOpservice 中的相应接口,下面我们来看下 AppOpservice 中的权限操作!
涉及的类如下:
- Settings 上层应用入口:
1 | packages/apps/Settings/src/com/android/settings/applications/AppOpsCategory.java |
- appops 的可执行文件:
位于 /system/bin/ 目录下!
1 | frameworks/native/libs/binder/AppOpsManager.cpp |
- 框架层 AppOps 核心实现类:
AppOpsService 是功能具体实现;AppOpsManager 是 AppOpsService 提供给其他进程的一个入口; AppOpsPolicy大概意思是,系统出厂时预制的系统 App(System-app)和用户 app(User-app)的一些默认的权限!1
2frameworks/base/services/core/java/com/android/server/AppOpsService.java
frameworks/base/core/java/android/app/AppOpsManager.java
这里我们重点关注核心服务 AppOpsService 的实现,下面分析下一些核心的方法接口!
1 AppOpsService.checkOperation - 检查操作权限
checkOperation 用于检查指定 package 是否有权限执行指定操作!
1 |
|
其实我们知道,该方法返回值有如下几种:MODE_ALLOWED,MODE_IGNORED,MODE_ERRORED 和 MODE_DEFAULT!
1.1 verifyIncomingUid
校验 uid 是否具有 android.Manifest.permission.UPDATE_APP_OPS_STATS 权限!
1 | private void verifyIncomingUid(int uid) { |
如果 uid 没有 android.Manifest.permission.UPDATE_APP_OPS_STATS 权限,抛出异常!
1.2 verifyIncomingOp
校验 op 是否正确!1
2
3
4
5
6private void verifyIncomingOp(int op) {
if (op >= 0 && op < AppOpsManager._NUM_OP) {
return;
}
throw new IllegalArgumentException("Bad operation #" + op);
}
如果 op 不在 [0, AppOpsManager._NUM_OP) 范围内,抛出异常;
1.3 resolvePackageName
处理一些特殊 uid 所属包名!
1 | private static String resolvePackageName(int uid, String packageName) { |
不多说!
1.4 isOpRestrictedLocked
判断该操作是否被用户限制!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
28private boolean isOpRestrictedLocked(int uid, int code, String packageName) {
//【1】获得该 uid 所属的 userId!
int userHandle = UserHandle.getUserId(uid);
final int restrictionSetCount = mOpUserRestrictions.size();
for (int i = 0; i < restrictionSetCount; i++) {
//【2】遍历每一个 ClientRestrictionState,检查该 op 是否在 uid 所在的用户下被限制,同时也检查
// 属于该 uid 的 package 是否在白名单中,如果在白名单中,那就不受限制!
ClientRestrictionState restrictionState = mOpUserRestrictions.valueAt(i);
//【×1.4.1】校验该 package 的 op 在指定的 userid 下是否收到限制,如果有返回 true!
if (restrictionState.hasRestriction(code, packageName, userHandle)) {
//【3】如果确实在该 userId 下受到限制,那就在判断下是否允许 system/system ui 绕过限制!
if (AppOpsManager.opAllowSystemBypassRestriction(code)) {
synchronized (this) {
//【*1.4.2】获得该 package 的所有 op 操作
Ops ops = getOpsRawLocked(uid, packageName, true);
if ((ops != null) && ops.isPrivileged) {
//【3.1】如果 ops.isPrivileged 为 true,说明该 package 是特权应用,要绕过用户限制!
return false;
}
}
}
//【3.2】受到限制但是不允许绕过!
return true;
}
}
return false; // 不受限制返回 false;
}
AppOpsService 用一个 ArrayMap 保存所有的用户限制状态信息!1
private final ArrayMap<IBinder, ClientRestrictionState> mOpUserRestrictions = new ArrayMap<>();
1.4.1 ClientRestrictionState.hasRestriction
该方法用于判断指定 package 的指定 op 是否在 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
26public boolean hasRestriction(int restriction, String packageName, int userId) {
//【1】如果 perUserRestrictions 为 null,不受限制!
if (perUserRestrictions == null) {
return false;
}
//【2】如果在 userId 下没有限制信息,不受限制!
boolean[] restrictions = perUserRestrictions.get(userId);
if (restrictions == null) {
return false;
}
//【3】如果在 userId 下,restrictions[restriction] 为 false,不受限制!
if (!restrictions[restriction]) {
return false;
}
//【4】如果在 userId 下,restrictions[restriction] 为 true,说明在该用户下,该 op 是受限制的
// 那就要看下 package 是否在白名单中,如果没有白名单,那么就是首先的!
if (perUserExcludedPackages == null) {
return true;
}
String[] perUserExclusions = perUserExcludedPackages.get(userId);
if (perUserExclusions == null) {
return true;
}
//【5】如果该 package 是在白名单中,那么就是不受限的,返回 false;
return !ArrayUtils.contains(perUserExclusions, packageName);
}
方法很简单,不多说了!
1.4.2 getOpsRawLocked
1 | private Ops getOpsRawLocked(int uid, String packageName, boolean edit) { |
不多说了!
1.5 getUidStateLocked
获得 uid 的 op 管理信息:
1 | private UidState getUidStateLocked(int uid, boolean edit) { |
不多说!
1.6 getOpLocked
getOpLocked 用于获得指定 package 的特定 op 对应的 Op 对象,该 Op 对象封装了操作的模式等相关信息,参数 boolean edit 这里传入的是 true!
1 | private Op getOpLocked(int code, int uid, String packageName, boolean edit) { |
调用了三参数函数 getOpLocked!1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16private Op getOpLocked(Ops ops, int code, boolean edit) {
//【1】获得 code 对应的 Op 对象!
Op op = ops.get(code);
if (op == null) {
if (!edit) {
return null;
}
// 如果没有,由于 edit 为 true,这里会默认初始化创建一个 Op 对象!
op = new Op(ops.uidState.uid, ops.packageName, code);
ops.put(code, op);
}
if (edit) { // 同时更新本地持久化文件!
scheduleWriteLocked();
}
return op; // 返回!
}
2 AppOpsService.noteOperation - 检查操作权限
检查指定的 package 是否允许执行 op 对应的操作,同时会更新 op 相关的信息!
noteOperation 用于短期权限的检查。
1 |
|
继续分析:
2.1 noteOperationUnchecked
我们去看看 5 参函数:
1 | private int noteOperationUnchecked(int code, int uid, String packageName, |
方法很简单,不多说了!
3 AppOpsService.setUidMode - 设置 uid 的 op 模式
设置指定 uid 的相应 op 的模式,如果 uid 的 op 模式发生后,那么属于该 uid 的所有 package 也会受到影响!!
1 |
|
设置完 uid 的 op 后,会触发那些监听 op 变化的回调!
4 AppOpsService.setMode - 设置 package 的 op 模式
设置指定 package 的指定 op 的模式值,参数 int uid 是该 package 所属的 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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
public void setMode(int code, int uid, String packageName, int mode) {
//【1】校验权限,因为 setMode 方法只能是系统调用,系统是默认有 UPDATE_APP_OPS_STATS 权限的!
if (Binder.getCallingPid() != Process.myPid()) {
mContext.enforcePermission(android.Manifest.permission.UPDATE_APP_OPS_STATS,
Binder.getCallingPid(), Binder.getCallingUid(), null);
}
verifyIncomingOp(code);
ArrayList<Callback> repCbs = null;
//【2】获得决定 code 指定的 op 的模式的 op,调用 AppOpsManager.opToSwitch 方法!
code = AppOpsManager.opToSwitch(code);
synchronized (this) {
//【3】获得 uid 对应的 UidState 对象!
UidState uidState = getUidStateLocked(uid, false);
//【×1.5】获得 code(switch) 对应的 Op 对象!
Op op = getOpLocked(code, uid, packageName, true);
if (op != null) {
//【5】如果 op 的现有模式和本次要设置的模式不同,更新模式值!
if (op.mode != mode) {
op.mode = mode;
//【5.1】更新了模式值后,收集所有监听该 op 模式变化的观察者!
ArrayList<Callback> cbs = mOpModeWatchers.get(code);
if (cbs != null) {
if (repCbs == null) {
repCbs = new ArrayList<Callback>();
}
repCbs.addAll(cbs);
}
//【5.2】更新了模式值后,收集所有监听该 packageName 操作模式变化的观察者!
cbs = mPackageModeWatchers.get(packageName);
if (cbs != null) {
if (repCbs == null) {
repCbs = new ArrayList<Callback>();
}
repCbs.addAll(cbs);
}
//【×4.1】如果本次设置是恢复默认模式,那么就移除该 op;
if (mode == AppOpsManager.opToDefaultMode(op.op)) {
pruneOp(op, uid, packageName);
}
//【×4.2】更新本地文件;
scheduleFastWriteLocked();
}
}
}
//【6】当 repCbs 不为 null,通知所有监听者!
if (repCbs != null) {
// 将远程调用者的 uid/pid 转为系统进程的 uid/pid 防止权限相关问题!
final long identity = Binder.clearCallingIdentity();
try {
for (int i = 0; i < repCbs.size(); i++) {
try {
//【6.1】触发 callback 的 opChanged 回调!
repCbs.get(i).mCallback.opChanged(code, uid, packageName);
} catch (RemoteException e) {
}
}
} finally {
Binder.restoreCallingIdentity(identity);
}
}
}
方法流程很简单,不多说了!
4.1 pruneOp
移除指定的 Op!1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24private void pruneOp(Op op, int uid, String packageName) {
if (op.time == 0 && op.rejectTime == 0) {
//【1】返回该 uid 下该 package 的所有 Op 信息!
Ops ops = getOpsRawLocked(uid, packageName, false);
if (ops != null) {
//【2】从中移除该 op!
ops.remove(op.op);
//【3】如果该 package 已经没有任何 Op,那就从所属于的 UidState 中移除相关记录!
if (ops.size() <= 0) {
UidState uidState = ops.uidState;
ArrayMap<String, Ops> pkgOps = uidState.pkgOps;
if (pkgOps != null) {
pkgOps.remove(ops.packageName);
if (pkgOps.isEmpty()) {
uidState.pkgOps = null;
}
if (uidState.isDefault()) {
mUidStates.remove(uid);
}
}
}
}
}
}
整个过程很简单,不多说了!
4.2 scheduleFastWriteLocked
1 | private void scheduleFastWriteLocked() { |
更新本地持久化文件,执行一个 Runnable 对象 mWriteRunner,在启动篇的时候,我们看过了,这里就不再多说了!
5 AppOpsService.startWatchingMode - 监听 op 变化
startWatchingMode 用于启动一个监听,他能够监听指定 package 的指定 op 操作的模式变化,当发生会变化后,会回调 callback!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
public void startWatchingMode(int op, String packageName, IAppOpsCallback callback) {
if (callback == null) {
return;
}
synchronized (this) {
//【1】获得决定 op 模式的 op 操作!
op = (op != AppOpsManager.OP_NONE) ? AppOpsManager.opToSwitch(op) : op;
//【3.1】将 callback 转为客户端代理,封装为一个 Callback 对象,添加到 mModeWatchers 中!
Callback cb = mModeWatchers.get(callback.asBinder());
if (cb == null) {
// 创建了一个 Callback 对象,用于监听远程 Binder 对象的死亡布告对象
// 这样当 IAppOpsCallback.Stub 死亡后,可以停止监听!
cb = new Callback(callback);
// 将 IAppOpsCallback.Stub 和 Callback 的映射保存到 mModeWatchers 中!
mModeWatchers.put(callback.asBinder(), cb);
}
//【3.2】将要监听的 op 操作和对应的监听回调 Callback 的映射关系保存到 mOpModeWatchers 中!
if (op != AppOpsManager.OP_NONE) {
ArrayList<Callback> cbs = mOpModeWatchers.get(op);
if (cbs == null) {
cbs = new ArrayList<Callback>();
mOpModeWatchers.put(op, cbs);
}
cbs.add(cb);
}
//【3.3】将要监听的 package 和对应的监听回调 Callback 的映射关系保存到 mPackageModeWatchers 中!
if (packageName != null) {
ArrayList<Callback> cbs = mPackageModeWatchers.get(packageName);
if (cbs == null) {
cbs = new ArrayList<Callback>();
mPackageModeWatchers.put(packageName, cbs);
}
cbs.add(cb);
}
}
}
AppOpsService 内部有多个集合:1
2
3//【1】用与 IBinder 作和对应的回调 Callback 的映射关系!
final ArrayMap<IBinder, Callback> mModeWatchers
= new ArrayMap<IBinder, Callback>();
用于保存远程回调 IAppOpsCallback.Stub 和其对应的死亡仆告对象 Callback 的映射关系,当 IAppOpsCallback.Stub 死亡后,Callback.binderDied 会被触发!!
1 | //【2】用与保存 op 操作和对应的回调 Callback 的映射关系! |
而 mOpModeWatchers 和 mPackageModeWatchers 则是从不同的角度来建立监听关系:mOpModeWatchers 是从具体 op 的角度,而 mPackageModeWatchers 则是从 package 的角度!
可以看到 Callback 的作用是作为远程回调的死亡仆告对象,用于停止监听。
5.1 new Callback
我们来看下 Callback 对象的创建,Callback 是一个回调对象,用于处理 op 变化后的操作;同时又是一个 DeathRecipient 对象,用来监听远程 IAppOpsCallback 桩对象是否死亡!1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21public final class Callback implements DeathRecipient {
final IAppOpsCallback mCallback;
public Callback(IAppOpsCallback callback) {
mCallback = callback;
try {
//【1】将自身注册为一个死亡仆告对象!
mCallback.asBinder().linkToDeath(this, 0);
} catch (RemoteException e) {
}
}
public void unlinkToDeath() { //【2】解除注册!
mCallback.asBinder().unlinkToDeath(this, 0);
}
public void binderDied() { //【1.6】当远程桩对象死亡后,停止监听!
stopWatchingMode(mCallback);
}
}
当远程的 IAppOpsCallback.Stub 死亡后,AppOpsService.stopWatchingMode 会被执行!
6 AppOpsService.stopWatchingMode - 停止 op 监听
startWatchingMode 用于停止一个监听!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 stopWatchingMode(IAppOpsCallback callback) {
if (callback == null) {
return;
}
synchronized (this) {
//【1】从 mModeWatchers 中移除 Callback!
Callback cb = mModeWatchers.remove(callback.asBinder());
if (cb != null) {
// 解除死亡仆告对象注册!
cb.unlinkToDeath();
//【2】从 mOpModeWatchers 移除该 Callback!
for (int i=mOpModeWatchers.size()-1; i>=0; i--) {
ArrayList<Callback> cbs = mOpModeWatchers.valueAt(i);
cbs.remove(cb);
if (cbs.size() <= 0) {
mOpModeWatchers.removeAt(i);
}
}
//【3】从 mPackageModeWatchers 移除该 Callback!
for (int i=mPackageModeWatchers.size()-1; i>=0; i--) {
ArrayList<Callback> cbs = mPackageModeWatchers.valueAt(i);
cbs.remove(cb);
if (cbs.size() <= 0) {
mPackageModeWatchers.removeAt(i);
}
}
}
}
}
方法很简单,就不多说了!
7 AppOpsService.startOperation - 开始监视操作
startOperation 用于监控一些长时间工作的操作,比如 Gps,像 Vibrator 等等,可以使用 startOperation 开始监视权限,使用 finishOperation 结束监视,要监视 op 前提是被监视者有权限执行 op 操作!!
1 |
|
startOperation 的第一个参数是一个 IBinder token,这个 token 通过如下方式获得:
1 | AppOpsManager.getToken(mAppOpsService) |
在 AppOpsManager 中我们有分析过这个方法,其返回的是一个 ClientState 对象!
7.1 AppOpsManager.getToken
我们来看看 getToken 方法,传入的是 mService。mService 是 AppOpsManager 的成员变量:1
IAppOpsService mService;
其实 mService 就是 AppOpsService 的 Proxy 对象!
1 | /** @hide */ |
这里会创建一个 Binder 对象,表示当前进程实体!
sToken 也是一个 Binder 对象:1
static IBinder sToken;
用于保存 AppOpsService 返回的 ClientState 对象!
7.2 AppOpsService.getToken
通过 Binder 通信,AppOpsManager 创建的 Binder 对象,传递到了 AppOpsService.getToken 方法中!1
2
3
4
5
6
7
8
9
10
11
12
public IBinder getToken(IBinder clientToken) {
synchronized (this) {
ClientState cs = mClients.get(clientToken);
if (cs == null) {
//【×7.2.1】创建一个 ClientState 对象,保存到 mClients 中!
cs = new ClientState(clientToken);
mClients.put(clientToken, cs);
}
return cs;
}
}
这里的 ClientToken,就是前面 new Binder 创建的实体!
AppOpsService 内部有一个 mClients 变量,用于保存调用方进程的 Binder 对象和对应的 ClientState 的映射关系!1
final ArrayMap<IBinder, ClientState> mClients = new ArrayMap<IBinder, ClientState>();
这个 ClientState 会记录调用方进程的 op 状态,同时其也实现了 DeathRecipient 接口,能够监听调用方进程的 Binder 对象是否死亡!
7.2.1 new ClientState - 被监控的实体信息
我们来看下 ClientState 的结构!
1 | public final class ClientState extends Binder implements DeathRecipient { |
对于 ClientState 的分析就到这里!
8 AppOpsService.finishOperation - 结束监视操作
finishOperation
第一个参数 IBinder token,必须是 ClientState 对象!也就是说,必须在调用了 startOperation 后,finishOperation 才有意义!
1 |
|
整个方法很简单,不所说了!
8.1 finishOperationLocked
finishOperationLocked 方法中会对该 Op 的属性进行更新!1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18void finishOperationLocked(Op op) {
if (op.nesting <= 1) {
if (op.nesting == 1) {
//【1】更新该操作的执行时长!
op.duration = (int)(System.currentTimeMillis() - op.time);
//【2】更新操作 op 的最新允许时间!
op.time += op.duration;
} else {
Slog.w(TAG, "Finishing op nesting under-run: uid " + op.uid + " pkg "
+ op.packageName + " code " + op.op + " time=" + op.time
+ " duration=" + op.duration + " nesting=" + op.nesting);
}
// 将 op.nesting 置为 0,表示 Op 的 start 操作完全结束!
op.nesting = 0;
} else {
op.nesting--; // start 次数减 1;
}
}
9 AppOpsService.setUserRestriction - 设置用户限制
setUserRestriction 主要是在 UserManagerService 方法中使用!
setUserRestriction 方法用于设置用户限制,他有 2 个重载函数,第一个 setUserRestriction 方法会设置所有 op 的用户限制!
参数 IBinder token 是一个 Binder 对象,用于表示远程调用者,UserManagerService 中直接传入的是一个 new Bindler 对象!1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public void setUserRestrictions(Bundle restrictions, IBinder token, int userHandle) {
checkSystemUid("setUserRestrictions"); / 如果不是系统进程调用会抛出异常!
Preconditions.checkNotNull(restrictions);
Preconditions.checkNotNull(token);
//【1】处理定义的每一个 operation,获得其所受的用户限制!
for (int i = 0; i < AppOpsManager._NUM_OP; i++) {
String restriction = AppOpsManager.opToRestriction(i);
// 如果该 op 确实会受到用户限制,继续处理!
if (restriction != null) {
//【×9.1】调用了 setUserRestrictionNoCheck 方法,进一步设置用户限制!
setUserRestrictionNoCheck(i, restrictions.getBoolean(restriction, false), token,
userHandle, null);
}
}
}
第二个 setUserRestriction 方法,第一个方法会设置指定 op 是否受到用户限制,同时额外传入了一个 String[] exceptionPackages,表示白名单,在该白名单中的 package 可以不受用户限制:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public void setUserRestriction(int code, boolean restricted, IBinder token, int userHandle,
String[] exceptionPackages) {
//【1】当不是当前进程调用,强制校验是否有 MANAGE_APP_OPS_RESTRICTIONS 权限!
if (Binder.getCallingPid() != Process.myPid()) {
mContext.enforcePermission(Manifest.permission.MANAGE_APP_OPS_RESTRICTIONS,
Binder.getCallingPid(), Binder.getCallingUid(), null);
}
//【2】如果是跨用户调用,要校验是否有 INTERACT_ACROSS_USERS_FULL 和 INTERACT_ACROSS_USERS 权限!
if (userHandle != UserHandle.getCallingUserId()) {
if (mContext.checkCallingOrSelfPermission(Manifest.permission
.INTERACT_ACROSS_USERS_FULL ) != PackageManager.PERMISSION_GRANTED
&& mContext.checkCallingOrSelfPermission(Manifest.permission
.INTERACT_ACROSS_USERS) != PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Need INTERACT_ACROSS_USERS_FULL or"
+ " INTERACT_ACROSS_USERS to interact cross user ");
}
}
verifyIncomingOp(code);
Preconditions.checkNotNull(token);
//【×9.1】调用了 setUserRestrictionNoCheck 方法,进一步设置用户限制!
setUserRestrictionNoCheck(code, restricted, token, userHandle, exceptionPackages);
}
9.1 setUserRestrictionNoCheck
参数 boolean restricted 表示是否限制,为 true 的话,表示打开限制!
这里的 token 来自 UserManagerService 中:
1 | private static final IBinder mUserRestriconToken = new Binder(); |
本质上是一个 Binder 对象!!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 void setUserRestrictionNoCheck(int code, boolean restricted, IBinder token,
int userHandle, String[] exceptionPackages) {
boolean notifyChange = false;
synchronized (AppOpsService.this) {
//【×9.2】从 mOpUserRestrictions 获得 IBinder 对应的 ClientRestrictionState 对象!
// 如果没有,就会创建一个新的实例,加入 mOpUserRestrictions!
ClientRestrictionState restrictionState = mOpUserRestrictions.get(token);
if (restrictionState == null) {
try {
restrictionState = new ClientRestrictionState(token);
} catch (RemoteException e) {
return;
}
mOpUserRestrictions.put(token, restrictionState);
}
//【×9.3】设置用户限制,如果有 op 的限制发生了变化,或者不受限制白名单发生了变化!
if (restrictionState.setRestriction(code, restricted, exceptionPackages, userHandle)) {
notifyChange = true;
}
//【×9.3.1】如果该 bindler 对应的 ClientRestrictionState 恢复了默认状态,那就移除!
if (restrictionState.isDefault()) {
mOpUserRestrictions.remove(token);
restrictionState.destroy();
}
}
//【×9.4】通知所有的监听者,该操作的用户限制状态发生了变化!
if (notifyChange) {
notifyWatchersOfChange(code);
}
}
AppOpsService 有一个 mOpUserRestrictions 的哈希表,用与保存所有的用于限制信息!1
private final ArrayMap<IBinder, ClientRestrictionState> mOpUserRestrictions = new ArrayMap<>();
9.2 new ClientRestrictionState
当第一次设置用户限制时,会创建 ClientRestrictionState 对象。保存用户限制状态!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
37private final class ClientRestrictionState implements DeathRecipient {
private final IBinder token;
SparseArray<boolean[]> perUserRestrictions; // 见下面;
SparseArray<String[]> perUserExcludedPackages; // 见下面;
public ClientRestrictionState(IBinder token)
throws RemoteException {
//【1】绑定远程进程的 Binder 对象!
token.linkToDeath(this, 0);
this.token = token;
}
public void binderDied() {
synchronized (AppOpsService.this) {
//【2】从 mOpUserRestrictions 中该 token 相应关系,解除了用户限制!
mOpUserRestrictions.remove(token);
if (perUserRestrictions == null) {
return;
}
final int userCount = perUserRestrictions.size();
for (int i = 0; i < userCount; i++) {
final boolean[] restrictions = perUserRestrictions.valueAt(i);
final int restrictionCount = restrictions.length;
for (int j = 0; j < restrictionCount; j++) {
//【1】如果有 op 之前是开启了用户限制,那么需要通知所有监听 op 状态变化的回调
//【1.9.4】调用了 notifyWatchersOfChange 方法!
if (restrictions[j]) {
final int changedCode = j;
mHandler.post(() -> notifyWatchersOfChange(changedCode));
}
}
}
destroy();
}
}
}
ClientRestrictionState 实现了 DeathRecipient 接口,当和其绑定的远程 Binder 对象死亡后会触发其 binderDied 方法!
perUserRestrictions 是一个 SparseArray,下标为 userId,而 SparseArray[i] 则是一个 boolean[] 数组,该数组长度为 AppOpsManager._NUM_OP 表示在 userId 下,每个 op 的限制情况,为 true 表示限制,为 false 表示不限制!
perUserExcludedPackages 也是一个 SparseArray,下标为 userId,而 SparseArray[i] 则是一个 String[] 数组,该数字保存白名单,在该名单中的应用不会受到用户限制!
当 token 对应的远程 Binder 对象死亡后,会触发 ClientRestrictionState.binderDied 方法
9.3 ClientRestrictionState.setRestriction
设置用户限制,restricted 表示是否限制!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
57public boolean setRestriction(int code, boolean restricted, String[] excludedPackages, int userId) {
boolean changed = false;
if (perUserRestrictions == null && restricted) {
perUserRestrictions = new SparseArray<>();
}
if (perUserRestrictions != null) {
//【1】获得 userId 下,所有 op 的限制情况!
boolean[] userRestrictions = perUserRestrictions.get(userId);
if (userRestrictions == null && restricted) {
//【1.1】如果 userRestrictions 为 null,且当前要设置为限制,那就会初始化 userRestrictions
// 为一个 AppOpsManager._NUM_OP 大小的数组,添加到 perUserRestrictions 中!
userRestrictions = new boolean[AppOpsManager._NUM_OP];
perUserRestrictions.put(userId, userRestrictions);
}
//【2】尝试更新 userRestrictions[code] 的值,userRestrictions[code] 表示的是 op 的限制状态!
// 只有当限制状态变化时,才会更新!
if (userRestrictions != null && userRestrictions[code] != restricted) {
// 更新 userRestrictions[code];
userRestrictions[code] = restricted;
//【×9.3.1】如果本次是取消用户限制,并且 userId 下的所有的 op 都是不开启限制的!
// 那么从 perUserRestrictions 中移除该 userId 的记录!
if (!restricted && isDefault(userRestrictions)) {
perUserRestrictions.remove(userId);
userRestrictions = null;
}
changed = true; // 表示发生了更新!
}
//【3】userRestrictions 不为 null,说明在该 userId 下对某些 op 进行了用户限制!
if (userRestrictions != null) {
final boolean noExcludedPackages = ArrayUtils.isEmpty(excludedPackages);
if (perUserExcludedPackages == null && !noExcludedPackages) {
//【3.1】如果本次指定了白名单,并且 perUserExcludedPackages 为 null
// 那么会进行初始化;
perUserExcludedPackages = new SparseArray<>();
}
//【3.2】本次指定的白名单 excludedPackages 和该 userId 下已有的名单不一样!
// 如果本次未指定白名单,那就移除该 userId 下已有的名单;
// 如果本次指定了白名单,那就更新该 userId 下已有的名单;
if (perUserExcludedPackages != null && !Arrays.equals(excludedPackages,
perUserExcludedPackages.get(userId))) {
if (noExcludedPackages) {
perUserExcludedPackages.remove(userId);
if (perUserExcludedPackages.size() <= 0) {
perUserExcludedPackages = null;
}
} else {
perUserExcludedPackages.put(userId, excludedPackages);
}
//【3.1】名单更新了,设置 changed 为 true!
changed = true;
}
}
}
return changed;
}
继续分析!
9.3.1 ClientRestrictionState.isDefault
有两个 default 方法:1
2
3public boolean isDefault() {
return perUserRestrictions == null || perUserRestrictions.size() <= 0;
}
无参数 isDefault 方法用来判断该 ClientRestrictionState 对象是否是默认状态!
1 | private boolean isDefault(boolean[] array) { |
一参数 isDefault 方法用来判断指定 ClientRestrictionState.perUserRestrictions 中指定 userId 下的用户限制是否是默认状态!
9.4 notifyWatchersOfChange
当用户限制状态发生变化后,AppOpsService 会调用 notifyWatchersOfChange 通知所有监听 op 变化的监听者!
int code 参数表示用户限制状态发生变化的 op code!
1 | private void notifyWatchersOfChange(int code) { |