2.1 Nature of Permissions

As we said in the previous chapter, Android applications run in a sandbox and by default only have access to their own files and a very limited set of system services. To interact with the system and other applications, Android applications can request a set of additional permissions granted at installation and cannot change them (with some exceptions, which are discussed below).

In Android, a permission is just a string to perform a particular action. The target action can be anything from accessing physical resources (such as the device’s SD card) or sharing data (such as registering a contact list) to launching or accessing components in third-party applications. Android comes with a built-in set of predefined permissions. Each version adds new permissions corresponding to new functionality.

Note: Depending on the targetSdkVersion specified in the application manifest, new built-in permissions can be applied conditionally, locking functions that previously did not require permissions: Applications that locate the version of Android released before the introduction of the new version of permissions cannot anticipate it, so permissions are often implicitly granted (not requested). However, implicitly granted permissions are still displayed in the permissions list on the application setup screen so that the user can know about them. Applications for later versions need to explicitly request new permissions.

The built-in permissions are documented in the platform API reference. Additional permissions, called custom permissions, can be defined by the system and user-installed applications.

To see the list of currently known permissions on your system, use the PM list permissions command. To display additional information about permissions (including defining packages, labels, descriptions, and protection levels), add the -f parameter to the command.

Permission names are usually prefixed by the package that defines their connection to the string. Permission. Since built-in permissions are defined in the Android package, their names begin with Android.Permission. For example, in the diagram above, REBOOT and BIND_VPN_SERVICE is built-in permissions, and GALLERY_PROVIDER by Gallery application (package com. Google. Android. Gallery3d) definition, RECEIVE_LAUNCH_BROADCASTS is defined by the default initiator application (Package com.android.launcher3).

2.2 Request Permission

An application requests permissions by adding one or more < uses-Permission > tags to its Androidmanifest.xml file, and can define new permissions using the tag. The following figure shows a sample manifest file requesting INTERNET and WRITE_EXTERNAL_STORAGE permissions.

2.3 Rights Management

The System Package Manager service assigns permissions to each application (identified by a unique package name) at installation time. The package manager maintains a central database of installed packages, including pre-installed and user-installed packages, as well as information about each package’s installation path, version, signing certificates and assigned permissions, and a list of all permissions defined on the device. (The PM list permissions command described in the previous section obtains this list by querying the package manager.) This package database stored in XML files/data/system/packages. The XML, the file each time you install the application is installed, update, or uninstall. The following figure shows a typical application entry in Packs.xml.

Each package is represented by a element that contains information about the assigned UID (① in the userId attribute), the signing certificate (② in

), and the assigned permissions (③ in the subtags of ). To programmatically access to information on the installed package, please use the android. Content. PM. The PackageManager class getPackageInfo () method, this method returns to encapsulate < package > tag of the information contained in the PackageInfo instance. If all permissions are assigned at installation time and cannot be changed or revoked without uninstalling the application, how will the package manager decide whether to grant requested permissions? To understand this, we need to discuss the level of permission protection.

2.4 Permission Protection Level

According to the official document, the protection level of a permission “represents the potential risks implied in a license and indicates the procedures the system should follow in determining whether or not to grant a license.” In practice, this means that permissions are granted depending on their level of protection. The following sections discuss the four levels of protection defined in Android and how the system handles each level.

Against 2.4.1 normal

This is the default value. It defines low-risk permissions to the system or other applications. Automatically grant normal protection level permissions without user confirmation. Examples are ACCESS_NETWORK_STATE, which allows applications to access information about the network, and GET_ACCOUNTS, which allows access to a list of accounts in the account service.

The dangerous 2.4.2

Permissions with hazardous protection levels allow access to user data or some form of control over the device. Examples are READ_SMS (which allows applications to read SMS messages) and CAMERA (which allows applications to access CAMERA devices). Before granting dangerous permissions, Android displays a confirmation dialog box with information about the requested permissions. Since Android requires that permissions be granted on all requests at installation time, the user can agree to install the application, thereby granting requested hazardous permissions or canceling the application installation.

2.4.3 signature

Signing permissions are granted only to applications that are signed with the same key as the application that claimed the permissions. This is the “strongest” level of permission because it requires an encryption key that is controlled only by the application (or platform) owner. Therefore, applications that use signing permissions are usually controlled by the same author. Built-in signature permissions are typically used by system applications that perform device management tasks. For example, NET_ADMIN (configuring network interfaces and IPSec) and ACCESS_ALL_EXTERNAL_STORAGE (accessing all multi-user external storage).

2.4.4 signatureOrSystem

Permissions with this level of protection are somewhat compromised: they are granted to applications that can either be part of the system image or can be signed with the same key as the application claiming the permissions. This allows vendors that preinstall their applications on Android devices to share specific features that require licenses without having to share signing keys. Prior to Android 4.3, signatureOrSystem permissions are automatically granted to any application installed on a system partition. Since Android 4.4, applications need to be installed in the /system/priv-app/ directory in order to grant permissions through this protection level.

2.5 Rights Assignment

Permissions are enforced at various levels in Android. Higher-level components, such as applications and system services, query the package manager to determine which permissions have been assigned to the application and decide whether to grant access. Low-level components such as native daemons typically do not have access to the package manager and rely on the Uids, Gids, and supplementary Gids assigned to the process to determine the permissions granted to it. Access to system resources such as device files, Unix domain sockets (local sockets), and network sockets is specified by the kernel based on the owner and access mode of the target resource and the UID and GID of the access process. We first discuss how permissions are mapped to operating system-level constructs, such as Uids and Gids, and how these process ids are used to enforce permissions.

2.5.1 Permission and Process Properties

As with any Linux system, Android processes have a number of associated process attributes, the most important being true and valid Uids and Gids and a set of supplementary Gids.

As discussed in Chapter 1, each Android application is assigned a unique UID when it is installed and executed in a dedicated process. When the application starts, the process’s UID and GID are set to the application UID assigned by the installer (package manager service). If additional permissions have been assigned to the application, they are mapped to the GID and assigned to the procedure as a supplementary GID. In the/etc/permission/platform. The XML file defines the built-in GID mapping access permissions. The following figure shows an excerpt of the platform. XML file on an Android 4.4 device.

Here, INTERNET permissions are associated with inet GID①, while WRITE_EXTERNAL_STORAGE permissions are associated with sdcard_r and sdcard_rw GID②. Therefore, any process of an application that has been granted INTERNET access is associated with a supplementary GID corresponding to inet and a process with WRITE_EXTERNAL_STORAGE permission adds the Gids of sdcard_r and sdcard_rw to the list of associated supplementary Gids.

The <assign-permission> tag serves the opposite purpose: it is used to assign higher-level permissions to system processes running under a particular UID without the corresponding package. The figure above shows that a process running with the media UID (which is actually mediaserver’s daemon) has been assigned MODIFY_AUDIO_SETTINGS③ and ACCESS_SURFACE_FLINGER④ permissions.

Android doesn’t have a /etc/group file, so the mapping from group names to Gids is static and defined in the android_filesystem_config.h header.

The android_filesystem_config.h file also defines the core Android system directory and file owner, access mode, and related functions (for executable files). The package manager reads platform.xml at startup and maintains a list of permissions and associated Gids. When granting permissions to packages during installation, the package manager checks that each permission has an associated GID. If so, the GID is added to the list of supplementary Gids associated with the application. The supplementary GID list is written as the last field in the packes.list file.

2.5.2 Assigning Process Attributes

Before we can see how the kernel and underlying system services check and enforce permissions, we need to examine how Android application processes start and assign process properties. As described in Chapter 1, Android applications are implemented in Java and executed by the Dalvik VM. Therefore, each application process is actually a Dalvik VM process that executes the application bytecode. To reduce application memory footprint and shorten startup time, Android does not start a new Dalvik VM process for each application. Instead, it uses a partial initialization process called Zygote and uses it whenever a new application needs to be started (using the fork() system call). However, instead of calling an exec() function as when a local process is started, it simply executes the main() function of the specified Java class. This process is called specialization because a generic Zygote process is translated into a specific application process, just as cells from Zygote cells specialize to perform different functions. Thus, the forked process inherits the memory image of the Zygote process, which has most of the core and application framework Java classes preinstalled. Because these classes never change, Linux uses a copy-on-write mechanism when forking processes, and all zygote’s child processes (that is, all Android applications) share the same copy of the framework Java classes.

The Zygote process is started by the init.rc initialization script and receives commands on Unix domain sockets (also known as Zygote). When Zygote receives a request to start a new application process, it forks itself, and the child process roughly executes the following code (abbreviated forkAndSpecializeCommon() in dalvik_system_zygote.cpp) to specialize itself, as shown in the figure below.

As shown above, the child process first sets its supplemental GID (corresponding to permissions) using setGroups (), called by ① at setgroupsIntarray(). Next, it uses setrLimit () to set the resource limits, called at ② by setrlimitsFromArray(), and then sets the real, valid, and saved user and group ids using setresgid()③ and setresuid()④.

The child process is able to change its resource limits and all process attributes because it initially executes as root, just like its parent process zygote. After setting the new process properties, the child process will execute with the assigned UID and GID and cannot return to execute as root because the saved user id is not 0.

After setting the UID and GID, the procedure sets its capabilities using capset(), called from setCapabilities()⑤. It then sets its scheduling policy by adding itself to one of the predefined control groups. In ⑦ the process sets the name (shown in the process list, usually the package name of the application) and the seinfo tag (used by SELinux, which we’ll discuss in Chapter 12). Finally, it can be debugged as needed.

Note: Android 4.4 introduces a new experimental RunTime called Android RunTime (ART), which is expected to replace Dalvik in future releases. While ART brings many changes to the current execution environment, the most important is AOT, which uses the same Zygote-based application execution model as Dalvik.

