基于android P平台权限管理详解

[TOC]# 一 概述Android 是一个权限分隔的操作系统,在安装应用时,Android 为每个软件包提供唯一的系统标识(Linux 用户 ID 和组 ID)。此 ID 在软件包在该设备上的使用寿命期间保持不变。系统各部分也分隔为不同的标识。Linux 据此将不同的应用以及应用与系统分隔开来。在默认情况下任何应用都没有权限执行对其他应用、操作系统或用户有不利影响的任何操作。这包括读...
摘要由CSDN通过智能技术生成

[TOC]

# 一 概述

Android 是一个权限分隔的操作系统,在安装应用时,Android 为每个软件包提供唯一的系统标识(Linux 用户 ID 和组 ID)。此 ID 在软件包在该设备上的使用寿命期间保持不变。系统各部分也分隔为不同的标识。Linux 据此将不同的应用以及应用与系统分隔开来。

在默认情况下任何应用都没有权限执行对其他应用、操作系统或用户有不利影响的任何操作。这包括读取或写入用户的私有数据(例如联系人或电子邮件)、读取或写入其他应用程序的文件、执行网络访问、使设备保持唤醒状态等。

由于每个 Android 应用都是在进程沙盒中运行,因此应用必须显式共享资源和数据。它们的方法是声明需要哪些权限来获取基本沙盒未提供的额外功能。应用以静态方式声明它们需要的权限,然后 Android 系统提示用户同意。

例如,需要监控传入的短信的应用要指定:

```
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.android.app.myapp" >
    <uses-permission android:name="android.permission.RECEIVE_SMS" />
    ...
</manifest>
```

如果在应用清单中列出`正常权限`(即不会对用户隐私或设备操作造成很大风险的权限),系统会自动授予这些权限。如果列出`危险权限`(即可能影响用户隐私或设备正常操作的权限),系统会要求用户明确授予这些权限。Android 发出请求的方式取决于系统版本,而系统版本是应用的目标:

- 如果设备运行的是 `Android 6.0(API 级别 23)或更高版本`,并且应用的 `targetSdkVersion` 是 23 或更高版本,则应用在运行时向用户请求权限。用户可随时调用权限,因此应用在每次运行时均需检查自身是否具备所需的权限。

- 如果设备运行的是 `Android 5.1(API 级别 22)或更低版本`,并且应用的 `targetSdkVersion` 是 22 或更低版本,则系统会在用户安装应用时要求用户授予权限。如果将新权限添加到更新的应用版本,系统会在用户更新应用时要求授予该权限。用户一旦安装应用,他们撤销权限的唯一方式是卸载应用。

通常,权限失效会导致 `SecurityException` 被扔回应用。但不能保证每个地方都是这样。例如,`sendBroadcast(Intent)` 方法在数据传递到每个接收者时会检查权限,在方法调用返回后,即使权限失效,也不会收到异常。但在几乎所有情况下,权限失效会记入系统日志。

Android 系统提供了一些默认权限,任何应用都可定义并实施自己的权限。可能在程序运行期间的多个位置实施特定权限:

- 在调用系统时,防止应用执行某些功能;

- 在启动 `Activity` 时,防止应用启动其他应用的 `Activity`;

- 在发送和接收 `Broadcast` 时,控制谁可以接收您的 `Broadcast` ,谁可以向您发送 `Broadcast`;

- 在访问和操作 `ContentProvider` 时;

- 绑定至服务或启动 `Service`。

# 二 Android 权限

Android 系统提供的权限

> frameworks/base/core/res/AndroidManifest.xml

```xml {.line-numbers}
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="android" coreApp="true" android:sharedUserId="android.uid.system"
    android:sharedUserLabel="@string/android_system_label">

    ...

    <!-- Used for runtime permissions related to contacts and profiles on this
        device. -->
    <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:request="@string/permgrouprequest_contacts"
        android:priority="100" />

    <!-- Allows an application to read the user's contacts data.
        <p>Protection level: dangerous
    -->
    <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.GET_ACCOUNTS"
        android:permissionGroup="android.permission-group.CONTACTS"
        android:protectionLevel="dangerous"
        android:description="@string/permdesc_getAccounts"
        android:label="@string/permlab_getAccounts" />
    <uses-permission android:name="android.permission.GET_ACCOUNTS"/>

    ...

</manifest>
```

定义权限可用的标签:

> frameworks/base/core/res/res/values/attrs_manifest.xml

