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

[TOC]

# 一 概述

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

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

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

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

```
    package="com.android.app.myapp" >
   
    ...

```

如果在应用清单中列出`正常权限`(即不会对用户隐私或设备操作造成很大风险的权限),系统会自动授予这些权限。如果列出`危险权限`(即可能影响用户隐私或设备正常操作的权限),系统会要求用户明确授予这些权限。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}
    package="android" coreApp="true" android:sharedUserId="android.uid.system"
    android:sharedUserLabel="@string/android_system_label">

    ...

   
            android:icon="@drawable/perm_group_contacts"
        android:label="@string/permgrouplab_contacts"
        android:description="@string/permgroupdesc_contacts"
        android:request="@string/permgrouprequest_contacts"
        android:priority="100" />

   
            android:permissionGroup="android.permission-group.CONTACTS"
        android:label="@string/permlab_readContacts"
        android:description="@string/permdesc_readContacts"
        android:protectionLevel="dangerous" />

    ...

            android:permissionGroup="android.permission-group.CONTACTS"
        android:protectionLevel="dangerous"
        android:description="@string/permdesc_getAccounts"
        android:label="@string/permlab_getAccounts" />
   

    ...


```

定义权限可用的标签:

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

```xml {.line-numbers}

    ...

   
       
       
       
       
       
       
       
       
       
       
       
       
   

    ...

   
       
       
       
       
       
       
       
       
       
       
       
   

    ...


```

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

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

    ```
   
    ```

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

    ```
   
    ```

- `icon`:权限图标

    ```
   
    ```

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

    ```
   
    ```

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

    ```
   
    ```

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

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

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

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

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

    ```
       
       
           
           
           
           
       

    ```

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

> 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}
       
                    android:protectionLevel="signature" />
    ```

## 2.3 特殊权限

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

### 2.3.1 SYSTEM_ALERT_WINDOW

申请使用悬浮窗权限:

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

    ```xml {.line-numbers}
   
    ```

- `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}
   
    ```

- `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`,系统将立即授予该权限。

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

如果设备运行的是 `Android 5.1(API 级别 22)或更低版本`,并且应用的 `targetSdkVersion` 是 22 或更低版本,则系统会在安装时要求用户授予权限。再次强调,系统只告诉用户应用需要的`权限组`,而不告知具体权限。

## 2.5 自定义权限及应用

应用可以使用 `` 定义自己的自定义权限,并通过定义 `` 元素请求其他应用的自定义权限。具体可参考 Android 系统中定义的 permission 进行自定义。

可以通过 `AndroidManifest.xml` 应用高级权限,限制访问系统或应用的全部组件。要执行此操作,在所需的组件上包含 `android:permission` 属性,为用于控制访问它的权限命名。

- `Activity` 权限(应用于 `` 标签)限制谁可以启动相关的 `Activity`。在 `Context.startActivity()` 和 `Activity.startActivityForResult()` 时会检查权限;如果调用方没有所需的权限,则调用会抛出 `SecurityException`。

- `Service` 权限(应用于 `` 标签)限制谁可以启动或绑定到相关的服务。在 `Context.startService()、Context.stopService()` 和 `Context.bindService()` 时会检查权限;如果调用方没有所需的权限,则调用会抛出 `SecurityException`。

- `BroadcastReceiver` 权限(应用于 `` 标签)限制谁可以发送广播给相关的接收方。在 `Context.sendBroadcast()` 返回后检查权限,因为系统会尝试将提交的广播传递到指定的接收方。因此,权限失效不会导致向调用方抛回异常;只是不会传递该 intent。同样,可以向 `Context.registerReceiver()` 提供权限来控制谁可以广播到以编程方式注册的接收方。另一方面,可以在调用 `Context.sendBroadcast()` 时提供权限来限制允许哪些 `BroadcastReceiver` 对象接收广播。

- `ContentProvider` 权限(应用于 `` 标签)限制谁可以访问 `ContentProvider` 中的数据。(`ContentProvider` 有重要的附加安全工具可用,称为 `URI 权限`,将在后面介绍。)与其他组件不同,您可以设置两个单独的权限属性:`android:readPermission` 限制谁可以读取,`android:writePermission` 限制谁可以写入。请注意,如果 `ContentProvider` 有读取和写入权限保护,仅拥有写入权限并不表示您可以读取。第一次检索 `ContentProvider` 时将会检查权限(如果没有任何权限,将会抛出 `SecurityException`),对 `ContentProvider` 执行操作时也会检查权限。使用 `ContentResolver.query()` 需要拥有读取权限;使用 `ContentResolver.insert()、ContentResolver.update()、ContentResolver.delete()` 需要写入权限。在所有这些情况下,没有所需的权限将导致调用抛出 `SecurityException`。

### 2.5.1 其他权限应用

可对任何服务调用实施任意细化的权限。这可通过 `Context.checkCallingPermission()` 方法完成。使用所需的权限字符串调用,它将返回一个整数,表示权限是否已授予当前的调用进程。请注意,仅在执行从另一个进程传入的调用(通常是通过从服务发布的 IDL 界面或者指定给另一进程的某种其他方式完成)时才可使用此方法。

检查权限还有许多其他有用的方法。如果您有另一个进程的 `pid`,可以使用 `Context` 方法 `Context.checkPermission(String, int, int)` 检查针对该 `pid` 的权限。如果您有另一个应用的软件包名称,可以使用直接的 `PackageManager` 方法 `PackageManager.checkPermission(String, String)` 了解是否已为特定软件包授予特定权限。

### 2.5.2 URI 权限

到目前为止所述的是标准权限系统,`ContentProvider` 仅仅使用此系统通常是不够的。`ContentProvider` 可能需要通过读取和写入权限保护自己,而其直接客户端也需要将特定 `URI` 传给其他应用以便于它们运行。邮件应用中的附件是一个典型的示例。应通过权限保护对邮件的访问,因为这是敏感的用户数据。但是,如果将图像附件的 `URI` 提供给图像查看程序,该图像查看程序不会有打开附件的权限,因为它没有理由拥有访问所有电子邮件的权限。

