0%

Android权限管理

1. Android权限管理

原作者: xiaoguang.dong

1.1. 基本概念

1.1.1. AndroidManifest权限相关标签

permissionimage9

1.1.1.1. permission 标签

permissionimage10

  • permission标签用于声明一个权限
  • android:name 指定权限名称
  • android:protectionLevel指定权限的保护级别
  • android:protectionLevel指定权限的保护级别

permissionimage11

1.1.1.2. uses-permission标签

permissionimage12

  • <uses-permission>用于申请特定权限
  • android:name指定申请的权限名称
  • android:maxSdkVersion用于指定可被授权的最高的API level

在4.4平台(API level 19)之前,使用某个接口依赖于权限A;但是在4.4平台上A权限已经被废弃或者该接口已经不依赖于A权限,那么可以做如下配置:<uses-permission android:name=“A” android:maxSdkVersion=“18”/>那么在4.4平台之后,系统将不会授予应用A权限

1.1.1.3. permission-group标签

permissionimage13

  • <permission-group>用于标记权限的逻辑分组。
  • android:name用于指定权限组的名称。
  • android:label用于指定权限组的标签。
  • android:descriptiopn用于指定权限组的描述信息。
  • android:icon用于指定权限组的图标。

如果权限有Permission-group属性,用户在Setting权限界面看到的就是permission-grouplabeldescription,否则使用permission本身的labeldescription

permissionimage14

1.1.1.4. permission-tree标签

1
2
3
<permission-tree android:icon="drawable resource"
android:label="string resource" ]
android:name="string" />

注意这个元素自身不声明一个权限,只是一个名字空间,更多的权限可以被放置在它里面。

  • <permission-tree>用于定义一组权限的命名空间。作用是可以使程序在运行时动态声明权限。
  • android:name用于指定权限树根节点的名称。
  • android:label 组的用户可读名称, 标签可以直接设置为一个原始字符串, 使它可以像用户界面中的其它字符串那样被本地化。

如果某个应用使用了<permission-tree>标签,可以通过调用PackageManager.addPermission()方法来动态声明新权限。假如根节点的名称是com.sprd.core,则可以声明如下权限: com.sprd.core.CONTACTS com.sprd.core.MMS

1.1.2. 权限控制

1.1.2.1. 四大组件权限检查

permissionimage16

  • android:permission适用于activity/service/provider/receiver标签。

activity为例,假如服务端activity配置了android:permission属性,客户端在调用 startActivity()startActivityForResult()启动服务端activity时,如果没有被授予指定权限,其 Intent 将不会传递给服务端Activity。如果未设置该属性,则对 Activity 应用 <application> 元素的 permission 属性设置的权限。 如果这两个属性均未设置,则 Activity 不受权限保护。

1.1.2.2. shareUserId 属性

permissionimage17

permissionimage18

  • android:SharedUserId指定与其它应用共享一个UID,只有两个使用相同证书签名(并且请求相同的 sharedUserId)的应用才被分配同一UID。通过共享UID,不同的APK之间可以实现数据共享。

PackageManager初始化时默认提供6个系统级sharedUserId,均使用platform证书验证。

1
2
3
4
5
6
android.uid.system
android.uid.phone
android.uid.log
android.uid.shell
android.uid.nfc
android.uid.bluetooth

1.1.2.3. 权限级别/ProtectionLevel

1.1.2.3.1. normal

低风险权限,只要申请了就可以使用(在AndroidManifest.xml中添加uses-permission标签),安装时不需要用户确认

1.1.2.3.2. Dangerous

高风险权限,需要用户的确认才可被授权

1.1.2.3.3. Signature

当申请权限的APK与声明此权限的APK的使用的签名证书相同时,才能被授权

1.1.2.3.4. SignatureOrSystem
  1. 当申请权限的APK与声明此权限的APK的使用的签名证书相同时,才能被授权。
  2. 申请权限的应用为系统应用

1.1.2.4. Signature vs SignatureOrSystem

  • 针对ProtectionLevelsignatureOrSystem的应用,有如下应用场景:如果有多个不同的厂商同时在system/app下预制了多个不同的APK,每个APK签名使用的证书是不同的。但是这些APK之间彼此还希望访问对方的一些数据。此时就可以使用signatureOrSystem权限来保护应用数据。
  • 与之而来的问题在于,所有/system/app下的应用都可以访问用signatureOrSystem访问的数据,虽然可以用getCallingUid,getCallingPackage来确认访问者的身份。但是这里是存在隐患的。
  • 添加priva-app目录解决了这个问题。在4.4以后SignatureOrSystem级别的权限只有具有相同签名,或者预制在priv-app下的应用才被授权。
  • 默认情况下预制在priv-app下的应用必须Google原生应用或是OEM厂商确保安全的第三方应用。

1.1.2.5. 定义权限作用?怎样通过权限实现应用数据和资源的保护?

  • 程序的基础是代码数据,所以定义权限的两个目的就是控制代码执行流程保护数据/文件
  • 针对AndroidManifest中定义的各种权限,其权限的授权状态都会存储在PackageManagerService中,系统/APP可以通过PackageManager相关接口来检查调用端进程UID是否具有特定权限,进而决定是否允许其进一步执行代码或者访问数据/文件。可以将PackageManager看做一个权限管理的中转站。

permissionimage19

1.1.2.6. Native层的权限控制

Native层的权限控制建立在Linux已有的uid/gid/gids基础上的。

  • UID: Android 每安装一个应用程序,就会为它分配一个 uid 。其中普通 Android 应用程序的 uid 是从 10000 开始分配 (Process.FIRST_APPLICATION_UID ), 10000 以下是系统应用预留的 uid
  • GID: 对于普通应用程序来说, gid 等于 uid
  • 由于每个应用程序的 uidgid 都不相同, 因此不管是 native 层还是 java 层都能够达到保护私有数据的作用。
  • GIDS: 在 Apk安装过程中生成,与申请的具体权限相关。 如果APK某个申请的permissiongranted ,而且这个权限在/system/etc/permisions/*xml中有对应的 gid, 那么 这个 Apk对应的进程gids中将 包含xml中配置的gid

Eg: 在Android5.1平台上,应用申请了如下权限:<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> ,同时在frameworks\base\data\etc\Platform.xml中由如下配置:

1
2
3
<permission name="android.permission.WRITE_EXTERNAL_STORAGE" >  
<group gid="sdcard_rw" />
</permission>

对于申请WRITE_EXTERNAL_STORAGE特权的应用,进程的gids就包含了sdcard_rw,就可以对sd卡中的文件进行操作。

1.2. 运行时权限

  • normal/signature/signatureOrSystem会在App安装时判断是否授权,可以看做是安装时权限。
  • AndroidM开始,Google提出了运行时权限(runtime permission)的概念。针对protectionLevledangerous的权限,在安装时默认不授权,而是在运行时由用户动态授权。
  • 对于运行时权限,要求App在代码中做一定的适配,在运行时动态向用户请求相关权限。
  • 在多用户模式下,所有用户共享安装时权限,独占运行时权限。

Android定义的Runtime权限一览共9组25个:

permissionimage20

permissionimage21

permissionimage22

permissionimage23

permissionimage24

permissionimage25

1.2.1. 注意事项

  1. 如果应用开发时使用的targetSDK低于23,在安装到M和以上平台时,考虑到不支持动态权限相关的接口。系统会默认授予所有权限。
  2. 同一组的任何一个权限被授权了,其他权限也自动被授权。不需要重复申请。
  3. 只申请需要的权限,需要的时候再申请权限;每次权限申请,都会弹出权限申请窗口,打断用户的操作。建议只申请你需要的权限。
  4. Setting中动态关闭权限会导致进程被Kill
  5. 权限请求需要弹出Dialog,因此请求代码要添加到ActivityFragment
  6. 尽可能避免在Service中申请权限。如果确实需要,要做一个work around。比如弹出一个Notification,提醒用户缺少相关权限。当用户点击Notification在弹出权限申请框,继续权限申请流程。

1.3. 默认授权机制

  • 为了确保Android原生应用,或者GMS包中的应用在开机后具有必需的权限。比如CameraApp默认授予Camera Group相关权限,Contacts默认授予Contacts Group相关权限….
  • PackageManager默认在首次开机/恢复出厂设置后,对一部分系统应用默认授予一些必需的权限。

1.3.1. 首次开机时默认授权的应用

应用使用requestPermissions接口申请运行时权限,首次开机时,满足下列条件时,会对相应的权限进行授予:

  • UID < 10000的应用
  • 同时满足如下条件的应用:
    1. 预制在/system/priv-app下。
    2. Application标签中配置了android:persistence=“true”
    3. 使用Platform证书签名。
  • 通过Intent匹配查询出的默认应用,为其授予合适分组的权限。
    • Music为例,代码中会自动匹配如下Intent对应的component
1
2
3
Intent musicIntent = new Intent(Intent.ACTION_VIEW);
musicIntent.addCategory(Intent.CATEGORY_DEFAULT);
musicIntent.setDataAndType(Uri.fromFile(new File("foo.mp3")), AUDIO_MIME_TYPE);

默认情况下,6.07.0下匹配结果是不一致的。如果有多个同类型的应用,在6.0上会根据返回结果取第一个元素进行授权;在7.0上则会进行一系列比较,选出优先级最高的那个进行授权。如果前两个元素的优先级相同,则不会对任何一个匹配到的元素授权

  • 对于首次开机授权的应用,有一部分权限是系统固定的,用户无法修改。在Setting->APP中也看不到对应的权限状态。
  • 修改默认授权的逻辑会导致GTS CASE失败。

1.3.2. 查看Package当前的权限状态

通过adb shell dumpsys package 可以查看某个Package当前的权限状态:

permissionimage26