权限
我们知道,Android应用都运行在沙盒中,默认情况下这些应用只能访问他们自己的域,即自己的文件和非常少量的系统服务。为了能够和系统或者其他应用交互,app就需要申请额外的一些权限。
permission(权限)实际上就是一个简单的字串,申明需要做哪些类型的操作。
Android系统预置了很多权限
权限的定义是在/frameworks/base/core/res/AndroidManifest.xml
如果要看系统内已知的权限列表,可以使用pm list permissions
命令:
加上 -f
选项,可以打印出定义权限的package、label、description和protection level。
权限管理
在应用安装的时候,PackageManagerService就对每个应用授予了权限。package manager管理了一个数据库,用来维护预置或者用户安装的package。维护的内容包括:安装路径,版本号,签名证书,每个package拿到的权限列表和一个在本设备上定义的所有权限列表。这个package数据库保存在/data/system/packages.xml路径下,每当有应用安装,更新或者卸载的时候都会更新这个xml文件。
权限组
在权限定义文件里会定义权限组,然后在单独的权限中指定该权限属于哪个权限组。
- 如果应用没有获得与当前申请的权限在同一权限组的其他权限的授权,那么系统将以这个权限组的描述信息去提示用户,而不是具体申请的权限的描述信息。比如,一个应用申请了READ_CONTACTS权限,系统会提示用户”应用需要访问设备的联系人(包含读写)”,如果用户同意授权,系统只会赋予应用之前申请的权限(在这里就只是READ_CONTACTS)。
- 如果应用已经获得了与正在申请的权限同一个权限组的其他权限的授权,那么系统会自动将正在申请的权限授予应用,不需要任何与用户的交互行为。比如,如果一个应用之前已经获得了READ_CONTACTS权限的授权,那么在之后应用请求WRITE_CONTACTS权限时,系统会自动将该权限授予应用。
权限保护等级ProtectionLevel
protectionLevel
是定义权限时的一个重要属性,它表示一个权限的级别,在很大程度上它也决定了一个权限被授权的方式(由系统安装时自动授权或者由用户来决定是否授权)。 protectionLevel
可以分为两类:基础权限级别和附加权限级别。
基础权限级别
normal
这是个默认值,也就是没有指定protectionLevel
的权限默认获得此level。它表示这是一个对系统和其他应用低风险的权限。有该标记的权限是不需要用户确认就可以直接赋予应用程序的。
dangerous
较高风险的权限。该标记的权限一般涉及到访问用户的隐私数据或者其他一些控制设备的行为,可能会给用户带来影响。所以系统不会自动授权这类权限,而是会弹出对话框告诉用户,由用户进行选择是否授予权限。
signature
如果请求权限的app与声明权限的app签名一致,系统会自动赋予权限,而不会通知用户或者征求用户的同意。 否则需要通过intent将用户引导到权限管理界面由用户决定是否授权。
这属于最高级的权限等级,因为它需要有加密密钥的拥有权,而这个密钥只有这个app或者系统平台才会拥有。这也就意味着其他人无法随意使用这个权限。系统内置的signature权限一般都是由管理设备的系统App使用,也就是需要系统签名。
signatureOrSystem
相当于signature | privileged
这个权限等级有两种应用可以自动获取该类型权限的授权:
与定义这个权限的apk拥有相同的签名的应用(这一点和Signature类型的权限相同)。
在/system/priv-app目录下的应用(即拥有超级权限的系统应用)。
这可以让制造商的预置应用即使没有与该权限一致的签名也可以通过作为系统应用去使用该权限。
附加权限级别
除了基础权限级别的其他权限级别都属于附加权限级别。它们必须附加在基础权限级别上使用。从目前系统定义的权限来看,附加权限级别基本都是与signature基础权限级别搭配使用。
可以理解为附加权限级别是在为signature级别的权限开后门,使signature级别的权限在特定的条件下能够授权给特定类型的应用。
- privileged:只能与signature同时使用。signature | privileged与signatureOrSystem意义相同。
- system:与privileged相同,是privileged的老版本。
- development:development applications可以被自动授予此权限。
- appop:此类权限会与AppOpsManager来配合完成对应用操作的限制(AppOpsManager在后面的小节介绍)。
- pre23:应用请求此类权限后,系统将在应用安装时自动授权给那些targetSdkVersion在23(Android 6.0)以下的应用。
- installer:此类权限自动被授权给那些负责安装apk的系统app。
- verifier:此类权限自动被授权给那些负责验证apk的系统app。
- preinstalled:此类权限可以自动被授权给任何预安装在system image中的app,不只是privileged app。
- setup:此类权限自动被授予安装向导app。
AppOps
初次接触Appops是因为,现在国内很多App都要过多的权限,比如随便一个App都要访问手机号,不给它权限的话它就退出不让你使用。后来发现,可以通过Appops默默修改权限,比如第一次打开App的时候给它所有权限,然后再用Appops偷偷把权限禁用掉,这样App可以正常打开使用,但它也偷不到对应的信息了。
adb shell appops set com.tencent.gamehelper.smoba READ_EXTERNAL_STORAGE ignore
这样就禁掉了腾讯游戏对设备存储的访问。当然除了这种用法,对于一些特殊权限比如悬浮窗权限的授权也可以使用AppOps来解决。
AppOps 是什么
Appops是Application Operations的简称,是关于应用权限管理的一套方案,但这里的应用指的是系统应用,这些API不对第三方应用开放。Google从4.3开始推出Appops, 但一直到最新的Android N都没有在Settings里面开放Appops的入口,但这套方案却一直在后台默默的运行着。
// 允许执行相关权限 public static final int MODE_ALLOWED = 0; // 表示当前应用没有此权限,如果尝试使用该权限,就会静态地进入失败状态,出现应用莫名其妙crash。 public static final int MODE_IGNORED = 1; // 表示当前应用没有此权限,并且如果使用此权限会导致SecurityException public static final int MODE_ERRORED = 2; // 表示默认值,应该使用其默认的安全检查。这个模式并不常用,它应该和appop权限一起使用,并且调用者必须显式地检查和使用它 public static final int MODE_DEFAULT = 3;
AppOps工作流程
Appops工作框架如下
可以看到Appops的两个重要组成部分是AppOpsManager
和AppOpsService
,它们是典型的客户端和服务端设计,通过Binder跨进程调用。
AppOpsService
是做最终检查的系统服务,它的注册名字是appops,应用可以类似于mAppOps=(AppOpsManager)getContext().getSystemService(Context.APP_OPS_SERVICE);
的方式来获取这个服务。
AppOpsManager
提供了接口,访问AppOpsService的核心方法。
AppOpsService
是在AMS构造函数中启动的:
mAppOpsService = new AppOpsService(new File(systemDir, "appops.xml"), mHandler); mAppOpsService.startWatchingMode(AppOpsManager.OP_RUN_IN_BACKGROUND, null, new IAppOpsCallback.Stub() { @Override public void opChanged(int op, int uid, String packageName) { if (op == AppOpsManager.OP_RUN_IN_BACKGROUND && packageName != null) { if (mAppOpsService.checkOperation(op, uid, packageName) != AppOpsManager.MODE_ALLOWED) { runInBackgroundDisabled(uid); } } } });
Api使用
AppOpsManager提供标准的API供APP调用,但google有明确说明,大部分只针对系统应用。但是想使用的话,可以尝试把Android源码里AppOpsManager.java打包一下,把jar包导入自己的工程,就可以使用了。
int checkOp(Stringop, int uid,StringpackageName)Op对应一个权限操作,该接口来检测应用是否具有该项操作权限。
int noteOp(Stringop, int uid,StringpackageName)和checkOp基本相同,但是在检验后会做记录。
int checkOpNoThrow(Stringop, int uid,StringpackageName)和checkOp类似,但是权限错误,不会抛出SecurityException,而是返回AppOpsManager.MODE_ERRORED。
int noteOpNoThrow(Stringop, int uid,StringpackageName) 类似noteOp,但不会抛出SecurityException。
void setMode( int code, int uid, String packageName, int mode) code代表具体的操作权限,mode代表要更改成的类型(允许/禁止/提示)
mAppOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
mAppOps.setMode(AppOpsManager.OP_SYSTEM_ALERT_WINDOW, getAppUid(packageGaode), packageGaode, AppOpsManager.MODE_ALLOWED);
private int getAppUid(String packageName) { int uid = 0; try { PackageManager pm = mContext.getPackageManager(); ApplicationInfo ai = pm.getApplicationInfo(packageName, 0); uid = ai.uid; } catch (NameNotFoundException e) { e.printStackTrace(); } return uid; }