此问题的解决方法是采用 `per-URI` 权限机制:在启动 `Activity` 或返回结果给 `Activity` 时,调用方可以设置 `Intent.FLAG_GRANT_READ_URI_PERMISSION` 和(或) `Intent.FLAG_GRANT_WRITE_URI_PERMISSION`。这将授予接收 `Activity` 权限访问 intent 中的特定数据 `URI`,而不管它是否具有访问 intent 对应的 `ContentProvider` 中数据的任何权限。

此机制支持常见的能力式模型,其中用户交互(打开附件、从列表中选择联系人等)驱动临时授予细化的权限。这是一项关键功能,可将应用所需的权限缩小至只与其行为直接相关的权限。

但授予细化的 `URI` 权限需要与拥有这些 `URI` 的 `ContentProvider` 进行一定的合作。强烈建议 `ContentProvider` 实施此功能,并且通过 `android:grantUriPermissions` 属性或 `` 标签声明支持此功能。

在 `Context.grantUriPermission()、Context.revokeUriPermission()` 和 `Context.checkUriPermission()` 方法中可以找到更多信息。

## 2.6 运行时权限

从 `Android 6.0(API 级别 23)` 开始,用户开始在应用运行时向其授予权限,而不是在应用安装时授予。此方法可以简化应用安装过程,因为用户在安装或更新应用时不需要授予权限。它还让用户可以对应用的功能进行更多控制;例如,用户可以选择为相机应用提供相机访问权限,而不提供设备位置的访问权限。用户可以随时进入应用的“Settings”界面管理权限。

### 2.6.1 检查权限

如果应用需要`危险权限`,则每次执行需要这一权限的操作时都必须检查自己是否具有该权限。用户始终可以自由管理此权限,因此,即使应用昨天使用了相机,它不能假设自己今天仍具有该权限。

要检查是否具有某项权限,请调用 `ContextCompat.checkSelfPermission()` 方法。例如,以下代码段显示了如何检查 `Activity` 是否具有在日历中进行写入的权限:

```java {.line-numbers}
// Assume thisActivity is the current activity
int permissionCheck = ContextCompat.checkSelfPermission(thisActivity,
        Manifest.permission.WRITE_CALENDAR);
```

如果应用具有此权限,方法将返回 `PackageManager.PERMISSION_GRANTED`,并且应用可以继续操作。如果应用不具有此权限,方法将返回 `PERMISSION_DENIED`,且应用必须明确向用户要求权限。

### 2.6.2 请求权限

如果应用需要应用清单中列出的`危险权限`,那么,它必须要求用户授予该权限。Android 提供了多种权限请求方式。调用这些方法将显示一个标准的 Android 对话框,不过不能对它们进行自定义。

- 解释应用为什么需要权限

    在某些情况下,可能需要帮助用户了解应用为什么需要某项权限。例如,如果用户启动一个摄影应用,用户对应用要求使用相机的权限可能不会感到吃惊,但用户可能无法理解为什么此应用想要访问用户的位置或联系人。在请求权限之前,不妨为用户提供一个解释。请记住,不需要通过解释来说服用户;如果提供太多解释,用户可能发现应用令人失望并将其移除。

    可以采用的一个方法是仅在用户已拒绝某项权限请求时提供解释。如果用户继续尝试使用需要某项权限的功能,但继续拒绝权限请求,则可能表明用户不理解应用为什么需要此权限才能提供相关功能。对于这种情况,比较好的做法是显示解释。

    为了帮助查找用户可能需要解释的情形,Android 提供了一个实用程序方法,即 `shouldShowRequestPermissionRationale()`。如果应用之前请求过此权限但用户拒绝了请求,此方法将返回 true。

    > 注:如果用户在过去拒绝了权限请求,并在权限请求系统对话框中选择了 `Don't ask again` 选项,此方法将返回 false。如果设备规范禁止应用具有该权限,此方法也会返回 false。

- 请求需要的权限

    如果应用尚无所需的权限,则应用必须调用一个 `requestPermissions()` 方法,以请求适当的权限。应用将传递其所需的权限,以及指定用于识别此权限请求 `requestCode`。此方法异步运行:它会立即返回,并且在用户响应对话框之后,系统会使用结果调用应用的回调方法,将应用传递的相同请求代码传递到 `requestPermissions()`。

    以下代码可以检查应用是否具备读取用户联系人的权限,并根据需要请求该权限:

    ```java {.line-numbers}
    // 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.
        }
    }
    ```

    > 注:当应用调用 `requestPermissions()` 时,系统将向用户显示一个标准对话框。应用无法配置或更改此对话框。如果需要为用户提供任何信息或解释,应在调用 `requestPermissions()` 之前进行,例如自定义界面显示该解释信息。

### 2.6.3 处理权限请求响应

当应用请求权限时,系统将向用户显示一个对话框。当用户响应时,系统将调用应用的 `onRequestPermissionsResult()` 方法,向其传递用户响应。应用必须覆写该方法,以了解是否已获得相应权限。例如,如果应用请求 `READ_CONTACTS` 访问权限,则它可能采用以下回调方法:

```java {.line-numbers}
@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
    }
}
```

系统显示的对话框说明了应用需要访问的`权限组`;它不会列出具体权限。例如,如果请求 `READ_CONTACTS` 权限,系统对话框只显示应用需要访问设备的联系人。用户只需要为每个`权限组`授予一次权限。如果应用请求该组中的任何其他权限(已在应用清单中列出),系统将自动授予应用这些权限。当请求此权限时,系统会调用 `onRequestPermissionsResult()` 回调方法,并传递 `PERMISSION_GRANTED`,如果用户已通过系统对话框明确同意权限请求,系统将采用相同方式操作。

