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)
Apps have uid/gids, not users
Apps have “home directories” for private storage
No facilities for filesystem sharing between applications
Apps 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 callscross 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
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
)
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);
}
}
System starts out by triggering the
PRE_BOOT_COMPLETED intent
Homescreen App displays homescreen (or “update” might run)
Homescreen triggers BOOT_COMPLETED intent
Homescreen App detects click triggers App intent
If App is running, pass it the message, otherwise start it!
App 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
permissionsfoundation for per-App access control/privileges
SystemManager nameserver maps unique
name to a Service
Apps 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:
uid/gidAndroid:
uid/gidand
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.