In the list of processes obtained using the ps command, the process relationship between Zygote and the application process is obvious, as shown in the figure below.

Here, the PID column indicates the process ID, the PPID column indicates the parent process ID, and the NAME column indicates the process NAME. As you can see, Zygote (PID 181②) is started by the init process (PID 1 ①), and all application processes have Zygote as their parent (PPID 181). Each process executes under a dedicated user, either built-in (radio, NFC) or automatically assigned at installation (U0_a7). The process name is set to the package name for each application (com.android.phone, com.android.nfc and com.google.android.gms).

2.6 Permission Execution

As discussed in the previous section, each application process is assigned a UID, GID, and supplementary GID when detached from Zygote. The kernel and system daemons use these process identifiers to decide whether to grant access to specific system resources or functions.

2.6.1 Kernel level execution

Access regular files, device nodes, and local sockets in the same way as on any Linux system. An Android-specific addition requires a process that wants to create a network socket belonging to inet. This Android kernel addition is called Paranoid Network Security and is implemented as an additional check in the Android kernel, as shown below.

Callers that do not belong to the AID_INET (GID 3003, Name inet) group and do not have CAP_NET_RAW (which allows RAW and PACKET sockets) receive access denial errors (① and ③). The NonAndroid kernel does not define CONFIG_ANDROID_PARANOID_NETWORK, so no special group membership is required to create sockets. In order to assign an INET component to an application process, it needs to be granted INTERNET rights. Therefore, only applications with INTERNET permissions can create network sockets. In addition to checking process credentials when creating a socket, the Android kernel grants certain capabilities to processes executing with a specific GID: A process that uses AID_NET_RAW (GID 3004) is assigned CAP_NET_RAW and uses AID_NET_ADMIN (GID 3005) CAP_NET_ADMIN.

Paranoid Network Security is also used to control access to Bluetooth sockets and the kernel tunneling driver (for VPN). A complete list of Android Gids handled by the kernel in a special way can be found in the include/ Linux /android_aid.h file in the kernel source code.

2.6.2 Native daemon level execution

While Binder is the preferred IPC mechanism in Android, lower-level local daemons typically use Unix Domain sockets (local sockets) for IPC. Because Unix domain sockets are represented as nodes on a file system, access can be controlled using standard file system permissions.

Since most sockets are created using an access mode that only allows access to their owners and groups, clients running under different Uids and Gids cannot connect to the socket. The local socket of the system daemon is defined in init.rc and created by init at startup using the specified access mode. For example, the following figure shows how to define a volume management daemon (vold) in init.rc:

Vold uses the 0660 access mode to declare a socket named vold, which is owned by root and whose group is set to mount ①. The vold daemon needs to run as superuser to mount or unmount volumes, but members of mount groups (AID_MOUNT, GID 1009) can send commands over local sockets without running as superuser. The Android daemon’s local socket is created in the /dev/socket-/ directory. The following figure shows that vold socket ① has the owner and permissions specified in init.rc.

Unix domain sockets allow the SCM_CREDENTIALS control message and the SO_PEERCRED socket option to pass and query client credentials. Like valid UUIds and guids that are part of Binder transactions, peer certificates associated with local sockets are checked by the kernel and cannot be forged by user-level processes. This allows local daemons to exercise additional fine-grained control over what they allow a particular client to do, as shown below, in the case of the Vold daemon.

The vold daemon only allows you to execute encrypted container management commands against clients running as root (UID 0) or system (AID_SYSTEM, UID 1000) users. Here, the UID returned by SocketClient-> getUid()① is initialized using the client UID obtained by getsockopt (SO_PEERCRED), as shown below.

The local socket connection functionality, encapsulated in the android.net.LocalSocket class, is also available for Java applications, allowing higher-level system services to communicate with the local daemon without using JNI code. For example, the MountService framework class uses LocalSocket to send commands to the Vold daemon.

2.6.3 Framework-level implementation

As discussed in the Introduction to Android Permissions, permissions can be used to control access to Android components by declaring the required permissions in the manifest of closed applications. The system keeps track of the permissions associated with each component and checks whether the caller has been granted the required permissions before granting access. Because components cannot change the permissions they require at run time, the enforcement of the system is static. Static permissions are an example of declarative security. When declarative security is used, security attributes such as roles and permissions are placed in the component’s metadata (the Androidmanifest.xml file in Android), not in the component itself, and are enforced by the container or runtime environment. This has the advantage of isolating security decisions from business logic, but may not be as flexible as enforcing security checks in components.

Android components can also check whether the calling process has been granted specific permissions without having to declare permissions in the manifest. This dynamic permission execution requires more work, but allows for more fine-grained access control. Dynamic license enforcement is an example of mandatory security, because security decisions are made by each component rather than enforced by the runtime environment.