```xml {.line-numbers}
<resources>

    ...

    <declare-styleable name="AndroidManifestPermission" parent="AndroidManifest">
        <!-- Required public name of the permission, which other components and
        packages will use when referring to this permission.  This is a string using
        Java-style scoping to ensure it is unique.  The prefix will often
        be the same as our overall package name, for example
        "com.mycompany.android.myapp.SomePermission". -->
        <attr name="name" />
        <attr name="label" />
        <attr name="icon" />
        <attr name="roundIcon" />
        <attr name="banner" />
        <attr name="logo" />
        <attr name="permissionGroup" />
        <attr name="description" />
        <attr name="request" />
        <attr name="protectionLevel" />
        <attr name="permissionFlags" />
    </declare-styleable>

    ...

    <declare-styleable name="AndroidManifestPermissionGroup" parent="AndroidManifest">
        <!-- Required public name of the permission group, permissions will use
        to specify the group they are in.  This is a string using
        Java-style scoping to ensure it is unique.  The prefix will often
        be the same as our overall package name, for example
        "com.mycompany.android.myapp.SomePermission". -->
        <attr name="name" />
        <attr name="label" />
        <attr name="icon" />
        <attr name="roundIcon" />
        <attr name="banner" />
        <attr name="logo" />
        <attr name="description" />
        <attr name="request" />
        <attr name="permissionGroupFlags" />
        <attr name="priority" />
    </declare-styleable>

    ...

<resources/>
```

简要介绍下 permission 几个主要标签的含义:

- `name`:必选,权限的名字,供 uses-permission 等标签使用;

    ```
    <attr name="name" format="string" />
    ```

- `label`:提示给用户的权限名;

    ```
    <attr name="label" format="reference|string" />
    ```

- `icon`:权限图标

    ```
    <attr name="icon" format="reference" />
    ```

- `permissionGroup`:权限组,系统定义的 dengerous 权限都是有权限组的

    ```
    <attr name="permissionGroup" format="string" />
    ```

- `description`:提示给用户的权限描述

    ```
    <attr name="description" format="reference" />
    ```

- `protectionLevel`:必选,分为 normal、dengerous、signature、signatureOrSystem

    - `normal`:风险较低的权限,任何应用都可以申请,在安装应用时,不会直接提示给用户,点击全部才会展示;
    
    - `dengerous`:风险较高的权限,任何应用都可以申请,使用时需要用户确认才能使用;

    - `signature`:仅当申请该权限的应用程序与声明该权限的程序使用相同的签名时,才赋予该权限;

    - `signatureOrSystem`:仅当申请该权限的应用程序位于相同的 Android 系统镜像中,或申请该权限的应用程序与声明该权限的程序使用相同的签名时,才赋予该权限。

- `permissionFlags`:表明权限更多上下文的标识

    ```
        <!-- Flags indicating more context for a permission. -->
        <attr name="permissionFlags">
            <!-- Set to indicate that this permission allows an operation that
                 may cost the user money.  Such permissions may be highlighted
                when shown to the user with this additional information.  -->
            <flag name="costsMoney" value="0x0001" />
            <!-- Additional flag from base permission type: this permission has been
                 removed and it is no longer enforced. It shouldn't be shown in the
                 UI. Removed permissions are kept as normal permissions for backwards
                 compatibility as apps may be checking them before calling an API.
            -->
            <flag name="removed" value="0x2" />
        </attr>
    ```

可以使用以下命令查看当前设备的所有权限:

> adb shell pm list permissions

## 2.1 自动权限调整

随着时间的推移,平台中可能会加入新的限制,要想使用特定 API,您的应用可能必须请求之前不需要的权限。因为现有应用假设可随意获取这些 API 应用的访问权限,所以 Android 可能会将新的权限请求应用到应用清单,以免在新平台版本上中断应用。Android 将根据为 `targetSdkVersion` 属性提供的值决定应用是否需要权限。如果该值低于在其中添加权限的版本,则 Android 会添加该权限。

例如,API 级别 4 中加入了 `WRITE_EXTERNAL_STORAGE` 权限,用以限制访问共享存储空间。如果您的 `targetSdkVersion` 为 3 或更低版本,则会向更新 Android 版本设备上的应用添加此权限。

> 注意:如果某权限自动添加到应用,则即使您的应用可能实际并不需要这些附加权限,`Google Play` 上的应用列表也会列出它们。

为避免这种情况,并且删除您不需要的默认权限,请始终将 `targetSdkVersion` 更新至最高版本。

## 2.2 权限保护级别

系统权限分为几个保护级别。需要了解的两个最重要保护级别是`正常权限`和`危险权限`:

- `正常权限`涵盖应用需要访问其沙盒外部数据或资源,但对用户隐私或其他应用操作风险很小的区域。例如,设置时区的权限就是`正常权限`。如果应用声明其需要`正常权限`,系统会自动向应用授予该权限;