> 注:应用仍需要明确请求其需要的每项权限,即使用户已向应用授予该权限组中的其他权限。此外,`权限分组`在将来的 Android 版本中可能会发生变化。代码不应依赖特定权限属于或不属于相同组这种假设。

例如,假设在应用清单中列出了 `READ_CONTACTS` 和 `WRITE_CONTACTS`。如果请求 `READ_CONTACTS` 且用户授予了此权限,那么,当请求 `WRITE_CONTACTS` 时,系统将立即授予该权限,不会与用户交互。

如果用户拒绝了某项权限请求,应用应采取适当的操作。例如,应用可能显示一个对话框,解释它为什么无法执行用户已经拒绝但需要该权限的操作。

当系统要求用户授予权限时,用户可以选择指示系统不再要求提供该权限。这种情况下,无论应用在什么时候使用 `requestPermissions()` 再次要求该权限,系统都会立即拒绝此请求。系统会调用 `onRequestPermissionsResult()` 回调方法,并传递 `PERMISSION_DENIED`,如果用户再次明确拒绝了请求,系统将采用相同方式操作。这意味着当调用 `requestPermissions()` 时,不能假设已经发生与用户的任何直接交互。

# 三 权限管理

## 3.1 权限解析

> frameworks/base/core/java/android/content/pm/PackageParser.java

![image](res/PackageParser.png)

```java {.line-numbers}
public class PackageParser {

    ...

    private static final String TAG_PERMISSION_GROUP = "permission-group";
    private static final String TAG_PERMISSION = "permission";
    private static final String TAG_PERMISSION_TREE = "permission-tree";
    private static final String TAG_USES_PERMISSION = "uses-permission";
    private static final String TAG_USES_PERMISSION_SDK_M = "uses-permission-sdk-m";
    private static final String TAG_USES_PERMISSION_SDK_23 = "uses-permission-sdk-23";

    ...

    /**
     * Representation of a full package parsed from APK files on disk. A package
     * consists of a single base APK, and zero or more split APKs.
     */
    public final static class Package implements Parcelable {

        public String packageName;

        ...

        public final ArrayList permissions = new ArrayList(0);
        public final ArrayList permissionGroups = new ArrayList(0);

        ...

        public final ArrayList requestedPermissions = new ArrayList();

        ...

    }

    ...

    private Package parseBaseApkCommon(Package pkg, Set acceptedTags, Resources res,
            XmlResourceParser parser, int flags, String[] outError) throws XmlPullParserException,
            IOException {

        ...

        TypedArray sa = res.obtainAttributes(parser,
                com.android.internal.R.styleable.AndroidManifest);
        
        int outerDepth = parser.getDepth();
        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
            ...

            } else if (tagName.equals(TAG_PERMISSION_GROUP)) {
                if (!parsePermissionGroup(pkg, flags, res, parser, outError)) {
                    return null;
                }
            } else if (tagName.equals(TAG_PERMISSION)) {
                if (!parsePermission(pkg, res, parser, outError)) {
                    return null;
                }
            } else if (tagName.equals(TAG_PERMISSION_TREE)) {
                if (!parsePermissionTree(pkg, res, parser, outError)) {
                    return null;
                }
            } else if (tagName.equals(TAG_USES_PERMISSION)) {
                if (!parseUsesPermission(pkg, res, parser)) {
                    return null;
                }
            } else if (tagName.equals(TAG_USES_PERMISSION_SDK_M)
                    || tagName.equals(TAG_USES_PERMISSION_SDK_23)) {
                if (!parseUsesPermission(pkg, res, parser)) {
                    return null;
                }

                ...
            }
        }
    }

    ...

    private boolean parseUsesPermission(Package pkg, Resources res, XmlResourceParser parser)
            throws XmlPullParserException, IOException {
        TypedArray sa = res.obtainAttributes(parser,
                com.android.internal.R.styleable.AndroidManifestUsesPermission);

        // Note: don't allow this value to be a reference to a resource
        // that may change.
        String name = sa.getNonResourceString(
                com.android.internal.R.styleable.AndroidManifestUsesPermission_name);
        
        ...

        int index = pkg.requestedPermissions.indexOf(name);
        if (index == -1) {
            pkg.requestedPermissions.add(name.intern());
        } else {
            Slog.w(TAG, "Ignoring duplicate uses-permissions/uses-permissions-sdk-m: "
                    + name + " in package: " + pkg.packageName + " at: "
                    + parser.getPositionDescription());
        }

        return true;
    }


    private boolean parsePermissionGroup(Package owner, int flags, Resources res,
            XmlResourceParser parser, String[] outError)
            throws XmlPullParserException, IOException {
        PermissionGroup perm = new PermissionGroup(owner);

        TypedArray sa = res.obtainAttributes(parser,
                com.android.internal.R.styleable.AndroidManifestPermissionGroup);
        if (!parsePackageItemInfo(owner, perm.info, outError,
                "", sa, true /*nameRequired*/,
                com.android.internal.R.styleable.AndroidManifestPermissionGroup_name,
                com.android.internal.R.styleable.AndroidManifestPermissionGroup_label,
                com.android.internal.R.styleable.AndroidManifestPermissionGroup_icon,
                com.android.internal.R.styleable.AndroidManifestPermissionGroup_roundIcon,
                com.android.internal.R.styleable.AndroidManifestPermissionGroup_logo,
                com.android.internal.R.styleable.AndroidManifestPermissionGroup_banner)) {
            sa.recycle();
            mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
            return false;
        }

        ...

        owner.permissionGroups.add(perm);

        return true;
    }

    private boolean parsePermission(Package owner, Resources res,
            XmlResourceParser parser, String[] outError)
        throws XmlPullParserException, IOException {

        TypedArray sa = res.obtainAttributes(parser,
                com.android.internal.R.styleable.AndroidManifestPermission);

        Permission perm = new Permission(owner);
        if (!parsePackageItemInfo(owner, perm.info, outError,
                "", sa, true /*nameRequired*/,
                com.android.internal.R.styleable.AndroidManifestPermission_name,
                com.android.internal.R.styleable.AndroidManifestPermission_label,
                com.android.internal.R.styleable.AndroidManifestPermission_icon,
                com.android.internal.R.styleable.AndroidManifestPermission_roundIcon,
                com.android.internal.R.styleable.AndroidManifestPermission_logo,
                com.android.internal.R.styleable.AndroidManifestPermission_banner)) {
            sa.recycle();
            mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
            return false;
        }

        ...

        perm.info.protectionLevel = sa.getInt(
                com.android.internal.R.styleable.AndroidManifestPermission_protectionLevel,
                PermissionInfo.PROTECTION_NORMAL);

        perm.info.flags = sa.getInt(
                com.android.internal.R.styleable.AndroidManifestPermission_permissionFlags, 0);

        ...

        owner.permissions.add(perm);

        return true;
    }

    private static boolean parsePackageItemInfo(Package owner, PackageItemInfo outInfo,
            String[] outError, String tag, TypedArray sa, boolean nameRequired,
            int nameRes, int labelRes, int iconRes, int roundIconRes, int logoRes, int bannerRes) {
        ...

        String name = sa.getNonConfigurationString(nameRes, 0);
        if (name == null) {
            if (nameRequired) {
                outError[0] = tag + " does not specify android:name";
                return false;
            }
        } else {
            outInfo.name
                = buildClassName(owner.applicationInfo.packageName, name, outError);
            if (outInfo.name == null) {
                return false;
            }
        }

        ...

        outInfo.packageName = owner.packageName;

        return true;
    }

    ...
}
```

