Android Framework权限篇一之RuntimePermission全体流程

Android Framework权限篇二之RuntimePermission数据结构解析

Android Framework权限篇三之后台定位权限源码剖析

Android Framework权限篇四之AppOps机制

Android Framework权限篇五之完成敏感权限行为提醒

概述

在前面Android Framework权限篇三之后台定位权限源码剖析这篇文章中介绍到了AppOps机制的一个重要功用是track,举的详细比如是和电量耗电相关的定位权限,约束运用退到后台之后拜访方位。这篇文章继续展开讲AppOps的access control

            /**
             * App-ops are used for two purposes: Access control and tracking.
             *
             * <p>App-ops cover a wide variety of functionality from helping with runtime permissions access
             * control and tracking to battery consumption tracking.

WRITE_SETTINGS权限

首要,咱们看下WRITE_SETTINGS这个权限,平时假如三方运用需求申请此权限的时分需求如下操作:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
    if (!Settings.System.canWrite(MainActivity.this)) {
        Intent intent = new Intent(android.provider.Settings.ACTION_MANAGE_WRITE_SETTINGS);
        intent.setData(Uri.parse("package:" + getPackageName()));
        //启动指定Action的权限管理运用的弹窗Activity
        startActivity(intent);
    } else {
        //如有该权限能够直接设置屏幕亮度
        setLight();
    }
}

Android Framework权限篇四之AppOps机制

然后就会呈现如上弹窗选择,这儿的权限申请流程和之前的runtime运行时权限不一样,走的是Appops查看机制。首要看下这个权限在framework中的界说,能够看到是signature|appop级别的;

  1. signature级别表明有体系platform体系签名的运用假如声明会默许授权
  2. appops级别表明能够经过appops机制授权也便是上面的弹窗授权办法

    /framework/base/core/res/AndroidManifest.xml
    <permission android:name="android.permission.WRITE_SETTINGS"
        android:label="@string/permlab_writeSettings"
        android:description="@string/permdesc_writeSettings"
        android:protectionLevel="signature|preinstalled|appop|pre23" />

这儿从Settings.System.canWrite()往下看源码是如何做查看的?这儿往下会走到isCallingPackageAllowedToPerformAppOpsProtectedOperation()

public static boolean isCallingPackageAllowedToPerformAppOpsProtectedOperation(Context context,int uid, String callingPackage, boolean throwException, int appOpsOpCode, String[]
permissions, boolean makeNote) {
    if (callingPackage == null) {
        return false;
    }
    //1.获取AppOpsManager
    AppOpsManager appOpsMgr = (AppOpsManager)context.getSystemService(Context.APP_OPS_SERVICE);
    int mode = AppOpsManager.MODE_DEFAULT;
    //2.查看AppOpsManager.OP_WRITE_SETTINGS权限授权值
    if (makeNote) {
        mode = appOpsMgr.noteOpNoThrow(appOpsOpCode, uid, callingPackage);
    } else {
        mode = appOpsMgr.checkOpNoThrow(appOpsOpCode, uid, callingPackage);
    }
    //3.根据授权值回来成果
    switch (mode) {
        case AppOpsManager.MODE_ALLOWED:
            return true;
        case AppOpsManager.MODE_DEFAULT:
            for (String permission : permissions) {
                if (context.checkCallingOrSelfPermission(permission) == PackageManager.PERMISSION_GRANTED) {
                    return true;
                }
            }
        default:
            // this is for all other cases trickled down here...
            if (!throwException) {
                return false;
            }
    }

全体流程如下;这儿补充下,查看mode值时,这儿WRITE_SETTINGS权限在AppOps中的默许值是MODE_DEFAULT;此时再进一步经过context.checkCallingOrSelfPermission(permission) == PackageManager.PERMISSION_GRANTED查看,假如是体系platform签名运用signature权限会默许授权,回来true,假如是三方运用则回来false;

Android Framework权限篇四之AppOps机制

Appops完成

权限状态值

/frameworks/base/core/java/android/app/AppOpsManager.java
//答应
public static final int MODE_ALLOWED = 0;
//回绝
public static final int MODE_IGNORED = 1;
//回绝并抛出异常
public static final int MODE_ERRORED = 2;
//default形式,会进一步check权限,如上面的"修正体系设置"权限
public static final int MODE_DEFAULT = 3;
//前台形式,主要是后台方位权限运用,能够结合第三篇权限文章食用,有比如用到
public static final int MODE_FOREGROUND = 4;

权限界说值

这儿关于appops权限界说的官方介绍是每个runtime permission权限值都会有对应的app op值对应;如下便是罗列了各个权限op对应的值;

<h3>Runtime permissions and app-ops</h3>
<p>Each platform defined runtime permission (beside background modifiers) has an associated app
op which is used for tracking but also to allow for silent failures.
public static final int OP_COARSE_LOCATION = 0;
/** @hide Access to fine location information. */
@UnsupportedAppUsage
public static final int OP_FINE_LOCATION = 1;
/** @hide Causing GPS to run. */
@UnsupportedAppUsage
public static final int OP_GPS = 2;
/** @hide */
@UnsupportedAppUsage
public static final int OP_VIBRATE = 3;
/** @hide */
@UnsupportedAppUsage
public static final int OP_READ_CONTACTS = 4;
/** @hide */
@UnsupportedAppUsage
public static final int OP_WRITE_CONTACTS = 5;
....