Let’s look at how to implement dynamic and static permission execution in more detail.

2.6.4 Dynamic Execution

As discussed in Chapter 1, the core of Android is implemented as a set of collaborative system services that can be invoked from other processes using the Binder IPC mechanism. Core services are registered with the service manager, and any application that knows their registered name can obtain a Binder reference. Because Binder has no built-in access control mechanism, when clients have references, they can invoke any method of the underlying system service by passing the appropriate parameters to binder.transact (). Therefore, access control needs to be implemented by each system service.

In Chapter 1, we showed that system services can mediate access to export operations by directly checking the caller’s UID obtained from binder.getCallinguid (). However, this approach requires the service to know ahead of time the list of permitted Uids, which only applies to well-known fixed Uids, such as those of root (UID 0) and system (UID 1000). In addition, most services don’t care about the actual UID of the caller; they just want to check that it has been granted some kind of permission.

Because every application UID in Android is associated with a unique package (unless it is part of a shared user ID) and the package manager records the permissions granted to each package, this can be done by querying the package manager service. Checking whether the caller has certain permissions is a very common operation, and Android provides some helper methods in the Android.Content.context class that can perform this check.

Let’s first look at how the int Context.checkPermission(String Permission, int pid, int uid) method works. This method returns PERMISSION_GRANTED if the UID passed has permission, PERMISSION_DENIED otherwise. If the caller is root or System, permissions are automatically granted. As a performance optimization, the actual permission is not checked if the requested permission has been declared by the calling application. If this is not the case, the method checks whether the target component is public (exported) or private and denies access to all private components. Finally, the code queries the package manager service to see if the caller has been granted permission to request it. The code for the PackageManagerService class is shown below.

Here, the PackageManagerService first determines the application ID of the application based on the UID① passed, and then obtains the granted permission set. This method returns PERMISSION_GRANTED② if the GrantedPermission class (which contains the actual java.util.Set<String> permission name) contains the target permission. If not, check whether the target permission should be automatically assigned to the incoming UID ③ (based on the <assign-permission> tag in platform.xml). If this check also fails, it will eventually return PERMISSION_DENIED.

The other permission checking helper methods in the Context class follow the same process. Int checkCallingOrSelfPermission (String permission) method for our call Binder. The getCallingUid () and Binder. The getCallingPid (), Then call checkPermission(String Permission, int PID, int uid) with the obtained value. If the permission is not granted, the enforcePermission(String Permission, int PID, int UID, String Message) method does not return the result but throws a SecurityException with the specified message. For example, the BatterStatsService class guarantees that only applications with BATTERY_STATS permission can get battery statistics by calling enforceCallingPermission() before executing any other code, as shown in the figure below. Callers who are not granted permission receive a SecurityException.

2.6.5 Static Execution

Static permission execution comes into play when an application tries to interact with a component declared by another application. The enforcement process takes into account the permissions, if any, declared for each target component and allows interaction if the caller process has been granted the required permissions.

Android uses intents to describe what it needs to do, and intents that fully specify the target component (by package and class name) are called explicit. Implicit intEnts, on the other hand, contain data that allows the system to find a matching component (usually just an abstract action, such as ACTION_SEND), but they do not fully specify the target component.

When the system receives an implicit intent, it first resolves it by searching for the matching component. If more than one matching component is found, the user is presented with a selection dialog. When a target component is selected, Android checks whether it has any associated permissions and, if so, whether they have been granted to the caller.

The general process is similar to dynamic execution: the caller’s UID and PID are obtained using binder.getCallinguid () and binder.getCallingPID (), the caller’s UID is mapped to the package name, and the associated permissions are retrieved. If the caller permission set contains the permissions required by the target component, the component starts; Otherwise, throw a SecurityException.

Permission checking is performed by ActivityManagerService, which resolves the specified intent and checks whether the target component has the associated permission properties. If so, it delegates permission checking to the package manager. The timing and order of permission checks varies slightly depending on the target component. (Next, we’ll look at how to perform checks for each component.)

2.6.6 Activity and Service Permission Execution

If the intent passed to context.startActivity () or startActivityForResult() resolves to be an activity that claims permissions, the activity’s permission check is performed. If the caller does not have the required permissions, a SecurityException is thrown. Since Android services can be started, stopped, and bound, calls to context.startService (), stopService(), and bindService() are checked if the target service claims permissions.

2.6.7 Content provider Permission Execution

Content provider permissions can protect an entire component or a specific export URI, and can specify different permissions for reads and writes. If different read and write permissions are specified, read permissions control who can call contentresolver.query () on the target provider or URI, and write permissions control who can call contentresolver.insert () on the provider or one of its exported URIs, ContentResolver. The update () and ContentResolver. The delete (). When one of these methods is called, these checks are performed synchronously.

2.6.8 Broadcast Permission Execution