## 3.2 安装确认

> PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java

```java {.line-numbers}
    private void startInstallConfirm() {

        ...

        // 判断应用是否支持运行时权限
        // If the app supports runtime permissions the new permissions will
        // be requested at runtime, hence we do not show them at install.
        boolean supportsRuntimePermissions = mPkgInfo.applicationInfo.targetSdkVersion
                >= Build.VERSION_CODES.M;
        // 是否显示权限列表
        boolean permVisible = false;
        // 用于显示权限列表
        mScrollView = null;
        // 确认安装
        mOkCanInstall = false;
        int msg = 0;

        // 该应用的权限及权限组信息
        AppSecurityPermissions perms = new AppSecurityPermissions(this, mPkgInfo);
        // WHICH_ALL 代表获取所有权限
        final int N = perms.getPermissionCount(AppSecurityPermissions.WHICH_ALL);
        if (mAppInfo != null) {
            // 如果已经安装过,则继续判断是否为系统应用升级
            msg = (mAppInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0
                    ? R.string.install_confirm_question_update_system
                    : R.string.install_confirm_question_update;
            mScrollView = new CaffeinatedScrollView(this);
            mScrollView.setFillViewport(true);
            boolean newPermissionsFound = false;
            if (!supportsRuntimePermissions) {
                // 不支持运行时权限,则在更新应用时检测是否增加新权限(WHICH_NEW)
                newPermissionsFound =
                        (perms.getPermissionCount(AppSecurityPermissions.WHICH_NEW) > 0);
                if (newPermissionsFound) {
                    // 将新增权限增加到视图中显示
                    permVisible = true;
                    mScrollView.addView(perms.getPermissionsView(
                            AppSecurityPermissions.WHICH_NEW));
                }
            }
            // 不支持运行时权限,则提示此次更新未增加新权限
            if (!supportsRuntimePermissions && !newPermissionsFound) {
                LayoutInflater inflater = (LayoutInflater)getSystemService(
                        Context.LAYOUT_INFLATER_SERVICE);
                TextView label = (TextView)inflater.inflate(R.layout.label, null);
                label.setText(R.string.no_new_perms);
                mScrollView.addView(label);
            }
            adapter.addTab(tabHost.newTabSpec(TAB_ID_NEW).setIndicator(
                    getText(R.string.newPerms)), mScrollView);
        }
        // 不支持运行时权限,且有请求权限,则添加权限列表至视图显示
        if (!supportsRuntimePermissions && N > 0) {
            permVisible = true;
            LayoutInflater inflater = (LayoutInflater)getSystemService(
                    Context.LAYOUT_INFLATER_SERVICE);
            View root = inflater.inflate(R.layout.permissions_list, null);
            if (mScrollView == null) {
                mScrollView = (CaffeinatedScrollView)root.findViewById(R.id.scrollview);
            }
            ((ViewGroup)root.findViewById(R.id.permission_list)).addView(
                        perms.getPermissionsView(AppSecurityPermissions.WHICH_ALL));
            adapter.addTab(tabHost.newTabSpec(TAB_ID_ALL).setIndicator(
                    getText(R.string.allPerms)), root);
        }
        // 支持运行时权限,不显示权限列表,仅做文字提示
        if (!permVisible) {
            // 更新
            if (mAppInfo != null) {
                // This is an update to an application, but there are no
                // permissions at all.
                msg = (mAppInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0
                        ? R.string.install_confirm_question_update_system_no_perms
                        : R.string.install_confirm_question_update_no_perms;
            } else {
                // 新的安装
                // This is a new application with no permissions.
                msg = R.string.install_confirm_question_no_perms;
            }

            // We do not need to show any permissions, load layout without permissions
            bindUi(R.layout.install_confirm, true);
            mScrollView = null;
        }

        ...
    }
```

## 3.3 安装

> frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java

