###1. AppOps

Android 的App在安装时, 会列出该App申请的权限, 用户只能选择同意该App使用所有申请的权限并安装, 或者拒绝安装, 并不能单独控制App的权限

Google 在android 4.3 引入了 App Ops(Application Operations), 用于管理App权限, 但因为种种原因, google 在 android 4.4.2 中 “隐藏” 了 AppOps 功能 (移除了 settings 中的 AppOps 配置界面’AppOpsSummary’, 但并未移除 AppOps 的相关功能)

从 Android M 开始, google 推出了新的权限管理机制Android Runtime Permission, 在 “settings” > “apps” > “permissions” 中可以配置App的权限, 并且在App执行过程中, 访问未授权的权限时, 弹框提示用户授权

Android Runtime Permission 只支持 targetSdk >= 23 的 App,但事实上, 在 Androi M 的系统上可以看到, targetSdk < 23 的App, 对应的 “settings” > “apps” > “permissions” 中, 也可以配置权限,这种情况下, 其实是利用 AppOps 来实现的

###2. AppOps 相关的代码

frameworks 中的代码:

// AppOps service
frameworks/base/services/core/java/com/android/server/AppOpsService.java

// AppOps Policy, 用于预先自定义某个 package 的 某个 op 的 mode, Android M 中移除 
frameworks/base/services/core/java/com/android/server/AppOpsPolicy.java

// AppOps Manager
frameworks/base/core/java/android/app/AppOpsManager.java 

Settings 中 AppOps 设置相关的代码

packages/apps/Settings/src/com/android/settings/applications/AppOpsCategory.java
packages/apps/Settings/src/com/android/settings/applications/AppOpsDetails.java
packages/apps/Settings/src/com/android/settings/applications/AppOpsState.java
packages/apps/Settings/src/com/android/settings/applications/AppOpsSummary.java

###3. AppOpsManager

####3.1 op

在framework中,将某一权限称为Op,即operation,操作的意思。在 AppOpsManager 类中用以OP_开头的int表示具体权限, 例如:

public static final int OP_NONE = -1;
public static final int OP_COARSE_LOCATION = 0;
public static final int OP_FINE_LOCATION = 1;
public static final int OP_GPS = 2;
public static final int OP_VIBRATE = 3;
......
public static final int OP_READ_EXTERNAL_STORAGE = 59;
public static final int OP_WRITE_EXTERNAL_STORAGE = 60;
public static final int OP_TURN_SCREEN_ON = 61;
public static final int OP_GET_ACCOUNTS = 62;

虽然AppOpsManager中定义了许多Op, 但内部在实际处理时, 可能会有多个Op被视作一个进行处理, AppOpsManager.sOpToSwitch 这一数组定义了 op 的映射关系, 例如 OP_COARSE_LOCATION,OP_FINE_LOCATION,OP_GPS 都会被映射到 OP_COARSE_LOCATION

AppOpsManager 中为许多 op 定义了一个对应的字符串来对 op 进行描述, 例如 OP_COARSE_LOCATION 对应的描述字符串为 OPSTR_COARSE_LOCATION,其值为 “android:coarse_location”, AppOpsManager.sOpToString 数组定义了 OP 到 OPSTR 的映射关系

每一个 op 都有一个名称, 例如, OP_COARSE_LOCATION 的 名称为 “COARSE_LOCATION”, AppOpsManager.sOpNames 数组按照 op 的索引存放了对应 op 的名称

####3.2 Op 和 android permission

大部分的 op 和 android permision 存在对应的关系, 例如 op “OP_COARSE_LOCATION” 对应着 permission “android.Manifest.permission.ACCESS_COARSE_LOCATION”

AppOpsManager.sOpPerms 数组保存了 op 到 permission 的映射关系, 而 AppOpsManager.sPermToOp 这一map保存了 permission 到 op 的映射

android permission 通常都有一个对应的 op, 但是 op 则不一定有对应的 android permision

####3.3 op mode

op mode 代表 op 的授权状态, AppOpsManager 中定义了如下的几种状态

public static final int MODE_ALLOWED = 0;   //访问被允许
public static final int MODE_IGNORED = 1;   //访问被禁止
public static final int MODE_ERRORED = 2;   //访问被禁止, 并且会引发fatal error
public static final int MODE_DEFAULT = 3;   //默认状态, 各op的默认状态可能不相同

AppOpsManager.sOpDefaultMode 数组依次存放了各个 op 的 default mode 值

有些厂商可能会扩展 mode 值, 例如 MODE_ASK, 在 检查 mode 值时, 弹出对话框, 询问用户是否允许 op 操作

####3.4 package 的所有 op 的状态信息

AppOpsManager.PackageOps 这一内部类 存储的给定的 package 的所有的 op 的状态信息

public static class PackageOps implements Parcelable {
    ......
    private final String mPackageName;
    private final int mUid;
    private final List<OpEntry> mEntries;
    ......
}

其中 class OpEntry 存储了单个 Op 的操作的详细信息, 每一个package的每一个 op 的操作都会被 AppOpsService 记录, 可以被转换成一个 OpEntry 对象

####3.5 单个 op 的状态信息

AppOpsManager.OpEntry 这一内部类存储了某个 package 的某个 op 的操作信息

public static class OpEntry implements Parcelable {
    private final int mOp;
    private final int mMode;
    private final long mTime;
    private final long mRejectTime;
    private final int mDuration;
    private final int mProxyUid;
    private final String mProxyPackageName;
    ......
}

其中:

  • mOP : Op 的编号, 例如 AppOpsManager.OP_GPS
  • mMode : Op 的授权状态, 例如 AppOpsManager.MODE_ALLOWED
  • mTime : Op 操作执行的时间
  • mRejectTime : Op 操作被禁止的时间
  • mDuration : Op 操作持续的时间
  • mProxyUid :
  • mProxyPackageName :

####3.6 检查 op 的 mode 值

AppOpsManager.checkOp() 接口用于快速检测 给定的 package 是否能够执行给定的 op

public int checkOp(int op, int uid, String packageName)
public int checkOp(String op, int uid, String packageName)

其中:

  • op : 要检查的op code
  • uid : 要检查的 uid
  • packageName : 要检查的 uid 下的 package name (共享uid时, 多个package可以具有相同的uid)
  • 返回 MODE_ALLOWED 或者 MODE_IGNORED, 若 Op 的状态为 MODE_ERRORED, 则会抛出 SecurityException 异常

若不希望 抛出异常, 则可以使对应的无异常版本的 api, 当 Op 的状态为 MODE_ERRORED 时也会返回 MODE_ERRORED, 而不是抛出 SecurityException 异常

public int checkOpNoThrow(String op, int uid, String packageName)
public int checkOpNoThrow(int op, int uid, String packageName)

noteOpxxx() 也是用于检测给定的 uid 的 给定的 packageName 是否能够进行给定的 op 操作, 不同的是, noteOpxxx()会记录这一次检测的结果

public int noteOp(String op, int uid, String packageName)
public int noteOpNoThrow(String op, int uid, String packageName)