- `危险权限`涵盖应用需要涉及用户隐私信息的数据或资源,或者可能对用户存储的数据或其他应用的操作产生影响的区域。例如,能够读取用户的联系人属于`危险权限`。如果应用声明其需要`危险权限`,则用户必须明确向应用授予该权限。

    使用以下命令可以查看当前设备的所有`危险权限`:

    > adb shell pm list permissions -d -g

    ![image](res/dengerous_permissions.png)

- `签名权限`需要应用具有相同的签名认证,在应用安装时系统会自动授予相同签名的应用声明要使用的该权限。使用系统签名的权限,一般三方应用是无法使用的。Android 系统提供了特殊方式去使用这种权限,例如:
    
    ```xml {.line-numbers}
        <!-- Must be required by an {@link android.accessibilityservice.AccessibilityService},
         to ensure that only the system can bind to it.
         <p>Protection level: signature
        -->
        <permission android:name="android.permission.BIND_ACCESSIBILITY_SERVICE"
            android:protectionLevel="signature" />
    ```

## 2.3 特殊权限

有些权限其行为方式与`正常权限`及`危险权限`都不同。`SYSTEM_ALERT_WINDOW` 和 `WRITE_SETTINGS` 特别敏感,因此大多数应用不应该使用它们。如果某应用需要其中一种权限,必须在清单中声明该权限,并且发送请求用户授权的 intent。系统将向用户显示详细管理屏幕,以响应该 intent。

### 2.3.1 SYSTEM_ALERT_WINDOW

申请使用悬浮窗权限:

- 在清单文件中申请使用该权限;

    ```xml {.line-numbers}
    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
    ```

- `API 23` 之后判断是否拥有该权限,之前是默认开启的;

    ```java {.line-numbers}
    // 检查是否已经授予权限
    if (Settings.canDrawOverlays(this)) {
        // TODO
    } else {
        // 若未授权则请求权限
        getOverlayPermission();
    }
    ```

- 申请权限(API>=23)

    ```java {.line-numbers}
    // 请求悬浮窗权限
    @TargetApi(Build.VERSION_CODES.M)
    private void getOverlayPermission() {
        Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
        intent.setData(Uri.parse("package:" + getPackageName()));
        startActivityForResult(intent, 0);
    }
    ```

- 在 `onActivityResult` 方法中再次判断并做相关处理;

### 2.3.2 WRITE_SETTINGS

申请读取或修改系统设置权限:

- 在清单文件中申请使用该权限;

    ```xml {.line-numbers}
    <uses-permission android:name="android.permission.WRITE_SETTINGS"/>
    ```

- `API 23` 之后判断是否拥有该权限,之前是默认开启的;

    ```java {.line-numbers}
    // 检查是否已经授予权限
    if (Settings.System.canWrite()) {
        // TODO
    } else {
        // 若未授权则请求权限
        getWriteSettingsPermission();
    }
    ```

- 申请权限(API>=23)

    ```java {.line-numbers}
    // 请求更改设置权限
    @TargetApi(Build.VERSION_CODES.M)
    private void getWriteSettingsPermission() {
        Intent intent = new Intent(Settings.ACTION_MANAGE_WRITE_SETTINGS);
        intent.setData(Uri.parse("package:" + getPackageName()));
        startActivityForResult(intent, 0);
    }
    ```

- 在 `onActivityResult` 方法中再次判断并做相关处理;

## 2.4 权限组

Android 系统中定义的所有`危险权限`都属于`权限组`。如果设备运行的是 `Android 6.0(API 级别 23)`,并且应用的 `targetSdkVersion` 是 23 或更高版本,则当用户请求`危险权限`时系统会发生以下行为:

- 如果应用请求其清单中列出的`危险权限`,而应用目前未获得该权限所在`权限组`中的任何权限,则系统会向用户显示一个对话框,描述应用要访问的`权限组`。对话框不描述该组内的具体权限。例如,如果应用请求 `READ_CONTACTS` 权限,系统对话框只说明该应用需要访问设备的联系信息。如果用户批准,系统将向应用授予其请求的权限;

- 如果应用请求其清单中列出的`危险权限`,而应用在同一`权限组`中已有另一项`危险权限`,则系统会立即授予该权限,而无需与用户进行任何交互。例如,如果某应用已经请求并且被授予了 `READ_CONTACTS` 权限,然后它又请求 `WRITE_CONTACTS`,系统将立即授予该权限。

任何权限都可属于一个`权限组`,包括`正常权限`和`应用定义的权限`。但`权限组`仅当权限危险时才影响用户体

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值