数据结构

AppOpsService.java中经过SparseArray<UidState> mUidStates来保护状态,key值为uid,value值为对应的UidState,相关类基本结构如下:

Android Framework权限篇四之AppOps机制

对应的持久化文件方位是在/data/system/appops.xml 文件内容如下:

  • uid标签 用于记录和 uid 相关的约束信息;
  • pkg标签 用于记录和详细运用的约束信息;
  • pkg标签下的uid标签的n :表明pkg所属的uid
  • pkg标签下的uid标签的p:表明是否是privileged运用
  • n:表明权限值
  • m:表明权限的授权值
  • st:标签下的是对应的权限的回绝时刻,答应时刻,继续运用时刻等
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<app-ops>
<uid n="10210">
<op n="0" m="1" />
<op n="87" m="0" />
</uid>
...
<pkg n="com.android.recentspsp">
<uid n="1000" p="true">
<op n="3">
<st n="214748364801" t="1606363097865" d="50" pu="0" />
</op>
</uid>
</pkg>
...

经过如下adb命令能够看到相应的信息

adb shell dumpsys appops

如这儿的COARSE_LOCATION 在 2020-11-23 11:24:50.018 时 查看权限是答应的

Android Framework权限篇四之AppOps机制

关于AppOpsManager中也有一些重要的变量和办法收拾如下:这儿面有一个sOpDefaultMode数组,表明每个权限op都有对应的默许授权mode值

Android Framework权限篇四之AppOps机制

Appops api

appops调用主要是经过AppOpsManager供给的api办法进行操作:

Android Framework权限篇四之AppOps机制

  1. 查看权限:AppOpsManager.checkOp()终究会调用到AppOpsService.checkOperationUnchecked():
private @Mode int checkOperationUnchecked(int code, int uid, @NonNull String packageName,boolean raw) {
    ...
        //1. 获取对应权限的op值
        code = AppOpsManager.opToSwitch(code);
        //2. 获取uid对应的UidState
        UidState uidState = getUidStateLocked(uid, false);
        //3. 假如在uid标签下有对应的值则直接回来,android10这儿的raw为false(主要增加前后台判断)
        if (uidState != null && uidState.opModes != null
                && uidState.opModes.indexOfKey(code) >= 0) {
            final int rawMode = uidState.opModes.get(code);
            return raw ? rawMode : uidState.evalMode(code, rawMode);
        }
        //4. 假如没有从uid标签下没有取到值,则会从pkg标签下取pkg对应的Op
        Op op = getOpLocked(code, uid, packageName, false, false);
        //5 假如也取不到的话,则直接回来该权限的默许状态值
        if (op == null) {
            return AppOpsManager.opToDefaultMode(code);
        }
        return raw ? op.mode : op.evalMode();
    }
}
  1. 查看权限并记录:AppOpsManager.noteOp()终究会调用到AppOpsService.noteOperationUnchecked():