```java {.line-numbers}
    private void installPackageLI(InstallArgs args, PackageInstalledInfo res) {

        ...

        synchronized (mPackages) {

            ...

            int N = pkg.permissions.size();
            // 遍历应用自定义的权限
            for (int i = N-1; i >= 0; i--) {
                final PackageParser.Permission perm = pkg.permissions.get(i);
                // 从系统中获取同名权限
                final BasePermission bp =
                        (BasePermission) mPermissionManager.getPermissionTEMP(perm.info.name);

                ...

                // Check whether the newly-scanned package wants to define an already-defined perm
                // 如果已存在同名权限
                if (bp != null) {
                    // If the defining package is signed with our cert, it's okay.  This
                    // also includes the "updating the same package" case, of course.
                    // "updating same package" could also involve key-rotation.
                    // 签名验证
                    final boolean sigsOk;
                    final String sourcePackageName = bp.getSourcePackageName();
                    final PackageSettingBase sourcePackageSetting = bp.getSourcePackageSetting();
                    final KeySetManagerService ksms = mSettings.mKeySetManagerService;
                    if (sourcePackageName.equals(pkg.packageName)
                            && (ksms.shouldCheckUpgradeKeySetLocked(
                                    sourcePackageSetting, scanFlags))) {
                        sigsOk = ksms.checkUpgradeKeySetLocked(sourcePackageSetting, pkg);
                    } else {
                            ...

                            sigsOk = false;
                    }
                    // 签名验证失败
                    if (!sigsOk) {
                        // If the owning package is the system itself, we log but allow
                        // install to proceed; we fail the install on all other permission
                        // redefinitions.
                        // 非“android”包重定义权限,则安装失败
                        if (!sourcePackageName.equals("android")) {
                            res.setError(INSTALL_FAILED_DUPLICATE_PERMISSION, "Package "
                                    + pkg.packageName + " attempting to redeclare permission "
                                    + perm.info.name + " already owned by " + sourcePackageName);
                            res.origPermission = perm.info.name;
                            res.origPackage = sourcePackageName;
                            return;
                        } else {
                            Slog.w(TAG, "Package " + pkg.packageName
                                    + " attempting to redeclare system permission "
                                    + perm.info.name + "; ignoring new declaration");
                            // “android”包可继续安装,但从该应用移除该重定义权限
                            pkg.permissions.remove(i);
                        }
                    } else if (!PLATFORM_PACKAGE_NAME.equals(pkg.packageName)) {
                        // Prevent apps to change protection level to dangerous from any other
                        // type as this would allow a privilege escalation where an app adds a
                        // normal/signature permission in other app's group and later redefines
                        // it as dangerous leading to the group auto-grant.
                        // 非“android”包重定义权限不可从非运行时权限更改为运行时权限
                        if ((perm.info.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE)
                                == PermissionInfo.PROTECTION_DANGEROUS) {
                            if (bp != null && !bp.isRuntime()) {
                                Slog.w(TAG, "Package " + pkg.packageName + " trying to change a "
                                        + "non-runtime permission " + perm.info.name
                                        + " to runtime; keeping old protection level");
                                perm.info.protectionLevel = bp.getProtectionLevel();
                            }
                        }
                    }
                }
            }
        }

        ...
    }
```

```java {.line-numbers}
    /**
     * Adds a scanned package to the system. When this method is finished, the package will
     * be available for query, resolution, etc...
     */
    private void commitPackageSettings(PackageParser.Package pkg,
            @Nullable PackageParser.Package oldPkg, PackageSetting pkgSetting, UserHandle user,
            final @ScanFlags int scanFlags, boolean chatty) {

        ...

        // 将应用自定义权限组添加至系统
        mPermissionManager.addAllPermissionGroups(pkg, chatty);

        ...

        // 将应用自定义权限添加至系统
        mPermissionManager.addAllPermissions(pkg, chatty);
    }
```

安装成功后

```java {.line-numbers}
    private void handlePackagePostInstall(PackageInstalledInfo res, boolean grantPermissions,
            boolean killApp, boolean virtualPreload, String[] grantedPermissions,
            boolean launchedForRestore, String installerPackage,
            IPackageInstallObserver2 installObserver) {
        if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
            ...

            // Now that we successfully installed the package, grant runtime
            // permissions if requested before broadcasting the install. Also
            // for legacy apps in permission review mode we clear the permission
            // review flag which is used to emulate runtime permissions for
            // legacy apps.
            // 例如通过:"adb install -g " 方式安装,grantPermissions 为 true
            if (grantPermissions) {
                final int callingUid = Binder.getCallingUid();
                mPermissionManager.grantRequestedRuntimePermissions(
                        res.pkg, res.newUsers, grantedPermissions, callingUid,
                        mPermissionCallback);
            }

            ...

            /// M: CTA requirement - permission control  @{
            grantCtaRuntimePerm(update, res);
            //@}

            //*/ freeme,chenming. 2018.03.23. Permission-PreAuth.
            if (sFreemePermissionManager.isPermissionAutoGranted(update)) {
                sFreemePermissionManager.grantFreemePermission(res.pkg.applicationInfo.packageName, res.newUsers);
            }
            //*/

            ...


        }

        ...
    }
```

## 3.4 检查权限

> frameworks/base/core/java/android/app/ContextImpl.java

```java {.line-numbers}
    @Override
    public int checkPermission(String permission, int pid, int uid) {
        if (permission == null) {
            throw new IllegalArgumentException("permission is null");
        }

        final IActivityManager am = ActivityManager.getService();
        if (am == null) {
            // Well this is super awkward; we somehow don't have an active
            // ActivityManager instance. If we're testing a root or system
            // UID, then they totally have whatever permission this is.
            final int appId = UserHandle.getAppId(uid);
            if (appId == Process.ROOT_UID || appId == Process.SYSTEM_UID) {
                Slog.w(TAG, "Missing ActivityManager; assuming " + uid + " holds " + permission);
                return PackageManager.PERMISSION_GRANTED;
            }
            Slog.w(TAG, "Missing ActivityManager; assuming " + uid + " does not hold "
                    + permission);
            return PackageManager.PERMISSION_DENIED;
        }

        try {
            return am.checkPermission(permission, pid, uid);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }
```

> frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