public int noteOp(String op, int uid, String packageName)
public int noteOpNoThrow(String op, int uid, String packageName)

当 app 执行一个长操作时(例如, 打开camera), 使用 noteOpxxx() 接口不太合适, 这是就需要使用 startOpxxx() finishOpxxx() 接口, 在开始op操作前, 调用 startOpxxx(), op操作结束后, 调用 finishOpxxx(), 

public int startOp(String op, int uid, String packageName)
public int startOpNoThrow(String op, int uid, String packageName)

public void finishOp(String op, int uid, String packageName)
public void finishOp(int op)

上述的这些接口的调用者必须申请 “android.Manifest.permission.UPDATE_APP_OPS_STATS” 权限

####3.7 设置 uid/package 的 op 的 mode 值

可通过 AppOpsManager 的 setUidMode() 和 setMode 接口分别设置指定的 uid 下所有的package 或者指定的 uid 下指定的pacakge 的 op 状态的接口, 调用这些接口需要 ”android.Manifest.permission.UPDATE_APP_OPS_STATS” 权限

public void setUidMode(int code, int uid, int mode);
public void setMode(int code, int uid, String packageName, int mode);

2者的差异如下:

  • AppOpsService 会为每一个 uid 的所有 op 存储mode, 相当于 ”总开关”
  • AppOpsService 会为同一个 uid 下的 不同 package, 单独存储各自的 op 的状态
  • AppOpsService 在检查 package 的 op 状态时, 先检查 对应的 uid 的 op 的状态(即该op的状态的总开关), 若总开关为 MODE_ALLOWED, 才继续检查 package 单独的 op 状态, 否则 直接返回总开关的状态

####3.8 获取 package 的 op 的状态

获取所有 package 的 指定的 op 的状态, 调用这一接口需要 ”android.Manifest.permission.GET_APP_OPS_STATS” 权限

public List<AppOpsManager.PackageOps> getPackagesForOps(int[] ops)

获取指定的 uid/package 的指定的 op 的状态, 调用这一接口需要 ”android.Manifest.permission.GET_APP_OPS_STATS” 权限

public List<AppOpsManager.PackageOps> getOpsForPackage(int uid, String packageName, int[] ops)

在实现 appOps 的配置管理时, 通常会使用到这2个接口

####3.9 监听 package 的 op 状态变化

通过 AppOpsManager 的 startWatchingMode() 和 stopWatchingMode() 接口可以注册对 package 的 op 状态变化的监听 

public void startWatchingMode(String op, String packageName,final OnOpChangedListener callback);
public void startWatchingMode(int op, String packageName, final OnOpChangedListener callback);

public void stopWatchingMode(OnOpChangedListener callback)

callback 的原型为:

public interface OnOpChangedListener {
    public void onOpChanged(String op, String packageName);
}

public static class OnOpChangedInternalListener implements OnOpChangedListener {
    public void onOpChanged(String op, String packageName) { }
    public void onOpChanged(int op, String packageName) { }
}

通常是实例化一个 OnOpChangedInternalListener 对象, 然后在注册 op 监听时传递进去

###4. AppOpsService

####4.1 class UidState

AppOpsService 的内部类 UidState 用于存储某一个 uid 对应的 AppOps 信息

private static final class UidState {
    public final int uid;                   //uid
    public ArrayMap<String, Ops> pkgOps;    //该 uid 下的所有 pcakage 的 name 到 Ops 的映射
    public SparseIntArray opModes;          //该 uid 的所有的 Op 和 其 mode 的映射, 这里的mode相当与总开关, 
                                            //一个 uid 下面可能有多个 package, 各个 package 的 mode 还可以分别配置 

    public UidState(int uid) {
        this.uid = uid;
    }

    public void clear() {
        pkgOps = null;
        opModes = null;
    }

    public boolean isDefault() {
        return (pkgOps == null || pkgOps.isEmpty())
                && (opModes == null || opModes.size() <= 0);
    }
}

AppOps 中的稀疏数组 final SparseArray<UidState> mUidStates 保存了所有 uid 到 UidState 对象的映射

####4.2 class Ops

AppOpsService 的内部类 Ops 存储了一个 package 的简单信息: name, 对应的 UidState, 是否是 privileged package

public final static class Ops extends SparseArray { public final String packageName; public final UidState uidState; public final boolean isPrivileged;

    public Ops(String _packageName, UidState _uidState, boolean _isPrivileged) {
        packageName = _packageName;
        uidState = _uidState;
        isPrivileged = _isPrivileged;
    }
}

**Ops 继承了 SparseArray, 存储了 op code 到 class Op 的 映射**

####4.3 class Op

AppOpsService 的内部类 class Op 存储了一个 package 的 op 的状态信息

public final static class Op {
    public final int uid;
    public final String packageName;
    public int proxyUid = -1;
    public String proxyPackageName;
    public final int op;
    public int mode;
    public int duration;
    public long time;
    public long rejectTime;
    public int nesting;

    public Op(int _uid, String _packageName, int _op) {
        uid = _uid;
        packageName = _packageName;
        op = _op;
        mode = AppOpsManager.opToDefaultMode(op);
    }
}

####4.4 restrict 模式

AppOpsService 中的 所有 op 可以有2种工作模式 :

  • normal : 按照正常的流程检查 op 的 mode
  • strict : 对于要检查的的 uid/package/op, 直接返回 AppOpsManager.MODE_IGNORED (当然,可以设置 某个op, 在restrict模式下, 不限定 privileges app), 相当于系统不区分user和uid,禁用了某个op

AppOpService.isRestricted() 方法用于判断某个 package 的 某个 Op 是否开启了 strict 模式, 其判断过程如下:

private boolean isOpRestricted(int uid, int code, String packageName) {
    int userHandle = UserHandle.getUserId(uid);
    
    // 从 mOpRestrictions 这一 SparseArray 中取出 对应的 user 的所有的 op 的 restrict 状态
    boolean[] opRestrictions = mOpRestrictions.get(userHandle);
    
    // 若该 uid 的 指定的 op enable 了 restrict
    if ((opRestrictions != null) && opRestrictions[code]) {
    
        // 即使该user的某个op enable 了 restrict 模式, 但是系统中可以针对所有的op (不区分user和uid),
        // 设置该op的 restrict 模式是否约束 system  app (即 applicationInfo 中有 PRIVATE_FLAG_PRIVILEGED 的标记)
        // AppOpsManager.sOpAllowSystemRestrictionBypass 数组中按照 op 的索引顺序 存放了该 op 是否可以被 system bypass
        // restrict 模式, AppOpsManager.opAllowSystemBypassRestriction() 可以获取 op 对应的值
        if (AppOpsManager.opAllowSystemBypassRestriction(code)) {
        
            synchronized (this) {
                Ops ops = getOpsLocked(uid, packageName, true);
                
                // 若该 op 可以被 syatem bypass, 且 给定的 package 为 privilege,
                // 则 认为给定的 uid 的 给定的 op 没有 enable restrict 
                if ((ops != null) && ops.isPrivileged) {
                    return false;
                }
            }
        }
        return true;
    }
    return false;
}