final Ops ops = getOpsRawLocked(uid, packageName, isPrivileged, true /* edit */);
...
final Op op = getOpLocked(ops, code, true);
...
final UidState uidState = ops.uidState;
...
final int switchCode = AppOpsManager.opToSwitch(code);
...
//1. 假如uid标签下有对应的值则读取
if (uidState.opModes != null && uidState.opModes.indexOfKey(switchCode) >= 0) {
    final int uidMode = uidState.evalMode(code, uidState.opModes.get(switchCode));
    //1.1 假如回绝的话,则直接回来,而且更新回绝时刻
    if (uidMode != AppOpsManager.MODE_ALLOWED) {
       ...
       op.rejected(System.currentTimeMillis(), proxyUid, proxyPackageName,
                uidState.state, flags);
        mHistoricalRegistry.incrementOpRejected(code, uid, packageName,
                uidState.state, flags);
        scheduleOpNotedIfNeededLocked(code, uid, packageName, uidMode);
        return uidMode;
    }
} else {
//2. uid标签下无的话从pkg标签下读取
    final Op switchOp = switchCode != code ? getOpLocked(ops, switchCode, true) : op;
    final int mode = switchOp.evalMode();
    //2.2 假如回绝的话,相同直接回来,并更新回绝时刻
    if (switchOp.mode != AppOpsManager.MODE_ALLOWED) {
        ...
        op.rejected(System.currentTimeMillis(), proxyUid, proxyPackageName,
                uidState.state, flags);
        mHistoricalRegistry.incrementOpRejected(code, uid, packageName,
                uidState.state, flags);
        scheduleOpNotedIfNeededLocked(code, uid, packageName, mode);
        return mode;
    }
}
//3. 走到这儿则为答应,而且更新答应时刻
op.accessed(System.currentTimeMillis(), proxyUid, proxyPackageName,
        uidState.state, flags);
mHistoricalRegistry.incrementOpAccessedCount(op.op, uid, packageName,
        uidState.state, flags);
scheduleOpNotedIfNeededLocked(code, uid, packageName,
        AppOpsManager.MODE_ALLOWED);
return AppOpsManager.MODE_ALLOWED;
   }
}
  1. 设置权限值: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 {
      if (uidState.opModes.indexOfKey(code) >= 0 && uidState.opModes.get(code) == mode) {
          return;
      }
      if (mode == defaultMode) {
          uidState.opModes.delete(code);
          if (uidState.opModes.size() <= 0) {
              uidState.opModes = null;
          }
      } else {
          uidState.opModes.put(code, mode);
      }
      scheduleWriteLocked();
  }
  uidState.evalForegroundOps(mOpModeWatchers);
  1. 监听权限改变: 如以下是监听悬浮窗权限状态改变;悬浮窗权限相似如上的修正体系设置权限,声明的级别也是appops权限,这儿添加的监听表明假如开关了权限,需求更新app状态,有可能app悬浮窗正在运用。
/frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
mAppOps = (AppOpsManager)context.getSystemService(Context.APP_OPS_SERVICE);
AppOpsManager.OnOpChangedInternalListener opListener =
        new AppOpsManager.OnOpChangedInternalListener() {
            @Override public void onOpChanged(int op, String packageName) {
                updateAppOpsState();
            }
        };
mAppOps.startWatchingMode(OP_SYSTEM_ALERT_WINDOW, null, opListener);
<permission android:name="android.permission.SYSTEM_ALERT_WINDOW"
        android:label="@string/permlab_systemAlertWindow"
        android:description="@string/permdesc_systemAlertWindow"
        android:protectionLevel="signature|preinstalled|appop|pre23|development" />

参考博客:

  • AppOps机制介绍