```java {.line-numbers}
    int checkComponentPermission(String permission, int pid, int uid,
            int owningUid, boolean exported) {
        if (pid == MY_PID) {
            return PackageManager.PERMISSION_GRANTED;
        }
        return ActivityManager.checkComponentPermission(permission, uid,
                owningUid, exported);
    }

    /**
     * As the only public entry point for permissions checking, this method
     * can enforce the semantic that requesting a check on a null global
     * permission is automatically denied.  (Internally a null permission
     * string is used when calling {@link #checkComponentPermission} in cases
     * when only uid-based security is needed.)
     *
     * This can be called with or without the global lock held.
     */
    @Override
    public int checkPermission(String permission, int pid, int uid) {
        if (permission == null) {
            return PackageManager.PERMISSION_DENIED;
        }
        return checkComponentPermission(permission, pid, uid, -1, true);
    }

    @Override
    public int checkPermissionWithToken(String permission, int pid, int uid, IBinder callerToken) {
        if (permission == null) {
            return PackageManager.PERMISSION_DENIED;
        }

        ...

        return checkComponentPermission(permission, pid, uid, -1, true);
    }
```

> frameworks/base/core/java/android/app/ActivityManager.java

```java {.line-numbers}
    /** @hide */
    public static int checkComponentPermission(String permission, int uid,
            int owningUid, boolean exported) {
        // Root, system server get to do everything.
        final int appId = UserHandle.getAppId(uid);
        if (appId == Process.ROOT_UID || appId == Process.SYSTEM_UID) {
            return PackageManager.PERMISSION_GRANTED;
        }
        // Isolated processes don't get any permissions.
        if (UserHandle.isIsolated(uid)) {
            return PackageManager.PERMISSION_DENIED;
        }
        // If there is a uid that owns whatever is being accessed, it has
        // blanket access to it regardless of the permissions it requires.
        if (owningUid >= 0 && UserHandle.isSameApp(uid, owningUid)) {
            return PackageManager.PERMISSION_GRANTED;
        }
        // If the target is not exported, then nobody else can get to it.
        if (!exported) {
            /*
            RuntimeException here = new RuntimeException("here");
            here.fillInStackTrace();
            Slog.w(TAG, "Permission denied: checkComponentPermission() owningUid=" + owningUid,
                    here);
            */
            return PackageManager.PERMISSION_DENIED;
        }
        if (permission == null) {
            return PackageManager.PERMISSION_GRANTED;
        }
        ...
        try {
            return AppGlobals.getPackageManager()
                    .checkUidPermission(permission, uid);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }
```

最终交给 PMS 处理

> frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java

```java {.line-numbers}
    @Override
    public int checkUidPermission(String permName, int uid) {
        /// M: CTA requirement - permission control  @{
        sCtaManager.reportPermRequestUsage(permName, uid);
        //@}
        synchronized (mPackages) {
            final String[] packageNames = getPackagesForUid(uid);
            final PackageParser.Package pkg = (packageNames != null && packageNames.length > 0)
                    ? mPackages.get(packageNames[0])
                    : null;
            return mPermissionManager.checkUidPermission(permName, pkg, uid, getCallingUid());
        }
    }
```

> frameworks/base/services/core/java/com/android/server/pm/permission/PermissionManagerService.java

```java {.line-numbers}
    private int checkUidPermission(String permName, PackageParser.Package pkg, int uid,
            int callingUid) {
        ...

        if (pkg != null) {
            ...
            final PermissionsState permissionsState =
                    ((PackageSetting) pkg.mExtras).getPermissionsState();
            if (permissionsState.hasPermission(permName, userId)) {
                ...
                return PackageManager.PERMISSION_GRANTED;
            }
            ...
        }
        return PackageManager.PERMISSION_DENIED;
    }
```

> frameworks/base/services/core/java/com/android/server/pm/permission/PermissionsState.java

```java {.line-numbers}
    /**
     * Gets whether the state has a given permission for the specified
     * user, regardless if this is an install or a runtime permission.
     *
     * @param name The permission name.
     * @param userId The device user id.
     * @return Whether the user has the permission.
     */
    public boolean hasPermission(String name, int userId) {
        enforceValidUserId(userId);

        if (mPermissions == null) {
            return false;
        }

        PermissionData permissionData = mPermissions.get(name);
        return permissionData != null && permissionData.isGranted(userId);
    }
```

## 3.5 请求权限

> frameworks/base/core/java/android/app/Activity.java

```java {.line-numbers}
    private static final String REQUEST_PERMISSIONS_WHO_PREFIX = "@android:requestPermissions:";

    public final void requestPermissions(@NonNull String[] permissions, int requestCode) {
        if (requestCode < 0) {
            throw new IllegalArgumentException("requestCode should be >= 0");
        }
        if (mHasCurrentPermissionsRequest) {
            Log.w(TAG, "Can request only one set of permissions at a time");
            // Dispatch the callback with empty arrays which means a cancellation.
            onRequestPermissionsResult(requestCode, new String[0], new int[0]);
            return;
        }
        Intent intent = getPackageManager().buildRequestPermissionsIntent(permissions);
        startActivityForResult(REQUEST_PERMISSIONS_WHO_PREFIX, intent, requestCode, null);
        mHasCurrentPermissionsRequest = true;
    }
```

> frameworks/base/core/java/android/content/pm/PackageManager.java