关键在于 AppOpsService.mOpRestrictions, 其中存储了所有 user 的所有的 op 的 restrict 的 enable 状态

private final SparseArray<boolean[]> mOpRestrictions = new SparseArray<boolean[]>();

那么, AppOpsService.mOpRestrictions 中存储的值是如何确定的呢: 只有 AppOpsService.setUserRestrictions() 方法可以设置其中的值

// UserHandler 参数指定了 user
// restrictions 参数中的 key-valaue 指定了 op 和 restrict 是否 enable

public void setUserRestrictions(Bundle restrictions, int userHandle);
    checkSystemUid("setUserRestrictions");
    boolean[] opRestrictions = mOpRestrictions.get(userHandle);
    if (opRestrictions == null) {
        opRestrictions = new boolean[AppOpsManager._NUM_OP];
        mOpRestrictions.put(userHandle, opRestrictions);
    }
    for (int i = 0; i < opRestrictions.length; ++i) {
    
        // 通过 op 的索引值, 获取其对应的 key 名, 然后从 形参 restrictions 中获取对应的 value 值
        String restriction = AppOpsManager.opToRestriction(i);
        if (restriction != null) {
            opRestrictions[i] = restrictions.getBoolean(restriction, false);
        } else {
            opRestrictions[i] = false;
        }
    }
}

需要注意的是, AppOpsService.setUserRestrictions() 中的 形参 restrictions 中的 key 名并非是 op 的名称, 而是在 UserManager 中定义, 所以需要 AppOpsManager.opToRestriction() 来转换 op 的索引值到对应的 key 名, 转换关系由 AppOpsManager.sOpRestrictions 这一数组定义

private static String[] sOpRestrictions = new String[] {
        UserManager.DISALLOW_SHARE_LOCATION, //COARSE_LOCATION
        UserManager.DISALLOW_SHARE_LOCATION, //FINE_LOCATION
        UserManager.DISALLOW_SHARE_LOCATION, //GPS
        null, //VIBRATE
        null, //READ_CONTACTS
        null, //WRITE_CONTACTS
        UserManager.DISALLOW_OUTGOING_CALLS, //READ_CALL_LOG
        ......
}

可见 在 AppOpsService.setUserRestrictions() 的 形参 restrictions 中, op OP_//COARSE_LOCATION 的 key 名为 UserManager.DISALLOW_SHARE_LOCATION, 即 “no_share_location”, 而对于没有定义 key 名的op, 是不支持 enable restrict 模式的

  • 首先从 AppOpsService.mOpRestrictions 这一稀疏数组中取出对应 uid 的 opRestrictions 数组, 然后获取指定的 Op 的 restrict 的状态
    • AppOpsService.setUserRestrictions() 方法用于设置 AppOpsService.mOpRestrictions 这一稀疏数组的值
    • AppOpsManager.opToRestriction() 方法则用于获取 指定的 Op 的 键名 (用于在 AppOpsService.setUserRestrictions() 的bundle参数中获取值)
    • AppOpsManager.opToRestriction() 的 Op 到 键名 的映射关系由 AppOpsManager.sOpRestrictions 数组定义
  • 若经过上一步的判断, 给定的 package 的给定的 Op 的 restrict 状态为 disable 的状态, 则直接返回 false, 否则, 继续下面的检查
    • 调用 AppOpsManager.opAllowSystemBypassRestriction() 检查给定的 Op 是否可以被 system priv app bypass (即 即使 enable 了 restrict 模式, system priv app 也可以 不必遵守),各 Op 是否可以 bypass restrict 模式, 要参照 AppOpsManager.sOpAllowSystemRestrictionBypass 数组的定义, 若不可以 bypass, 则直接返回 true
    • 若上一步的检查结果为 可以 bypass, 则检查 给定的 app 是否是 privileged app, 若是, 则返回 false, 否则, 返回true

若 op 的 restrict 状态为 true, 则 直接返回 AppOpsManager.MODE_IGNORE, 否则 返回实际的值

####4.5 AppOpsService 对 AppOpsManager 的接口的处理

AppOpsManager.checkOpxxx()  ->
    AppOpsService.checkOperation()
    
AppOpsManager.noteOpxxx() ->
    AppOpsService.noteOperation()

AppOpsManager.startOpxxx() ->
    AppOpsService.startOperation()


AppOpsManager.finishOp() ->
    AppOpsService.finishOperation()

AppOpsManager.setMode() ->
    AppOpsService.setMode()

AppOpsManager.setUidMode() ->
    AppOpsService.setUidMode()

AppOpsManager.getPackagesForOps() ->
    AppOpsService.getPackagesForOps()

AppOpsManager.getOpsForPackage() ->
    AppOpsService.getOpsForPackage()

AppOpsManager.startWatchingMode() ->
    AppOpsService.startWatchingMode()

AppOpsManager.stopWatchingMode() ->
    AppOpsService.stopWatchingMode()

####4.6 noteOperation() 的处理流程

noteOperation(int code, int uid, String packageName)    ->
    noteOperationUncheckedint code, int uid, String packageName, int proxyUid, String proxyPackageName)
    

private int noteOperationUnchecked(int code, int uid, String packageName, int proxyUid, String proxyPackageName) {
    synchronized (this) {
    
        // 获取 uid 和 package 对应的 ops 对象, 若不存在则实例化一个
        Ops ops = getOpsLocked(uid, packageName, true);
        ......
        
        // 获取 op  对应的 ops 对象, 若不存在则实例化一个
        Op op = getOpLocked(ops, code, true);
        
        // 若 uid 对应的 op enable 了 restrict mode, 则直接返回 AppOpsManager.MODE_IGNORED, 且不记录该次 op 操作
        if (isOpRestricted(uid, code, packageName)) {
            return AppOpsManager.MODE_IGNORED;
        }
        ......
        
        final int switchCode = AppOpsManager.opToSwitch(code);
        
        //若 UidState.opMode 存在 (即设置了该uid 的 该 op 的总开关), 先检查其中的mode值
        UidState uidState = ops.uidState;
        
        if (uidState.opModes != null) {
            final int uidMode = uidState.opModes.get(switchCode);
            if (uidMode != AppOpsManager.MODE_ALLOWED) {
                if (DEBUG) Log.d(TAG, "noteOperation: reject #" + op.mode + " for code "
                        + switchCode + " (" + code + ") uid " + uid + " package "
                        + packageName);
                // 若 op 操作不被允许, 则记录 op 的被拒绝的时间
                op.rejectTime = System.currentTimeMillis();
                
                return uidMode;
            }
        }
        
        // 若 op 在AppOpsService内部实际上被映射到另一个 op 来处理, 则获取实际的 Op 的 mode
        final Op switchOp = switchCode != code ? getOpLocked(ops, switchCode, true) : op;
        
        if (switchOp.mode != AppOpsManager.MODE_ALLOWED) {
            if (DEBUG) Log.d(TAG, "noteOperation: reject #" + op.mode + " for code "
                    + switchCode + " (" + code + ") uid " + uid + " package " + packageName);
            op.rejectTime = System.currentTimeMillis();
            return switchOp.mode;
        }
        if (DEBUG) Log.d(TAG, "noteOperation: allowing code " + code + " uid " + uid
                + " package " + packageName);
                
        //虽然多个 op 在 AppOpsService 的内部可能是被映射到同一个 op 来处理的, 单还是使用单独的 Op 对象分别别存储操作信息
        op.time = System.currentTimeMillis();
        op.rejectTime = 0;
        //固定为0
        op.proxyUid = proxyUid; 
        //固定为null
        op.proxyPackageName = proxyPackageName;
        return AppOpsManager.MODE_ALLOWED;
    }
}

