Android Framework权限篇之AppOps机制

Android Framework权限篇之AppOps机制这里关于 appops 权限定义的官方介绍是每个 runtimepermi 权限值都会有对应的 appop 值对应 如下就是罗列了各个权限 op 对应的值 由于许多 Android 开发者日常工

大家好,欢迎来到IT知识分享网。

概述

在前面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(); } } 

在这里插入图片描述

然后就会出现如上弹窗选择,这里的权限申请流程和之前的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;

在这里插入图片描述

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,相关类基本结构如下:

在这里插入图片描述

对应的持久化文件位置是在/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="1" t="65" d="50" pu="0" /> </op> </uid> </pkg> ... 

通过如下adb命令可以看到相应的信息

adb shell dumpsys appops 

如这里的COARSE_LOCATION 在 2020-11-23 11:24:50.018 时 检查权限是允许的

在这里插入图片描述

关于AppOpsManager中也有一些重要的变量和方法整理如下:这里面有一个sOpDefaultMode数组,表示每个权限op都有对应的默认授权mode值

在这里插入图片描述

Appops api

appops调用主要是通过AppOpsManager提供的api方法进行操作:

在这里插入图片描述

  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" /> 

最后

如何学习Framework?

由于许多Android开发者日常工作主要集中在业务层面,大量时间用于编写基础代码、应用现成框架,导致对底层技术如Framework、Handler源码、Binder机制等了解不足,仅停留在表面认知阶段。

为此,为了帮助广大开发者弥补这一短板,特此准备了一份详尽的Android Framework内核源码知识体系图解,以及配套的《Android Framework源码开发解析》学习笔记,旨在引导大家系统性地攻克Android Framework领域的核心技术,从而提升自身的竞争力,从容应对金三银四的求职挑战。

【有需要的朋友,扫描下方二维码即可领取!!】👇👇

Android Framework权限篇之AppOps机制

在这里插入图片描述

《Android Framework源码开发揭秘》

第一章 系统启动流程分析
  • 第一节 Android启动概括
  • 第二节 init.rc解析
  • 第三节 Zygote
  • 第四节 面试题
    在这里插入图片描述
第二章 跨进程通信IPC解析
  • 第一节 Service还可以这么理解
  • 第二节 Binder基础
  • 第三节 Binder应用
  • 第四节 AIDL应用(上)
  • 第五节 AIDL应用(下)
  • 第六节 Messenger原理及应用
  • 第七节 服务端回调
  • 第八节 获取服务(IBinder)
  • 第九节 Binder面试题全解析
    在这里插入图片描述
第三章 Handler源码解析
  • 第一节 源码分析
  • 第二节 难点问题
  • 第三节 Handler常问面试题在这里插入图片描述
第四章 AMS源码解析
  • 第一节 引言
  • 第二节 Android架构
  • 第三节 通信方式
  • 第四节 系统启动系列
  • 第五节 AMS
  • 第六节 AMS面试题解析在这里插入图片描述
第五章 WMS源码解析
  • 第一节 WMS与activity启动流程
  • 第二节 WMS绘制原理
  • 第三节 WMS角色与实例化过程
  • 第四节 WMS工作原理在这里插入图片描述
第六章 Surface源码解析
  • 第一节 创建流程及软硬件绘制
  • 第二节 双缓冲及Surface View解析
  • 第三节 Android图形系统综述在这里插入图片描述
第七章 基于Android12.0的SurfaceFlinger源码解析
  • 第一节 应用建立和SurfaceFlinger的沟通桥梁
  • 第二节 SurfaceFlinger的启动和消息队列处理机制
  • 第三节 SurfaceFlinger之VSyns(上)
  • 第四节 SurfaceFlinger之VSyns(中)
  • 第五节 SurfaceFlinger之VSyns(下)在这里插入图片描述
第八章 PKMS源码解析
  • 第一节 PKMS调用方式
  • 第二节 PKMS启动过程分析
  • 第三节 APK的扫描
  • 第四节 APK的安装
  • 第五节 PKMS之权限扫描
  • 第六节 静默安装
  • 第七节 requestPermissions源码流程解析
  • 第八节 PKMS面试题在这里插入图片描述
第九章 InputManagerService源码解析
  • 第一节 Android Input输入事件处理流程(1)
  • 第二节 Android Input输入事件处理流程(2)
  • 第三节 Android Input输入事件处理流程(3)在这里插入图片描述
第十章 DisplayManagerService源码解析
  • 第一节 DisplayManagerService启动
  • 第二节 DisplayAdepter和DisplayDevice的创建
  • 第三节 DMS部分亮灭屏流程
  • 第四节 亮度调节
  • 第五节 Proximity Sensor灭屏原理
  • 第六节 Logical Display和Physical Display配置的更新在这里插入图片描述
【有需要的朋友,扫描下方二维码即可领取!!】👇👇

Android Framework权限篇之AppOps机制

最后 在Android开发市场饱和的大背景下,初级开发者只有通过对Android Framework进行全面而深入的学习与实践,才能有效提高自身的专业素养和技术能力,从而在职场竞争中找准定位,实现从量到质的飞跃,成为行业内的佼佼者。

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/131824.html

(0)
上一篇 2025-08-05 14:10
下一篇 2025-08-05 14:15

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注微信