```java {.line-numbers}
    /**
     * The action used to request that the user approve a permission request
     * from the application.
     *
     * @hide
     */
    @SystemApi
    public static final String ACTION_REQUEST_PERMISSIONS =
            "android.content.pm.action.REQUEST_PERMISSIONS";

    /**
     * The names of the requested permissions.
     *


     * Type: String[]
     *


     *
     * @hide
     */
    @SystemApi
    public static final String EXTRA_REQUEST_PERMISSIONS_NAMES =
            "android.content.pm.extra.REQUEST_PERMISSIONS_NAMES";

    /**
     * Returns an {@link android.content.Intent} suitable for passing to
     * {@link android.app.Activity#startActivityForResult(android.content.Intent, int)}
     * which prompts the user to grant permissions to this application.
     *
     * @throws NullPointerException if {@code permissions} is {@code null} or empty.
     *
     * @hide
     */
    public Intent buildRequestPermissionsIntent(@NonNull String[] permissions) {
        if (ArrayUtils.isEmpty(permissions)) {
           throw new IllegalArgumentException("permission cannot be null or empty");
        }
        Intent intent = new Intent(ACTION_REQUEST_PERMISSIONS);
        intent.putExtra(EXTRA_REQUEST_PERMISSIONS_NAMES, permissions);
        // 最终在 PMS 中获取,其实就是 PackageInstaller
        intent.setPackage(getPermissionControllerPackageName());
        return intent;
    }
```

> PackageInstaller/AndroidManifest.xml

```xml {.line-numbers}
                        android:configChanges="orientation|keyboardHidden|screenSize"
                android:excludeFromRecents="true"
                android:theme="@style/GrantPermissions"
                android:visibleToInstantApps="true">
           
               
               
           

       

```

> PackageInstaller/src/com/android/packageinstaller/permission/ui/GrantPermissionsActivity.java

```java {.line-numbers}
    @Override
    public void onCreate(Bundle icicle) {
        super.onCreate(icicle);

        ...

            // UI 处理
            mViewHandler = new com.android.packageinstaller.permission.ui.handheld
                    .GrantPermissionsViewHandlerImpl(this, getCallingPackage())
                    .setResultListener(this);

        // 请求权限数组
        mRequestedPermissions = getIntent().getStringArrayExtra(
                PackageManager.EXTRA_REQUEST_PERMISSIONS_NAMES);
        if (mRequestedPermissions == null) {
            mRequestedPermissions = new String[0];
        }

        final int requestedPermCount = mRequestedPermissions.length;
        mGrantResults = new int[requestedPermCount];
        // 默认 PERMISSION_DENIED
        Arrays.fill(mGrantResults, PackageManager.PERMISSION_DENIED);

        if (requestedPermCount == 0) {
            setResultAndFinish();
            return;
        }

        PackageInfo callingPackageInfo = getCallingPackageInfo();

        // 清单文件中请求的权限为空,则直接返回
        if (callingPackageInfo == null || callingPackageInfo.requestedPermissions == null
                || callingPackageInfo.requestedPermissions.length <= 0) {
            setResultAndFinish();
            return;
        }

        // 不支持运行时权限
        // Don't allow legacy apps to request runtime permissions.
        if (callingPackageInfo.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M) {
            // Returning empty arrays means a cancellation.
            mRequestedPermissions = new String[0];
            mGrantResults = new int[0];
            setResultAndFinish();
            return;
        }

        DevicePolicyManager devicePolicyManager = getSystemService(DevicePolicyManager.class);
        final int permissionPolicy = devicePolicyManager.getPermissionPolicy(null);

        // 如果某个权限已经 PERMISSION_GRANTED,更新 mGrantResults
        // If calling package is null we default to deny all.
        updateDefaultResults(callingPackageInfo, permissionPolicy);

        // 根据应用的 requestedPermissions,生成相关权限组集合
        mAppPermissions = new AppPermissions(this, callingPackageInfo, null, false,
                new Runnable() {
                    @Override
                    public void run() {
                        setResultAndFinish();
                    }
                });

        // 更新权限组信息
        for (String requestedPermission : mRequestedPermissions) {
            ...
        }

        ...

        // 申请的权限都已 PERMISSION_GRANTED 则直接返回,否则显示对话框
        if (!showNextPermissionGroupGrantRequest()) {
            setResultAndFinish();
        }
        ...
    }
```

权限对话框 UI 处理

> PackageInstaller/src/com/android/packageinstaller/permission/ui/handheld/GrantPermissionsViewHandlerImpl.java

```java {.line-numbers}
    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.permission_allow_button:
                if (mResultListener != null) {
                    view.performAccessibilityAction(
                            AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS, null);
                    mResultListener.onPermissionGrantResult(mGroupName, true, false);
                }
                break;
            case R.id.permission_deny_button:
                mAllowButton.setEnabled(true);
                if (mResultListener != null) {
                    view.performAccessibilityAction(
                            AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS, null);
                    mResultListener.onPermissionGrantResult(mGroupName, false,
                            mShowDonNotAsk && mDoNotAskCheckbox.isChecked());
                }
                break;
            case R.id.permission_more_info_button:
                Intent intent = new Intent(Intent.ACTION_MANAGE_APP_PERMISSIONS);
                intent.putExtra(Intent.EXTRA_PACKAGE_NAME, mAppPackageName);
                intent.putExtra(ManagePermissionsActivity.EXTRA_ALL_PERMISSIONS, true);
                mActivity.startActivity(intent);
                break;
            case R.id.do_not_ask_checkbox:
                mAllowButton.setEnabled(!mDoNotAskCheckbox.isChecked());
                break;
        }
    }
```

> PackageInstaller/src/com/android/packageinstaller/permission/ui/GrantPermissionsActivity.java

```java {.line-numbers}
    @Override
    public void onPermissionGrantResult(String name, boolean granted, boolean doNotAskAgain) {
        ...

        GroupState groupState = mRequestGrantPermissionGroups.get(name);
        /// M: CTA requirement - permission control.
        if (!Utils.CTA_SUPPORT && groupState.mGroup != null) {
            if (granted) {
                groupState.mGroup.grantRuntimePermissions(doNotAskAgain,
                        groupState.affectedPermissions);
                groupState.mState = GroupState.STATE_ALLOWED;
            } else {
                groupState.mGroup.revokeRuntimePermissions(doNotAskAgain,
                        groupState.affectedPermissions);
                groupState.mState = GroupState.STATE_DENIED;

                ...
            }
            updateGrantResults(groupState.mGroup);
        }

        ...

        // 显示下一个待申请的权限,否则返回
        if (!showNextPermissionGroupGrantRequest()) {
            setResultAndFinish();
        }
    }
```