####4.7 startOperation() / finishOperation()

startOperation() / finishOperation() 用于处理一些持续时间较长的 Op 操作, 例如 定位, 使用摄像头等

AppOpsService 中将每一个调用 startOperation() / finishOperation() 的 Contex 视为 一个client, 使用 class ClientState 来表示一个 client 的信息

public final class ClientState extends Binder implements DeathRecipient {
    // client 端的 Binder
    final IBinder mAppToken;
    // client 端的 pid;
    final int mPid;
    // client 端正在 start 了的 op 操作
    final ArrayList<Op> mStartedOps;

    @Override
    public String toString() {
        ......
    }

    //处理 Binder died
    @Override
    public void binderDied() {
        ......
    }
}

在来看 startOperation() 的实现

public int startOperation(IBinder token, int code, int uid, String packageName) {
    ......
    
    // AppOpsManager 在调用 AppOpsService.startOperation() 之前, 会先调用 AppOpsService.getToken() 
    // 实例化一个 ClientState 对象, 再作为形参传递给 AppOpsService.startOperation() 
    ClientState client = (ClientState)token;
    synchronized (this) {
    
        // 这一部分和 noteOperationUnchecked() 的前半部分处理相同, 依次是 : 获取 uid / packages 对应的 
        // Ops 对象; 检查 op 对于该 package 是否处于 restriction 模式; 检查 UidState.opMode 中该 op 
        // 的 mode 或者 Op.mode 中该 op 的 mode, 这些过程不再赘述
        
        ......
              
        // 若是 client 第一次调用 startOp(), 或者 之前的每一次 startOp() 调用都调用了 finishOp()
        // 则记录 op 对象中的相关信息  
        if (op.nesting == 0) {
            op.time = System.currentTimeMillis();
            op.rejectTime = 0;
            op.duration = -1;
        }
        // 增加 op 对象的 startOp() 的调用计数
        op.nesting++;
        if (client.mStartedOps != null) {
            // 将 start 的 Op 对象记录到 对应的 ClientState 对象中
            // 可以重复添加
            client.mStartedOps.add(op);
        }
        return AppOpsManager.MODE_ALLOWED;
    }
}

再来看  

public void finishOperation(IBinder token, int code, int uid, String packageName) {
    ......
    
    ClientState client = (ClientState)token;
    synchronized (this) {
        Op op = getOpLocked(code, uid, packageName, true);
        // 若找不到对应的 Op 对象, 说明没有对应的 startOp() 操作
        if (op == null) {
            return;
        }
        if (client.mStartedOps != null) {
            // 移除 ClientState 对象中记录的 op 操作
            if (!client.mStartedOps.remove(op)) {
                throw new IllegalStateException("Operation not started: uid" + op.uid
                        + " pkg=" + op.packageName + " op=" + op.op);
            }
        }
        finishOperationLocked(op);
    }
}

void finishOperationLocked(Op op) {
    // 若只有一个或者没有 被 finish 的 startOp() 调用
    // 则 更新 Op 对象中的信息
    if (op.nesting <= 1) {
        if (op.nesting == 1) {
            op.duration = (int)(System.currentTimeMillis() - op.time);
            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;
    } else {
        op.nesting--;
    }
}

####4.8 startWatchingMode() 的处理流程

AppOpsManager 提供的 startWatchingMode() 有3个参数:

public void startWatchingMode(int op, String packageName, final OnOpChangedListener callback);

其中, callback 参数是必须指定的,而 op 和 packageName 参数则可以不指定(分别为 OP_NONE 和 null), 按照这2者的不同组合, 可以分为4种情况:

  • 指定 op 参数, 指定 packageName 参数 : 只监听指定的 pakcge 的 指定的 op 的 mode 变化
  • 指定 op 参数, 不指定 packageName 参数 : 监听所有的 pakcge 的 指定的 op 的 mode 变化
  • 不指定 op 参数, 指定 packageName 参数 : 只监听指定的 pakcge 的所有的 op 的 mode 变化
  • 不指定 op 参数, 不指定 packageName 参数 : 监听所有的 pakcge 的所有的 op 的 mode 变化

针对这几种情况, AppOpsService 中使用了如下的成员来记录注册的监听 :

// 存储 op 到 其所有注册的 callback 的映射
final SparseArray<ArrayList<Callback>> mOpModeWatchers = new SparseArray<ArrayList<Callback>>();

// 存储 package name 到所有注册的对其的监听的 callback 的映射
final ArrayMap<String, ArrayList<Callback>> mPackageModeWatchers = new ArrayMap<String, ArrayList<Callback>>();

// 存储 calleback 的 binder 到其对应的 Callback的映射, 仅仅起来一个记录的作用
final ArrayMap<IBinder, Callback> mModeWatchers = new ArrayMap<IBinder, Callback>();

AppOpsService 中使用 Class Callback 来存储一个 callback 的信息, 其中主要是维护注册 callback 的一端的 Binder 信息, 因为需要通过 Binder 回调 client 端的 callback

public final class Callback implements DeathRecipient {
    final IAppOpsCallback mCallback;

    public Callback(IAppOpsCallback callback) {
        mCallback = callback;
        try {
            mCallback.asBinder().linkToDeath(this, 0);
        } catch (RemoteException e) {
        }
    }

    public void unlinkToDeath() {
        mCallback.asBinder().unlinkToDeath(this, 0);
    }

    @Override
    public void binderDied() {
        stopWatchingMode(mCallback);
    }
}

再来看 startWatchingMode 的处理过程

public void startWatchingMode(int op, String packageName, IAppOpsCallback callback) {
    synchronized (this) {
    
        // 转换 op 为 内部实际处理的 op
        op = (op != AppOpsManager.OP_NONE) ? AppOpsManager.opToSwitch(op) : op;
        
        // 即使是在同一个 Context 中, 调用 startWatchingMode()时 指定的不同 callback,
        // 在 AppOpsService 中 callback.asBinder() 的值都会不同, 当然,在同一个 Context 
        // 中, 多次调用 调用 startWatchingMode() 注册相同的 callback, 在 在 AppOpsService 
        // 中 callback.asBinder() 的值会相同, AppOpsManager.mModeWatchers 中会记录已经
        // 注册的 callback, 不会为其生成新的 binder
        // AppOpsService 中, 相同的 callback 会共享同一个 CallBack 对象
        Callback cb = mModeWatchers.get(callback.asBinder());
        if (cb == null) {
            cb = new Callback(callback);
            
            // 将 callback 记录在  mModeWatchers 中
            mModeWatchers.put(callback.asBinder(), cb);
        }
        
        // 若指定了 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);
        }
        
        // 若指定了 package name, 则将 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);
        }
    }
}