When sending a broadcast, an application can ask the receiver to retain a specific permission by using the context. sendBroadcast(Intent Intent, String receiverPermission) method. Because the broadcast is asynchronous, no permission check is performed when this method is called. Checks are performed when intentions are transmitted to registered recipients. If the target recipient does not have the required permissions, it will be skipped and will not receive the broadcast, but no exception will be thrown. Broadcast receivers, in turn, may require the broadcaster to hold specific licenses in order to be able to locate them.

The required permissions are specified in the manifest or when registering broadcasts dynamically. This permission check is also performed when a broadcast is transmitted and does not cause a SecurityException. Therefore, transmitting a broadcast may require two permission checks: one for the broadcast sender (if the receiver has specified permission) and one for the broadcast receiver (if the sender has specified permission).

2.6.9 Protected and sticky broadcasts

Some system broadcasts are declared to be protected (for example, BOOT_COMPLETED and PACKAGE_INSTALLED) and can only be sent by a system process running as one of SYSTEM_UID, PHONE_UID, SHELL_UID, BLUETOOTH_UID, or root. If a process running under a different UID tries to send a protected broadcast, it will receive a SecurityException when one of the sendBroadcast() methods is called. Sending “sticky” broadcasts (if the Intent is labeled sticky, the system retains the Intent object sent after the broadcast is complete) requires the sender to have BROADCAST_STICKY privileges. Otherwise, a SecurityException is thrown and no broadcast is sent.

2.7 System Rights

Android’s built-in permissions are defined in the Android package, sometimes referred to as a “framework” or “platform.” As we learned in Chapter 1, the core Android framework is a set of classes shared by system services, some of which are exposed through a public SDK. Framework classes are packaged in JAR files under the /system/framework/ directory (there are about 40 of them in the latest version).

In addition to the JAR libraries, the framework also contains an APK file framework-res.apk. As the name suggests, it packages framework resources (animations, drawables, layouts, etc.) but has no actual code. Most importantly, it defines the Android package and system permissions. Since Framework-res.apk is an APK file, it contains an Androidmanifest.xml file that declares permission groups and permissions (see figure below).

As shown in the figure, the androidmanifest.xml file also declares system-protected broadcasts ①. Permission group ② Specify a name for a group of related permissions. A single permission can be added to a group by specifying the group name in their permissionGroup attribute ③.

Permission groups are used to display related permissions in the system UI, but each permission still needs to be requested separately. That is, applications cannot request that they be granted all permissions in a group. Recall that each permission has an associated protectionLevel declared using the protectionLevel attribute, as shown in ④.

The protection level can be used in conjunction with the protection flag to further restrict how permissions can be granted. The flags currently defined are System (0x10) and Development (0x20). The system flag requires that the application be part of the system image (that is, installed on a read-only system partition) to be licensed. For example, MANAGE_USB permission, which allows applications to manage preferences and permissions for USB devices, is only granted to applications signed with platform signing keys and installed on system partition ⑤. Development flags mark development permissions (⑥), which we will discuss after signing permissions are provided.

2.7.1 Signing Permission

As discussed in Chapter 1, all Android applications need to sign code using a signing key controlled by the developer. This also applies to system applications and framework resource packs. We’ll discuss package signing in more detail in Chapter 3, but for now let’s talk about how system applications sign.

System applications are signed by platform keys. By default, the current Android source code has four different keys: platform, shared, media, and test key (releasekey for release). All packages considered part of the core platform (system UI, Settings, phone, Bluetooth, etc.) are signed using the platform key; Enveloped shared keys related to search and contacts; Gallery applications and media-related providers use media keys; Testkey (or Releasekey) is used for everything else (including software packages that do not explicitly specify the signing key in the Makefile). Framework for defining system permissions -res.apk APK uses platform keys to sign. Therefore, any application that attempts to request system privileges with a signature protection level needs to be signed with the same key as the framework resource bundle.

For example, the NET_ADMIN permission (which allows authorized applications to control network interfaces) signature protection level ④, shown in the figure above, is declared and can only be granted to applications that use the platform key signature.

Note: The Android Open Source Repository (AOSP) contains pre-generated test keys that are used by default when signing compiled packages. They should not be used in production versions because they are public and available to anyone who downloads the Android source code. The release should be signed with a newly generated private key that generates only the owner. The key can be generated using the make_key script contained in the development/tools/AOSP directory. Detailed information about the platform key generation, please refer to the build/target/product/security/README file.

2.7.2 Development Permission

Traditionally, the Android permission model does not allow permissions to be granted and revoked dynamically, and the set of permissions granted by an application is fixed at installation time. However, since Android 4.2, this rule has been relaxed a bit by adding many development permissions, such as READ_LOGS and WRITE_SECURE_SETTINGS. You can use the PM grant and PM REVOKE commands on the Android shell to grant or revoke development permissions on demand.

Note: Of course, this action is not for everyone and is protected by the GRANT_REVOKE_PERMISSIONS signature permission. It is granted the Android.uuid. shell shared user ID (UID 2000) and all processes started from the Android shell (also running as UID 2000).

