From 767ef39c1a6a3764c593abfe0b44685da1b67397 Mon Sep 17 00:00:00 2001 From: "Philip P. Moltmann" Date: Fri, 14 Feb 2020 08:24:46 -0800 Subject: [PATCH] Add docs for users, permissions, appops. Test: Only docs changes Change-Id: I2115b32c41a500d3a9847a5ea3c1d8b82dd5112c Fixes: 151379035 --- core/java/android/app/AppOps.md | 212 +++++ core/java/android/os/Users.md | 109 +++ core/java/android/permission/Permissions.md | 832 ++++++++++++++++++++ 3 files changed, 1153 insertions(+) create mode 100644 core/java/android/app/AppOps.md create mode 100644 core/java/android/os/Users.md create mode 100644 core/java/android/permission/Permissions.md diff --git a/core/java/android/app/AppOps.md b/core/java/android/app/AppOps.md new file mode 100644 index 0000000000000..bee701addca80 --- /dev/null +++ b/core/java/android/app/AppOps.md @@ -0,0 +1,212 @@ + + +# 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. diff --git a/core/java/android/os/Users.md b/core/java/android/os/Users.md new file mode 100644 index 0000000000000..3bbbe5452fd36 --- /dev/null +++ b/core/java/android/os/Users.md @@ -0,0 +1,109 @@ + + +# 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 diff --git a/core/java/android/permission/Permissions.md b/core/java/android/permission/Permissions.md new file mode 100644 index 0000000000000..e62bd0a88bf6b --- /dev/null +++ b/core/java/android/permission/Permissions.md @@ -0,0 +1,832 @@ + + +# 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 +`` + +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 +`` + +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 + + + + + + + +``` + +##### 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 + + + + + +``` + +### 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 + +``` + +#### Requesting a runtime permission + +Similar to install time permissions any app can request a runtime permission by adding the +`` +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 + + + + + + + +``` + +##### 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 + + + + + +``` + +##### 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, + 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 + + + + + +``` + +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 + `` 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 + + + +``` + +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 + + + + + + + + + +``` + +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.