####4.9 stopWatchingMode()

public void stopWatchingMode(IAppOpsCallback callback) {
    synchronized (this) {
    
        // 从 mModeWatchers 中移除 callback 对应的 Callback 对象相关的信息
        Callback cb = mModeWatchers.remove(callback.asBinder());
        if (cb != null) {
            cb.unlinkToDeath();
            
            // 遍历 mOpModeWatchers, 移除 callback 对应的 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);
                }
            }
            
            // 遍历 mPackageModeWatchers, 移除 callback 对应的 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);
                }
            }
        }
    }
}

对比 startWatchingMode() 和 stopWatchingMode() 参数, 在同一个 Context中, 即使多次调用startWatchingMode()使用不同的 op 和 packageName 参数注册相同的callback,只需要调用一次 stopWatchingMode() 即可以取消这些 startWatchingMode() 调用

public void startWatchingMode(String op, String packageName, final OnOpChangedListener callback);
public void stopWatchingMode(OnOpChangedListener callback);

####4.10 setUidMode() / setMode()

public void setMode(int code, int uid, String packageName, int mode);
public void setUidMode(int code, int uid, int mode);

setMode() 修改的是 UidState.pkgOps 中 的 Op.mode 中存储的 mode 值, 而 setUidMode() 修改的是 UidState.opMode 中存储的状态, 它们修改 op 值的的处理过程都比较简单, 不再详细解释, 其后面的处理监听 op 的回调 的部分会单独解释

需要注意的是, 无论是 setMode() 还是 setUidMode(), 在设置 op 的 mode 时, 都有一个原则 : 若要设置的 mode 值 和 defalut 值相同, 则删除 UidState.pkgOps 中 的 op 信息 或者 删除 UidState.opMode 中存储的 op 信息, 尽可能减少需要存储的信息, 例如在 setMode() 中

public void setMode(int code, int uid, String packageName, int mode) {
    ......
                if (mode == AppOpsManager.opToDefaultMode(op.op)) {
                    // If going into the default mode, prune this op
                    // if there is nothing else interesting in it.
                    pruneOp(op, uid, packageName);
                }
    ......
}

private void pruneOp(Op op, int uid, String packageName) {
    if (op.time == 0 && op.rejectTime == 0) {
        Ops ops = getOpsLocked(uid, packageName, false);
        if (ops != null) {
            // 删除 Ops 中对应的 Op 对象
            ops.remove(op.op);
            if (ops.size() <= 0) {
                UidState uidState = ops.uidState;
                ArrayMap<String, Ops> pkgOps = uidState.pkgOps;
                if (pkgOps != null) {
                    //删除 UidState.pkgOps 中对应的 Ops 对象
                    pkgOps.remove(ops.packageName);
                    if (pkgOps.isEmpty()) {
                        uidState.pkgOps = null;
                    }
                    if (uidState.isDefault()) {
                        // 删除 mUidStates 中对应的 UidState 对象
                        mUidStates.remove(uid);
                    }
                }
            }
        }
    }
}

####4.11 回调 监听 op 状态变化的 callback

已 setUidMode() 为例

public void setUidMode(int code, int uid, int mode) {
    ......

    // 获取修改了 op code 的 uid 下的所有的 package
    String[] uidPackageNames = getPackagesForUid(uid);
    ArrayMap<Callback, ArraySet<String>> callbackSpecs = null;

    // 遍历出所有在注册时指定的 op code 和 变化的 op code 相同 的 callback
    ArrayList<Callback> callbacks = mOpModeWatchers.get(code);
    if (callbacks != null) {
        final int callbackCount = callbacks.size();
        for (int i = 0; i < callbackCount; i++) {
            Callback callback = callbacks.get(i);
            ArraySet<String> changedPackages = new ArraySet<>();
            Collections.addAll(changedPackages, uidPackageNames);
            callbackSpecs = new ArrayMap<>();
            // 将 callback 和该 uid 下的所有 packagename 添加到 callbackSpecs 中
            callbackSpecs.put(callback, changedPackages);
        }
    }

    //遍历该 uid 下的所有的 package
    for (String uidPackageName : uidPackageNames) {
        // 获取监听了该 package 的 callback 
        callbacks = mPackageModeWatchers.get(uidPackageName);
        if (callbacks != null) {
            if (callbackSpecs == null) {
                callbackSpecs = new ArrayMap<>();
            }
            final int callbackCount = callbacks.size();
            // 遍历所有的 监听了 该 package 的 callback 
            for (int i = 0; i < callbackCount; i++) {
                Callback callback = callbacks.get(i);
                ArraySet<String> changedPackages = callbackSpecs.get(callback);
                if (changedPackages == null) {
                    changedPackages = new ArraySet<>();
                    // 将 callback 和该 callback 监听的 packagename 添加到 changedPackages
                    callbackSpecs.put(callback, changedPackages);
                }
                // 将 package 添加到 callback 监听的 packagename 列表中 (若已经存在则不会添加)
                changedPackages.add(uidPackageName);
            }
        }
    }

    // 若没有 callback 需要处理, 则直接返回
    if (callbackSpecs == null) {
        return;
    }

    // There are components watching for mode changes such as window manager
    // and location manager which are in our process. The callbacks in these
    // components may require permissions our remote caller does not have.
    final long identity = Binder.clearCallingIdentity();
    try {
        for (int i = 0; i < callbackSpecs.size(); i++) {
            Callback callback = callbackSpecs.keyAt(i);
            // 获取某个 callback 监听的所有的 package name 列表
            ArraySet<String> reportedPackageNames = callbackSpecs.valueAt(i);
            try {
                if (reportedPackageNames == null) {
                    callback.mCallback.opChanged(code, null);
                } else {
                    final int reportedPackageCount = reportedPackageNames.size();
                    // 遍历某个 callback 监听的所有的 package name 列表, 并处理回调
                    for (int j = 0; j < reportedPackageCount; j++) {
                        String reportedPackageName = reportedPackageNames.valueAt(j);
                        callback.mCallback.opChanged(code, reportedPackageName);
                    }
                }
            } catch (RemoteException e) {
                Log.w(TAG, "Error dispatching op op change", e);
            }
        }
    } finally {
        Binder.restoreCallingIdentity(identity);
    }
}

从 回调的处理过程来看, public void startWatchingMode(int op, String packageName, IAppOpsCallback callback) 中的 op 参数和 packlgeName 参数, 在指定监听条件时, 并非是 ‘与’ 操作, 而是 ’或’ 操作, 即监听 指定的 op 或者指定的 package 的状态变化

####4.12 appOps 的持久存储