> PackageInstaller/src/com/android/packageinstaller/permission/model/AppPermissionGroup.java

```java {.line-numbers}
    public boolean grantRuntimePermissions(boolean fixedByTheUser, String[] filterPermissions) {
        final int uid = mPackageInfo.applicationInfo.uid;

        // We toggle permissions only to apps that support runtime
        // permissions, otherwise we toggle the app op corresponding
        // to the permission if the permission is granted to the app.
        for (Permission permission : mPermissions.values()) {
            ...

            if (mAppSupportsRuntimePermissions) {
                ...

                // Grant the permission if needed.
                if (!permission.isGranted()) {
                    permission.setGranted(true);
                    mPackageManager.grantRuntimePermission(mPackageInfo.packageName,
                            permission.getName(), mUserHandle);
                }

                // Update the permission flags.
                if (!fixedByTheUser) {
                    // Now the apps can ask for the permission as the user
                    // no longer has it fixed in a denied state.
                    if (permission.isUserFixed() || permission.isUserSet()) {
                        permission.setUserFixed(false);
                        permission.setUserSet(false);
                        mPackageManager.updatePermissionFlags(permission.getName(),
                                mPackageInfo.packageName,
                                PackageManager.FLAG_PERMISSION_USER_FIXED
                                        | PackageManager.FLAG_PERMISSION_USER_SET,
                                0, mUserHandle);
                    }
                }

                ...
            }
        }

        return true;
    }

    ...

    public boolean revokeRuntimePermissions(boolean fixedByTheUser, String[] filterPermissions) {
        final int uid = mPackageInfo.applicationInfo.uid;

        // We toggle permissions only to apps that support runtime
        // permissions, otherwise we toggle the app op corresponding
        // to the permission if the permission is granted to the app.
        for (Permission permission : mPermissions.values()) {
            ...

            if (mAppSupportsRuntimePermissions) {
                ...

                // Revoke the permission if needed.
                if (permission.isGranted()) {
                    permission.setGranted(false);
                    mPackageManager.revokeRuntimePermission(mPackageInfo.packageName,
                            permission.getName(), mUserHandle);
                }

                // Update the permission flags.
                if (fixedByTheUser) {
                    // Take a note that the user fixed the permission.
                    if (permission.isUserSet() || !permission.isUserFixed()) {
                        permission.setUserSet(false);
                        permission.setUserFixed(true);
                        mPackageManager.updatePermissionFlags(permission.getName(),
                                mPackageInfo.packageName,
                                PackageManager.FLAG_PERMISSION_USER_SET
                                        | PackageManager.FLAG_PERMISSION_USER_FIXED,
                                PackageManager.FLAG_PERMISSION_USER_FIXED,
                                mUserHandle);
                    }
                } else {
                    if (!permission.isUserSet() || permission.isUserFixed()) {
                        permission.setUserSet(true);
                        permission.setUserFixed(false);
                        // Take a note that the user already chose once.
                        mPackageManager.updatePermissionFlags(permission.getName(),
                                mPackageInfo.packageName,
                                PackageManager.FLAG_PERMISSION_USER_SET
                                        | PackageManager.FLAG_PERMISSION_USER_FIXED,
                                PackageManager.FLAG_PERMISSION_USER_SET,
                                mUserHandle);
                    }
                }

                ...
            }
        }

        return true;
    }
```

注意这里几个重要的 flag

> frameworks/base/core/java/android/content/pm/PackageManager.java

```java {.line-numbers}
   /**
    * Permission flag: The permission is set in its current state
    * by the user and apps can still request it at runtime.
    *
    * @hide
    */
    @SystemApi
    public static final int FLAG_PERMISSION_USER_SET = 1 << 0;

    /**
     * Permission flag: The permission is set in its current state
     * by the user and it is fixed, i.e. apps can no longer request
     * this permission.
     *
     * @hide
     */
    @SystemApi
    public static final int FLAG_PERMISSION_USER_FIXED =  1 << 1;
```

在 Android P 上经 PMS 再转交给 PermissionManagerService 记录权限申请结果

> frameworks/base/services/core/java/com/android/server/pm/permission/PermissionManagerService.java

```java {.line-numbers}
    private void grantRuntimePermission(String permName, String packageName, boolean overridePolicy,
            int callingUid, final int userId, PermissionCallback callback) {
        ...

        final PackageParser.Package pkg = mPackageManagerInt.getPackage(packageName);
        if (pkg == null || pkg.mExtras == null) {
            throw new IllegalArgumentException("Unknown package: " + packageName);
        }
        final BasePermission bp;
        synchronized(mLock) {
            bp = mSettings.getPermissionLocked(permName);
        }
        if (bp == null) {
            throw new IllegalArgumentException("Unknown permission: " + permName);
        }

        ...

        final int uid = UserHandle.getUid(userId, pkg.applicationInfo.uid);

        final PackageSetting ps = (PackageSetting) pkg.mExtras;
        final PermissionsState permissionsState = ps.getPermissionsState();

        ...

        final int result = permissionsState.grantRuntimePermission(bp, userId);

        ...
    }

```

> frameworks/base/services/core/java/com/android/server/pm/permission/PermissionsState.java

```java {.line-numbers}
        public boolean grant(int userId) {
            ...

            PermissionState userState = mUserStates.get(userId);
            if (userState == null) {
                userState = new PermissionState(mPerm.getName());
                mUserStates.put(userId, userState);
            }

            userState.mGranted = true;

            return true;
        }
```

# 四 参考资料

- [系统权限](https://developer.android.com/guide/topics/security/permissions.html?hl=zh-cn)

你可能感兴趣的:(Android)