2.8 Share User ID

Android applications signed with the same key can request to run with the same UID and can choose to run in the same process. This feature is called shared user ID and is widely used in core framework services and system applications. Because it can have a subtle impact on process counting and application management, the Android team doesn’t recommend it for third-party applications, but it also works with user-installed applications. In addition, switching from an existing application that does not use a shared user id to a shared user id is not supported, so you should design and publish collaborative applications that require a shared user ID from the outset.

Enable shared user ids by adding the sharedUserId attribute to the root element of androidmanifest.xml. The user ID specified in the listing needs to be in Java package format (with at least one dot [.]) and used as an identifier, much like the application’s package name. If the specified share UID does not exist, it is created. If another package with the same shared UID has already been installed, the signing certificate is compared to the existing package, and if they do not match, the INSTALL_FAILED_SHARED_USER_INCOMPATIBLE error is returned, and the installation fails.

Adding the sharedUserId attribute to a new version of an installed application will cause it to change its UID, which will cause it to be unable to access its own files (as was the case in some earlier Versions of Android). Therefore, the system does not allow this and will reject updates with the INSTALL_FAILED_UID_CHANGED error. In short, if you are going to use a shared UID for your application, you must design it from the start, and you must use it after the first release.

The shared UID itself is the first class object in the system package database and is very similar to the application: it has associated signing certificates and permissions. Android has five built-in share uuIds that are automatically added when the system is booted:

  • android.uid.system (SYSTEM_UID, 1000)
  • android.uid.phone (PHONE_UID, 1001)
  • android.uid.bluetooth (BLUETOOH_UID, 1002)
  • android.uid.log (LOG_UID, 1007)
  • android.uid.nfc (NFC_UID, 1027)

The following figure shows how to define an Android.uid. system shared user

As you can see, this definition is very similar to the package declaration shown earlier, except that it has an awful lot of permissions (about 66 on 4.4 devices). In contrast, packages that are part of a shared user do not have an associated list of granted permissions. Instead, they inherit permissions for the shared user, which is the union of permissions requested by all currently installed packages using the same shared user identity. A side effect of this is that if a package is part of a shared user, then as long as a package with the same shared user identity has already requested these packages, it can access apis for which it has not explicitly requested their permissions. Although the package is installed or uninstalled, permissions are dynamically removed from the <shared-user> definition, so the set of available permissions is neither guaranteed nor invariant.

The following figure shows the declaration of how the KeyChain system application runs under the shared user ID. As you can see, it references the shared user using the sharedUserId attribute and lacks explicit permission declarations:

A shared UID is more than just a package management structure. It also maps to a shared Linux UID at run time. The following figure shows an example of two system applications running as system user (UID 1000) :

Applications that are part of a shared user can run in the same process, and because they already have the same Linux UID and access to the same system resources, this usually doesn’t require any additional modifications. Common processes can be requested by specifying the same process name in the Process attribute of the < Application > tag in the list of all applications that need to run in a process. While the obvious result of this is that applications can share memory and communicate directly with it, some system services allow special access to components running in the same process (such as direct access to cached passwords or access to authentication tokens without displaying UI prompts). Google applications, such as the Play service and Google Location Service, run through requests in the same process as the Google Login service so that they can synchronize background data without user interaction. Of course, they are signed using the same certificate and are part of the com.google.uid.shared shared user.

2.9 User-defined Permissions

Custom permissions are only those declared by third-party applications. When declared, they can be added to application components for the system to enforce statically, or the application can dynamically check whether the caller has been granted permissions using the checkPermission() or enforcePermission() methods of the Context class. As with built-in permissions, applications can define permission groups to which custom permissions are added. For example, the following figure shows the declaration for permission group ② and the permissions belonging to that group ③.

As with system permissions, if the protection level is normal or dangerous, customization permissions will be granted automatically when the user confirms the confirmation dialog box. To be able to control which applications are granted custom permissions, you need to declare it with a signature protection level to ensure that it will only be granted to applications signed with the same key.

Note: The system can only grant permissions that it knows about, which means that applications that define custom permissions need to be installed before applications that use these permissions can be installed. If an application requests a permission unknown to the system, the permission is ignored and not granted.

Applications can also use the android. Content. PM. PackageManager. AddPermission () API dynamically add new permissions, and use the matching removePermision () API to delete them. This dynamically added permission must belong to the application-defined permission tree. An application can only add or remove permissions from a permission tree in its own package or another package that runs as the same shared user identity. The permission tree name is the reverse domain name representation. If a permission name is preceded by a dot (.) , the permission is considered to be in the permission tree. For example, com. Example. App. Permission. PERMISSION2 permissions are defined above (1) of the com. Example. App. A member of the permission tree.

The following figure shows how to add dynamic permissions programmatically.

Dynamically add permissions are added to the package database (/ data/system/packages. The XML). They persist at reboot, just like the permissions defined in the listing, but they have other type properties that are set to dynamic.