appOps 利用xml文件来持久存储 appOps 中的状态信息, 文件的路径是在 AppManagerService 的构造函数中实例化 AppOpsService 时传递给 AppOpsService 的, 目前使用的文件路径是 “/data/system/appops.xml”

appops.xml 中的 Tag 机及其 attribute 如下:

  • <app-ops> </app-ops> : appops.xml 的根元素
  • <pkg> </pkg> : 保存一个 package 的 op 状态, <pkg> Tag 必须有如下的 attribute
    • n : 保存 package name
  • <uid> </uid> : 保存某个 pakcage 在某个uid下的 op 的状态, 该 Tag 可以有如下的 attribute :
    • n : uid 的数值
    • p : package 是否是 privaled package, 值为 true 或者 false
  • <op/> : 某个op的详细信息, 该 Tag 可以有如下的 attribute :
    • n : 该 op 的 编号数值
    • m : 该 op 的 mode, 值为 AppOpsManager.MODE_xxx
    • t : 该 op 上一次操作的时间
    • r : 该 op 上一次被拒绝操作的时间
    • d : 该 op 上一次操作的持续时间
    • pu : 该 op 上一次操作的 proxy uid
    • pp : 该 op 上一次操作的 procy package name

同一个 package, 若有多个 uid (例如多用户), 则在 appops.xml 中会使用多个 <pkg> Tag 分开存储

appops.xml 文件示例:

</app-ops>        

......

<pkg n="com.app.tes">
<uid n="10113" p="false">
<op n="59" t="1474694534542" pu="0" />
<op n="60" t="1474694534542" pu="0" />
<op n="63" m="1" />
</uid>
</pkg>

......

<pkg n="com.app.test">
<uid n="99910113" p="false">
<op n="59" t="1474695029074" pu="0" />
<op n="60" t="1474695029074" pu="0" />
</uid>
</pkg>

......

</app-ops>

#####4.12.1 writeState()

