AppOps全称是 Application Operations,类似我们平时常说的应用程序的操作(权限)管理。用控制蓝牙开关,摄像头,Wi-Fi等功能的操作权限。比如,我们安装了一个第三方蓝牙应用,在该应用要打开蓝牙开关时就会弹出如下对话框让用户确认:
至于为什么在系统自带的Settings里操作蓝牙开关没弹对话框,之后会讲到。
AppOps总体概览
核心服务:AppOpsService
系统启动时该服务会启动运行。
参考以下ActivityManagerService.java,ActivityManagerService启动过程中:
配置文件:appops.xml appops_policy.xml
appops.xml位于 /data/system/目录下,存储各个app的权限设置和操作信息。
appops_policy.xml位于 /system/etc/目录下,里面存有对特定App的权限配置。
API接口:AppOpsManagerAppOpsService实现了大部分的核心功能逻辑,但它不能被其他模块直接调用访问,而是通过AppOpsManager提供访问接口。
AppOps使用逻辑举例以蓝牙开关为例,来讲一下AppOps的使用和基本逻辑。
如上图,在开始之前,我们需先在/system/build.prop里添加配置:
persist.sys.strict_op_enable=true
用于使能AppOps机制。
1、App在打开蓝牙开关时,需调用到BluetoothManagerService.enable(String callingPackage),具体流程不表;
在BluetoothManagerService.enable(String callingPackage)里:
java
AppOpsManager appOps = (AppOpsManager) mContext
.getSystemService(Context.APP_OPS_SERVICE);
int callingUid = Binder.getCallingUid();
if (appOps.noteOp(AppOpsManager.OP_BLUETOOTH_CHANGE, callingUid,
callingPackage) != AppOpsManager.MODE_ALLOWED)
return false;
a.首先获取APP_OPS_SERVICE(即AppOpsService)服务的代理对象appops,用于调用AppOpsService提供的功能。
b.appOps.noteOp()用来判断蓝牙开关权限AppOpsManager.OP_BLUETOOTH_CHANGE是否为AppOpsManager.MODE_ALLOWED,是的话就继续执行打开蓝牙的流程,否的话就立即退出。
- 在AppOpsManager里有:
a. 以 MODE_ 开头的权限模式
MODE_ALLOWED = 0 //允许执行
MODE_IGNORED = 1 //忽略请求,不允许执行
MODE_ERRORED = 2 //发生错误,不允许执行
MODE_DEFAULT = 3 //调用者需使用默认的安全检查方法,不常用
MODE_ASK = 4 //弹出对话框让用户选择允许或不允许执行
b. 以 OP_ 开头的各个具体操作权限
OP_COARSE_LOCATION = 0 //试图获取位置信息
OP_CALL_PHONE = 13; //试图拨打电话
OP_BLUETOOTH_CHANGE = 49 //试图开关蓝牙
OP_SEND_MMS = 50 //试图发送彩信
...
c. mService : IAppOpsService,代表AppOpsService服务,用于调用AppOpsService里实现的功能
d. isStrictEnable() : boolean 通过System Properties中的persist.sys.strict_op_enable来判断是否要开启AppOps功能
e. noteOp() 这个接口是用来权限提醒和保存。
- AppOpsManager.noteOp()的实现:
mService.noteOperation(op, uid, packageName) 进入了AppOpsService.noteOperation()的处理流程
-
AppOpsService接收AppOpsManager发过来的调用指令,开始执行noteOperation()。在noteOperation()里会调用到 getOpsLocked() 和 getOpLocked() 来取得权限模式,然后根据获取到的mode判断是否给予允许执行,或则弹出对话框让用户选择。
-
在相应的操作权限还未鉴权过的,在getOpLocked()里会调用到getDefaultMode()用于获取代码里数组AppOpsManager.sOpDefaultStrictMode和文件appops_policy.xml中预配置模式。
-
mPolicy是AppOpsPolicy的实例,在AppOpsPolicy中实现了对appops_policy.xml的读取和解析。
-
AppOpsPolicy.getDefualtMode(int code, String packageName),根据appops_policy.xml中获取权限模式。在appops_policy.xml中我们看到:
xml
<user-app permission="ask" show="true"/>
<system-app permission="allowed" show="false"/>
user-app和system-app分别表示第三方应用和系统预装的应用,permission="ask"和permission="allowed"分别表示MODE_ASK和MODE_ALLOWED。所以第三方应用试图获取某项权限时都会弹出对话框让用户进行授权,而系统预装的应用试图获取某项权限时就直接被允许,无需用户授权,如本文开头所讲的系统自带的Settings里操作蓝牙开关没弹对话框就可以打开蓝牙开关。
xml
<pkg name="com.android.calendar" type="system-app">
<op name="android:read_contacts" permission="ask" show="true"/>
</pkg>
<pkg> 里是对指定App中的特定操作权限进行配置。
- 假如获取到的权限模式为MODE_ASK则调用askOperationLocked()弹出对话框,让用户进行授权: