User-centric - programs run on behalf of users, thus can access any of the user’s files!
Running a UNIX program:
fork + execld.so dynamic library loading + sharing → memory savingComposition on shell:
pipes + fork + exec
$ echo $PATH | tr : "\n" | wc -l
filesdirectories/proc/*/dev/*→ Shell composition of programs can process on all system resources!
→ UNIX APIs for file manipulation manipulate all resources (polymorphism)!
setuid BitsWe can use setuid bits to coordinate services that serve multiple users.
/etc/passwd)setuid bit programs update filesdaemons) consume and act on filesrootroot permission is all or nothingroot required for many roles (e.g. chown)Examples:
root required for login? Only require 1. access to password file, and 2. ability to setuid to user./etc/ owned by root; why are the files in /dev/ all owned by root (e.g. /dev/mem)Traditional services:
→ Each user (and all of their programs) can interact with the service
Should everyone’s programs be able to interact with core services?
Discretionary Access Control (DAC): Users restrict access to their own files/data, and can indiscriminately pass that privilege.
Mandatory Access Control (MAC): Users restricted in sharing files/data by system-wide policies that restrict sharing relationships.
All of a user’s data is accessible to every program they execute. Assume a program will be attacked and compromised. How can we minimize its negative effects?
Principle of least privilege: Only give the access required to accomplish a program’s goals.
Examples:
App-store model is central
Apps cannot access each other’s resources!
Default: share nothing.
App-centric - programs have separate, limited access to files
Sensitive resources are not just files
Must limit App access to each of these, based on App’s goals/requirements.
By-default deny App access to resources, mediated IPC
Idea: Apps are not trusted. Isolate them from each other!
They are run by the user, but
Example: Banking App running alongside flashlight app
Services oversee key abstraction/resources
Resources include
Only Apps with permissions given by the user should be able to communicate with these services.
UNIX programs:
fork + execld.so dynamic library loadingJava VM program execution:
forkclasspath)imports)An application’s computation is always within a JVM. This means:
JVM-first execution abstractions
The Java VM has a huge number of large packages
Zygote: single process that is the parent of all Apps and Services.
forks the new App/Servicesetuid to the App’s uid (zygote is root)
uid/gids, not usersApps explicitly specify their resource requirements
Apps have a permission manifest: what resources they can access.
<permission name="android.permission.BLUETOOTH" >
<group gid="net_bt" />
</permission>
<permission name="android.permission.WRITE_MEDIA_STORAGE" >
<group gid="media_rw" />
<group gid="sdcard_rw" />
</permission>
<permission name="android.permission.INTERNET" >
<group gid="inet" />
</permission>
All of the system’s potential permissions.
READ_CONTACTS permissionApp and Service permissions are tracked and queried:
PermissionManager + PackageManagerreturn AppGlobals.getPackageManager()
.checkUidPermission(permission, uid);
Applications have access only to a small part of the filesystem
uid and gid are a unique, per-app values app_N. I.e. app_6/data/app/<app_name>/ binary and “ELF-like” data/data/data/<app_name>/ storageIf Apps cannot access each other’s data, how do we have a cohesive, single experience as user?
Our human experience is clicking buttons, and seeing actions. The system need only provide the illusion of a single, shared device.
How?
Illusion of a single, shared device:
Apps communicate to services that provide shared resources
Composition of Apps: communication through intents
/* This is the master Users and Groups config for the platform.*/
#define AID_ROOT 0 /* traditional unix root user */
#define AID_SYSTEM 1000 /* system server */
#define AID_RADIO 1001 /* telephony subsystem, RIL */
#define AID_BLUETOOTH 1002 /* bluetooth subsystem */
#define AID_GRAPHICS 1003 /* graphics devices */
#define AID_INPUT 1004 /* input devices */
#define AID_AUDIO 1005 /* audio devices */
#define AID_CAMERA 1006 /* camera devices */
#define AID_LOG 1007 /* log devices */
#define AID_COMPASS 1008 /* compass device */
#define AID_MOUNT 1009 /* mountd socket */
#define AID_WIFI 1010 /* wifi subsystem */
#define ...
#define AID_WEBVIEW_ZYGOTE 1053 /* WebView zygote process */
/* The 3000 series are intended for use as supplemental group id's only*/
#define AID_NET_BT_ADMIN 3001 /* bluetooth: create any socket */
#define AID_NET_BT 3002 /* bluetooth: create sco, rfcomm, ... */
#define AID_APP 10000 /* first app user */
#define AID_USER 100000 /* offset for uid ranges for each user */
0/1000 - root + System, 1001...1052 - Devices1053 - Zygote, 10000+ - AppsBinder: domain-socket analog for IPC
namesnames can mimic function calls→ cross App/Service “function invocations”
IPC Connections can be passed to Apps
ServiceManager nameserver: “here you go, have this connection”uid/gid of Apps using connection can be queried
AID_INPUT to be able to input?Package/PermissionManager: “are they allowed?”Who is making a request to a service? Do they have permissions?
pid and euidBinder.getCallingPid() & Binder.getCallingUid()ActivityManager.checkComponentPermission(permission, uid, owningUid, exported)
android.os.ServiceManager includes:
public static void addService(String name, IBinder service) { /* ... */ }
public static IBinder getService(String name) { /* ... */ }
We can see that at its core, Android has a nameserver to map names to IPC channels.
surfaceflinger to display contents)Coordination:
UNIX focuses on pipelines for composition
stdin, output on stdoutpipes for compositionread/write callsAndroid:
Intents: simple action you’d like to perform, identified by a name
Activity A creates an Intent with an action description and passes it to startActivity(). [2] The “Android System” searches all apps for an intent filter that matches the intent. When a match is found, [3] the system starts the matching activity (Activity B) by invoking its onCreate() method and passing it the Intent. Credit: Picture & text from https://developer.android.com .List of default intents. These include:
ActivityManager is nameserver for intents
// java code to send an email!
public void composeEmail(String[] addresses, String subject, Uri attachment) {
Intent intent = new Intent(Intent.ACTION_SEND);
intent.setType("*/*");
intent.putExtra(Intent.EXTRA_EMAIL, addresses);
intent.putExtra(Intent.EXTRA_SUBJECT, subject);
intent.putExtra(Intent.EXTRA_STREAM, attachment);
if (intent.resolveActivity(getPackageManager()) != null) {
startActivity(intent);
}
}
PRE_BOOT_COMPLETED intentBOOT_COMPLETED intentApp might trigger many intents
INPUT_METHOD_SERVICE to read from onscreen keyboardACTION_SEND to send emailActivityManager implements this intent system
SystemServer processFor functionality: void startActivity (Intent intent)
intentCurrentApp launches NextApp through an intent provided by NextApp. Credit: videothe InputManager multiplexes user input between InputMethods.
stdin? Up to the system to decide if stdin is a pipe, or a terminal. Polymorphic!InputMethods (link) are things like on-screen keyboards that are Services activated by intents from the InputManager.
INPUT_METHOD_SERVICE intent0”; we know it will give us input.App → ActivityManager(INPUT_METHOD_SERVICE) → InputMethod
uid/pid of clientActivityManager checks if the client has permissions to access resource/trigger intentroot// frameworks/base/core/java/android/app/ActivityManager.java
public class ActivityManager {
public static int checkComponentPermission(String permission, int uid,
int owningUid, boolean exported) {
final int appId = UserHandle.getAppId(uid);
if (appId == Process.ROOT_UID || appId == Process.SYSTEM_UID) {
return PackageManager.PERMISSION_GRANTED;
}
if (UserHandle.isIsolated(uid)) {
return PackageManager.PERMISSION_DENIED;
}
if (owningUid >= 0 && UserHandle.isSameApp(uid, owningUid)) {
return PackageManager.PERMISSION_GRANTED;
}
if (!exported) {
return PackageManager.PERMISSION_DENIED;
}
if (permission == null) {
return PackageManager.PERMISSION_GRANTED;
}
try {
return AppGlobals.getPackageManager()
.checkUidPermission(permission, uid);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
}
uid) for each App/Serviceuid/pid of calling AppPackage/PermissionManager lets us query App permissions→ foundation for per-App access control/privileges
SystemManager nameserver maps unique name to a ServiceApps use IPC to request resources from Services
Zygote as the parent of each App
forksetuid to restrict App’s privilegesIntentsApps and Services are activated by intents.
Polymorphic
PRE_BOOT_COMPLETED intent.INPUT_METHOD_SERVICE intent.Drives App/Service activation
Communication driven by the ActivityManager
UNIX:
open(Stringpath) → intdescriptor
uid/giddescriptortable(intdescriptor) → char[]file
Android:
open(Stringpath) → intdescriptor
uid/giddescriptortable(intdescriptor) → char[]file
and
ActivityManager(StringIntent) → BinderChannel
PackageManagerBinderChannel(fn, args…) Service
| POSIX | Android |
|---|---|
| User-centric | App-centric, per-App permissions |
| Security through FS | Security through permission manifests |
| Shared resources on FS | Shared resources via Services |
| Everything’s a file | Everything’s App (JVM) execution |
Shell + pipe composition |
Composition via App IPC through Intents |
Similar set of system calls to normal UNIX system…
…augmented with pervasive use of IPC…
…to coordinate between Apps and Services…
…while checking per-App permissions…
…all driven by the special trust relationship of untrusted Apps.