void writeState() {
    synchronized (mFile) {
        //从 AppOpsService.mUidStates 中收集所有的信息, 转换为 AppOpsManager.PackageOps
        List<AppOpsManager.PackageOps> allOps = getPackagesForOps(null);

        FileOutputStream stream;
        try {
            stream = mFile.startWrite();
        } catch (IOException e) {
            Slog.w(TAG, "Failed to write state: " + e);
            return;
        }

        try {
            XmlSerializer out = new FastXmlSerializer();
            out.setOutput(stream, StandardCharsets.UTF_8.name());
            out.startDocument(null, true);
            //向 appops.xml 中写入 "<app-ops>"
            out.startTag(null, "app-ops");

            final int uidStateCount = mUidStates.size();
            for (int i = 0; i < uidStateCount; i++) {
                UidState uidState = mUidStates.valueAt(i);
                //UidState.opMode 中保存了 该 uid 的所有的 Op 和 对应的 mode
                if (uidState.opModes != null && uidState.opModes.size() > 0) {
                    //向 appops.xml 中写入 Tag "<uid>"
                    out.startTag(null, "uid");
                    //为上一步写入的 Tag "<uid>" 添加 uid 值属性 "n=xxxx" 
                    out.attribute(null, "n", Integer.toString(uidState.uid));
                    SparseIntArray uidOpModes = uidState.opModes;
                    final int opCount = uidOpModes.size();
                    //遍历 某个 uid 的所有的 op 到 mode 的映射
                    for (int j = 0; j < opCount; j++) {
                        final int op = uidOpModes.keyAt(j);
                        final int mode = uidOpModes.valueAt(j);
                        //在 Tag "<uid>" 后添加 Tag "<op>"
                        out.startTag(null, "op");
                        //在 Tag ”<op>“ 中添加 op 数值属性 "n=xxx"
                        out.attribute(null, "n", Integer.toString(op));
                        //在 Tag ”<op>“ 中添加 op mode属性 "m=xxx"
                        out.attribute(null, "m", Integer.toString(mode));
                        //在 Tag "<op>" 中添加结束符 "/"
                        out.endTag(null, "op");
                    }
                    //添加 "</uid>" , 为 Tag "<uid>" 结尾
                    out.endTag(null, "uid");
                }
            }

            if (allOps != null) {
                String lastPkg = null;
                for (int i=0; i<allOps.size(); i++) {
                    AppOpsManager.PackageOps pkg = allOps.get(i);
                    if (!pkg.getPackageName().equals(lastPkg)) {
                        if (lastPkg != null) {
                            // 若上已个pkg 的信息已经写入完成, 则向 xml 中添加 "</pkg>", 结束 Tag "<pkg>"
                            out.endTag(null, "pkg");
                        }
                        lastPkg = pkg.getPackageName();
                        //向 xml 中添加 Tag "<pkg>"
                        out.startTag(null, "pkg");
                        //向 Tag "<pkg>" 中添加pkg name属性 “n=xxx”
                        out.attribute(null, "n", lastPkg);
                    }
                    //向 xml 中添加 Tag "<uid>"
                    out.startTag(null, "uid");
                    //向 Tag "<uid>" 中添加 uid 值属性 "n=xxx"
                    out.attribute(null, "n", Integer.toString(pkg.getUid()));
                    synchronized (this) {
                        // 从 AppOpsService.mUidStates 中获取对应 uid 的 UidState对象, 再从 UidState 
                        // 对象中获取对应的 packge 的 Ops 对象
                        Ops ops = getOpsLocked(pkg.getUid(), pkg.getPackageName(), false);
                        // Should always be present as the list of PackageOps is generated
                        // from Ops.
                        // 向 Tag "<uid>" 中添加 privileged 属性 "p=xxxx" 默认值为 false
                        if (ops != null) {
                            out.attribute(null, "p", Boolean.toString(ops.isPrivileged));
                        } else {
                            out.attribute(null, "p", Boolean.toString(false));
                        }
                    }
                    //遍历某一 pkg 的 所有 op 的状态记录
                    List<AppOpsManager.OpEntry> ops = pkg.getOps();
                    for (int j=0; j<ops.size(); j++) {
                        AppOpsManager.OpEntry op = ops.get(j);
                        // 向添 xml 中加 Tag “<op>” 
                        out.startTag(null, "op");
                        // 向 Tag “<op>” 中添加 op 数值属性 "n=xxx"
                        out.attribute(null, "n", Integer.toString(op.getOp()));
                        // 若 op 的 mode 和默认值不同, 则向 Tag “<op>” 中添加 mode 属性 "m=xxx"
                        if (op.getMode() != AppOpsManager.opToDefaultMode(op.getOp())) {
                            out.attribute(null, "m", Integer.toString(op.getMode()));
                        }
                        // 若有记录上一次 op 操作的时间, 则向 Tag “<op>” 中添加 执行时间 属性 "t=xxx"
                        long time = op.getTime();
                        if (time != 0) {
                            out.attribute(null, "t", Long.toString(time));
                        }
                        // 若有记录上一次 op 操作被拒绝的时间, 则向 Tag “<op>” 中添加 拒绝时间 属性 "r=xxx"
                        time = op.getRejectTime();
                        if (time != 0) {
                            out.attribute(null, "r", Long.toString(time));
                        }
                        // 若有记录上一次 op 操作持续的时间, 则向 Tag “<op>” 中添加 持续时间 属性 "d=xxx"
                        int dur = op.getDuration();
                        if (dur != 0) {
                            out.attribute(null, "d", Integer.toString(dur));
                        }
                        // 若有记录上一次操作的 proxy uid, 则向 Tag “<op>” 中添加 proxy uid 属性 "pu=xxx"
                        int proxyUid = op.getProxyUid();
                        if (proxyUid != -1) {
                            out.attribute(null, "pu", Integer.toString(proxyUid));
                        }
                        // 若有记录上一次操作的 proxy package, 则向 Tag “<op>” 中添加 proxy package name 属性 "pp=xxx"
                        String proxyPackageName = op.getProxyPackageName();
                        if (proxyPackageName != null) {
                            out.attribute(null, "pp", proxyPackageName);
                        }
                        // 向 Tag "<op>" 中添加结束符 "/"
                        out.endTag(null, "op");
                    }
                    // 向 xml 中写入 “</uid>” 结束 Tag "<uid>"
                    out.endTag(null, "uid");
                }
                if (lastPkg != null) {
                    // 所有的package 的op状态都遍历完成后, 向 xml 中写入 “</uid>” 结束 Tag "<uid>"
                    out.endTag(null, "pkg");
                }
            }

            // 向 xml 中写入 "</app-ops>" 结束 Tag "</app-ops>"
            out.endTag(null, "app-ops");
            out.endDocument();
            // 将 xml 内容写入到 appops.xml 文件
            mFile.finishWrite(stream);
        } catch (IOException e) {
            Slog.w(TAG, "Failed to write state, restoring backup.", e);
            mFile.failWrite(stream);
        }
    }

AppOpsService 中会通过如下的方式调用 writeState() :

  • 如下场景会直接调用 writeState()
    1. 执行 shutdown() 处理关机事件
    2. 执行 dump() 处理 adb shell dumpsys appops write-settings 命令
  • 如下场景会调用 scheduleWriteLocked(), 延迟 1s (DBG模式) 或者 30m(普通模式)执行 writeState()
    1. 执行 setUidMode(), 设置指定的 uid 的指定的 op 的mode, 更新AppOpsService.mUidStates中的信息, 然后回写到 appops.xml
    2. 在 getOpLocked() 中实例化了 OP 对象并添加到 AppOpsService.mUidStates 中后, 回写到 appops.xml
  • 如下场景会调用 scheduleFastWriteLocked(), 延迟10s 执行 writeState()
    1. 执行 systemReady(), 从 appops.xml 读取数据到 AppOpsService.mUidStates 中后, 清理AppOpsService.mUidStates中不存在的package的信息, 然后回写到 appops.xml
    2. 执行 packageRemoved(), 处理 package 移除的事件, 清理AppOpsService.mUidStates中不存在的package的信息, 然后回写到 appops.xml
    3. 执行 uidRemoved(), 处理 uid 的移除事件, 清理AppOpsService.mUidStates中不存在的 uid 的信息, 然后回写到 appops.xml
    4. 执行 setMode(), 设置指定的 uid 的指定的 package 的 指定的 op 的 mode, 更新AppOpsService.mUidStates中的信息, 然后回写到 appops.xml
    5. 执行 resetAllModes(), 重置指定的 user 的 指定的 package 的 op 状态, 更新AppOpsService.mUidStates中的信息, 然后回写到 appops.xml

#####4.12.2 readState()

AppOpsService.readState() 的处理过程和 AppOpsService.writeState() 正好相反, 从 appos.xml 中读取信息, 生成对应的 AppOpsService.Ops对象, 然后将其和对应的 packageName 添加到 AppOpsService.mUiStates 中, 对应的 UiState 对象的 pkgOps 中

readState() 被调用的2个场景如下:

AppOpsService(new File(systemDir, "appops.xml"), mHandler)  ->
    readState()
    
AppOpsService.dump()    ->
    //处理 read-settings 参数
    readState()

即在初始化 AppOpsService 时, 会读取 appops.xml 中的信息, 或者可以使用 adb shell dumpsys appops read-settings 中 强制 AppOpsService 读取 appops.xml 文件

####4.13 op 的初始状态

在系统开机时, appops.xml 中的内容为空, 那么, AppOpsService 中, 每一个 uid 的 package 的 每一个 op 的初始状态是如何决定的呢

需要注意的是2个地方

  • AppOpsService.UidState.opMode : 其类型为 SparseIntArray, 保存了某一个 uid 的 op 和其对应的 mode 的映射
  • AppOpsService.Op.mode : 类型为 int, AppOpsService.UidState.pkgOps 的类型为 AppOpsService.Ops, 继承了 SparseArray, 保存了 op 的编号 到 AppOpsService.Op 的映射, AppOpsService.Op 中保存了 op 的 mode

需要注意的是, 在检查op值时, 上述的2者中存储的 op 的 mode 是优先级的

  • AppOpsService.UidState.opMode 中存储的是某个 uid 的 op 的 mode 值
  • AppOpsService.Op.mode 中存储的是某个 uid 的 某个 package 的 op 的 mode 值 (共享uid时, 一个uid 可能有多个package)
  • 在检查 op 的 mode 值时, 若存在 AppOpsService.UidState.opMode, 则先检查 AppOpsService.UidState.opMode, 若其值为 AppOpsManager.MODE_ALLOW, 则 才会去检查 AppOpsService.Op.mode (因此使用 AppOpsService.UidState.opMode 可以方便地关闭某个 uid 的所有的 package 的某个 op 的操作权限)

先来看 AppOpsService.UidState.opMode 的初始状态, AppOpsService.UidState 的构造函数中, 并不会初始化 AppOpsService.UidState.opMode 的值,唯一设置 AppOpsService.UidState.opMode 值的地方, 是 AppOpsService.setUidMode() 方法

    public void setUidMode(int code, int uid, int mode) {
        ......
        final int defaultMode = AppOpsManager.opToDefaultMode(code);

        UidState uidState = getUidStateLocked(uid, false);
        if (uidState == null) {
            if (mode == defaultMode) {
                return;
            }
            uidState = new UidState(uid);
            uidState.opModes = new SparseIntArray();
            uidState.opModes.put(code, mode);
            mUidStates.put(uid, uidState);
            scheduleWriteLocked();
        } else if (uidState.opModes == null) {
            if (mode != defaultMode) {
                uidState.opModes = new SparseIntArray();
                uidState.opModes.put(code, mode);
                scheduleWriteLocked();
            }
        } else {
        ......
    }

只有当调用 setUidMode() 设置 指定的 uid 的指定的 op 的 mode 时, 且 mode 值和该 op 的 default mode 不相同时, 才会去设置 AppOpsService.UidState.opMode 的值, 目前 android frameworks 中并未调用 setUidMode(), 只有一些权限管理的package (例如 PackageInstaller) 会调用 setUidMode()

再继续来看 AppOpsService.Op.mode 的值, AppOpsService.getOpLocked() 方法用于从 AppOpsService.UidState.pkgOps 中获取指定的 uid 的指定的 package 对应的 的指定的 AppOpsService.Ops 对象中获取 AppOpsService.Op 对象

//edit 参数用于指定, 若对应的 Op 对象不存在, 是否实要例化新的 Op 对象了来返回
private Op getOpLocked(int code, int uid, String packageName, boolean edit)

在来看 AppOpsService 提供的 op mode 查询和 设置接口, 其中都直接或者间接调用了 getOpLocked():

  • AppOpsService.setMode() : 直接调用 getOpLocked(), edit 参数为true
  • AppOpsService.checkOperation() : 直接调用 getOpLocked(), edit 参数为false
  • AppOpsService.noteOperation : 间接调用 getOpLocked(), edit 参数为true
  • AppOpsService.startOperation() : 直接调用 getOpLocked(), edit 参数为true
  • AppOpsService.finishOpeartion() : 直接调用 getOpLocked(), edit 参数为true

当 对应的 Op 对象不存在时 :

  • 当edit 参数为true时, 实例化 Op 对象, 在其构造函数中, Op.mode 的值初始化为 该 op 的 default mode, 通过 AppOpsManager.opToDefaultMode() 获得
  • 当edit 参数为false时 (只有AppOpsService.checkOperation()方法会遇到), 则获取不到 Op 对象, 此时会返回该 op 的 default mode 值, 同样通过 AppOpsManager.opToDefaultMode() 获得

因此, op 的初始 mode 由 AppOpsManager.opToDefaultMode() 决定

####4.14 处理 package / uid remove

当有 package / uid 的 remove event 发生时, ActivityManagerService 会分别回调 AppOpsService.packageRemoved() 和 AppOpsService.uidRemoved() 进行处理

###5. AppOps debug tool

####5.1 dumpsys

adb shell dumpsys appops 命令可以 dump 出 AppOpsService 的相关信息

$ adb shell dumpsys appops -h
AppOps service (appops) dump options:
  [-h] [CMD]
  -h: print this help text.
Commands:
  write-settings
    Immediately write pending changes to storage.
  read-settings
    Read the last written settings, replacing current state in RAM.

执行 dumpsys appopos 时, 若不指定参数, 则 dump 出 AppOpsService 中的状态信息, “write-settings” 用于强制 AppOpsService 将其中存贮的 package/op/mode 信息回写到 appops.xml 中, 而 ”read-settings” 用于强制 AppOpsService 将其从 appops.xml 中读取 package/op/mode 信息

直接执行 dumpsys appops 可以 dump 出如下的信息:

  • Op mode watcher : 即 AppOpsServiuce.mOpModeWatchers 中存储的 callback 信息
  • package mode watcher : 即 AppOpsService.mPackageModeWatchers 中存储的 callback 信息
  • all mode watcher : 即 AppOpsService.mModeWatchers 中存储的 callback 信息
  • Clients :
  • uid/packages/ops : 所有 uid/packages/appops 信息

例如 :

$ adb shell dumpsys appops
Current AppOps Service state:
  Op mode watchers:
    Op COARSE_LOCATION:
      #0: com.android.server.AppOpsService$Callback@427326a
    Op SYSTEM_ALERT_WINDOW:
      #0: com.android.server.AppOpsService$Callback@e08f35b
    Op PLAY_AUDIO:
      #0: com.android.server.AppOpsService$Callback@75787f8
      #1: com.android.server.AppOpsService$Callback@d82a4d1
      #2: com.android.server.AppOpsService$Callback@97e0b36
    Op TOAST_WINDOW:
      #0: com.android.server.AppOpsService$Callback@e08f35b
  Package mode watchers:
    Pkg com.android.systemui:
      #0: com.android.server.AppOpsService$Callback@75787f8
      #1: com.android.server.AppOpsService$Callback@d82a4d1
  All mode watchers:
    android.app.AppOpsManager$1@22e47d9 -> com.android.server.AppOpsService$Callback@427326a
    android.app.AppOpsManager$1@4a562d1 -> com.android.server.AppOpsService$Callback@e08f35b
    android.os.BinderProxy@a66b6e9 -> com.android.server.AppOpsService$Callback@d82a4d1
    android.os.BinderProxy@c6c5e67 -> com.android.server.AppOpsService$Callback@75787f8
    android.media.SoundPool$1@fda4916 -> com.android.server.AppOpsService$Callback@97e0b36
  Clients:
    android.os.Binder@f4cf53:
      ClientState{mAppToken=android.os.Binder@f4cf53, local}

  Uid 1000:
    .....
    Package android:
      COARSE_LOCATION: mode=0
      VIBRATE: mode=0; time=+1h50m53s949ms ago; duration=+34ms
      POST_NOTIFICATION: mode=0; time=+1h51m12s615ms ago
      WAKE_LOCK: mode=0; time=+38m46s443ms ago; duration=+146ms
      MONITOR_LOCATION: mode=0; time=+1h54m37s269ms ago (running)
      TURN_ON_SCREEN: mode=0; time=+1h51m46s538ms ago
      GET_ACCOUNTS: mode=0; time=+1h54m22s180ms ago
    ......
    Package com.qualcomm.location:
      COARSE_LOCATION: mode=0
      FINE_LOCATION: mode=0; time=+1h54m15s452ms ago
      WAKE_LOCK: mode=0; time=+1h54m15s380ms ago; duration=+46ms
    ......
  Uid 1001:
    Package com.android.providers.telephony:
        WRITE_SMS: mode=0
    Package com.android.mms.service:
      WRITE_SMS: mode=0
    Package org.codeaurora.ims:
      WAKE_LOCK: mode=0; time=+1h54m30s415ms ago; duration=+6ms
      READ_EXTERNAL_STORAGE: mode=0; time=+1h54m6s494ms ago
      WRITE_EXTERNAL_STORAGE: mode=0; time=+1h54m6s494ms ago
    ......

####5.2 appops

android shell 中的 appops 命令可以 查询/设置/重置 指定的 uid/package/op 的状态

# appops                                                     
usage: appops set [--user <USER_ID>] <PACKAGE> <OP> <MODE>
       appops get [--user <USER_ID>] <PACKAGE> [<OP>]
       appops reset [--user <USER_ID>] [<PACKAGE>]
  <PACKAGE> an Android package name.
  <OP>      an AppOps operation.
  <MODE>    one of allow, ignore, deny, or default
  <USER_ID> the user id under which the package is installed. If --user is not
            specified, the current user is assumed. 

例如 :

# appops get com.android.settings                            
COARSE_LOCATION: allow
WIFI_SCAN: allow; time=+6h10m53s269ms ago
USE_FINGERPRINT: allow; time=+6h10m53s515ms ago
READ_EXTERNAL_STORAGE: allow; time=+6h10m53s775ms ago
WRITE_EXTERNAL_STORAGE: allow; time=+6h10m53s775ms ago