[TOC]
# 一 概述
Android 是一个权限分隔的操作系统,在安装应用时,Android 为每个软件包提供唯一的系统标识(Linux 用户 ID 和组 ID)。此 ID 在软件包在该设备上的使用寿命期间保持不变。系统各部分也分隔为不同的标识。Linux 据此将不同的应用以及应用与系统分隔开来。
在默认情况下任何应用都没有权限执行对其他应用、操作系统或用户有不利影响的任何操作。这包括读取或写入用户的私有数据(例如联系人或电子邮件)、读取或写入其他应用程序的文件、执行网络访问、使设备保持唤醒状态等。
由于每个 Android 应用都是在进程沙盒中运行,因此应用必须显式共享资源和数据。它们的方法是声明需要哪些权限来获取基本沙盒未提供的额外功能。应用以静态方式声明它们需要的权限,然后 Android 系统提示用户同意。
例如,需要监控传入的短信的应用要指定:
```
...
```
如果在应用清单中列出`正常权限`(即不会对用户隐私或设备操作造成很大风险的权限),系统会自动授予这些权限。如果列出`危险权限`(即可能影响用户隐私或设备正常操作的权限),系统会要求用户明确授予这些权限。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}
android:sharedUserLabel="@string/android_system_label">
...
android:label="@string/permgrouplab_contacts"
android:description="@string/permgroupdesc_contacts"
android:request="@string/permgrouprequest_contacts"
android:priority="100" />
android:label="@string/permlab_readContacts"
android:description="@string/permdesc_readContacts"
android:protectionLevel="dangerous" />
...
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}
```
## 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 自定义权限及应用
应用可以使用 `
可以通过 `AndroidManifest.xml` 应用高级权限,限制访问系统或应用的全部组件。要执行此操作,在所需的组件上包含 `android:permission` 属性,为用于控制访问它的权限命名。
- `Activity` 权限(应用于 `
- `Service` 权限(应用于 `
- `BroadcastReceiver` 权限(应用于 `
- `ContentProvider` 权限(应用于 `
### 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
public final ArrayList
...
public final ArrayList
...
}
...
private Package parseBaseApkCommon(Package pkg, Set
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,
"
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,
"
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
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[]
*
/**
* 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: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)