Reading the book: Android Security Internals I'm reading at Chapter 2 - Permission Management paragraph that permissions are assigned to application at install time by the package manager with a mapping like Apk's UID <-> Permissions.

Later in the book (Permission Assignment - Permissions and Process Attributes paragraph) I read that

If additional permissions have been assigned to the application, they are mapped to GIDs and assigned as supplementary GIDs to the process.

And, since android.permission.INTERNET permission is associated with inet GID:

any process for an app that has been granted the INTERNET permission is associated with the supplementary GID correspond- ing to the inet group

But I was asking me if this is always necessary.

Imagining a case where you install APK X declaring android.permission.INTERNET permission in AndroidManifest.xml, since mapping of permissions with UIDs is done at install time, PackageManager should be able to map the permission directly with the APK's UID instead of adding inet GID to your APK package. So I'm wondering why it has to be that:


Anyone can clarify me this?

  • That is simply the way it works. Checking if a user is member of a specific group is much simpler than on every request having to check if a user is authorized to this and that. – Robert Mar 3 '19 at 12:47

Android's permission management can be divided majorly in two parts: Linux kernel and Android framework.


Discretionary Access Control (DAC) based on users, groups and access mode predates back to early days of UNIX. Every file (including directories) and process has a user owner (UID) and a group owner (GID). So a process is allowed to Read Write and eXecute a resource depending on its access mode bits (RWX) for User, Group and Others (UGO). These permissions attributes are saved along with files on filesystem.
UID/GID 0 is special for Linux kernel; it's the super user (administrator) that is never denied from doing anything. All other users in range 1-65534 are non-privileged IDs which an OS can use in whatever way it wants to control access.

Android reserves UIDs in range 1000-9999 for system use unlike Linux distros where 1000 is usually the first UID assigned to human users. Android is designed for single human user but every Java app, whether preinstalled (as system or user app) or installed by user, is treated as a user. UID range 10000-19999 is reserved for these apps. At first boot (for preinstalled apps) or when installing a new app, a unique UID/GID is assigned to the app which is static unless the app is uninstalled. This app vs. UID mapping can be seen in file /data/system/packages.list or using command id (1) if the app provides a shell or can execute native binaries.

All of these UID's have hard-coded names on Android which are predefined, unlike Linux distros, where there are almost standardized naming conventions for users and groups which can be added / removed in /etc/passwd and /etc/group. Android shares only root user name with Linux. However these names are only for the ease of human users, for kernel only numbers (UID/GID) mean.

Access Control Lists (ACLs) can be used to extend DAC for more granular privileges but those are rarely used on Linux and (AFAIK) not used on Android.


Root user's authorities are further divided into subgroups called capabilities. Instead of using UID 0 whenever an elevated task is to be performed, a non-privileged UID can be granted only the required capability. File capabilities which are attached to files using Extended Attributes (XATTR), can also be used to elevate process capabilities whenever required. Android makes use of File Capabilities as well as Ambient Capabilities. See this thread for more details.


Mandatory Access Control (MAC) was introduced later to supplement DAC for better security. Android uses SELinux as part of its security implementations. SELinux also makes use of Extended Attributes to label files with a context while every process is also run with a SELinux context. Then a policy is defined which includes thousands of rules allowing one context access the other, denied otherwise by default. See this answer for more details on SELinux and an example how Android makes use of DAC, capabilities and MAC.

By making use of Linux kernel's DAC, MAC and capabilities, Android launches every app in a sandboxed virtual machine forked from zygote process, with its unique ID and with all capabilities dropped, so that forced to access only it's own files in /data/data/<app_pkg_name> or in external storage. See this answer for more details.


To have a further fine-grained control of system resources, in addition to Linux's access control mechanisms at kernel level, Android enforces it's own Manifest Permissions to installed apps within its core framework. These permissions are of three types: Normal, Dangerous and Signature.
All normal permission are granted to a requesting app without human interaction. For instance a user can't restrict an app from accessing internet if the app is requesting android.permission.INTERNET. Some of such permissions are controlled by using third party tools.
Dangerous permissions - such as READ_CONTACTS - aren't granted to an app without user's approval. Some of Signature permissions treated as Special permissions - such as REQUEST_INSTALL_PACKAGES - can also be controlled from Settings app > Apps & notifications > Advanced > Special app access menu on newer Android versions.
Additionally some of the permissions for which user doesn't have independent control such as GET_ACCOUNTS, or the operations which don't have corresponding permissions at all such as RUN_IN_BACKGROUND, can be controlled from a hidden permissions manager of Android: AppOps. But this is meant only for OS use and not for end users.


Permission preferences are stored by Android Package Manager in different files such as /data/system/packages.list, /data/system/packages.xml, /data/system/users/0/runtime-permissions.xml at app install time or at runtime. These permission checks are then enforced on multiple events such as when accessing intents, starting an activity, sending/receiving broadcast, accessing a content provider, starting/binding to a service etc.

Though the whole permission checking and enforcing process happens under zygote in which ActivityManager and PackageManager - which are part of system_server - play major role, the core framework doesn't handle every permission check on its own. It again takes help from kernel to make permission checks more robust and efficient.
For instance READ/WRITE_EXTERNAL_STORAGE permissions are enforced through UIDs/GIDs by bind-mounting different views of Storage in a mount namespace for every app. See this answer for further details.


Another special case where Android relies on Linux kernel for permission check is android.permission.INTERNET. As stated earlier, only UID/GID 0 is special for standard Linux kernel. However Android applied a special patch PARANOID_NETWORKING to Linux kernel which maps certain capabilities to certain group IDs in range 3000-3999 making them special. One of those is INET (3003) group which allows its members to access internet (create PACKET sockets) which isn't possible without capability NET_RAW otherwise. All permission <-> GID mappings can be found in file /system/etc/permissions/platform.xml.

Coming to your question:


So the answer is, PackageManager doesn't handle android.permission.INTERNET on its own. It just adds the app to INET group and saves the configuration. Let's take example of Termux:

~# grep termux /data/system/packages.list
com.termux 10142 0 /data/user/0/com.termux default:targetSdkVersion=28 3003

Next time the app is launched with 3003 in its supplementary groups so that kernel doesn't restrict the app from accessing internet when enforcing DAC:

~# ps -p $(pgrep com.termux) -o cmd,uid,gid,supgrp
CMD                           UID   GID SUPGRP
com.termux                  10142 10142 3003,9997,20142,50142


Your Answer