2.10 Public and Private Components

Components defined in the Androidmanifest.xml file can be public or private. Private components can only be invoked by declarative applications, while public applications can also be used by other applications.

All components except the content provider are private by default. Since the purpose of content providers is to share data with other applications, content providers are initially exposed by default, but this behavior changes in Android 4.2 (API Level 17). Applications with target API Level 17 or higher now get private content providers by default, but they remain public for backward compatibility at lower API levels.

Components can be exposed either explicitly by setting the exported property to True, or implicitly by declaring an intent filter. Components with intent filtering that do not need to be exposed can be made private by setting the export property to false. If the component is not exported, calls from external applications are blocked by the activity manager, regardless of permission granted to the calling process (unless running as root or system). The following figure shows how to keep the component private by setting the export property to false.

Unless explicitly used for public consumption, all public components should be protected by custom permissions.

2.11 Rights of Activities and Services

Activities and services can be secured by a single permission set using the target component’s permission property. When other applications call Context. StartActivity () or Context. StartActivityForResult (), will check the permissions, parsing and intent for the activity. For services, permissions are checked when another application calls one of Context.startService(), stopService(), or bindService() with an intent resolved as a service. For example, the following figure shows two custom permissions, START_MY_ACTIVITY and USE_MY_SERVICE, set to activity ① and service ② respectively. Applications that want to use these components need to request the appropriate permission using the < uses-Permission > tag in the listing.

2.12 Broadcast Permission

