###1. android permision
android 采用了和Linux类型的权限隔离机制,每个应用使用独立的系统标识(uid 和)来运行,从而使得不同应用之间以及应用和系统之间隔离开来, 每一个Android 应用都是在一个进程沙箱中运行的,缺省情况下,应用无权执行可能对其它应用,操作系统或是用户造成影响的任何操作,这些操作可能包括读写用户个人数据(比如通讯录或是电子邮件),读写其它应用的文件,访问网络,拨打电话,发送短信, 录音, 拍照, 读取定位信息等
如有需要,应用必须明确指明需要和其它应用共享的资源或数据。这需要通过申明缺省“沙箱”未支持的“Permissions”来获得这些额外的功能, 应用在 AndroidManifest.xml 文件中静态声明所需permission,Android系统在安装应用时会询问用户是否赋予该应用所需权限。为避免用户决定安全问题的复杂性,Android系统不支持在运行时动态赋予应用所需permission(android M开始, 已经支持 runtime permission)
例如:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.app.myapp" >
<uses-permission android:name="android.permission.RECEIVE_SMS" />
...
</manifest>
###2. PermissionInfo / PermissionGroupInfo
android framework 中使用 class PermissionInfo 来存储permission 相关的信息, 其中几个主要的成员如下:
- public int protectionLevel;
- 该permission的保护级别, 即获取该permission需要的条件
- public String group;
- 该permission所属的permission group
- public int flags;
- 该permission相关的flag, 共有如下几种
- FLAG_COSTS_MONEY : 该permission会涉及资费
- FLAG_HIDDEN : 该permission在dump时不显示给用户
- FLAG_INSTALLED : 该permission被安装到系统全局定义的权限中
- public int descriptionRes;
- 该permission相关的描述信息的字符串资源的id, 若没有则置为0
Android 6.0 支持多种permission保护级别, 在 permissionInfo 中有:
- public static final int PROTECTION_NORMAL = 0;
- AndroidManifest.xml 中使用 ”dangerous“ 来表示
- 普通permission, 这些permission对于用户隐私和设备操作不会造成太多危险(比如开启手电筒的权限),apk在AndroidManifest中申明后, 系统会自动授予这些permission
- public static final int PROTECTION_DANGEROUS = 1;
- AndroidManifest.xml 中使用 ”dangerous“ 来表示
- 即 Runtime permission, 这些permission可能会影响用户隐私和设备的普通操作(请求获取用户联系人信息的权限), 系统会明确的让用户决定是否授予这些permission(Android 6.0以前, 在安装apk时授予这些权限, Android 6.0后, 可在安装后动态开关这些permission, 因此又称为运行时权限
- public static final int PROTECTION_SIGNATURE = 2;
- AndroidManifest.xml 中使用 ”signature“ 来表示 + 签名级别的permission, 即申请该permission的apk必须和声明该permission的apk具有相同的签名才能申请的permission, 若签名匹配, 系统会自动授予这些permission
- public static final int PROTECTION_SIGNATURE_OR_SYSTEM = 3;
- AndroidManifest.xml 中使用 ”signatureOrSystem“ 来表示
+ 已废弃, 使用
PROTECTION_SIGNATURE | PROTECTION_FLAG_PRIVILEGED
来代替
- AndroidManifest.xml 中使用 ”signatureOrSystem“ 来表示
+ 已废弃, 使用
以上的几种为基本的保护级别, 不能同时使用, 另外还有若干 extral flag, 需要和以上的几种 flag 配合使用才有意义
- public static final int PROTECTION_FLAG_PRIVILEGED = 0x10;
- AndroidManifest.xml 中使用
| privileged
来表示 - /system/priv-app 目录下的app 才能获取该permission
- AndroidManifest.xml 中使用
- public static final int PROTECTION_FLAG_SYSTEM = 0x10;
- AndroidManifest.xml 中使用
| system
来表示 - PROTECTION_FLAG_PRIVILEGED 的旧名称
- AndroidManifest.xml 中使用
- public static final int PROTECTION_FLAG_DEVELOPMENT = 0x20;
- AndroidManifest.xml 中使用
| development
来表示 - 具有该保护级别的permission, 只有之前被授权过, 才能被授权
- AndroidManifest.xml 中使用
- public static final int PROTECTION_FLAG_APPOP = 0x40;
- AndroidManifest.xml 中使用
| appop
来表示 - 该属性和 AppOps相关
- AndroidManifest.xml 中使用
- public static final int PROTECTION_FLAG_PRE23 = 0x80;
- AndroidManifest.xml 中使用
| pre23
来表示 - pre SDK 23
- AndroidManifest.xml 中使用
- public static final int PROTECTION_FLAG_INSTALLER = 0x100;
- AndroidManifest.xml 中使用
| installer
来表示 - 该permission被授予给package installer 组件
- AndroidManifest.xml 中使用
- public static final int PROTECTION_FLAG_VERIFIER = 0x200;
- AndroidManifest.xml 中使用
| verifier
来表示 - 该permission将被授予给 system verifier 组件
- AndroidManifest.xml 中使用
- public static final int PROTECTION_FLAG_PREINSTALLED = 0x400;
- AndroidManifest.xml 中使用
| preinstalled
来表示 - 该permission将被授予给 预安装 的app
- AndroidManifest.xml 中使用
不同的保护级别可以使用 ”位或” 操作来同时使用, 例如 PROTECTION_SIGNATURE | PROTECTION_FLAG_PRIVILEGED
表示要满足 PROTECTION_SIGNATURE 或者 PROTECTION_FLAG_PRIVILEGED 即可获取该permission
android frameworks 中使用 class PermissionInfo 来存储permission group相关的信息
拥有system级别permission的使用者可以access其他普通permission 和 signature permission
权限检查中有些特例, 可以参见第16节
###3. PackageParse.Permission / PackageParse.PermissionGroup
PackageManagerService 会分析所有的package, 收集系统中所有定义的permission
PackageParse.Permission / PackageParse.PermissionGroup 是对 PermissionInfo / PermissionGroupInfo 的包装
com.android.server.pm.PackageManagerService 中保存了系统中所有的 permission group 的信息
// Mapping from permission names to info about them.
final ArrayMap<String, PackageParser.PermissionGroup> mPermissionGroups =
new ArrayMap<String, PackageParser.PermissionGroup>();
通过如下的 api 可以获取到 PermissionGroupInfo
public PermissionGroupInfo getPermissionGroupInfo(String name, int flags)
public List<PermissionGroupInfo> getAllPermissionGroups(int flags)
com.android.server.pm.Settings 中存储了所有 permission 的信息
// Mapping from permission names to info about them.
final ArrayMap<String, BasePermission> mPermissions =
new ArrayMap<String, BasePermission>();
通过如下的 api 可以获取到 PermissionInfo
public PermissionInfo getPermissionInfo(String name, int flags)
###4. android framework定义的permission
Android 系统定义的permission位于源码的 “framework/base/core/res/AndroidManifest.xml”中
<permission-group android:name="android.permission-group.CONTACTS"
android:icon="@drawable/perm_group_contacts"
android:label="@string/permgrouplab_contacts"
android:description="@string/permgroupdesc_contacts"
android:priority="100" />
<permission android:name="android.permission.READ_CONTACTS"
android:permissionGroup="android.permission-group.CONTACTS"
android:label="@string/permlab_readContacts"
android:description="@string/permdesc_readContacts"
android:protectionLevel="dangerous" />
......
<permission android:name="android.permission.USE_FINGERPRINT"
android:permissionGroup="android.permission-group.SENSORS"
android:label="@string/permlab_useFingerprint"
android:description="@string/permdesc_useFingerprint"
android:protectionLevel="normal" />
......
<permission android:name="android.permission.BLUETOOTH_PRIVILEGED"
android:protectionLevel="system|signature" />
......
<permission android:name="android.permission.RECEIVE_BLUETOOTH_MAP"
android:protectionLevel="signature|privileged" />
......
- <permission-group> 定义了一个 permission group, 其中
- icon : 该permission group对应的图标
- label : 该permission group的标题
- priority : 该permission group的优先级, 即列出这些permission group时的先后顺序
- description : 该permission group的详细描述
- <permission> 定义了一个permission, 其中
- permissionGroup : 该permission所属的permission group
- label : 该permission的标题
- priority : 该permission的优先级, 即列出这些permission group时的先后顺序
- description : 该permission的详细描述, 规则是两句话,第一句描述,第二句警告用户如果授权会发生什么后果
- protectionLevel : 该permission的保护级别
可以看到, 有很多permission是未分组的
在编译android 源码的过程中, 会根据 “framework/base/core/res/AndroidManifest.xml” 生成 “out/target/common/R/android/Manifest.java”文件, 其中有
package android;
public final class Manifest {
public static final class permission {
......
public static final String ACCESS_FINE_LOCATION="android.permission.ACCESS_FINE_LOCATION";
......
public static final String BLUETOOTH_ADMIN="android.permission.BLUETOOTH_ADMIN";
......
public static final String CALL_PHONE="android.permission.CALL_PHONE";
......
}
public static final class permission_group {
......
public static final String CAMERA="android.permission-group.CAMERA";
......
public static final String PHONE="android.permission-group.PHONE";
......
}
因此, 在app开发中可以直接使用 android.Manifest.permission.CALL_PHONE / android.Manifest.permission_group.PHONE 这样的方式来引用 permission / permission group 的名称
###5. 自定义permission / permission-group
在package 的 AndroidManifest.xml 中:
- 可使用 <permission-group>标签来自定义一个 permission group
- android:name 属于必填项, 且必须至少包含2个”.”
- 可使用<permission>标签 来自定义一个 permission
- android:name 属于必填项, 且必须至少包含2个”.”
- android:protectionLevel : 属于必填项, 且取值范围为”normal”, “dangerous”, “signature”, “signatureOrSystem”
例如
<permission-group
android:name="com.whut3.sven.GROUP"
android:icon="@mipmap/ic_perm_group"
android:label="@string/perm_group_label"
android:description="@string/perm_group_desc"
android:priority="100"/>
<permission
android:permissionGroup="com.whut3.sven.GROUP"
android:name="com.whut3.sven.test"
android:icon="@mipmap/ic_perm_test"
android:label="@string/perm_test_label"
android:description="@string/perm_test_desc"
android:protectionLevel="dangerous"/>
定义了一个 permission group “com.whut3.sven.GROUP”, 一个 permission “com.whut3.sven.test”, 并且将该 permission 划分为归属 ”permission group “com.whut3.sven.GROUP” 这一permission group
同样, 在编译 package 的过程中, 也会生成 Manifest.java, 例如 “out/target/common/obj/APPS/Bluetooth_intermediates/src/com/android/bluetooth/Manifest.java”
package com.android.bluetooth;
public final class Manifest {
public static final class permission {
/** Allows access to the Bluetooth Share Manager
*/
public static final String ACCESS_BLUETOOTH_SHARE="android.permission.ACCESS_BLUETOOTH_SHARE";
......
}
}onSetUserDataEnabled
在相应的 package 内,可以使用 com.android.bluetooth.Manifest.permission.ACCESS_BLUETOOTH_SHARE 这样的方式直接引用permission的名称
还可以可以使用 <permission-tree> 标签配合 PackageManager.addPermission() 来动态添加 permission
<permission-tree android:icon="drawable resource"
android:label="string resource"
android:name="string" />
- name : 定义该命名空间内的 permission / permission group 的基础名称
- icon : 代表该命名空间里面所有permission的图标, 当然,定义 permission / permission group 时还可以单独指定 icon
- label : 用户可见的权限名称, 当然,定义 permission / permission group 时还可以单独指定 label
<permission-tree>标签的作用类似于定义一个命名空间, 声明该命名空间中的 permission 都属于定义该 <permission> 的package的uid
例如, 某个 package 中定义了名为 ”com.test.whut3” 的 permission-tree, 则 在package中可以 调用 PackageManager.addPermission() 添加以 “com.test.whut3” 为前缀名的 permission, 以这种方式添加的 permission 具有如下的特征
- 类型为 TYPE_DYNAMIC
- 没有 protect level 和 所属的 group, 只保存 package name 和 permission name
- 必须在任何其它 package 使用它之前被添加
- 被添加后, 一直到设备重启, 或者调用 PackageManager.removePermission() 移除该 permission 之前都存在
###6. runtime permission
runtime permission 即 protect level 为 dangerous 的 permission
在 android M 之前, app在 AndroidManifest.xml 中声明所需的permission, 然后在app的安装过程中, 系统会提示用户所需的permission, 若用户同意安装, 则授予 app 所请求的所有permission,用户不能单独只对一部分 permission 进行授权并且安装完成后不能进行更改
在 android M 中, 引入了 runtime permission 的概念, 可在 app 运行阶段由 app 请求用户授权, 并且用户在授权后在可以在 “设置” > “应用” > “权限” 中取消授权
runtime permission 及其所属的 permission group:
- CALENDAR
- READ_CALENDAR
- WRITE_CALENDAR
- CAMERA
- CAMERA
- CONTACTS
- READ_CONTACTS
- WRITE_CONTACTS
- GET_ACCOUNTS
- LOCATION
- ACCESS_FINE_LOCATION
- ACCESS_COARSE_LOCATION
- MICROPHONE
- RECORD_AUDIO
- PHONE
- READ_PHONE_STATE
- CALL_PHONE
- READ_CALL_LOG
- WRITE_CALL_LOG
- ADD_VOICEMAIL
- USE_SIP
- PROCESS_OUTGOING_CALLS
- SENSORS
- BODY_SENSORS
- SMS
- SEND_SMS
- RECEIVE_SMS
- READ_SMS
- RECEIVE_WAP_PUSH
- RECEIVE_MMS
- STORAGE
- READ_EXTERNAL_STORAGE
- WRITE_EXTERNAL_STORAGE
“runtime permission”的控制粒度为组, 即只要获取到了某一 permission group 中的任意一个permission, 即系统会默认为已授予了该 permission group 内的所有permission, 后续申请该 permission group 内的其它 permission 时, 不再弹出提示框
有两个 permission 比较特殊,分别是SYSTEM_ALERT_WINDOW 和 WRITE_SETTINGS, 都属于比较敏感的permission,很多时候都用不到,如果需要这个 permission,应该首先声明,然后发送Intent去请求用户授权,然后系统会显示详细的管理界面
###7. 查看系统中的permission
使用 adb shell pm list permission-groups
命令可以查看系统中所有的permission group, 例如
$ pm list permission-groups
permission group:android.permission-group.CONTACTS
permission group:android.permission-group.OTHER
permission group:android.permission-group.PHONE
permission group:android.permission-group.CALENDAR
permission group:android.permission-group.CAMERA
permission group:android.permission-group.SENSORS
......
使用 adb shell pm list permissions
命令可以查看系统中所有声明的permission, 可以使用如下选项:
- -g : 按权限组显示, 若无 ’-g’ 选项则只列出未分组的 permission
- -f : 显示详细信息, 包括protect level, description, 以及定义该permission的package等信息
- -d : 只列出 dangerous permission
例如:
$ adb shell pm list permissions -g
All Permissions:
......
group:android.permission-group.CAMERA
permission:android.permission.CAMERA
......
group:android.permission-group.SENSORS
permission:android.permission.BODY_SENSORS
permission:android.permission.USE_FINGERPRINT
group:android.permission-group.LOCATION
permission:android.permission.ACCESS_FINE_LOCATION
permission:android.permission.ACCESS_COARSE_LOCATION
......
$ adb shell pm list permissions -f
All Permissions:
......
+ permission:asus.permission.GET_LOCATION
package:com.asus.taskwidget
label:null
description:null
protectionLevel:signature
+ permission:android.permission.INSTALL_LOCATION_PROVIDER
package:android
label:null
description:null
protectionLevel:signature|privileged
+ permission:com.asus.gallery.filtershow.permission.WRITE
package:com.asus.gallery
label:null
description:null
protectionLevel:signature
......
‘-g’ 选项能够按group 来列出permission ‘-f’ 选项能够列出permission的 名称 / 定义该permissiond的package / 标签 / 描述信息 / 保护级别
###8. PermissionsState
android 系统中定义了众多的 permission, 每一个 app 可以申请不同的 permission, 一个 app 内部的 permission 的状态 使用 PermissionState 来表示
- PackageManagerService
- mPackages 保存了 package name 到 PackageParser.Package 的映射
- PackageParser.Package
- 保存了 PackageSetting 的实例
- 如果该 package 有 shared user id, 则 PackageSetting.sharedUser 将指向对应的 SharedUserSetting
- SettingsBase.mPermissionState
- 指向一个 PermissionState, 存储一个 package 或者 SharedUserId 的所有的 permission 的状态
- PermissionState.mPermissions
- 保存了pcakage内 permission name 到 PermissionState.PermisdsionData 的映射
- PermissionState.PermissionData
- mPerm保存了 一个permission 的 基本信息, 例如 name, type 等
- mUserStates 保存了package内各个user的permission的状态信息
- 提供了 isGranted(), grant(), revoke(), getFlags(), updateFlags(), getPermissionState() 等接口 来 查询/修改 permission 的状态
- PermissionState.PermissionState
- 最终存储了 packege 内一个permission对应某个user的配置状态
对于 BasePermission.type 可以分为3种情况:
- TYPE_BUILTIN : ”android” package 中定义的 permission
- TYPE_NORMAL : 在 AndroidManifest.xml 中静态定义的 permission
- TYPE_DYNAMIC : 使用 PackageManagerService.addPermission() 接口添加的permission
对于某个 package 或者 Shared User Id, PackageManager 中某个 permission 的授权状态有2种表示
- PackageManager.PERMISSION_GRANTED : 已获得该 permission 的授权
- PackageManager.PERMISSION_DENIED : 未获得该 permission 的授权
另外, 还有一组 flag 来标识 permission 的相关状态, 取值范围如下:
- PackageManager.FLAG_PERMISSION_USER_SET
- 表示 permission 当前的授权状态是用户设置的, app可以请求修改该 permission 的授权状态
- PackageManager.FLAG_PERMISSION_USER_FIXED
- 表示 permission 当前的授权状态是用户固定的, app可以不能再请求修改该 permission 的授权状态
- android runtime permission 的权限请求弹框上的 ”拒绝后不再询问” 框和该标记相关
- PackageManager.FLAG_PERMISSION_POLICY_FIXED
- 表示 permission 当前的授权状态是 device policy 固定的, app 和 用户都不能改变该 permission 的授权状态
- 具有该标记的 permission, 只有 system 用户才能修改它的授权状态和该 flag
- PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE
- 表示 app 是不支持 runtime permission 的, 在升级后开始支持 runtime permission, 因此, 在升级过程中, 取消了该 permission 的 授权
- PackageManager.FLAG_PERMISSION_SYSTEM_FIXED
- 表示 permission 当前的授权状态是因为该 package 是系统组件
- 具有该标记的 permission, 只有 system 用户才能修改它的授权状态和该 flag
- PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT
- 表示 permission 当前的授权状态是因为默认授权该 permission, 以便提供更好的 用户体验
###9. DefaultPermissionGrantPolicy 和 runtime permission
DefaultPermissionGrantPolicy 用于为系统组件或者 system handler 授予 runtime permission 授权
PackageManagerService.SystemReady()
DefaultPermissionPolicy.grantDefaultPermissions()
grantPermissionsToSysComponentsAndPrivApps()
grantDefaultSystemHandlerPermissions()
PackageManagerService.newUserCreated()
DefaultPermissionPolicy.grantDefaultPermissions()
grantPermissionsToSysComponentsAndPrivApps()
grantDefaultSystemHandlerPermissions()
DefaultPermissionPolicy.grantDefaultPermissions() 会为 需要预授权的package 的需要预授权的 permission 进行授权
- DefaultPermissionGrantPolicy.grantPermissionsToSysComponentsAndPrivApps() : 用于为系统组件或者使用 platform key sign 且 persistent 的 priv app 请求的所有的runtime permission 授权
- UID 小于 FIRST_APPLICATION_UID 的 package 会被视为 系统组件, 会被自动授予所有请求的runtime permission
- 若 apk设置了 PRIVATE_FLAG_PRIVILEGED 和 FLAG_PERSISTENT 标志且使用 platform key sign, 则也会被自动授予所有请求的runtime permission
- 如果某个 permission 的 flag 包含 PackageManager.FLAG_PERMISSION_POLICY_FIXED 或者 PackageManager.FLAG_PERMISSION_SYSTEM_FIXED, 则不处理该 permission
- 通过该接口设置权限状态成功的 permission 会被设置 PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT 和 PackageManager.FLAG_PERMISSION_SYSTEM_FIXED flag
- DefaultPermissionPolicy.grantDefaultSystemHandlerPermissions() 为需要预授权的system handler 的runtime permission 进行授权
- 通过该接口设置权限状态成功的 permission 会被设置 PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT flag, 但不会被设置 PackageManager.FLAG_PERMISSION_SYSTEM_FIXED flag
- 为 installer pacakge 授予 storage group 的 runtime permission
- 为 verifier package 授予 storage phone sms 3个group 的 runtime permission
- 为 SetupWizard (即 category 为 Intent.CATEGORY_SETUP_WIZARD 的所有 activity) 授予 phone 和 contacts group 权限
- 为 camera package 授予 camera microphone storage 3 个 group 的 runtime permission
- 为 Media provider 授予 storage 1个 group 的 runtime permision
- 为 Downloads provider 授予 storage 1个 group 的 runtime permission
- 为 Downloads UI 授予 storage 1个 group 的 runtime permission
- 为 Storage provider 授予 storage 1个 group 的 runtime permission
- 为 CertInstaller 授予 storage 1个 group 的 runtime permission
- 为 Dialer 授予 phone contacts sms microphone 4 个 group 的 runtime permission
- 为 Sim call manager 授予 phone 和 microphone 2 个 group 的 runtime permission
- 为 SMS 授予 phone contacts sms 3 个 group 的 runtime permission
- 为 Cell Broadcast Receiver 授予 sms 1个group 的 runtime permission
- 为 Carrier Provisioning Service 授予 sms 1个 group 的 runtime permission
- 为 Calendar 授予 calendar 和 contacts 2个 group 的 runtime permission
- 为 Calendar provider 授予 contacts calendar storage 3个 group 的 runtime permission
- 为 Calendar provider sync adapters 授予 calendar 1个 group 的 runtime permission
- 为 Contacts 授予 contacts phone 2个 group 的 runtime permission
- 为 Contacts provider sync adapters 授予 contact 1个 group 的 runtime permission
- 为 Contacts provider 授予 contacts phone storage 3个 group 的 runtime permission
- 为 Device provisioning 授予 contact 1个 group 的 runtime permission
- 为 Maps 授予 location 一个group 的 runtime permission
- 为 Gallery 授予 storage 1个 group 的 runtime permission
- 为 Email 授予 contacts 1个 group 的 runtime permission
- 为 Browser 授予 location 1个 group 的 runtime permission
- 为 IME 授予 contacts 1个 group 的 runtime permission
- 为 Voice interaction 授予 contacts calendar microphone phone sms location 6个 group 的 runtime permission
- 为 PackageManagerService.PackageManagerInternalImpl.setLocationPackagesProvider()
- 为 Voice recognition 授予 microphone 1个 group 的 runtime permission
- 为 Location 授予 contacts calendar microphone phone sms location camera sensors storage 9 个 group 的 permission
- 为 Music 授予 storage 1个 group 的 runtime permission
###10. DevicePolicyManagerService 和 runtime permission
android DevicePolicyManagerService 中也可以 修改 android runtime permission 的状态
DevicePolicyManagerService.setPermissionGrantState()
通过该接口将 runtime permission 设置为 grant 或者 deny 状态 时, 会设置 PackageManager.FLAG_PERMISSION_POLICY_FIXED flag, 设置为 default 状态时, 会清除 DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT flag
###11. 查看apk的权限
目前已有多个工具可以检测Android app所请求的Permission,这类工具有:aapt、apktool、androguard等等, 以 aapt (android 源码可以build出该host tool)为例
$ ./aapt dump permissions xxx.apk
package: com.xxx.wallpaper
uses-permission: name='android.permission.READ_PHONE_STATE'
uses-permission: name='android.permission.INTERNET'
uses-permission: name='android.permission.ACCESS_NETWORK_STATE'
uses-permission: name='android.permission.WRITE_EXTERNAL_STORAGE'
......
pm 命令可以检查检系统中的package所请求的permission及授权状态
$ pm dump com.android.phone
......
requested permissions:
android.permission.BROADCAST_STICKY
android.permission.CALL_PHONE
android.permission.CALL_PRIVILEGED
android.permission.WRITE_SETTINGS
android.permission.WRITE_SECURE_SETTINGS
android.permission.READ_CONTACTS
android.permission.READ_CALL_LOG
......
install permissions:
android.permission.SEND_RECEIVE_STK_INTENT: granted=true
android.permission.WRITE_SETTINGS: granted=true
android.permission.MODIFY_AUDIO_SETTINGS: granted=true
android.permission.MANAGE_ACCOUNTS: granted=true
android.permission.SYSTEM_ALERT_WINDOW: granted=true
......
Shared users:
SharedUser [android.uid.phone] (6df2b55):
userId=1001
install permissions:
android.permission.SEND_RECEIVE_STK_INTENT: granted=true
android.permission.WRITE_SETTINGS: granted=true
android.permission.MODIFY_AUDIO_SETTINGS: granted=true
android.permission.MANAGE_ACCOUNTS: granted=true
android.permission.SYSTEM_ALERT_WINDOW: granted=true
......
User 0:
gids=[1005, 3002, 3003, 3001, 3009]
runtime permissions:
android.permission.READ_SMS: granted=true, flags=[ GRANTED_BY_DEFAULT ]
android.permission.READ_CALL_LOG: granted=true, flags=[ GRANTED_BY_DEFAULT ]
android.permission.RECEIVE_SMS: granted=true, flags=[ GRANTED_BY_DEFAULT ]
android.permission.READ_EXTERNAL_STORAGE: granted=true, flags=[ GRANTED_BY_DEFAULT ]
- requested permissions: 该package申请的所有的 permission
- install permissions: 安装时授予的权限(非runtime permission)
- runtime permissions: runtime permission的授权状态
runtime permission 后的 ”flags=[ xxx ]” 值, 列出了该 permission 的flag
####12. 在app中请求runtime permission
app中可以使用如下的方式来 检查/申请 runtime permission
//检查给定的permission是否被授予
Contex.checkSelfPermission()
//请求获取指定的permission
Activity.requestPermissions()
//用于检查在请求permission前是否需要给出提示
Activity.shouldShowRequestPermissionRationale()
//permission请求的授权结果的回调方法
Activity.onRequestPermissionsResult()
这些接口是在 android M 中新添加的, 如果app需要在 targetSdk 小于 23 来编译, 就会不通过, 最简单粗暴的解决方法可能就是利用Build.VERSION.SDK_INT >= 23这个判断语句来判断了, 方便的是, support-v4 兼容库提供了如下的api, 可以支持同时在 targetsdk23 或旧的 sdk 编译运行
//检查给定的permission是否被授予
contextCompat.checkSelfPermission()
//请求获取指定的permission
ActivityCompat.requestPermission()
//用于检查在请求permission前是否需要给出提示
ActivityCompat.shouldShowRequestPermissionRationale()
//permission请求的授权结果的回调方法
ActivityCompat.onRequestPermissionsResult()
示例
// Here, thisActivity is the current activity
if (ContextCompat.checkSelfPermission(thisActivity,
Manifest.permission.READ_CONTACTS)
!= PackageManager.PERMISSION_GRANTED) {
// Should we show an explanation?
if (ActivityCompat.shouldShowRequestPermissionRationale(thisActivity,
Manifest.permission.READ_CONTACTS)) {
// Show an expanation to the user *asynchronously* -- don't block
// this thread waiting for the user's response! After the user
// sees the explanation, try again to request the permission.
} else {
// No explanation needed, we can request the permission.
ActivityCompat.requestPermissions(thisActivity,
new String[]{Manifest.permission.READ_CONTACTS},
MY_PERMISSIONS_REQUEST_READ_CONTACTS);
// MY_PERMISSIONS_REQUEST_READ_CONTACTS is an
// app-defined int constant. The callback method gets the
// result of the request.
}
}
@Override
public void onRequestPermissionsResult(int requestCode,
String permissions[], int[] grantResults) {
switch (requestCode) {
case MY_PERMISSIONS_REQUEST_READ_CONTACTS: {
// If request is cancelled, the result arrays are empty.
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// permission was granted, yay! Do the
// contacts-related task you need to do.
} else {
// permission denied, boo! Disable the
// functionality that depends on this permission.
}
return;
}
// other 'case' lines to check for other
// permissions this app might request
}
}
runtime permission 请求弹框由 PackageInstaller 弹出:
- 第一次请求permission时, 不会有 “不再提醒” 选项, 第二次请求permission时,才会有“不再提醒”的选项, 若用户一直拒绝, 并且没有选择“不再提醒”的选项, 下次请求permission时, 会继续有”不再提醒的选项”
- 第一次请求permission时, 若用户拒绝了, 后续若继续点击’拒绝’, 则每一次调用 shouldShowRequestPermissionRationale() 均将返回true
- 若在runtime permision请求弹框上勾选了“不再提醒”的选项时,并且点击了’拒绝’, 后续调用 shouldShowRequestPermissionRationale() 将返回false
- 若设备的策略禁止当前的应用获取这个permission的授权, 则 shouldShowRequestPermissionRationale() 一直返回false
###13. 系统组件的权限
通过在 AndroidManifest 使用“android:permission”属性, 可以设置所需的permission, 以限制对系统或者应用程序的组件的访问
####13.1 Activity permission
Activity permission 在 AndroidManifest.xml 中的 “<activity>”标签中声明, 用于限定启动该activity所需要的permission, permission检查在 Context.startActivity()
和 Activity.startActivityForResult()
中进行
####13.2 Service permission
Service permission 在 AndroidManifest.xml 中的 “<service>”标签中声明, 用于限定启动/连接到该service所需要的permission, permission检查在 Context.startService()
, Context.stopService()
和 Context.bindService()
中进行
####13.3 BroadcastReceiver permission
BroadcastReceiver permission 在 AndroidManifest.xml 中的 “<receiver>”标签中声明, 用于限制谁能够发送广播到相关的receiver
BroadcastReceiver permission可以分为2种情况:
- 有时候一些敏感的广播并不想让第三方的应用收到
- 有时后需要限定广播的发送方, 防止伪造广播
由于 BroadcastReceiver permission 的检查将在Context.sendBroadcast()
返回后进行,若permission检查失败, 也不会引发异常, 只是广播不会被分发到receiver, (若Sender 和 Receiver 有一方对某一要求BroadcastReceiver permission, 则必须双方都满足权限检查才能成功分发该广播)
使用 BroadcastReceiver permission 的步骤大约如下:
- 在 Sender 或者 Receiver 的AndroidManifest.xml 中自定义所需的permission(若使用系统中已有的permission则可以忽略这一步)
- 例如
<permission android:name = "com.android.permission.XXX"/>
- 例如
- 在 Sender app 的 AndroidManifest.xml 中声明使用该permission
- 例如
<uses-permission android:name="com.android.permission.XXX"></uses-permission>
- 例如
- 在 Sender app 中发送广播时, 将所需的permission作为参数
- 例如
sendBroadcast("com.android.XXX_ACTION", "com.android.permission.XXX");
- 例如
- 在 Receiver app的AndroidManifest.xml 中声明使用该permission
- 例如
<uses-permission android:name="com.android.permission.RECV_XXX"></uses-permission>
- 例如
- 在 Receiver app的Androidmanifest.xml中的<receiver>tag里添加该权限的permission
- 例如
<receiver android:name=".XXXReceiver" android:permission="com.android.permission.SEND_XXX">
- 或者在
Context.registerReceiver()
中传入所需的参数也是等价的
- 例如
####13.4 ContentProvider permissions
ContentProvider permissions AndroidManifest.xml 中的 “<provider>”标签中声明, 用于限定访问该provider中的数据所需要的permission
与上述的3种permission不同, provider具有2种分离的permission属性, 若一个 provider 受某个权限保护, 但是 accesser 并不具有该permission, 则会引发 accesser SecurityException
- android:readPermission
- 在
ContentResolver.query()
中检查
- 在
- android:writePermission
- 在
ContentResolver.insert()
,ContentResolver.update()
,ContentResolver.delete()
中检查
- 在
###14. URI permission
标准的permission系统对于content provider来说是不够的,一个content provider可能想保护它的读写权限,而同时与它对应的直属客户端也需要将特定的URI传递给其它应用程序,以便其它应用程序对该URI进行操作。一个典型的例子就是邮件程序处理带有附件的邮件。进入邮件需要使用permission来保护,因为这些是敏感的用户数据。然而,如果有一个指向图片附件的URI需要传递给图片浏览器,那个图片浏览器是不会有访问附件的权利的,因为他不可能拥有所有的邮件的访问权限。
针对这个问题的解决方案就是per-URI permission:当启动一个activity或者给一个activity返回结果的时候,呼叫方可以设置Intent.FLAG_GRANT_READ_URI_PERMISSION和/或 Intent.FLAG_GRANT_WRITE_URI_PERMISSION . 这会使接收该intent的activity获取到进入该Intent指定的URI的权限,而不论它是否有权限进入该intent对应的content provider
开放 URI permission 需要 Content Provider 在其 “<provider>” 标签中声明
android:grantUriPermissions="true"
全局开关, 即该provider支持URI permission<grant-uri-permissions>
可以控制允许部分的URI访问权限, 例如<grant-uri-permission android:pathPrefix="/hello" />
Context 提供2个方法
public void grantUriPermission(String toPackage, Uri uri, int modeFlags)
- 为某个 package 添加访问 content Uri 的读或者写权限
public void revokeUriPermission(Uri uri, int modeFlags)
- 移除所有对给定Uri的访问权限
###15. permission检查的api
PackageManager 提供了一个接口用于检查指定的 package 是否具有给定的permission
int checkPermission(String permName, String pkgName);
返回值为 PackageManager.PERMISSION_GRANTED
或者 PackageManager.PERMISSION_DENIED
ContextWrapper 提供了一些接口用来对外来的访问(包括自己)进行permission检查,具体实现在 ContextImpl 中 ,如果 package 接受到外来访问者的操作请求,那么可以调用这些接口进行permission检查。一般情况下可以把这些接口的检查接口分为两种
- 返回错误的api
- 抛出异常的api
####15.1 返回错误的api
- 检查权限, 返回值为
PackageManager.PERMISSION_GRANTED
或者PackageManager.PERMISSION_DENIED
int checkPermission(@NonNull String permission, int pid, int uid)
- 检查某个 uid 和 pid 是否有 permission 权限
int checkCallingPermission(String permission)
- 检查调用者是否有 permission 权限,如果调用者是自己那么返回 PackageManager.PERMISSION_DENIED
- 通常用于在Service中检查调用者的permission
int checkCallingOrSelfPermission(String permission)
- 检查自己或者其它调用者是否有 permission 权限
- 检查Uri权限, 返回值为
PackageManager.PERMISSION_GRANTED
或者PackageManager.PERMISSION_DENIED
int checkUriPermission(Uri uri, int pid, int uid, int modeFlags)
- 检查给定 uid pid, 对于给定的URI是否具有给定的权限(读/写), 只有显式地授予权限才会检查成功, 否则, 即使默认具有权限, 也会报fail
int checkCallingUriPermission(Uri uri, int modeFlags)
- 检查调用者是否对给定的uri具有给定的权限, 只有显式地授予权限才会检查成功, 否则, 即使默认具有权限, 也会报fail, 另外若调用者是自己, 也会fail
int checkCallingOrSelfUriPermission(Uri uri, int modeFlags)
- 检查调用者或者自己, 是否对给定的uri具有给定的权限, 只有显式地授予权限才会检查成功, 否则, 即使默认具有权限, 也会报fail
int checkUriPermission(Uri uri, String readPermission,String writePermission, int pid, int uid, int modeFlags)
- 相当于同时执行 checkPermission() 和 checkUriPermission()
####15.2 抛出异常的api
主要有如下几组接口
- 检查permission, 检查不通过则会抛出异常, 打印消息
void enforcePermission(String permission, int pid, int uid, String message)
- 检查某个 uid 和 pid 是否有给定的 permission 授权
void enforceCallingPermission(String permission, String message)
- 检查调用者是否有给定 permission 授权,如果调用者是自己则不通过
enforceCallingOrSelfPermission(String permission, String message)
- 检查自己或者其它调用者是否有给定的 permission 授权
- 检查Uri permission,检查不通过则会抛出异常, 打印消息
void enforceUriPermission(Uri uri, int pid, int uid, int modeFlags, String message)
- 检查给定 uid pid, 对于给定的URI是否具有给定的permission(读/写) 授权, 只有显式地授予permission才会检查成功, 否则, 即使默认具有permission, 也会检查不通过
void enforceCallingUriPermission(Uri uri, int modeFlags, String message)
- 检查调用者是否对给定的uri具有给定的permission, 只有显式地授予permission才会检查成功, 否则, 即使默认具有permission, 也会检查不通过, 另外若调用者是自己, 也会检查不通过
void enforceCallingOrSelfUriPermission(Uri uri, int modeFlags, String message)
- 检查调用者或者自己, 是否对给定的uri具有给定的permission, 只有显式地授予权限才会检查成功, 否则, 即使默认具有permission, 也会检查不通过
void enforceUriPermission(Uri uri, String readPermission, String writePermission,int pid, int uid, int modeFlags, String message)
- 相当于同时执行 checkPermission() 和 checkUriPermission()
下面这一组和上面类似,如果遇到检查不通过时,会抛出异常,打印消息
###16. permission检查的实现
ContextImpl.java 中提供的 API ,其实都是由 ActivityManagerService 中的如下几个接口进行的封装
int checkPermission(String permission, int pid, int uid) throws RemoteException;
int checkUriPermission(Uri uri, int pid, int uid, int mode) throws RemoteException;
以 checkPermission 为例, 其执行流程如下
- ActivityManagerService.CheckPermission()
- 如果 permission 为 null, 则返回 PackageManager.PERMISSION_DENIED
- ActivityManagerService.checkComponentPermission()
- 如果 pid 为system server 的pid, 则不做控制, 返回 PackageManager.PERMISSION_GRANTED
- ActivityManager.checkComponentPermission()
- 如果 uid 为 root 或者 system 则不做控制, 返回 PackageManager.PERMISSION_GRANTED
- 如果是被隔离的uid (99000 ~ 99999) , 则不授予任何权限, 返回 PackageManager.PERMISSION_DENIED
- 如果被检查权限的uid和 定义该权限的app的uid相同, 则不做控制, 返回 PackageManager.PERMISSION_DENIED
- 如果 permissiion 为null则返回 PackageManager.PERMISSION_DENIED
- PackageManagerService.checkUidPermission()
- 调用 Settings.getUserIdLPr() 去 PackageManagerService.Setting.mUserIds 数组中,根据 uid 查找 uid (也就是 package )的权限列表。一旦找到,就表示有相应的权限
- 如果没有找到,那么再去 PackageManagerService.mSystemPermissions 中找。这些信息是启动时,从 /system/etc/permissions/platform.xml 中读取的。这里记录了一些系统级的应用的 uid 对应的 permission