Add docs for users, permissions, appops. am: 767ef39c1a am: 0d147638fc am: a11325a84e am: fe2e86fccd
Change-Id: I625ef9faaccd07cb9cd132cb71b83b49e776596d
This commit is contained in:
212
core/java/android/app/AppOps.md
Normal file
212
core/java/android/app/AppOps.md
Normal file
@@ -0,0 +1,212 @@
|
||||
<!--
|
||||
Copyright (C) 2020 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License
|
||||
-->
|
||||
|
||||
# App-ops
|
||||
|
||||
App-ops are used for two purposes: Access control and tracking.
|
||||
|
||||
App-ops cover a wide variety of functionality from helping with runtime permissions to battery
|
||||
consumption tracking.
|
||||
|
||||
App-ops are defined in `AppOpsManager` as `OP_...` and need to be continuously numbered. The
|
||||
integer values of the app-ops are not exposed. For app-ops visible to 3rd party apps,
|
||||
the name of the app-op might be exposed as `OPSTR_`. As the integers are not part of the API, they
|
||||
might (and have) changed between platform versions and OEM implementations.
|
||||
`AppOpsManager.opToPublicName` and `AppOpsManager.strOpToOp` allow for conversion between integer
|
||||
and string identifier for the op.
|
||||
|
||||
## App-ops as access restrictions
|
||||
|
||||
App-ops can either be controlled for each [uid](../os/Users.md#int-uid) or for each package. Which
|
||||
one is used depends on the API provider maintaining this app-op.
|
||||
|
||||
For any security or privacy related app-ops the provider needs to control the app-op per uid
|
||||
as all security and privacy is based on uid in Android.
|
||||
|
||||
App-op used for non-security related tasks are usually controlled per package to provide finer
|
||||
granularity.
|
||||
|
||||
### Setting the app-op mode
|
||||
|
||||
To control access the app-op can be set to:
|
||||
|
||||
`MODE_DEFAULT`
|
||||
: Default behavior, might differ from app-op to app-op
|
||||
|
||||
`MODE_ALLOWED`
|
||||
: Allow the access
|
||||
|
||||
`MODE_FOREGROUND`
|
||||
: Allow the access but only if the app is currently in the [foreground](#foreground)
|
||||
|
||||
`MODE_IGNORED`
|
||||
: Don't allow the access, i.e. don't perform the requested action or return dummy data
|
||||
|
||||
`MODE_ERRORED`
|
||||
: Throw a `SecurityException` on access. This can be suppressed by using a `...noThrow` method to
|
||||
check the mode
|
||||
|
||||
The initial state of an app-op is defined in `AppOpsManager.sOpDefaultMode`. Confusingly the
|
||||
initial state is often not `MODE_DEFAULT`
|
||||
|
||||
Per-package modes can be set using `AppOpsManager.setMode` and per-uid modes can be set using
|
||||
`AppOpsManager.setUidMode`.
|
||||
|
||||
**Warning**: Do not use `setMode` and `setUidMode` for the same app-op. Due to the way the
|
||||
internal storage for the mode works this can lead to very confusing behavior. If this ever happened
|
||||
by accident this needs to be cleaned up for any affected user as the app-op mode is retained over
|
||||
reboot.
|
||||
|
||||
App-ops can also be set via the shell using the `appops set` command. The target package/uid can be
|
||||
defined via parameters to this command.
|
||||
|
||||
The current state of the app-op can be read via the `appops get` command or via `dumpsys appops`.
|
||||
If the app-op is not mentioned in the output the app-op is in it's initial state.
|
||||
|
||||
For example `dumpsys appops`:
|
||||
```
|
||||
[...]
|
||||
Uid 2000:
|
||||
[...]
|
||||
COARSE_LOCATION: mode=foreground
|
||||
START_FOREGROUND: mode=foreground
|
||||
LEGACY_STORAGE: mode=ignore
|
||||
[...]
|
||||
```
|
||||
|
||||
### Guarding access based on app-ops
|
||||
|
||||
API providers need to check the mode returned by `AppOpsManager.noteOp` if they are are allowing
|
||||
access to operations gated by the app-op. `AppOpsManager.unsafeCheckOp` should be used to check the
|
||||
mode if no access is granted. E.g. this can be for displaying app-op state in the UI or when
|
||||
checking the state before later calling `noteOp` anyway.
|
||||
|
||||
If an operation refers to a time span (e.g. a audio-recording session) the API provider should
|
||||
use `AppOpsManager.startOp` and `AppOpsManager.finishOp` instead of `noteOp`.
|
||||
|
||||
`noteOp` and `startOp` take a `packageName` and `featureId` parameter. These need to be read from
|
||||
the calling apps context as `Context.getOpPackageName` and `Context.getFeatureId`, then send to
|
||||
the data provider and then passed on the `noteOp`/`startOp` method.
|
||||
|
||||
#### App-ops and permissions
|
||||
|
||||
Access guarding is often done in combination with permissions using [runtime permissions
|
||||
](../permission/Permissions.md#runtime-permissions-and-app-ops) or [app-op permissions
|
||||
](../permission/Permissions.md#app-op-permissions). This is preferred over just using an app-op
|
||||
as permissions a concept more familiar to app developers.
|
||||
|
||||
### Foreground
|
||||
|
||||
The `AppOpsService` tracks the apps' proc state (== foreground-ness) by following the
|
||||
`ActivityManagerService`'s proc state. It reduces the possible proc states to only those needed
|
||||
for app-ops. It also delays the changes by a _settle time_. This delay is needed as the proc state
|
||||
can fluctuate when switching apps. By delaying the change the appops service is not affected by
|
||||
those.
|
||||
|
||||
The proc state is used for two use cases: Firstly, Tracking remembers the proc state for each
|
||||
tracked event. Secondly, `noteOp`/`checkOp` calls for app-op that are set to `MODE_FOREGROUND` are
|
||||
translated using the `AppOpsService.UidState.evalMode` method into `MODE_ALLOWED` when the app is
|
||||
counted as foreground and `MODE_IGNORED` when the app is counted as background. `checkOpRaw`
|
||||
calls are not affected.
|
||||
|
||||
The current proc state for an app can be read from `dumpsys appops`. The tracking information can
|
||||
be read from `dumpsys appops`
|
||||
|
||||
```
|
||||
Uid u0a118:
|
||||
state=fg
|
||||
capability=6
|
||||
```
|
||||
|
||||
## App-ops for tracking
|
||||
|
||||
App-ops track many important events, including all accesses to runtime permission protected
|
||||
APIs. This is done by tracking when an app-op was noted or started. The tracked data can only be
|
||||
read by system components.
|
||||
|
||||
**Note:** Only `noteOp`/`startOp` calls are tracked; `unsafeCheckOp` is not tracked. Hence it is
|
||||
important to eventually call `noteOp` or `startOp` when providing access to protected operations
|
||||
or data.
|
||||
|
||||
Some apps are forwarding access to other apps. E.g. an app might get the location from the
|
||||
system's location provider and then send the location further to a 3rd app. In this case the
|
||||
app passing on the data needs to call `AppOpsManager.noteProxyOp` to signal the access proxying.
|
||||
This might also make sense inside of a single app if the access is forwarded between two features of
|
||||
the app. In this case an app-op is noted for the forwarding app (proxy) and the app that received
|
||||
the data (proxied). As any app can do it is important to track how much the system trusts this
|
||||
proxy-access-tracking. For more details see `AppOpService.noteProxyOperation`.
|
||||
|
||||
The tracking information can be read from `dumpsys appops` split by feature, proc state and
|
||||
proxying information with the syntax
|
||||
|
||||
```
|
||||
Package THE_PACKAGE_NAME:
|
||||
AN_APP_OP (CURRENT_MODE):
|
||||
FEATURE_ID (or null for default feature)=[
|
||||
ACCESS_OR_REJECT: [PROC_STATE-PROXYING_TAG] TIME proxy[INFO_ABOUT_PROXY IF_PROXY_ACCESS]
|
||||
```
|
||||
|
||||
Example:
|
||||
|
||||
```
|
||||
Package com.google.android.gms:
|
||||
READ_CONTACTS (allow):
|
||||
null=[
|
||||
Access: [fgsvc-s] 2020-02-14 14:24:10.559 (-3d23h15m43s642ms)
|
||||
Access: [fgsvc-tp] 2020-02-14 14:23:58.189 (-3d23h15m56s12ms)
|
||||
]
|
||||
apkappcontext=[
|
||||
Access: [fg-tp] 2020-02-17 14:24:54.721 (-23h14m59s480ms)
|
||||
]
|
||||
com.google.android.gms.icing=[
|
||||
Access: [fgsvc-tpd] 2020-02-14 14:26:27.018 (-3d23h13m27s183ms) proxy[uid=10070, pkg=com.android.providers.contacts, feature=null]
|
||||
Access: [fg-tpd] 2020-02-18 02:26:08.711 (-11h13m45s490ms) proxy[uid=10070, pkg=com.android.providers.contacts, feature=null]
|
||||
Access: [bg-tpd] 2020-02-14 14:34:55.310 (-3d23h4m58s891ms) proxy[uid=10070, pkg=com.android.providers.contacts, feature=null]
|
||||
]
|
||||
MANAGE_EXTERNAL_STORAGE (default):
|
||||
null=[
|
||||
Reject: [fg-s]2020-02-18 08:00:04.444 (-5h39m49s757ms)
|
||||
Reject: [bg-s]2020-02-18 08:00:04.427 (-5h39m49s774ms)
|
||||
]
|
||||
```
|
||||
|
||||
### Tracking an app's own private data accesses
|
||||
|
||||
An app can register an `AppOpsManager.OnOpNotedCallback` to get informed about what accesses the
|
||||
system is tracking for it. As each runtime permission has an associated app-op this API is
|
||||
particularly useful for an app that want to find unexpected private data accesses.
|
||||
|
||||
## Listening to app-op events
|
||||
|
||||
System apps (with the appropriate permissions) can listen to most app-op events, such as
|
||||
|
||||
`noteOp`
|
||||
: `startWatchingNoted`
|
||||
|
||||
`startOp`/`finishOp`
|
||||
: `startWatchingActive`
|
||||
|
||||
mode changes
|
||||
: `startWatchingMode`
|
||||
|
||||
[foreground](#foreground)-ness changes
|
||||
: `startWatchingMode` using the `WATCH_FOREGROUND_CHANGES` flag
|
||||
|
||||
Watching such events is only ever as good as the tracked events. E.g. if the audio provider does
|
||||
not call `startOp` for a audio-session, the app's activeness for the record-audio app-op is not
|
||||
changed. Further there were cases where app-ops were noted even though no data was accessed or
|
||||
operation was performed. Hence before relying on the data from app-ops, double check if the data
|
||||
is actually reliable.
|
||||
109
core/java/android/os/Users.md
Normal file
109
core/java/android/os/Users.md
Normal file
@@ -0,0 +1,109 @@
|
||||
<!--
|
||||
Copyright (C) 2020 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License
|
||||
-->
|
||||
|
||||
# Users for system developers
|
||||
|
||||
## Concepts
|
||||
|
||||
### User
|
||||
|
||||
A user of a device e.g. usually a human being. Each user has its own home screen.
|
||||
|
||||
#### User Profile
|
||||
|
||||
A user can have multiple profiles. E.g. one for the private life and one for work. Each profile
|
||||
has a different set of apps and accounts but they share one home screen. All profiles of a
|
||||
profile group can be active at the same time.
|
||||
|
||||
Each profile has a separate [`userId`](#int-userid). Unless needed user profiles are treated as
|
||||
completely separate users.
|
||||
|
||||
#### Profile Group
|
||||
|
||||
All user profiles that share a home screen. You can list the profiles of a user via
|
||||
`UserManager#getEnabledProfiles` (you usually don't deal with disabled profiles)
|
||||
|
||||
#### Foreground user vs background user
|
||||
|
||||
Only a single user profile group can be in the foreground. This is the user profile the user
|
||||
currently interacts with.
|
||||
|
||||
#### Parent user (profile)
|
||||
|
||||
The main profile of a profile group, usually the personal (as opposed to work) profile. Get this via
|
||||
`UserManager#getProfileParent` (returns `null` if the user does not have profiles)
|
||||
|
||||
#### Managed user (profile)
|
||||
|
||||
The other profiles of a profile group. The name comes from the fact that these profiles are usually
|
||||
managed by a device policy controller app. You can create a managed profile from within the device
|
||||
policy controller app on your phone.
|
||||
|
||||
#### Account
|
||||
|
||||
An account of a user profile with a (usually internet based) service. E.g. aname@gmail.com or
|
||||
aname@yahoo.com. Each profile can have multiple accounts. A profile does not have to have a
|
||||
account.
|
||||
|
||||
## Data types
|
||||
|
||||
### int userId
|
||||
|
||||
... usually marked as `@UserIdInt`
|
||||
|
||||
The id of a user profile. List all users via `adb shell dumpsys user`. There is no data type for a
|
||||
user, all you can do is using the user id of the parent profile as a proxy for the user.
|
||||
|
||||
### int uid
|
||||
|
||||
Identity of an app. This is the same as a Linux uid, but in Android there is one uid per package,
|
||||
per user.
|
||||
|
||||
It is highly discouraged, but uids can be shared between multiple packages using the
|
||||
`android:sharedUserId` manifest attribute.
|
||||
|
||||
### class UserHandle
|
||||
|
||||
A wrapper for userId. Used esp. in public APIs instead of `int userId` as it clearly distinguishes
|
||||
from uid.
|
||||
|
||||
## Security model
|
||||
|
||||
Multiple packages can share an uid by using `android:sharedUserId` manifest attribute. If packages
|
||||
share a uid they can run in the same process via `android:process` manifest attribute. Further file
|
||||
level access is also tracked by uid. Hence any security or privacy mechanism needs to be built on
|
||||
a uid granularity.
|
||||
|
||||
On the other hand apps belonging to the same user cannot see each others files. They can only
|
||||
interact via activity launches, broadcasts, providers, and service bindings. All of them can be be
|
||||
protected by [permissions](../permission/Permissions.md). Hence any new general communication
|
||||
mechanism should be access controlled by permissions.
|
||||
|
||||
## Lifecycle
|
||||
|
||||
A system service should deal with users being started and stopped by overriding
|
||||
`SystemService.onSwitchUser` and `SystemService.onStopUser`.
|
||||
|
||||
If users profiles become inactive the system should stop all apps of this profile from interacting
|
||||
with other apps or the system.
|
||||
|
||||
Another important lifecycle event is `onUnlockUser`. Only for unlocked user profiles you can access
|
||||
all data, e.g. which packages are installed.
|
||||
|
||||
You only want to deal with user profiles that
|
||||
|
||||
- are in the profile group of the foreground user
|
||||
- the user profile is unlocked and not yet stopped
|
||||
832
core/java/android/permission/Permissions.md
Normal file
832
core/java/android/permission/Permissions.md
Normal file
@@ -0,0 +1,832 @@
|
||||
<!--
|
||||
Copyright (C) 2020 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License
|
||||
-->
|
||||
|
||||
# Android permissions for system developers
|
||||
|
||||
This document targets system developers. App developers should refer to the [public
|
||||
documentation](https://developer.android.com/guide/topics/permissions/overview).
|
||||
|
||||
## Definitions
|
||||
|
||||
Each app (often called package) has a unique name called package name. Each package has a manifest
|
||||
file describing properties of the package. The android system server is in a special package named
|
||||
"android".
|
||||
|
||||
When a package gets installed the package is (usually) assigned a unique identifier called [uid
|
||||
](../os/Users.md#int-uid).
|
||||
This is the same as a uid in Linux, but this often leads to confusion. It is easiest to see a uid
|
||||
as a unique identifier for a package.
|
||||
|
||||
Usually an app is running in a container called a process. Each process has a unique id called
|
||||
pid, but unlike the uid the pid changes each time the process is restarted and app that are not
|
||||
currently running don't have a pid. The process container makes sure that other apps cannot
|
||||
negatively interact with an app. Processes can only interact via controlled interactions called
|
||||
remote procedure calls (RPCs). Android’s RPC mechanism is called _Binder_.
|
||||
|
||||
As no app code can be trusted the permission need to be checked on the receiving side of the
|
||||
Binder call.
|
||||
|
||||
For more details please take a look at [Android's security model](../os/Users.md#security-model).
|
||||
|
||||
## Permissions for regular apps
|
||||
|
||||
### Install time permissions
|
||||
|
||||
The purpose of install time permissions is to control access to APIs where it does not makes sense
|
||||
to involve the user. This can be either because the API is not sensitive, or because additional
|
||||
checks exist.
|
||||
|
||||
#### Defining a permission
|
||||
|
||||
Any package can define a permission. For that it simply adds an entry in the manifest file
|
||||
`<permission android:name="com.example.myapp.myfirstpermission" />`
|
||||
|
||||
Any package can do this, including the system package. When talking about [permissions for system
|
||||
apps](#permissions-for-system-apps) we will see that it is important which package defines a
|
||||
permission.
|
||||
|
||||
It is common good practice to prefix the permission name with the package name to avoid collisions.
|
||||
|
||||
#### Requesting a permission
|
||||
|
||||
Any app can request any permission via adding an entry in the manifest file like
|
||||
`<uses-permission android:name="com.example.myapp.myfirstpermission" />`
|
||||
|
||||
A requested permission does not necessarily mean that the permission is granted. When and how a
|
||||
permission is granted depends on the protection level of the permission. If no protection level is
|
||||
set, the permission will always be granted. Such "normal" permissions can still be useful as it
|
||||
will be easy to find apps using a certain functionality on app stores and by checking `dumpsys
|
||||
package`.
|
||||
|
||||
#### Checking a permission
|
||||
|
||||
`Context.checkPermission(permission, pid, uid)` returns if the pid/uid has the permission. By far
|
||||
the most common case is to check the permission on the receiving end of a binder call. In this case
|
||||
the pid can be read as `Binder.callingPid()` and the uid as `Binder.callingUid()`. The uid is a
|
||||
mandatory argument as permissions are maintained per uid. The pid can be set to -1
|
||||
if not pid is available. The context class contains handy wrappers for `checkPermission`, such as
|
||||
`enforeCallingPermission` which calls checkPermission with `Binder.callingPid`/`Binder.callingUid`
|
||||
and throws a SecurityException when the permission is not granted.
|
||||
|
||||
#### Verifying an app has an install time permission
|
||||
|
||||
In `dumpsys package my.package.name` there are two sections. In requested permissions all
|
||||
permissions of the `uses-permission` tags are listed. In install permission the permissions with
|
||||
their grant state are listed. If an install time permission is not listed here, it is not granted.
|
||||
|
||||
```
|
||||
Packages:
|
||||
Package [com.android.packageinstaller] (2eb7062):
|
||||
userId=10071
|
||||
[...]
|
||||
requested permissions:
|
||||
android.permission.MANAGE_USERS
|
||||
android.permission.INSTALL_PACKAGES
|
||||
android.permission.DELETE_PACKAGES
|
||||
android.permission.READ_INSTALL_SESSIONS
|
||||
android.permission.RECEIVE_BOOT_COMPLETED
|
||||
android.permission.HIDE_NON_SYSTEM_OVERLAY_WINDOWS
|
||||
android.permission.USE_RESERVED_DISK
|
||||
android.permission.UPDATE_APP_OPS_STATS
|
||||
android.permission.MANAGE_APP_OPS_MODES
|
||||
android.permission.INTERACT_ACROSS_USERS_FULL
|
||||
android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME
|
||||
android.permission.PACKAGE_USAGE_STATS
|
||||
install permissions:
|
||||
android.permission.USE_RESERVED_DISK: granted=true
|
||||
android.permission.INSTALL_PACKAGES: granted=true
|
||||
android.permission.RECEIVE_BOOT_COMPLETED: granted=true
|
||||
android.permission.INTERACT_ACROSS_USERS_FULL: granted=true
|
||||
android.permission.PACKAGE_USAGE_STATS: granted=true
|
||||
android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME: granted=true
|
||||
android.permission.READ_INSTALL_SESSIONS: granted=true
|
||||
android.permission.MANAGE_USERS: granted=true
|
||||
android.permission.HIDE_NON_SYSTEM_OVERLAY_WINDOWS: granted=true
|
||||
android.permission.MANAGE_APP_OPS_MODES: granted=true
|
||||
android.permission.UPDATE_APP_OPS_STATS: granted=true
|
||||
android.permission.DELETE_PACKAGES: granted=true
|
||||
```
|
||||
|
||||
#### End-to-end: Protecting an RPC call via a permission
|
||||
|
||||
##### Service Manifest
|
||||
|
||||
```xml
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.android.example.myservice">
|
||||
<!-- Define a permission -->
|
||||
<permission android:name="com.android.example.myservice.MY_PERMISSION" />
|
||||
<application>
|
||||
<service android:name=".MyService" android:exported="true" />
|
||||
</application>
|
||||
</manifest>
|
||||
```
|
||||
|
||||
##### Service code
|
||||
|
||||
```kotlin
|
||||
class MyService : Service() {
|
||||
override fun onBind(intent: Intent?): IBinder? {
|
||||
return object : IMyService.Stub() {
|
||||
override fun doSomething() {
|
||||
// Verify that calling UID has the permission
|
||||
enforceCallingPermission(
|
||||
"com.android.example.myservice.MY_PERMISSION",
|
||||
"Need to hold permission"
|
||||
)
|
||||
// do something
|
||||
}
|
||||
}.asBinder()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
##### Caller Manifest
|
||||
|
||||
```xml
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.android.example.myapp">
|
||||
<!-- request a permission -->
|
||||
<uses-permission android:name="com.android.example.myservice.MY_PERMISSION" />
|
||||
<application />
|
||||
</manifest>
|
||||
```
|
||||
|
||||
### Runtime permissions
|
||||
|
||||
Runtime permission must be granted by the user during runtime. This is needed if the API protects
|
||||
data or functionality that is sensitive for the user. E.g. the users current location is protected
|
||||
by a runtime permission.
|
||||
|
||||
Users want a system that is secure and privacy focused by default. User can also often not make a
|
||||
good choice when asked at the wrong time without enough context. Hence in general runtime
|
||||
permissions should be avoided and the API should be built in a way where no private data needs to be
|
||||
leaked.
|
||||
|
||||
#### Defining a runtime permission
|
||||
|
||||
Runtime permissions are defined in the same way as install time permissions. To tag them as runtime
|
||||
permissions the `protectionLevel` needs to be set to dangerous. Dangerous is a synonym for
|
||||
runtime permissions in the Android platform.
|
||||
|
||||
```xml
|
||||
<uses-permission android:name="com.example.myapp.myfirstruntimepermission"
|
||||
android:protectionLevel="dangerous" />
|
||||
```
|
||||
|
||||
#### Requesting a runtime permission
|
||||
|
||||
Similar to install time permissions any app can request a runtime permission by adding the
|
||||
`<uses-permission android:name="com.example.myapp.myfirstruntimepermission" />`
|
||||
to the manifest.
|
||||
|
||||
By default runtime permissions are not granted. The app needs to call `Activity.requestPermissions`
|
||||
during runtime to ask the user for the permission. The user might then grant or deny and once the
|
||||
decision is made the activity is called by via `Activity.onPermissionGranted`.
|
||||
|
||||
During development and testing a runtime permission can be granted via the `pm` shell command or by
|
||||
using the `UiAutomator.grantRuntimePermission` API call. Please note that this does _not_ grant the
|
||||
[app-op](#runtime-permissions-and-app-ops) synchronously. Unless the app needs to test the actual
|
||||
permission grant flow it is recommended to grant the runtime permissions during install using
|
||||
`adb install -g /my/package.apk`.
|
||||
|
||||
#### Checking a runtime permission
|
||||
|
||||
For runtime permissions defined by a 3rd party apps it is fine to check a runtime
|
||||
permission like an install time permission. For system defined permissions you need to check all
|
||||
runtime permissions by using the `PermissionChecker` utility. It is good practice to use the tool
|
||||
anywhere possible.
|
||||
|
||||
The permission checker might return `PERMISSION_DENIED_APP_OP` which should lead to a silent
|
||||
failure. This can only happen for system defined runtime permissions.
|
||||
|
||||
##### Runtime permissions and app-ops
|
||||
|
||||
> See [App-ops](../app/AppOps.md).
|
||||
|
||||
The PermissionChecker code fundamentally looks like this:
|
||||
|
||||
```kotlin
|
||||
class PermissionChecker {
|
||||
fun checkCallingPermission(context: Context, permission: String) {
|
||||
if (isRuntimePermission(permission)) {
|
||||
if (context.checkPermission(uid, permission) == DENIED) {
|
||||
return PERMISSION_DENIED
|
||||
}
|
||||
|
||||
val appOpOfPermission = AppOpsManager.permissionToOp(permission)
|
||||
if (appOpOfPermission == null) {
|
||||
// not platform defined
|
||||
return PERMISSION_GRANTED
|
||||
}
|
||||
|
||||
val appOpMode = appOpsManager.noteOp(appOpOfPermission)
|
||||
if (appOpMode == AppOpsManager.MODE_ALLOWED) {
|
||||
return PERMISSION_GRANTED
|
||||
} else {
|
||||
return PERMISSION_DENIED_APP_OP
|
||||
}
|
||||
} else {
|
||||
return PERMISSION_DENIED
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
For each platform defined runtime permission there is a matching app-op. When calling
|
||||
`AppOpsManager.noteOp` this returns either `MODE_ALLOWED` or `MODE_IGNORED`.
|
||||
|
||||
This value is then used to decide between `PERMISSION_DENIED_APP_OP` and `PERMISSION_GRANTED`.
|
||||
|
||||
The primary purpose of the special `PERMISSION_DENIED_APP_OP` state was to support apps targeting an
|
||||
SDK lower than 23. These apps do not understand the concept of denied runtime permissions. Hence
|
||||
they would crash when getting a `SecurityException`. To protect the users' privacy while still not
|
||||
crashing the app the special `PERMISSION_DENIED_APP_OP` mandates that the API should somehow
|
||||
silently fail.
|
||||
|
||||
A secondary use case of the `AppOpsManager.noteOp` calls is to
|
||||
[track](../app/AppOps.md#Appops-for-tracking) which apps perform what runtime protected actions.
|
||||
|
||||
#### Verifying an app has a runtime time permission
|
||||
|
||||
In `dumpsys package my.package.name` the runtime permissions are listed per uid. I.e. different
|
||||
users might have different runtime permission grants and shared uids share a grant-set. If a runtime
|
||||
permission is listed as requested but not in the runtime permission section it is in it’s initial
|
||||
state, i.e. not granted.
|
||||
|
||||
```
|
||||
Packages:
|
||||
Package [com.google.android.GoogleCamera] (ccb6af):
|
||||
userId=10181
|
||||
[...]
|
||||
requested permissions:
|
||||
android.permission.ACCESS_COARSE_LOCATION
|
||||
android.permission.ACCESS_FINE_LOCATION
|
||||
android.permission.ACCESS_NETWORK_STATE
|
||||
android.permission.ACCESS_NOTIFICATION_POLICY
|
||||
android.permission.ACCESS_WIFI_STATE
|
||||
android.permission.BIND_WALLPAPER
|
||||
android.permission.CAMERA
|
||||
android.permission.CHANGE_WIFI_STATE
|
||||
android.permission.INTERNET
|
||||
android.permission.GET_PACKAGE_SIZE
|
||||
android.permission.NFC
|
||||
android.permission.READ_SYNC_SETTINGS
|
||||
android.permission.RECEIVE_BOOT_COMPLETED
|
||||
android.permission.RECORD_AUDIO
|
||||
android.permission.SET_WALLPAPER
|
||||
android.permission.USE_CREDENTIALS
|
||||
android.permission.VIBRATE
|
||||
android.permission.WAKE_LOCK
|
||||
android.permission.WRITE_EXTERNAL_STORAGE [ ... ]
|
||||
android.permission.WRITE_SETTINGS
|
||||
android.permission.WRITE_SYNC_SETTINGS
|
||||
com.google.android.elmyra.permission.CONFIGURE_ASSIST_GESTURE
|
||||
com.google.android.providers.gsf.permission.READ_GSERVICES
|
||||
android.permission.FOREGROUND_SERVICE
|
||||
com.google.android.googlequicksearchbox.permission.LENSVIEW_BROADCAST
|
||||
android.permission.READ_EXTERNAL_STORAGE [ ... ]
|
||||
[...]
|
||||
User 0: [ ... ]
|
||||
overlay paths:
|
||||
runtime permissions:
|
||||
android.permission.ACCESS_FINE_LOCATION: granted=false [ ... ]
|
||||
android.permission.READ_EXTERNAL_STORAGE: granted=true [ ... ]
|
||||
android.permission.ACCESS_COARSE_LOCATION: granted=false [ ... ]
|
||||
android.permission.CAMERA: granted=true [ ... ]
|
||||
android.permission.WRITE_EXTERNAL_STORAGE: granted=true [ ... ]
|
||||
android.permission.RECORD_AUDIO: granted=true[ ... ]
|
||||
```
|
||||
|
||||
#### End-to-end: Protecting an RPC call via a runtime permission
|
||||
|
||||
##### Service Manifest
|
||||
|
||||
```xml
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.android.example.myservice">
|
||||
<!-- Define a runtime permission -->
|
||||
<permission android:name="com.android.example.myservice.MY_RUNTIME_PERMISSION"
|
||||
android:protectionLevel="dangerous" />
|
||||
<application>
|
||||
<service android:name=".MyService" android:exported="true" />
|
||||
</application>
|
||||
</manifest>
|
||||
```
|
||||
|
||||
##### Service code
|
||||
|
||||
```kotlin
|
||||
class MyService : Service() {
|
||||
override fun onBind(intent: Intent?): IBinder? {
|
||||
return object : IMyService.Stub() {
|
||||
override fun doSomething(callingPackage: String?, callingFeatureId: String?) {
|
||||
Objects.requireNonNull(callingPackageName)
|
||||
|
||||
// Verify that calling UID has the permission
|
||||
when (run {
|
||||
PermissionChecker.checkCallingPermission(
|
||||
this@MyService,
|
||||
"com.android.example.myservice.MY_RUNTIME_PERMISSION",
|
||||
callingPackageName,
|
||||
callingFeatureId,
|
||||
"Did something"
|
||||
)
|
||||
}) {
|
||||
PERMISSION_GRANTED -> /* do something */
|
||||
PERMISSION_DENIED_APP_OP -> /* silent failure, do nothing */
|
||||
else -> throw SecurityException(
|
||||
"Cannot do something as caller is missing "
|
||||
+ "com.android.example.myservice.MY_RUNTIME_PERMISSION"
|
||||
)
|
||||
}
|
||||
}
|
||||
}.asBinder()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
##### Caller Manifest
|
||||
|
||||
```xml
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.android.example.myapp">
|
||||
<!-- request a permission -->
|
||||
<uses-permission android:name="com.android.example.myservice.MY_RUNTIME_PERMISSION" />
|
||||
<application />
|
||||
</manifest>
|
||||
```
|
||||
|
||||
##### Caller code
|
||||
|
||||
```kotlin
|
||||
class MyActivity : Activity {
|
||||
fun callDoSomething() {
|
||||
if (checkSelfPermission("com.android.example.myservice.MY_RUNTIME_PERMISSION") == PERMISSION_DENIED) {
|
||||
// Interrupt operation and request permission
|
||||
requestPermissions(arrayOf("com.android.example.myservice.MY_RUNTIME_PERMISSION"), 23)
|
||||
} else {
|
||||
myService.doSomething(this@MyActivity.opPackageName, this@MyActivity.featureId)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onRequestPermissionsResult(
|
||||
requestCode: Int,
|
||||
permissions: Array<String>,
|
||||
grantResults: IntArray
|
||||
) {
|
||||
if (requestCode == 23 && grantResults[0] == PERMISSION_GRANTED) {
|
||||
// Finish operation
|
||||
callDoSomething()
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Restricted permissions
|
||||
|
||||
Some runtime permissions are restricted. They are annotated in the platforms `AndroidManifest.xml`
|
||||
has `hardRestricted` or `softRestricted`.
|
||||
|
||||
Restricted permissions behave uncommon when not whitelisted. When whitelisted the permissions
|
||||
behave normally. What uncommon means depends on the whether they are hard or soft restricted.
|
||||
|
||||
They can either be whitelisted during upgrade P->Q, but the system or need to be whitelisted by the
|
||||
installer via `PackageInstaller.SessionParams.setWhitelistedRestrictedPermissions`. If this method
|
||||
is not used all permissions will be whitelisted.
|
||||
|
||||
Afterwards the app that originally installed the app can change the whitelisting state via
|
||||
`PackageManager.addWhitelistedRestrictedPermission` and
|
||||
`PackageManager.removeWhitelistedRestrictedPermission`.
|
||||
|
||||
The system tracks the source of the whitelisting by having three different flags
|
||||
`RESTRICTION_SYSTEM_EXEMPT`, `RESTRICTION_UPGRADE_EXEMPT`, and `RESTRICTION_INSTALLER_EXEMPT`,
|
||||
|
||||
The flags can be checked in `dumpsys package my.package.name`
|
||||
|
||||
```
|
||||
User 0:
|
||||
[...]
|
||||
runtime permissions:
|
||||
android.permission.READ_EXTERNAL_STORAGE: granted=false, flags=[ RESTRICTION_UPGRADE_EXEMPT ]
|
||||
android.permission.ACCESS_FINE_LOCATION: granted=true, flags=[ RESTRICTION_SYSTEM_EXEMPT|RESTRICTION_UPGRADE_EXEMPT ]
|
||||
```
|
||||
|
||||
##### Hard restricted
|
||||
|
||||
Hard restricted permissions need to be whitelisted to be grant-able.
|
||||
|
||||
##### Soft restricted
|
||||
|
||||
The behavior of non-whitelisted soft restricted permissions is not uniform. The behavior is
|
||||
defined in the `SoftRestrictedPermissionPolicy`.
|
||||
|
||||
#### System fixed permission
|
||||
|
||||
Some runtime permissions are required for normal operation of the device. In this case the system
|
||||
can grant the permission as `SYSTEM_FIXED`. In this case the permission can be seen in the
|
||||
[permission management settings](#settings) but cannot be revoked by the user.
|
||||
|
||||
The flag can be checked in `dumpsys package my.package.name`
|
||||
```
|
||||
User 0:
|
||||
[...]
|
||||
runtime permissions:
|
||||
android.permission.READ_EXTERNAL_STORAGE: granted=true, flags=[ SYSTEM_FIXED|GRANTED_BY_DEFAULT ]
|
||||
```
|
||||
|
||||
#### Background access
|
||||
|
||||
Whether the app is currently visible to the user is reflected in the `ActivityManager`'s proc state.
|
||||
There is a lot of granularity to this, but runtime permissions are using the [app-ops services'
|
||||
](../app/AppOps.md) definition of foreground and background.
|
||||
|
||||
Most runtime permissions are not affected by foreground/background-ness. Microphone and Camera are
|
||||
foreground-only while Location is usually foreground-only, but background access can be added by
|
||||
granting the `ACCESS_BACKGROUND_LOCATION` modifier runtime permission.
|
||||
|
||||
##### Microphone and Camera
|
||||
|
||||
Currently these only allow access while in the app is in foreground. There is a manual whitelist
|
||||
for e.g. the voice interaction service.
|
||||
|
||||
This is currently (Mar 2020) reworked and will behave like [location](#location) soon.
|
||||
|
||||
##### Location
|
||||
|
||||
As described [above](#runtime-permissions-and-app-ops) the app-op mode for granted permissions is
|
||||
`MODE_ALLOWED` to allow access or `MODE_IGNORED` to suppress access.
|
||||
|
||||
The important case is the case where the permission is granted and the app-op is `MODE_IGNORED`. In
|
||||
the case of location this state causes the `LocationManagerService` to stop delivering locations to
|
||||
the app. This is not a breaking behavior as the same scenarios happens if e.g. no satellites
|
||||
could be found.
|
||||
|
||||
This behavior is used to implement the foregound/background behavior for location. If the app is
|
||||
in the foreground the app-op mode is `MODE_ALLOWED` and works normally. If the app goes into
|
||||
background the app-op mode changes to `MODE_IGNORED`. This means that locations are delivered while
|
||||
the app is in foreground and while the app is background, the app won't get any locations.
|
||||
|
||||
The automatic switching between `MODE_ALLOWED` and `MODE_IGNORED` is done inside of
|
||||
[`AppOpsManager`](../app/AppOps.md#foreground).
|
||||
|
||||
Background access can be enabled by also granting the `ACCESS_BACKGROUND_LOCATION` to the app. In
|
||||
this case the app-op mode will always be `MODE_ALLOWED`.
|
||||
|
||||
#### UI
|
||||
|
||||
##### Granting
|
||||
|
||||
An app following the best practices does not ask for any runtime permissions until absolutely
|
||||
needed. Once needed the request should be made in context. I.e. the user should understand from the
|
||||
current state of the app and the user's action why the request is made. E.g. if the user presses
|
||||
a "show me the next ATM"-button the user is most likely expecting a request for the location
|
||||
permission.
|
||||
|
||||
This is central premise to the runtime permission UI. It is the app's responsibility to avoid
|
||||
showing permission requests dialogs to the user which might get denied. These dialogs are not
|
||||
meant to be user-choices, they are meant to be user-confirmations.
|
||||
|
||||
Hence any denied permission dialog is probably due to the app asking for permissions the user
|
||||
does not expect. If too many permission requests get denied the app is apparently trying to get
|
||||
more than the user wants to give to the app. In this case the permission gets permanently denied
|
||||
and all future requests will be denied automatically without showing a UI.
|
||||
|
||||
`Context.requestPermission` calls for more than one permission are allowed and might result in
|
||||
multiple dialogs in sequence. This might make sense for e.g. getting microphone and camera
|
||||
permission when starting a video call.
|
||||
|
||||
Each time the the user makes a choice (either to grant or the deny) a permission request the
|
||||
permission is marked as `USER_SET`. If a permission gets permanently denied the permission is marked
|
||||
as `USER_FIXED`.
|
||||
|
||||
This can be found in `dumpsys package my.package.name`
|
||||
```
|
||||
User 0:
|
||||
[...]
|
||||
runtime permissions:
|
||||
android.permission.READ_EXTERNAL_STORAGE: granted=false, flags=[ USER_SET|USER_FIXED ]
|
||||
android.permission.ACCESS_FINE_LOCATION: granted=true, flags=[ USER_SET ]
|
||||
```
|
||||
|
||||
##### Settings
|
||||
|
||||
By far most interactions with the permission system are via the [permission grant flow](#granting).
|
||||
The main purpose of the permission settings is to show the user the previous choices and allow
|
||||
the user to revisit previous choices. In reality few users do that.
|
||||
|
||||
##### Grouping
|
||||
|
||||
There are too many runtime permissions for the user to individually manage. Hence the UI bundles the
|
||||
permissions into groups. **Apps should never assume the grouping**. The grouping might change
|
||||
with SDK updates, but also at any other time. Certain form factors or locales might use other
|
||||
permission models and sometimes some of the permissions of a group cannot be granted for various
|
||||
reasons. The grouping is defined inside the permission controller app.
|
||||
|
||||
If two permissions belong to a group and the first permission is already granted the second one
|
||||
will be granted on request of the app without user interaction. For that reason a permission
|
||||
group with at least one individual permission granted will show up as granted in the UI.
|
||||
|
||||
##### Alternate permission management
|
||||
|
||||
It is not allowed to build alternate permission management UIs. While restricting innovation is not
|
||||
a good choice this is a required one to enforce a consistent, predictable, but flexible permission
|
||||
model for users and app developers.
|
||||
|
||||
Further some data needed for permission management (e.g. the grouping) is not available outside
|
||||
the permission controller app.
|
||||
|
||||
Hence all permission management UI needs to be integrated with AOSP.
|
||||
|
||||
#### Pre granting
|
||||
|
||||
Runtime permissions protect user private data. It is a violation of user trust to give the data
|
||||
to an app without explicit user consent (i.e. the user [granting](#granting) the permission
|
||||
). Still the user expects certain functionality (e.g. receiving a phone call) to work out of the
|
||||
box.
|
||||
|
||||
Hence the `DefaultPermissionGrantPolicy` and roles allow to grant permission without the user
|
||||
. The default permission grant policy grants permissions for three categories of apps
|
||||
- Apps running in well defined [uids](../os/Users.md#int-uid) as they are considered as part of
|
||||
the platform
|
||||
- Apps that are in certain predefined categories, e.g. the browser and the SMS app. This is
|
||||
meant for the most basic phone functionality, not for all pre-installed apps.
|
||||
- Apps that are explicitly mentioned as a pre-grant-exceptions. This is meant to be used for setup
|
||||
and other highly critical use cases, not to improve the user experience. The exceptions are listed
|
||||
in xml files in `etc/` and follow the following syntax
|
||||
```xml
|
||||
<exceptions>
|
||||
<exception package="my.package.name">
|
||||
<permission name="android.permission.ACCESS_FINE_LOCATION" fixed="false"/>
|
||||
</exception>
|
||||
</exceptions>
|
||||
```
|
||||
|
||||
Pre-granted runtime permissions can still be revoked by the user in [settings](#settings) unless
|
||||
they are granted as `SYSTEM_FIXED`.
|
||||
|
||||
Whether a permission was granted by the default can be checked in the permission flags of
|
||||
`dumpsys package my.package.name`
|
||||
|
||||
```
|
||||
User 0:
|
||||
[...]
|
||||
runtime permissions:
|
||||
android.permission.ACCESS_FINE_LOCATION: granted=true, flags=[ GRANTED_BY_DEFAULT ]
|
||||
```
|
||||
|
||||
### Permission restricted components
|
||||
|
||||
As [publicly documented](https://developer.android.com/guide/topics/permissions/overview#permission_enforcement)
|
||||
it is possible to restrict starting an activity/binding to a service by using permission. It is
|
||||
a common pattern to
|
||||
|
||||
- define a permission in the platform as `signature`
|
||||
- protect a service in an app by this permission using the `android:permission` attribute of the
|
||||
`<service>` tag
|
||||
|
||||
Then it is guaranteed that only the system can bind to such service. This is used for services
|
||||
that provide extensions to platform functionality, such as auto-fill services, print services, and
|
||||
accessibility services.
|
||||
|
||||
This does not work for app-op or runtime permissions as the way to check these permissions is
|
||||
more complex than install time permissions.
|
||||
|
||||
## Permissions for system apps
|
||||
|
||||
System apps need to integrate deeper with the system than regular apps. Hence they need to be
|
||||
able to call APIs not available to other apps. This is implemented by granting permissions to
|
||||
these system apps and then enforcing the permissions in the API similar to other [install time
|
||||
permissions](#checking-a-permission).
|
||||
|
||||
System apps are not different from regular apps, but the protection levels (e.g.
|
||||
[privileged](#privileged-permissions), [preinstalled](#preinstalled-permissions)) mentioned in this
|
||||
section are more commonly used by system apps.
|
||||
|
||||
### Multiple permission levels
|
||||
|
||||
It is possible to assign multiple protection levels to a permission. Very common combinations are
|
||||
for example adding `signature` to all permissions to make sure the platform signed apps can be
|
||||
granted the permission, e.g. `privileged|signature`.
|
||||
|
||||
The permission will be granted if the app qualifies for _any_ of the permission levels.
|
||||
|
||||
### App-op permissions
|
||||
|
||||
> See [App-ops](../app/AppOps.md).
|
||||
|
||||
App-op permissions are user-switchable permissions that are not runtime permissions. This should
|
||||
be used for permissions that are really only meant to be ever granted to a very small amount of
|
||||
apps. Traditionally granting these permissions is intentionally very heavy weight so that the
|
||||
user really needs to understand the use case. For example one use case is the
|
||||
`INTERACT_ACROSS_PROFILES` permission that allows apps of different
|
||||
[user profiles](../os/Users.md#user-profile) to interact. Of course this is breaking a very basic
|
||||
security container and hence should only every be granted with a lot of care.
|
||||
|
||||
**Warning:** Most app-op permissions follow this logic, but most of them also have exceptions
|
||||
and special behavior. Hence this section is a guideline, not a rule.
|
||||
|
||||
#### Defining an app-op permission
|
||||
|
||||
Only the platform can reasonably define an app-op permission. The permission is defined in the
|
||||
platforms manifest using the `appop` protection level
|
||||
|
||||
```xml
|
||||
<manifest package="android">
|
||||
<permission android:name="android.permission.MY_APPOP_PERMISSION"
|
||||
android:protectionLevel="appop|signature" />
|
||||
</manifest>
|
||||
```
|
||||
|
||||
Almost always the protection level is app-op | something else, like
|
||||
[signature](#signature-permissions) (in the case above) or [privileged](#privileged-permissions).
|
||||
|
||||
#### Checking a app-op permission
|
||||
|
||||
The `PermissionChecker` utility can check app-op permissions with the [same syntax as runtime
|
||||
permissions](#checking-a-runtime-permission).
|
||||
|
||||
The permission checker internally follows this flow
|
||||
|
||||
```kotlin
|
||||
class PermissionChecker {
|
||||
fun checkCallingPermission(context: Context, permission: String) {
|
||||
if (isAppOpPermission(permission)) {
|
||||
val appopOfPermission = AppOpsManager.permissionToOp(permission)
|
||||
if (appopOfPermission == null) {
|
||||
// not platform defined
|
||||
return PERMISSION_DENIED
|
||||
}
|
||||
|
||||
val appopMode = appOpsManager.noteOp(appopOfPermission)
|
||||
when (appopMode) {
|
||||
AppOpsManager.MODE_ALLOWED -> return PERMISSION_GRANTED
|
||||
AppOpsManager.MODE_IGNORED -> return PERMISSION_DENIED
|
||||
AppOpsManager.MODE_DEFAULT -> {
|
||||
if (context.checkPermission(uid, permission) == GRANTED) {
|
||||
return PERMISSION_GRANTED
|
||||
} else {
|
||||
return PERMISSION_DENIED
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return PERMISSION_DENIED
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Granting a app-op permission
|
||||
|
||||
The permission's grant state is only considered if the app-op's mode is `MODE_DEFAULT`. This
|
||||
allows to have default grants while still being overridden by the app-op.
|
||||
|
||||
The permission is then granted by setting the app-op mode. This is usually done via dedicated APIs
|
||||
for each use cases. Similarly whether and how an app can request the permission is different for
|
||||
each app-op permission.
|
||||
|
||||
When implementing a new app-op permission, make sure to set the app-op mode using `AppOpsManager
|
||||
.setUidMode` to make sure the permission is granted on the uid as this is the security domain.
|
||||
|
||||
During development app-ops can be grated to app via the `appops set` shell command. E.g.
|
||||
|
||||
```
|
||||
adb shell appops set 10187 INTERACT_ACROSS_PROFILES allow
|
||||
```
|
||||
|
||||
sets the `INTERACT_ACROSS_PROFILES` app-op for uid 10187 to allow thereby granting apps in this
|
||||
uid the ability to interact across profiles.
|
||||
|
||||
##### UI
|
||||
|
||||
Most UIs for app-op permissions are in the "Special app access" section of the settings app.
|
||||
|
||||
In most cases the permission should only be granted with the user's explicit agreement, usually by
|
||||
allowing the app to directly open the "Special app access" page for this permission and app.
|
||||
|
||||
To repeat: this is a guideline for app-op permissions and there are many exceptions.
|
||||
|
||||
### Signature permissions
|
||||
|
||||
Only apps signed with the defining app's certificate will be granted the permission. This is
|
||||
used to restrict APIs to apps of the same developer.
|
||||
|
||||
This is frequently used to restrict permissions defined by the platform to apps also signed with
|
||||
the platform's certificate. As this is a very tight restriction this is recommended for
|
||||
permissions that are only used by apps built out of AOSP which are signed with the platform
|
||||
certificate.
|
||||
|
||||
Please note that OEMs sign their platform them self. I.e. OEMs can implement new apps using these
|
||||
permissions. It is unlikely that 3rd party apps will be able to use APIs protected by signature
|
||||
permissions as they are usually not signed with the platform certificate.
|
||||
|
||||
Such permissions are defined and checked like an install time permission.
|
||||
|
||||
### Preinstalled permissions
|
||||
|
||||
This means that the app has to be pre-installed. There is no restriction what apps are pre-installed
|
||||
on a particular device install there. Hence it can be really any app including 3rd party apps.
|
||||
|
||||
Hence this permission level is discouraged unless there are
|
||||
[further restrictions](#restricted-by-tests).
|
||||
|
||||
Such permissions are defined and checked like an install time permission.
|
||||
|
||||
### Privileged permissions
|
||||
|
||||
This means that the app has to be pre-installed and in the `system/priv` directory in the
|
||||
filesystem. There is no restriction what apps are in this directory on a particular device
|
||||
install there. Hence it can be really any app including 3rd party apps.
|
||||
|
||||
An app is only ever granted privileged permissions requested by the pre-installed apk. I.e.
|
||||
privileged permissions added in updates will never be granted.
|
||||
|
||||
Hence this permission level is discouraged unless there are
|
||||
[further restrictions](#restricted-by-tests).
|
||||
|
||||
Such permissions are defined and checked like an install time permission.
|
||||
|
||||
#### Restricted by tests
|
||||
|
||||
As all apps that might get preinstalled or privilidged permissions need to be pre-installed and new
|
||||
images need to pass compliance tests it is possible to use a test to whitelist the apps that can
|
||||
request the permission.
|
||||
|
||||
Example of such a test:
|
||||
```kotlin
|
||||
/* Add new whitelisted packages to this list */
|
||||
private val whitelistedPkgs = listOf("my.whitelisted.package")
|
||||
|
||||
@Test
|
||||
fun onlySomeAppsAreAllowedToHavePermissionGranted() {
|
||||
assertThat(whitelistedPkgs).containsAllIn(
|
||||
context.packageManager.getInstalledPackages(MATCH_ALL)
|
||||
.filter { pkg ->
|
||||
context.checkPermission(android.Manifest.permission.MY_PRIVILEGED_PERMISSION, -1,
|
||||
pkg.applicationInfo.uid) == PERMISSION_GRANTED
|
||||
/* The permission is defined by the system and hence granted to it */
|
||||
}.filter { pkg -> pkg.applicationInfo.uid != SYSTEM_UID }
|
||||
.map { it.packageName }
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
#### Whitelist
|
||||
|
||||
As mentioned above it is not suggested, but still common practice to install 3rd party apps as
|
||||
privilidged. To verify and restrict which privilidged permissions those apps get granted all
|
||||
privilidged permissions need to be explicitly whitelisted in a file `/etc`.
|
||||
|
||||
```xml
|
||||
<permissions>
|
||||
<privapp-permissions package="my.privileged.package">
|
||||
<!-- allow the app to request a permission -->
|
||||
<permission name="android.permission.MY_PRIVILEGED_PERMISSION"/>
|
||||
|
||||
<!-- Even though the app requests the permission, do not grant it -->
|
||||
<deny-permission name="android.permission.MY_OTHER_PRIVILEGED_PERMISSION"/>
|
||||
</privapp-permissions>
|
||||
</permissions>
|
||||
```
|
||||
|
||||
If the pre-installed apk of app requests a privileged permission that is not mentioned in any
|
||||
whitelist or that is not denied the system will refuse to boot. As mentioned above privileged
|
||||
permissions added in updates to the pre-installed app will never be granted.
|
||||
|
||||
### Limited permissions
|
||||
|
||||
E.g. installer, wellbeing, documenter, etc... This allows the system to restrict the permission to a
|
||||
well defined app or set of apps. It is possible to add new types in `PackageManagerService`.
|
||||
|
||||
Which apps qualify for such a permission level is flexible and custom for each such level. Usually
|
||||
they refer to a single or small set of apps, usually - but not always - apps defined in AOSP.
|
||||
|
||||
These permissions are defined and checked like an install time permission.
|
||||
|
||||
### Development permissions
|
||||
|
||||
> Not recommended
|
||||
|
||||
By adding the `development` protection level to any permissions the permission can be granted via
|
||||
the `pm grant` shell command. This appears to be useful for development and testing, but it is very
|
||||
highly discouraged. Any user can grant them permanently via adb, hence adding this tag removes
|
||||
all guarantees the permission might otherwise provide.
|
||||
|
||||
### Other protection levels
|
||||
|
||||
There are other levels (such as `runtime`) but they are for special purposes on should not be
|
||||
used by platform developers.
|
||||
Reference in New Issue
Block a user