Unlike activities and services, the permissions of broadcast receivers can be specified by the receivers themselves and by the application that sends the broadcast. When sending a broadcast, an application can use the context.sendBroadcast (Intent Intent) method to send a broadcast to all registered receivers, Or use context. sendBroadcast(Intent Intent, String receiverPermission). The receiverPermission parameter specifies the permissions that interested recipients need to retain in order to receive broadcasts. Or, starting with Android 4.0, senders can use Intent.setPackage(String packageName) to limit the receiver’s scope to the range defined in the specified package. On multi-user devices, System applications with INTERACT_ACROSS_USERS permission can use sendBroadcastAsUser(Intent Intent, The UserHandle User and sendBroadcastAsUser(Intent Intent, UserHandle User, String receiverPermission) methods send broadcasts intended only for specific users.

Receivers can specify a permission for statically registered receivers by using the Permission attribute of the < Receiver > tag in the listing, BroadcastReceiver (BroadcastReceiver, IntentFilter filter, String broadcastPermission, The Handler scheduler) method dynamically registers receivers.

Only broadcasters with the required licenses can transmit broadcasts to this receiver. For example, device management applications that enforce system-wide security policies (we’ll discuss device management in Chapter 9) require BIND_DEVICE_ADMIN permission to receive DEVICE_ADMIN_ENABLED broadcasts. Because this is a system permission with a protection level signature, permissions are required to ensure that only the system can activate the device management application. For example, the following figure shows how the default Android email application assigns BIND_DEVICE_ADMIN① permissions to its PolicyAdmin recipients.

Like other components, a dedicated broadcast receiver can only receive broadcasts from the same application.

2.13 Content provider Rights

As described earlier in “Permission nature,” content providers have a more complex permission model than other components, as we’ll cover in detail in this section.

2.13.1 Static Provider Permissions

Although you can use the Permission attribute to specify a single permission that controls access to the entire provider, most providers use different read and write permissions, and you can also specify permissions for each URI. An example of a provider with different permissions for reading and writing is the built-in ContactsProvider. The following figure shows the declaration for its ContactsProvider2 class.

The provider uses the readPermission attribute to specify a permission to read data (READ_CONTACTS ①) and a separate permission to write data using the writePermission attribute (WRITE_CONTACTS ②). Therefore, an application with only READ_CONTACTS permission can only call the provider’s query() method, and calls to INSERT (), UPDATE (), or DELETE () require the caller to have WRITE_CONTACTS permission. Applications that need to read and write to contact providers need to have both permissions.

When global read and write permissions are not flexible, the provider can specify permissions for each URI to protect a subset of its data. Each URI permission takes precedence over component-level permission (read and write permission if specified separately). Therefore, if an application wants to access the content provider URI with the relevant permissions, it only needs to retain the permissions on the target URI, not the component-level permissions. In the figure above, ContactsProvider2 uses the < path-Permission > tag to require GLOBAL_SEARCH permission ③ for an application trying to read a contact’s photo. Because each URI permission overrides the global read permission, interested applications do not need to keep READ_CONTACTS permission. In practice, the GLOBAL_SEARCH permission is used to grant read-only access to some system provider data for the Android search system, which cannot expect to have read permission for all providers.

2.13.2 Dynamic Provider Permissions

While statically defined permissions for each URI can be powerful, applications sometimes need to grant temporary access to specific pieces of data (referenced by their URIs) from other applications without requiring them to have specific permissions. For example, an E-mail or messaging application might need to work with an image viewer application to display attachments. Because the application cannot know the URI of the attachment in advance, if it uses static per-URI permissions, it would need to grant read permissions to all attachments of the image viewer application, which is not desirable.

To avoid this situation and potential safety problems, an application can use the Context. The grantUriPermission (String toPackage, Uri Uri, int modeFlags) methods to dynamically granted temporary per – Uri access, And revoke access with the matching revokeUriPermission(Uri Uri, int modeFlags) method. Enable temporary per-URI access for specific URIs by setting the global grantUriPermissions attribute to true or by adding the

tag. For example, the following figure shows how an Email application uses the grantUriPermissions attribute ① to allow temporary access to attachments without the need for READ_ATTACHMENT permissions.

In practice, applications rarely use the context.grantPermission () and revokePermission() methods directly to allow access to each URI. Instead, they set the FLAG_GRANT_READ_URI_PERMISSION or FLAG_GRANT_WRITE_URI_PERMISSION flag as an intent (in this case, an image viewer) to launch a collaborative application. When these flags are set, the intent recipient is granted read or write permissions to the URI in the intent data.

As of Android 4.4 (API Level 19), if an intent is received with the FLAG_GRANT_PERSISTABLE_URI_PERMISSION flag, Then each URI access authorization through ContentResolver. TakePersistableUriPermission restart persisted across equipment () method. Authorized persisted in/data/system/urigrants. The XML file, and can be invoked by releasePersistableUriPermission () method to withdraw. Temporary and persistent authorization for each URI access is managed by the system ActivityManagerService, and these apis are associated with each URI access call internally.

Starting with Android 4.1 (API level 16), applications can use the Intent of the ClipData tool to add multiple content URIs to temporarily grant access.

Use one of the FLAG_GRANT_* intent flags to grant access to each URI and revoke it automatically when the task of the called application is complete, so revokePermission() is not required. The following figure shows how an E-mail application creates an Intent that launches the attachment viewer application.

2.14 Pending Intent (Pending Intent)

The intents in question are neither Android components nor permissions, but because they allow applications to grant their permissions to other applications, we discuss them here.

An intent to be processed encapsulates an intent and a target action to execute it (start an activity, send a broadcast, and so on). The main difference from “regular” Intents is that the intents to be processed also include the identity of the application that created them. This allows pending intents to be handed to other applications, which can use them to perform specified actions with the original application’s identity and permissions. The identity stored in the pending intents is guaranteed by the system ActivityManagerService, which keeps track of the active pending Intents.

Pending IntEnts are used to implement alerts and notifications in Android. Alerts and notifications allow any application to specify actions that need to be performed on its behalf, either at a specified time for alerts or when a user interacts with system notifications. Alerts and notifications can be triggered when the application that created them is no longer running, and the system uses the information in the pending intent to launch it and perform the intended action on its behalf. The diagram below shows the email application. How to use the PendingIntent getBroadcast () (1) create the pending intent scheduling synchronized trigger email broadcast.

Pending Intents can also be delivered to non-system applications. The same rule applies: an application that receives an instance of the Intent to be handled can perform the specified action with the same permissions and identifiers as the creator application. Therefore, care should be taken when building the base intent, and the base intent should be as specific as possible (explicitly specifying the component name) to ensure that the target component can receive the intent.

The implementation of suspended intents is fairly complex, but it is based on the same IPC and sandbox principles used to build other Android components. When an application creates a pending intent, the system uses binder.getCallinguid () and binder.getCallingPid () to retrieve its UID and PID. Based on this, the system retrieves the creator’s package name and user ID (on multi-user devices) and stores them in the PendingIntentRecord along with the base Intent and any other metadata. The activity manager stores the list of pending Intents for the activity by storing the corresponding PendingIntentRecords and retrieving the necessary records when triggered. It then uses the information in the record to assume the identity of the pending intention creator and perform the specified action. From there, the process is the same as when you start any Android component and perform the same permission checks.

conclusion

Android runs each application in a restricted sandbox and requires applications to request specific permissions to interact with other applications or systems. A permission is a string that represents the ability to perform a particular operation. They are granted when the application is installed and (with the exception of development permissions) remain unchanged for the life of the application. Permissions can be mapped to Linux supplemental group ids that are checked by the kernel before granting access to system resources.

Higher-level system services enforce permissions by using binders to get the UID of the calling application and look up its permissions in the package manager database. Permissions associated with components declared in the application manifest file are automatically performed by the system, but applications can choose to perform other permissions checks on the fly. In addition to using built-in permissions, applications can define custom permissions and associate them with their components to control access.

Each Android component may require permissions, and the content provider can specify read and write permissions for each URI separately. Pending Intents encapsulate the identity of the application that created them as well as the intent and action to be performed, which allows system or third-party applications to perform actions on behalf of the original application with the same identity and permissions.