Chapter IV - Android Runtime Services
Android has quite a few daemons running in the background for providing its miscellaneous housekeeping and operational functions. The services are mostly strewn in /init.rc without much ordering, save the service class. The "core" services start first, followed by the "main" ones. The rc also defines a "late_start" class, for services which depend on the
We begin with a discussion of init, which is the very first process to launch (PID 1), as thus serves as the progenitor of all user mode processes. The Android
As it so happens, init also fills additional roles - assuming the guise of ueventd and watchdogd, two important core services which are also implemented by init, loaded through a symbolic link. The discussion thus continues to the other Core Services - adbd, the servicemanager and KitKat's healthd.
All other services are generally classified into the "main" category, so a subcategorization by Network Services (netd, mdnsd, mtpd and rild), and Graphics and Media Services (surfaceflinger, bootanimation, mediaserver and drmserver) follows. The remaining services are hard to group, so they are placed into the "Other Services" category, which includes installd, keystore, debuggerd, sdcard and Zygote.
init
Like most UN*X kernels, the Linux kernel looks for a hard-coded binary to launch as the first user mode process. On desktop Linux systems, this has traditionally been
| Linux /sbin/init | Android /init | |
|---|---|---|
| Config file | imported file (commonly | |
| Multiple configurations | Supported through the notion of "run-levels" (0: shutdown, 1, single user, 2-3 multi-user, etc). Each "run level" loads scripts from | No run-levels, but offers configuration options through triggers and system properties |
| Watchdog functionality | Yes: Daemons defined with the respawn keyword are restarted on exit, unless they repeatedly crash, in which case they are suspended for a few minutes. | Yes: Services are kept alive by default, unless defined as oneshot. Services may also further be defined as critical, which will force the system to reboot if they cannot be restarted. |
| Adopting orphan processes | Yes: /sbin/init will call wait4() to reap the return code, and avoid zombies. | Yes: /init registers a handler for SIGCHLD which the kernel will automatically send on child process exit. Most processes are silently wait()ed for and their exit code discarded. |
| System Properties | No: Linux /sbin/init does not support the notion of system properties | /init provides read access to properties (getprop) to all processes on the system via shared memory, and a property_service which allows write access (setprop). |
| Triggered operation | No: Linux allows only very specific triggers, such as ctrl-alt-del and UPS power events, but does not allow arbitrary triggers | Yes: /init can execute commands on any system property change, allowing it to run pre-defined commands on triggers that can be set by any (or some) users |
| Handling uevents | No: Linux relies on the hotplug daemon (usually udevd) | Sort of: /init also spawns itself as ueventd, with separate config files |
System Properties
The Android System Properties provide a globally accessible repository of configuration settings. They are somehwat similar in form and function to sysctl(2) MIBs, but are implemented in user mode by init. The property_service code in init loads properties from several files, in the order shown in table 4-props:
| File | Contains |
|---|---|
(PROP_PATH_RAMDISK_DEFAULT) | Initial settings. Note this file is part of the initramfs, and not present on the device's flash partitions. |
(PROP_PATH_SYSTEM_BUILD) | Settings generated by the Android build process |
(PROP_PATH_SYSTEM_DEFAULT) | Settings usually added by vendor |
(PROP_PATH_LOCAL_OVERRIDE) | Loaded if init was compiled with ALLOW_LOCAL_PROP_OVERRIDE, and the ro.debuggable property is set to 1. This enables developers to override previous settings by dropping a file into |
(PERSISTENT_PROPERTY_DIR) | Persistent properties. These are properties prefixed by persist, whose values are saved across reboot individually in files in this directory. Init can also re-load them at any time using the load_persist_props directive in the |
An additional property file, PROP_PATH_FACTORY (
Because init is the ancestor of all processes in the system, it is only natural that it implement the property store. Early in its initialization, the init code calls property_init() to set up system properties. This function (eventually) calls map_prop_area(), which opens the PROP_FILENAME (#defined as mmap(2)s into memory with read/write permissions, before closing it. Additionally, init re-opens the file, this time for O_READONLY, and then unlinks it.
The read-only file descriptor of the property file is set to be inheritable by children. This allows any process in the system easy access to system properties, albeit read-only, by mmap(2)ing the descriptor early on. This clever approach effectively allows all users of the properties area to share the same physical memory backing the property area (#defined as PA_SIZE, 128k by default). The only write access to this area, however, remains in the hands (and memory) of init. You can see the shared memory area in all user mode processes on the system easily by looking at the
# In init: (note area is writable) root@generic:/ # grep __properties /proc/1/maps b6f2f000-b6f4f000 rw-s 00000000 00:0b 1369 /dev/__properties__ # In any user mode process (in this case, the shell) root@generic:/ # grep __properties /proc/$$/maps b6e5a000-b6e7a000 r--s 00000000 00:0b 1369 /dev/__properties__
Though most developers remain agnostic to the internal structure, the properties are stored in a hybrid trie/tree, depicted in figure 4-propstruct:
The property_service
In order to service write requests, init opens up a dedicated UNIX domain socket - init to enforce permissions on the properties, which are hard-coded in an ever increasing list called property_perms. The permissions are based on simple UID and GID checks, (which init obtains from the socket caller credentials), as shown in table 4-propn. UID 0 is allowed full access to the properties. When SELinux is enabled, however, property namespaces are further protected by security contexts, as defined in
| Namespace | Owning UID | Contains |
|---|---|---|
net.rmnet0
| AID_RADIO |
Network properties, used by rild |
gsm | GSM related settings | |
persist.radio | Persistent radio settings | |
net.dns | DNS resolver settings (in loco /etc/resolv.conf) | |
sys.usb.config | @TODO | |
net | AID_SYSTEM | All network settings (including those owned by AID_RADIO) |
dev | All device settings | |
runtime | @TODO | |
hw | hardware related settings | |
[persist.]sys | system related settings | |
[persist.]service | service start/stop keys | |
persist.security | security @TODO | |
wlan | Wireless LAN (WiFi) settings | |
selinux | Security Enhanced Linux settings | |
dhcp | AID_SYSTEM AID_DHCP | DHCP settings |
debug | AID_SYSTEM AID_SHELL | Debug settings |
log | AID_SHELL | Logging settings |
service.adb.root | AID_SHELL | Used by ADB if running as root |
service.adb.tcp.port | AID_SHELL | Used by ADB if running over TCP/IP |
sys.powerctl | AID_SHELL | Power Management Control |
bluetooth | AID_BLUETOOTH | Bluetooth settings |
persist.service.bdroid | Bluetooth settings @TODO |
Special namespace prefixes
init recognizes several special prefixes, which govern how it handles the properties:
- The
persistpseudo-prefix: designates the property as meant to survive reboot. Persistent properties are backed up by files in/data/property/ , which must be owned by root:root, with no links. - The
ropseudo-prefix: is used for "read-only" properties. These, like C constants, may be set once and once-only, irrespective of owner UID. Normally these are set as early as possible. - The
ctlprefix: is used to provide a convenient way to control init's services, by setting thectl.start.serviceorctl.stop.serviceproperties (respectively) with the service name. A separate ACL is maintained in thecontrol_permsarray, to restrict services by UID/GID. As of KitKat, this list currently definesdumpstate(shell:log) andril-daemon(radio:radio).
Accessing properties
The toolbox command provides command line property access through getprop/setprop, and a property listener in the watchprop command. The native API for properties are defined in
int property_get(const char *key, char *value, const char *default_value)- To retrieve a property, optionally specifying a default value if it does not exist. This simply accesses the shared memory area.int property_set(const char *key, const char *value)- To set the value of a property. This serializes the key and value, and sends them over the property service socket.int property_list(void (*propfn)(const char *key, const char *value, void *cookie), void *cookie)- To enumerate properties using a callback function which will be invoked per property, with a pre-specified cookie
The <sys/_system_properties.h> file includes a few other undocumented (though accessible) functions, the most useful of which is __system_property_wait_any(unsigned int serial), which blocks until any property is set. This is used by the watchprop command.
Framework level access to system properties is carried out through android.os.SystemProperties, which accesses the properties via JNI calls to the API calls.
The .rc Files
init's main operation involves loading its configuration file, and acting upon its directives. Traditionally, two files were used: The main androidboot.hardware kernel argument, or
As of JB, the only hard-coded rc file is import directive is used to include additional rc files explicitly. JB's default /init.rc also includes init. An additional /init.trace.rc is also present in the default build, to enable the ftrace kernel facility to be used for debugging (Discussed in Chapter 17).
Triggers, actions, and services
The rc files are composed of trigger and service blocks. Trigger blocks contain commands, to be executed when a trigger is satisfied. Service blocks define daemons, which init can start by command and be responsible for, with optional modifiers (options) per such service. Service blocks start with the service keyword, followed by a name and the command line. Triggers are defined by the on keyword, followed by an argument, which is either a well-known name of a boot stage, or the property keyword, followed by a property=value expression (in case the trigger is tied to a property value change). When executing a given action or command, init sets the init.action or init.command properties, respectively. Well known boot stages are shown in table 4-initst, but note, that not all boot stages need be used, and vendors often deviate (e.g. mount filesystems in the init stage)
| Init stage | Contents |
|---|---|
| early-init | Very first stage of initialization. Used for SELinux and OOM settings |
| init | Creates file systems, mount points, and writes kernel variables |
| early-fs | Run just before filesystems are ready to be mounted |
| fs | Specifies which partitions to load |
| post-fs | Commands to run after filesystems (other than |
| post-fs-data | |
| early-boot | Run after property service has been initialized, but before booting rest |
| boot | Normal boot commands |
| charger | Commands used when device is in charger mode |
init.rc syntax and command set
The init.rc and its imported files are very well annotated - but also quite long- so instead of cutting/pasting them, we next focus on their syntax and other features, which are relatively undocumented or little known. You may want to look at
The init_parser recognizes two types of keywords when parsing the rc files: COMMANDs, naming actions to execute on a trigger/boot-stage (valid only in a trigger block) and OPTIONs, modifiers pertaining to a service declaration (valid only in a service block). Table 4-initcs shows the commands supported by init,from
| Command syntax | Notes |
|---|---|
chdir directory | as cd command (calls chdir(2)) |
chmod octal_perms file | Change octal_perms masks of file |
chown user group file | Same as chown user:group file |
chroot directory | as Linux chroot command (calls chroot(2)) |
class_reset service_class | Stop/Start all services associated with service_class |
class_start service_class | Start all services associated with service_class |
class_stop service_class | Stop all services associated with service_class |
copy src_file dst_file | Same as cp(1) command |
domainname domainname | Writes domainname to |
exec command | No longer supported |
export variable value | Export environment variable. Will be inherited by all children |
hostname hostname | Writes hostnname to |
ifup interface | Bring up an interface (same as |
insmod module.ko | Load a kernel module |
import filename.rc | Include an additional rc file |
load_persist_props | (Re)-Load all persistent properties from |
loglevel level | Set kernel loglevel (printk) |
mkdir directory | Create a directory (calls mkdir(2)) |
mount fstype fs point | Mount a file system of fs_htype from fs on mount point |
mount_all | Mount all file systems in /system/etc/fstab |
powerctl shutdown/reboot | shutdown/reboot wrapper |
[re]start service_name | Start or restart service specified in service block matching service_name |
restorecon path | Restore SELinux context for |
rm[dir] filename | Remove a file or directory (calls unlink(2)/rmdir(2), respectively) |
setcon SEcontext | Set (change) SELinux context. Init uses u:r:init:s0 |
setenforce [0|1] | Toggle SELinux enforcement on/off |
setkey table index value | Set key table |
setprop key value | Set a system property |
setsebool value | Set an SELinux boolean property. value can be 0/false/off or 1/true/on |
setrlimit category min max | use setrlimit(2) system call to enforce process (q.v. ulimit(1) |
stop service_name | Stop service specified in service block matching service_name |
swapon_all.. | Activate all swap partitions in |
symlink target src | Creates a symbolic link (as ln -s - calls symlink(2)) |
sysclktz tzoffset | Set system clock timezone (using settimeofday(2) |
trigger trigger_name | Activate a trigger (causing init to re-run corresponding commands) |
wait file timeout | Wait up to timeout seconds for file to be created. |
write file value | Writes value to file. Same as echo value > file |
If you look through your init determines how the services are to be run, and monitored. Table 4-initopts lists the available options.
| Option Syntax | Notes |
|---|---|
capability | Supports Linux capabilities(7) (or at least, will, at some point in the future) |
class | Defines the service to be part of a service group. Classes can then be manipulated together by the class_[start|stop|reset] commands. |
console | Defines the service as a console service. stdin/stdout/stderr linked to |
critical | Defines the service as a critical one. Critical services are automatically restarted. If they crash more than CRITICAL_CRASH_THRESHOLD (4) times in CRITICAL_CRASH_WINDOW (240) seconds, the system will auto-reboot into recovery mode |
disabled | Indicates service will not be started. Service can still be started manually |
group | Specifies the gid to start the service as. init will call setgid(2) for this. |
ioprio | Specifies the I/O priority for the service. init will call ioprio_set |
keycodes | Specifies a key chord that can trigger this service. (discussed below) |
oneshot | Tells init to start the service, but not worry about it (that is, ignore its SIGCHLD). |
onrestart | Lists which commands to invoke if/when the service needs to be restarted. This is commonly used to restart dependent services |
seclabel | Specifies the SELinux label to apply to this service |
setenv | Set an environment variable prior to fork()ing and exec()ing the service. Unlike export, this environment variable will only be seen by the service |
socket | Tells init to open this UNIX Domain socket and let the process inherit the open socket descriptor. This enables services to work with stdin/stdout, and not worry about which sockets to open or the permissions they may require |
user | Specifies the uid to start the service as. init will call setuid(2) for this. |
Starting services
Although the syntax is different, when starting services init assumes the traditional function of PID 1 (the traditional init, or launchd, to start up services: It fork()s, sets up the service's permissions (by calling setuid(2)/setgid(2)), sets up any input (UNIX domain) sockets and any environment variables, I/O priority (for services with ioprio), and SELinux context. For services defined with console, init connects init will also set the capability set for services defined with capability (as discussed in Chapter 21). Only once all of these operations have been performed, will init call execve to launch the service binary.
After the service is started, init maintains a parental link to it - should the service terminate or crash, init will receive a SIGCHLD signal, notifying it of the event - and allowing the service to be restarted. The onrestart option allows init to form dependencies between services, and run additional commands or restart dependent services when a particular service needs restarting. The critical option defines the service as a "must-have", and if init encounters a restart loop for a service deemed critical (that is, it restarts the service, only to have it crash again), it will reboot the entire system into recovery mode. For every service, init also maintains a corresponding init.svc.service property to reflect the service status (running/stopped/restarting).
Keychords
An interesting, (though little known) function of init is starting services in response to keychords. The chords are defined as combinations of keys (on devices with a physical keyboard) or buttons pressed by the user at any time (akin to key combinations one would press on a piano). The keys are specified by their IDs, which are taken from Linux's evdev input mechanism.
Note the keychords follow codes specified in Android's key layout files (usually found in bugreport, defined on some devices (like the Nexus 5) to be associated with the volume and power buttons. You can find its definition in the Nexus'
service bugreport /system/bin/dumpstate -d -p -B \
-o /data/data/com.android.shell/files/bugreports/bugreport
class main
disabled
oneshot
keycodes 114 115 116
The VOLUME_DOWN, VOLUME_UP and POWER, respectively.
For keychords to be supported, INPUT_KEYCHORD, or the driver was installed as a module. The driver can be considered an "Androidism" of sorts, and is discussed in more detail in Chapter 19.
Putting it all together: The flow of init
Initialization
init is a relatively simple C program, with a classic server setup: initialization, followed by a run-loop. The initialization consists of the following steps:
- Check if the binary was invoked as
ueventdor (as of KitKat)watchdogd. If so, the rest of the flow is diverted to the corresponding main loop for either of those daemons, discussed later in this chapter . - Create directory entries for
/dev, proc , andsys , and mount them. - Touch (open and then close)
/dev/.booting . This file is cleared once startup is complete (bycheck_startup(q.v. figure 4-initst). open_devnull_stdio()"daemonizes"initby linkingstdin,stdoutandstderrto/dev/null .klog_init()creates/dev/__kmsg__ (Major 1, Minor 11), and immediately deletes it.property_init()creates the shared property area in memory, as discussed earlier in this chapter in "System Properties"get_hardware_name()gets the hardware name by reading/proc/cpuinfo and extracting the "Hardware:" line. Rather crude, but it works (at least, on ARM architectures)process_kernel_cmdlinereads/proc/cmdline and imports as properties any arguments beginning withandroidbootasro.boot.- SELinux is initialized, on JellyBean and later. In JB it is still conditionally #ifdef'ed HAVE_SELINUX. In KK SELinux is assumed to be available by default. The SELinux security contexts are restored for
/dev and/sys . - A special check is made to see if the device is in "charger mode" (as indicated by an
androidbootkernel argument). This will affect the flow of init (for example, skip file system mounting), as shown in Figure 4-initstfig. If the device is not in charger mode,initproceeds to load/default.prop . init_parse_config_file()is called to parse/init.rc .initenqueues the actions supplied in theinit.rc sections (usingaction_for_each_trigger()) and the built-in actions (queue_builtin_action) on anaction_queue. The resulting queue is shown in figure 4-initstfig.
Eventually, the main loop iterates through at all the init spends most of its days asleep, polling the file descriptors, optionally logging to bootchart, and waking up only when necessary. You can see init's file descriptors by looking at the
root@generic:/proc/1 # ls -l fd lrwx------ .... 0 -> /dev/__null__ (deleted) # lrwx------ .... 1 -> /dev/__null__ (deleted) # stdin, stdout and stderr closed lrwx------ .... 2 -> /dev/__null__ (deleted) # l-wx------ .... 3 -> /dev/__kmsg__ (deleted) # Major: 1, Minor: 11 lr-x------ .... 4 -> /dev/__properties__ # read-only property store, for children lrwx------ .... 5 -> socket:[1643] # property_set_fd (/dev/socket/property_service) lrwx------ .... 6 -> socket:[1645] # signal_fd (socketpair[0]) lrwx------ .... 7 -> socket:[1646] # signal_recv_fd (socketpair[1]) lrwx------ .... 9 -> socket:[1784]
The run loop
The main run-loop is also quite simple, consisting only of three steps:
execute_one_command()- dequeues the action at the head of the queue, if any.restart_processes()- which iterates over all registered services and restarts them, if necessary- Set up and poll (monitor) three socket descriptors:
- The
property_set_fd(/dev/socket/property_service ), through which client processes who wish to set a property pass the property key and value. Theproperty_servicecode obtains the peer's credentials (usinggetsockopt(..SO_PEERCRED..)), and performs the permission checks on the property. If it can be set, any triggers or related services (forctl.start/stopproperties) are executed as well. - The
keychord_fd(/dev/keychord , if it exists), which handles any service key-combinations, as discussed previously - The
signal_recv_fd, one end of asocketpair, created to handleSIGCHLDfrom dead offspring. When the signal is received, thesigchld_handlerwrites data to the other end of thesocketpair(signal_fd), making data available on the receiving end, and causinginitto callwait_for_one_process(0). This reaps the process' return value (so as to lay it to rest, and avoid a zombie), cleans up any sockets, and potentially restarts the process, if it is a tracked service.
- The
The other roles of init
As discussed in the last section, init can also be fill additional roles - that of ueventd and (as of KitKat) watchdogd. Even though these are filled by the same binary, the code path taken is an entirely different one, and is chosen before any other initalization step.
ueventd
The ueventd can be thought of another persona of init: When it loads, init checks its basename, and if it is ueventd, it executes ueventd_main instead of entering its own main loop. It follows the same convention with respect to initialization files - that is, it consults androidboot.hardware kernel argument. Unlike init, however, the configuration file(s) only contain entries related to device nodes and their permissions. ueventd iterates over the lines of the file and calls set_device_permission() for every device entry.
Following the verification of device node permissions, the role of ueventd becomes very simple: continuously poll on a NETLINK UEVENT socket, read events as they become available, and handle them. Events may be of two general types:
- device events: These events are generated by kernel subsystems when devices are added or removed. In this sense,
ueventdfunctions like the traditional Linuxudevd: It creates or removes/devnodes corresponding to the devices. - firmware events:
ueventdlistens on firmware "add" events and attempts to load firmware updates from/etc/firmware ,/vendor/firmware and/firmware/image .
If ueventd is compiled with -DLOG_UEVENTS, events will be logged as INFO messages.
watchdogd
Just like ueventd, watchdogd is another facet of init. If init is invoked as watchdogd, it transfers control to watchdogd_main instead of following the regular startup path. In this identity, it is responsible for interfacing with the hardware watchdog timer, if any, by setting a timeout value, and sending a keepalive signal (a null byte) through watchdogd fails to wake up in time, the hardware timer interrupt can be used by the kernel to reset. While a somewhat drastic measure, the only reason watchdogd wouldn't wake up in time would be a system hang. It's likely the system wouldn't recover from such a hang, and and therefore it is simpler to restart the device.
Note, that not all devices necessarily have a hardware watchdog; In those that do not, there is no watchdogd isn't started.
Core Services
The services in the "core" class are the first to be started during user-mode boot. These services do not access the /data partition, and therefore can run irrespective of whether or not it is mounted.
adbd
If you're reading this book, likely ADB needs no introduction: It is through this medium, known as the Android Debugger Bridge, that the host and the device communicate. The bridge can be used either directly (using the adb command) or indirectly (using ddms). The adb command itself is well documented, and running it without any arguments will display a (rather lengthy) usage message. Of more interest to our discussion is how ADB actually works.
In its basic configuration, the adbd, which is the device daemon providing the server functionality of ADB, is defined in the sys.usb.config property contains "adb" - which is what the well-known "USB Debugging" GUI option activates1:
# adbd is controlled via property triggers in init.
Note that the adbd is run by default as uid root. It does, however, drop privileges to run as uid shell:shell, along with several other groups, as shown in this snippet from adb.c:
int adb_main(int is_daemon, int server_port)
..
property_get("ro.adb.secure", value, "0");
auth_enabled = !strcmp(value, "1");
..
/* don't listen on a port (default 5037) if running in secure mode */
/* don't run as root if we are running in secure mode */
if (should_drop_privileges()) {
drop_capabilities_bounding_set_if_needed();
/* add extra groups:
** AID_ADB to access the USB driver
** AID_LOG to read system logs (adb logcat)
** AID_INPUT to diagnose input issues (getevent)
** AID_INET to diagnose network issues (netcfg, ping)
** AID_GRAPHICS to access the frame buffer
** AID_NET_BT and AID_NET_BT_ADMIN to diagnose bluetooth (hcidump)
** AID_SDCARD_R to allow reading from the SD card
** AID_SDCARD_RW to allow writing to the SD card
** AID_MOUNT to allow unmounting the SD card before rebooting
** AID_NET_BW_STATS to read out qtaguid statistics
*/
gid_t groups[] = { AID_ADB, AID_LOG, AID_INPUT, AID_INET, AID_GRAPHICS,
AID_NET_BT, AID_NET_BT_ADMIN, AID_SDCARD_R, AID_SDCARD_RW,
AID_MOUNT, AID_NET_BW_STATS };
if (setgroups(sizeof(groups)/sizeof(groups[0]), groups) != 0) {
exit(1);
}
/* then switch user and group to "shell" */
if (setgid(AID_SHELL) != 0) {
exit(1);
}
if (setuid(AID_SHELL) != 0) {
exit(1);
}
..
It's possible to make adbd to retain its privileges (from the host, running adb root, which sets the service.adb.root to 1). A limitation in the adb source permits this only if the ro.debuggable property is set to 1 (and otherwise prints the familiar error "adbd cannot run as root in production builds"). The persist.adb.trace_mask can contain a hexadecimal value specifying the logging mask (try setting it to 0xff for maximum verbosity). If the property exists and is valid, adb will log to
The adbd normally uses the init, but also uses adbd can also be made to listen on the TCP port specified by the service.adb.tcp.port property. In either case, the architecture can be described as shown in figure 4-adbarch:
The internals of ADB, including protocol specification, kernel support and security, are covered in depth in Chapter 16.
servicemanager
The servicemanager is a key component of Android's IPC mechanism. Though a small binary, it is an important one, without which inter process communication would be severely impaired. This is reflected in its defition in the
service servicemanager /system/bin/servicemanager
class core
user system
group system
critical
onrestart restart healthd
onrestart restart zygote
onrestart restart media
onrestart restart surfaceflinger
onrestart restart inputflinger // Android L
onrestart restart drm
What makes servicemanager so critical, and makes so many other services dependent upon it, is its function as a service mapper. Virtually every IPC mechanism requires a mapper to enable clients to find and connect to the services - UN*X has its portmapper (for sunrpc), Windows has its DCE endpoint mapper - and the servicemanager fulfills this function in Android. Given this, the definition in servicemanager, so much as that in the case of its untimely demise, clients would be unable to find them. When the servicemanager is restarted, it does so with a tabula rasa - which requires services to re-register in order to be found. Since there is no method for services to detect the manager is dead, the only way is to get them to re-register is to force restart them as well.
The servicemanager certainly merits more attention, as do all the framework services, which it supports. The next chapter discusses it in detail, alongside system_server (the process serves as the service host) and its individual services.
healthd
The "health daemon" is a new addition in Kitkat. It is meant to service general "device health" tasks periodically, though at present the only tasks are battery related (this will likely change in future releases). The daemon registers itself as the BatteryPropertiesRegistrar service (batterypropreg or batteryproperties in L). As the Registrar, healthd provides the framework services (e.g. BatteryStatsService) with up-to-date battery statistics, which it obtains from sysfs.
Like most daemons, healthd sets up an initial configration, and then enters a run loop. The detailed flow is shown in Figure 4-hdsf:
Healthd main loop blocks on the Linux epoll(2) API to multiplex read operations on three descriptors, and registers actions for each, as shown in the following table:
| Descriptor | Type | Purpose |
|---|---|---|
| event_fd | Netlink | Reads kernel notification events. healthd only concerns itself with those of the power subsystem (SUBSYSTEM=POWER). These events include battery and charger notifications, and healthd runs battery_update(). |
| wakealarm_fd | TimerFD | Timer set to fire every periodic_chores_interval seconds. Upon wakeup, healthd runs periodic_chores. |
| binder_fd | /dev/binder | Listener updates by framework clients (when acting as batterypropreg) |
The main descriptor polled is the wakealarm_fd, which healthd uses for its periodic chores. Two interval types are used: fast (1 minute), when the system is on AC power, and slow (10 minutes), when the system is on battery. The only chore presently defined is battery_update(), which updates battery statistics in healthd's role as the BatteryPropertiesRegistrar. This is also called when events from the power subsystem are received over netlink from event_fd: healthd makes no attempt to parse the events, and merely refereshes the battery statistics. The latter mode is required in order for healthd to respond to events such as charger [dis]connection, or other power management alerts. Finally, the binder_fd is used to interact with the framework listeners (primarily, the BatteryStatsService), as described in the next chapter.
healthd
Using the powerful strace(1) utility you can watch healthd behind the scenes: By attaching to its process ID (as root) and calling on the ptrace(2) API, strace(1) can get notifications of system calls. Because anything meaningful a process does goes through a system call, this will provide a detailed trace of the activity, and reveal the names of the sysfs files healthd uses to obtain its statistics, as shown in the following annotated output:
strace(1) on healthdroot@htc_m8wl:/ # ls -l /proc/$healthd_pid/fd | cut -c'1-10,55-' lrwx------ 0 -> /dev/null lrwx------ 1 -> /dev/null lrwx------ 2 -> /dev/null l-wx------ 3 -> /dev/__kmsg__ (deleted) # Output: Log to kernel lrwx------ 4 -> socket:[6951] # event_fd (Netlink socket) lrwx------ 5 -> /dev/binder # binder_fd lrwx------ 6 -> anon_inode:[eventpoll] # epollfd l-wx------ 7 -> /dev/cpuctl/apps/tasks l-wx------ 8 -> /dev/cpuctl/apps/bg_non_interactive/tasks lr-x------ 9 -> /dev/__properties__ root@htc_m8wl:/ # strace -p $healthd_pid Process $healthd_pid attached - interrupt to quit # healthd patiently polling (0xffffffff = indefinitely) until an fd signals an event epoll_wait(0x6, 0xbebb5898, 0x2, 0xffffffff) = 1 # Netlink msg received on fd 4 (event_fd) - indicating core state change (going offline) recvmsg(4, {msg_name(12)={sa_family=AF_NETLINK, pid=0, groups=00000001}, msg_iov(1)=[{"offline@/devices/system/cpu/cpu1"..., 1024}], msg_controllen=24, .... # healthd's not interested, so it goes back to polling epoll_wait(0x6, 0xbebb5898, 0x2, 0xffffffff) = 1 # message indicating change in battery status: recvmsg(4, {msg_name(12)={sa_family=AF_NETLINK, pid=0, groups=00000001}, msg_iov(1)=[{"change@/devices/platform/htc_bat"..., 1024}], msg_controllen=24, {cmsg_len=24, cmsg_level=SOL_SOCKET, cmsg_type=SCM_CREDENTIALS{pid=0, uid=0, gid=0}}, msg_flags=0}, 0) = 488 # healthd goes into a flurry of statistics collection, opening and closing files: open("/sys/class/power_supply/battery/present", O_RDONLY) = 10 # Is battery present? read(10, "1\n", 16) = 2 # Yes (1) close(10) = 0 open("/sys/class/power_supply/battery/capacity", O_RDONLY) = 10 # What is its capacity? read(10, "96\n", 128) = 3 # 96% close(10) = 0 open("/sys/class/power_supply/battery/batt_vol", O_RDONLY) = 10 # Voltage? read(10, "4303\n", 128) = 5 close(10) = 0 open("/sys/class/power_supply/battery/batt_temp", O_RDONLY) = 10 # Temperature? read(10, "265\n", 128) = 4 close(10) = 0 open("/sys/class/power_supply/battery/status", O_RDONLY) = 10 # Charge status? read(10, "Charging\n", 128) = 9 # Charging close(10) = 0 open("/sys/class/power_supply/battery/health", O_RDONLY) = 10 read(10, "Good\n", 128) = 5 close(10) = 0 open("/sys/class/power_supply/battery/technology", O_RDONLY) = 10 read(10, "Li-poly\n", 128) = 8 close(10) = 0 open("/sys/class/power_supply/ac/online", O_RDONLY) = 10 read(10, "0\n", 128) = 2 close(10) = 0 open("/sys/class/power_supply/usb/online", O_RDONLY) = 10 # USB is connected read(10, "1\n", 128) = 2 close(10) = 0 open("/sys/class/power_supply/usb/type", O_RDONLY) = 10 read(10, "USB\n", 128) = 4 close(10) = 0 open("/sys/class/power_supply/wireless/online", O_RDONLY) = 10 # Alas, no wireless charging read(10, "0\n", 128) = 2 # for the M8 close(10) = 0 write(3, "<6>healthd: battery l=96 v=4 t=2".., 51) = 51 # Report to kernel log ioctl(5, 0xc0186201, 0xbebb5070) = 0 # Report to clients (BINDER_WRITE_READ) epoll_wait(0x6, 0xbebb5898, 0x2, 0xffffffff) = .. # Back to polling
Note the sysfs psuedo files (
As an improvement on the above, you might want to send the strace into the background (by using &) and then disconnect and reconnect the USB cable. You will then see the netlink notification for battery change, followed by a change in
logd (Android L)
Android L defines a new, much needed logging mechanism with its logd daemon. This daemon serves as a centralized user-mode logger (as opposed to Android's syslog. The service is defined in
logd definition in /init.rc
service logd /system/bin/logd
class core
socket logd stream 0666 logd logd
socket logdr seqpacket 0666 logd logd
socket logdw dgram 0222 logd logd
seclabel u:r:logd:s0
Note this service is designed with not one, but three sockets:
/dev/socket/logd :/dev/socket/logdw : A write-only socket (permissions 022 =-w--w--w-)./dev/socket/logdr : A read-write socket, designed for reading. Unlike thelogd UN*X domain socket, this is a seqpacket (sequential packet) socket.
lmkd (Android L)
Android L uses another specialized core service class daemon called lmkd. It is defined in the lmkd definition in /init.rc
service lmkd /system/bin/lmkd
class core
critical
socket lmkd seqpacket 0660 system system
The lmkd daemon provides an interface to the kernel's Low Memory Killer mechanism, which is an Androidism of the kernel (that is, a feature present in Android kernels, but not Linux ones). The Low Memory Killer allows Android finer control over the Linux Out-Of-Memory (OOM) mechanism, which automatically selects and terminates tasks during memory pressure. This mechanism is described in detail in Chapter 18.
vold
The Android vold is a volume-management daemon. This concept, which originally appeared in the (now deceased) Solaris operating system, employs a user-space daemon to automatically mount file systems ("volumes") as they are detected by the kernel. Beginning with HoneyComb, vold also enables file system encryption, in particular vold:
/init.rc (KitKat)
service vold /system/bin/vold
class core
socket vold stream 0660 root mount
ioprio be 2
The vold internally comprises three components:
VolumeManager: Responsible for maintaining volume state, and handling various volume operationsNetlinkManager: Responsible for listening on kernel netlink events of theblocksubsystems, by passing them to the volumeManagerCommandListener: Responsible for listening on the/dev/socket/vold socket, for commands issued by the framework.
The main client of vold is com.android.server.MountService, though applications cannot call this service directly, and must instead use android.os.storage.StorageManager. The MountService maintains a NativeDaemonConnector which uses the client side of the socket to send commands to vold's CommandListener. Most Android devices have a
| Cmd | Subcmd | Arguments | Purpose |
|---|---|---|---|
| dump | Dumps loop, device mapper, and mounted filesystems | ||
| volume | list | List mounted volumes | |
| debug | on|off | Toggle debug messages for formatting/unmounting | |
| mount | path | Mount a file system | |
| unmount | path[force[_and_revert]] | Unmount a file system, possibly forcefully | |
| [un]share | ums | Share/unshare USB Mass Storage | |
| shared | ums | Return share state (enabled/disabled) of USB Mass Storage | |
| mkdirs | path | Make a directory/mount point | |
| format | [wipe] path | Format a FAT volume, optionally erasing its contents first | |
| storage | users | List PIDs using a mounted volume (like fuser) | |
| asec | list | List all Android Secure Storage containers | |
| create | cid mb fstype key uid | Create new asec with cid, as a filesystem fstype of size mb | |
| destroy | cid [force] | Destroy the asec identified by cid, possibly forcefully | |
| finalize | cid | Finalize container cid. | |
| fixperms | cid gid filename | Fix permissions in container cid so as to be owned by gid. | |
| mount | cid key uid | Mount the container cid under app-id uid, with key. | |
| unmount | cid [force] | Unmount the container cid, possibly forcefully if in use. | |
| path | cid | Return the path to the container cid. | |
| rename | old_cid new_cid | Change the name of old_cid to new_cid | |
| fspath | cid | Return file system path corresponding to cid | |
| obb | list | List all mounted opaque binary blobs | |
| mount | filename key ownerGid | mount the opaque binary blob specified by filename for app ownerGid, with optional key | |
| unmount | source [force] | unmount the opaque binary blob specified by source filename | |
| path | source | ||
| xwarp | enable/disable | toggle xwarp mirroring | |
| status | return status (enabled/disabled) | ||
| cryptfs | restart | Signal init to restart frameworks | |
| cryptocomplete | Query if filesystem is fully encrypted | ||
| enablecrypto | inplace|wipe password | Encrypt filesystem, possibly erasing first | |
| changepw | old_passwd new_passwd | Change encryption password | |
| checkpw | passwd | Check if supplied password can mount encrypted fs | |
| verifypw | passwd | Used by BackupManagerService | |
| getfield | name | Get metadata field from cryptfs | |
| setfield | name value | Set metadata field in cryptfs | |
| fstrim | dotrim | ||
Of particular interest is vold's filesystem encryption handling. The Android Documentation provides a detailed explanation of the process as implemented in Honeycomb, as does this book, next.
Decrypting filesystems
With Honeycomb, Android brings support for disk encryption. By extending Linux's dm-crypt mechanism, which already provides the foundation for the asec mechanism, Android enables the entire user data partition to be encrypted. The system partition still remains very much cleartext, because the system has to somehow boot, but this is quite fine - The system partition is, for all intents and purposes, identical on all devices, and never actually holds any user-specific data, so there would be little advantage in encrypting it.
The dm-crypt feature is described in more detail in Chapter 21. At a high level view, however, suffice it to say that dm-crypt transparently encrypts and decrypts block devices. The password required for doing so, however, needs to be supplied in user mode. Android derives the passcode or pattern the user is already using for the lock screencom.android.settings.CryptKeeper activity to prompt the user for the credentials required to unlock the device, without which /data cannot be mounted.
What follows, therefore, is a choreography between init (driving the system startup), vold (providing the actual mount services), and CryptKeeper (handling the UI displayed to the user). This is shown in figure 4-voldch:
vold and init
The Android Explorations blog[2] shows how to decouple the encryption password from the pattern, by using
vdc cryptfs changepw.
During boot, init calls on the fs_mgr to mount the file systems. If none are encrypted, in sets the ro.crypto.state to "unencrypted" and enqueues any actions associated with the "nonencrypted" trigger - usually those services in the late_start class. If a file system is encrypted, however, an encrypted mount will obviously fail, unless the password is supplied. The fs_mgr mounts the filesystem instead as a tmpfs, and returns 1 to init, which sets ro.crypto.state to "encrypted", and informs vold of the need to decrypt vold.decrypt to 1). Prior to kitkat, vold would use the value of the ro.crypto.tmpfs_options property as mount options, but these options are now hardcoded. The mounting of vold.decrypt is set, the SystemServer only runs the "core" apps and services.
The com.android.settings.CryptKeeper activity registers itself as the home screen (using an IntentFilter), with a higher priority, so as to ensure it starts first. When it loads, it checks the value of vold.decrypt in its onCreate(). If unset, it simply exits, making room for the "real" home screen. If the filesystem is encrypted, however, the CryptKeeper start an async ValidationTask in its onStart() to contact the com.android.server.MountService, calling its getEncryptionState() to see if the partition is indeed properly encrypted.
Recall, that the MountService is connected to the vold socket. It can thus send the daemon the cryptfs cryptocomplete command. This makes vold check if the encryption is indeed recoverable (as it may be that the encryption has been interrupted, rendering cryptocomplete operation is successful, the CryptKeeper calls setupUi to input the user for the decryption password or sequence. It passes this again to MountService, which sends it to vold as a cryptfs checkpw command.
If the password is correct, the MountService sends the cryptfs restart command. This makes vold update the vold.decrypt property to trigger_reset_main, and sleep for 2 seconds in the hopes that all the services loaded under the main class will be stopped, and the tmpfs vold remounts the (now unecrypted) vold.decrypt property - first to load_persist_props (since those reside in /data), next to trigger_post_fs_data (to get init to set up any paths in trigger_restart_framework, which makes init restart the frameworks. The properties must be defined in
on nonencrypted
class_start late_start
on charger
class_start charger
on property:vold.decrypt=trigger_reset_main
class_reset main
on property:vold.decrypt=trigger_load_persist_props
load_persist_props
on property:vold.decrypt=trigger_post_fs_data
# This will call on post-fs-data, which must end with a post-fs-data-done
trigger post-fs-data
on property:vold.decrypt=trigger_restart_min_framework
class_start main
on property:vold.decrypt=trigger_restart_framework
class_start main
class_start late_start
on property:vold.decrypt=trigger_shutdown_framework
class_reset late_start
class_reset main
Encrypting filesystems
Encrypting file systems is handled in a similar manner to decrypting them. Once again, the UI is supplied by CryptKeeper, with the MountService providing the Dalvik level bridge to vold. The UI prompts the user for the encryption password, and verifies the device is on AC power, to prevent any power outage which may disrupt the encryption. The MountManager's encryptStorage method is called, which sends vold the cryptfs enablecrypto, with either wipe (to format /data before encrypting it) or inplace argument, and the password.
Upon getting the command and verifying it can proceed, vold sets the vold.decrypt to trigger_shutdown_framework. This causes init to stop all services but the core ones. This is exactly the mirror image of the state the system is in while booting, before the user password is entered, and vold then sets vold.encrypt_progress to start at 0, and vold.decrypt to trigger_restart_min_framework, to get init to restart the main services.
Once again, CryptKeeper loads as the home app. Upon seeing vold.encrypt_progress, it loads the status bar UI.. If all goes well, the progress bar reaches 100%. If not, vold.encrypt_progress is set to an error_ string, and the user is left with no choice but to reset the device to defaults.
The technical aspects of encryption, as well as the kernel perspective, are discussed in Chapter21.
Network Services
netd
Android uses a dedicated daemon to control network interfaces and configuration management. If you've ever used tethering, firewalling or WiFi Access-Point features, or even a basic DNS lookup - consider yourself a proud client of netd. The daemon is defined in
netd service defition in init.rc
service netd /system/bin/netd
class main
socket netd stream 0660 root system
socket dnsproxyd stream 0660 root inet
socket mdns stream 0660 root system
socket fwmarkd stream 0660 root inet // Android L
The netd shares many structural similarities with vold, and in fact shares some code with it, using the native FrameworkListener class (among others) in its socket handlers. Figure 4-netdarch shows the architecture (compare with 4-voldarch). netd's internal structure comprises four components:
CommandListener: Responsible for listening on the/dev/socket/netd socket, for commands issued by the framework, and sending notifications (broadcasts) to connected clients. As withvold, the emulator includes a special utility -ndc- which can be used as a client to issue any of the commands, shown in table 4-netdc:Table 4-netdc: Cmd Subcommand Purpose interface list List all interfaces route iface default/secondary dest prefix gateway fwmark ... list_ttys List ttys used by the PPP daemon as interfaces ipfwd enable|disable|status Toggle IP Forwarding or query status tether nat enable|disable pppd attach tty local remote [dns1] [dns2]Attach PPPd to tty to set up a point-to-point connection between local and remote IP addresses, specifying optional name server IPs. detach tty Execs /system/bin/pppd -detachsoftap startap|stopap|statusToggle Access point (by exec()ing or kill()ing /system/bin/hostapd), or query statusfwreload iface AP|P2P|STAset iface SSID hidden/* channel security keySet access point parameters. If any other word but "hidden" is specified, AP will be broadcast resolver See table 4-resolverc Commands to control DNS resolution. See "DNS proxying" below bandwidth idletimer firewall
The functionality for most commands is achieved by a set of internal controller classes, which interface with the kernel's netfilter mechanism through the crudest form possible: Crafting and executing an
iptablescommand line.NetlinkManager: Responsible for listening on kernel netlink events through three separate sockets:NETLINK_KOBJECT_UEVENT:NETLINK_ROUTE: These events, from the "net" subsystem, notifynetdof interface modifications (addition, removal, IP address changes, etc).-
NETLINK_NFLOG.
DnsProxyListener: A type ofFrameworkListenerresponsible for listening on the/dev/socket/dnsproxyd socket for name resolution commands. Currently, supported commands are getaddrinfo, gethostbyaddr, and gethostbyname, which emulate the functionality of the well known library calls. See "DNS Proxying", below.MDnsSdListener: Responsible for listening on multicast DNS requests generated by apps, and interfacing with themdnsd(discussed later). Supported commands are shown in table 4-mdnssdcmds. All commands also have astop-counterpart, accepting a request id, which has been omitted for brevity.Table 4-mdnssdcmds: Cmd Arguments Purpose discover rId svcType .. register rId svcName ServiceType port .. resolve rId ifaceName svcName RegType Domain .. start-service Starts the mdnsddaemonsethostname rId hostname .. getaddrinfo rId hostname ..
Events broadcast by Netlink are intercepted by its internal NetlinkHandler class, which is responsbible for broadcasting them to clients connected to the CommandListener)
DNS Proxying
A rather clever feature in Android is the restriction of DNS functionality based on uids. Since each uid represents a different application, this translates to fine grained control of DNS functionality on a per-app basis.
| Cmd | Arguments | Purpose |
|---|
mdnsd
Multicast DNS (mDNS) is a popular discovery protocol first adopted by Apple (as its "Bonjour" service). It is used extensively in iOS's family of "Air" protocols (e.g. "AirPlay"), and allows devices to find one another by sending a multicast request to group 224.0.0.253 (or IPv6's FF02::fc) and UDP port 5353. Unsurprisingly, Android chose to adopt this from iOS as well, and it serves as the basis for "WiFi Direct". Beginning with JellyBean,
mdns service defition in init.rc
service mdnsd /system/bin/mdnsd
class main
user mdnsr
group inet net_raw
socket mdnsd stream 0660 mdnsr inet
disabled
oneshot
Because of its multicast capabilities, the service is granted membership in both the inet group (allowing general TCP/IP capabilities) and the net_raw group (allowing "advanced" capabilities, such as raw sockets and crafting non-standard IP packets). The service listens on MDNS_UDS_SERVERPATH) for requests, and has another socket (netd.
Todo: Mention NsdService
The mDNS implementation is found in the #ifdef __ANDROID__ blocks.
mtpd
Though the acronym MTP is normally associated in Android (and elsewhere) with the Media Transfer Protocol, the mtpd couldn't be further from it - It is the daemon responsible for PPP and L2TP (but not IPSec). It is defined in mtpd service defition in init.rc
service mtpd /system/bin/mtpd
class main
socket mtpd stream 600 system system
user vpn
group vpn net_admin inet net_raw
disabled
oneshot
The group permissions granted to mtpd reflect its need for network access (inet) setting up a network interface (net_admin), and tunneling IP (net_raw). As a oneshot and disabled service, the mtpd must be started manually. Indeed, the com.android.server.connectivity.Vpn does so (in its startLegacyVPN inner class). There is no programming interface for VPN functionality, which is meant to be started or stopped from the Android system GUI.
racoon
Racoon is a de facto standard VPN daemon. As an external project, it is not part of Android per se, but is used extensively (in both Android and iOS) to provide VPN services. VPN connectivity is discussed in Chapter 15.
mtpd service defition in init.rc
service racoon /system/bin/racoon
class main
socket racoon stream 600 system system
# IKE uses UDP port 500. Racoon will setuid to vpn after binding the port.
group vpn net_admin inet
disabled
oneshot
Note that racoon starts as root (in order to bind the ISAKMP well known port, which is a privileged port), but then drops privileges. This is why it requires the extra group memberships, which (as we discuss in Chapter 21) allow network connectivity. From a strict security standpoint, it would have been better to relinquish root altogether, and use capabilities (in particular CAP_NET_BIND_SERVICE) instead. This is especially important considering racoon has had a history of exploits (and was in fact used to jailbreak iOS 5) before.
rild
If your Android device is a phone or 3G/LTE connected tablet, rild is undoubtedly one of the more important system processes. The Radio Interface Layer Daemon provides virtually all the telephony capabilities for these devices, by interfacing with the baseband. It is defined in
rild service defition in init.rc
service ril-daemon /system/bin/rild
class main
socket rild stream 660 root radio
socket rild-debug stream 660 radio system
user root
group radio cache inet misc audio log
Though most users don't give this a passing thought, the telephony APIs used in today's mobile devices aren't that far from the modems employed in the previous millenium. In fact, the low-level call control is still carried out with the same set of commands used by modems - the "AT" commands - which may be familiar to anyone who's ever used minicom and kermit back in the day. The rild is responsible for opening the telephony device (which is basically a serial port) and generating these commands.
As shown in the above definition, the daemon uses two sockets:
/dev/socket/rild : This socket is the interface by means of which the phone application can connect to the daemon, and issue various phone-related requests (e.g. dialing, answering, hanging up). The socket is not meant to be used directly./dev/socket/rild-debug : As the name implies, this socket is intended for use in debugging, and is undocumented save for the source of thedebugCallbackof libril. It defines a set of codes, which will cause requests to be artificially injected into the RIL. Table 4-rdc shows the codes and the resulting requests:
Table 4-rdc: rild-debug codes and their resulting RIL requests Request Issued Function 0 RIL_REQUEST_RESET_RADIOReset the radio 1 RIL_REQUEST_RADIO_POWERPower the radio off 2 RIL_UNSOL_RESPONSE_VOICE_NETWORK_STATE_CHANGEDCalls voice network change callback 3 RIL_REQUEST_OEM_HOOK_RAWEnable the QXDM log 4 Disable the QXDM log 5 RIL_REQUEST_RADIO_POWER
RIL_REQUEST_SET_NETWORK_SELECTION_AUTOMATICPower the radio on 6 RIL_REQUEST_SETUP_DATA_CALLSet up a data call 7 RIL_REQUEST_DEACTIVATE_DATA_CALLDeactivate (tear down) a data call 8 RIL_REQUEST_DIALDial a number 9 RIL_REQUEST_ANSWERAnswer an incoming call 10 RIL_REQUEST_HANGUPDisconnectr an incoming call
The daemon also has its own debug facility - radio - which results in a dedicated logging device - logcat -b radio will dump plentiful amounts of debug information, which may (in some versions of Android) show the "AT" commands used by rild to dial numbers and establish codes.
If you look through the rild source, you might be suprised to find it consists of merely two files: the main libril and in other support libraries. The vendor usually supplies its own RIL implementation, so long as it conforms with the basic abstractions provided by the Android code. These abstractions are discussed in more detail in Chapter 15, which deals with connectivity.
Graphics and Media Services
surfaceflinger
The surfaceflinger provides the heart of the Android Graphics Stack. The notion of a "flinger", or in other words, a "compositor", is a component which merges one or more layers of input into a single layer of output. In the case of surfaceflinger, the components are graphics "surfaces" (instances of android.view.Surface), which are either rendered by the framework as the user lays out various views, or by the developer, in the case of raw or GL Surfaces. To communicate with the surfaceflinger, the framework looks up the SurfaceFlinger service using servicemanager. The flinger therefore needs no sockets, and its definition in
/init.rc
service surfaceflinger /system/bin/surfaceflinger
class main
user system
group graphics
onrestart restart zygote
Android also uses the term "flinger" in its audio stack, with the "AudioFlinger" service, which is a component of mediaserver. This makes sense, as both flingers perform what is essentially the same function: adding bitmaps (surfaces) or samples (audio) together*. The surfaceflinger's architecture is quite complicated, and this hardly scratches its surface (no pun intended). Chapter 13 discusses this in more detail.
InputFlinger has been added. This will probably combine multiple input sources, which could be useful for multiple controllers (in games, or TV). At the time of writing, however, this flinger has yet to be implemented.
bootanimation
The bootanimation service is a small binary in /system/bin, which is started by /init as a placeholder while the media frameworks, and in particular surfaceflinger, are loading. It is defined in /init.rc
service bootanim /system/bin/bootanimation
class main
user graphics
group graphics
disabled
oneshot
The binary is essentially a very simple one - it starts up and looks for one of three zip files:
/system/media/bootanimation-encrypted.zip : Used if thevold.decryptproperty is set, indicating the file system is encrypted./data/local/bootanimation.zip : Allowing the (advanced) user to drop their own animation file viaadb push. If present, this will override the system boot animation./system/media/bootanimation.zip : The system default animation, usually supplied by the device vendor
The three files are tried in order, and if none of them can be found, bootanimation defaults to alternating between two images -
Most device vendors will provide a bootanimation will cycle through, as shown in Listing 4-ba. The first frame can be made to overlap with the ROM bootup image, if any, ensuring a smooth transition into the animation. Note that some vendors may drop the default binary in favor of their own animation and accompanying sound (e.g. as Samsung has done, with
The Nexus 5's boot animation is in adb, and inspect its contents like so:
morpheus@Forge (~)$ adb pull /system/media/bootanimation.zip
1275 KB/s (1068873 bytes in 0.818s)
morpheus@Forge (~)$ unzip -t bootanimation.zip
Archive: bootanimation.zip
testing: desc.txt OK
testing: part0/ OK
testing: part0/000.png OK
testing: part0/001.png OK
..
testing: part1/057.png OK
testing: part1/058.png OK
testing: part1/059.png OK
# After unzipping:
morpheus@Forge (~)$ cat desc.txt
1080 230 24
p 1 0 part0
p 0 0 part1
morpheus@Forge (~)$ file part1/000.png
000.png: PNG image data, 1080 x 230, 8-bit/color RGB, non-interlaced
The first line of the
The book's website contains miscellaneous boot animations you can experiment with on your device. Try dropping them into
What makes bootanimation interesting is its raw (that is, non-framework) graphics capability. Since it is one of the first services to load, the frameworks have yet to initialize, leaving bootanimation to fend for itself using low level OpenGL and SKIA calls. These result in direct access to the device's frame buffer (
bootanimation override surfaceflinger even when it is active, as you can verify by running bootanimation via adb shell on your device. The device likely has it by default, though you can always upload it from the emulator image. Running bootanimation when your device is active will hide your display behind the boot animation - either completely (in portrait mode) or partially (in landscape mode). Touch screen input will still work - but you'll likely not be able to see what you're doing until you exit (by using CTRL-C).
mediaserver
The mediaserver is one of Android's most important components. It serves as a focal point for multimedia handling, controlling both playback and recording. It is defined in
service media /system/bin/mediaserver
class main
user media
group audio camera inet net_bt net_bt_admin net_bw_acct drmrpc mediadrm
ioprio rt 4
As can be seen from the group membership, mediaserver requires permissions for audio, camera, network services, and the DRM framework (described next). The mediaserver, however, is really just a container for the actual services, somewhat like the concept of a service host (svchost.exe) in Windows. Table 4-mediasrv shows the services hosted:
| Service | ServiceManager name | Provides |
|---|---|---|
| AudioFlinger | media.audio_flinger | Audio playback. The service gets one or more PCM audio streams as input, and "flings" them into a merged stream. |
| AudioPolicyService | media.audio_policy | Audio policy. The policy tells the AudioFlinger what the volume setting is, as well as which device is the target audio device | CameraService | media.camera | Camera services. Its main client is the camera app, whether the Android supplied one, or the vendor's |
| MediaPlayerService | media.player | Playing audio and video. |
KitKat's mediaserver lays the groundwork for extensions, by providing a registerExtensions() function, though at present no extensions are defined. We discuss the services in more detail in Chapter 11 and Chapter 13, for audio and video, respectively
drmserver
Android provides a Digital Rights Management (DRM) framework for copy-protected content, and the drmserver is the component responsible for being the focal point of all DRM requests. It is defined in
service drm /system/bin/drmserver
class main
user drm
group drm system inet drmrpc
In truth, to say that Android provides a "framework" is somewhat of a misnomer, since it defines just the APIs, but not any actual content verification logic. The actual work is left for vendors to implement by a plug-in architecture. The plug-ins are shared object files, loaded by enumerating drmserver is thus quite small, consisting only of a main() with a few lines , which register a DrmManagerService (drm.drmManager) with the ServiceManager, and call on its internal DrmManager class to service incoming DRM calls from the framework, by finding the appropriate plug-in for the content.
shell@android:/ $ ls -l /vendor/lib/drm /vendor/lib/drm: No such file or directory # No vendor specific DRM modules 1|shell@android:/ $ ls -l /system/lib/drm -rw-r--r-- root root 48336 2012-05-21 17:42 libdivxplugin.so -rw-r--r-- root root 117224 2012-05-21 17:42 libdrmwvmplugin.so -rw-r--r-- root root 68944 2012-05-21 17:42 libenyplugin.so -rw-r--r-- root root 48604 2012-05-21 17:42 libfwdlockengine.so -rw-r--r-- root root 65312 2012-05-21 17:42 libomaplugin.so -rw-r--r-- root root 48212 2012-05-21 17:42 libpiffplugin.so -rw-r--r-- root root 65012 2012-05-21 17:42 libplayreadyplugin.so -rw-r--r-- root root 64836 2012-05-21 17:42 libtzprplugin.so
To be considered valid and called by the DrmManager, a plug-in must conform to the DrmEngine specification defined in
The sources also include two plug-ins in
Other Services
The remaining services in the main class are somewhat difficult to group, as they provide different facets of system support. Nonetheless, that does not make them any less important.
installd
The installd daemon is responsible for package installation and removal. Whichever way a package is installed - by downloading the adb install - installd gets involved in the process. The daemon itself, however, is passive, listening on a socket set up by init, over which commands (generated by the Android framework) are delivered. The socket is defined in the daemon's
service installd /system/bin/installd
class main
socket installd stream 600 system system
Startup
startup of installd proceeds as shown in Figure s-ins:
installd daemon:Upon startup, the installd is charged with setting up and maintaining the directory structure where apps are to be installed. The base name is obtained from the ANDROID_DATA environment variable, set up by installd appends APP_SUBDIR (PRIVATE_APP_SUBDIR (APP_LIB_SUBDIR (MEDIA_SUBDIR (
installd also obtains two other environment variables - ANDROID_ROOT (pointing to ASEC_MOUNTPOINT (pointing to installd is also charged with migrating the directory structure to allow multi-user. The steps taken are as follows:
- Create the
/data/user directory, with ownershipsystem:systemand moderwx--x--x - Create a symbolic link from
/data/user/0 to/data/data - Upgrade
/data/media to/data/media/0 , moving any preexisting media there - Create a
/data/media/## directory for any other existing users - Move OBBs from
/data/media/0/Android/obb to/data/media/obb so they can be shared amongst users, and reduce overall filesystem usage - Ensure user media folders (
/data/media/## ) exist and aremedia:mediarwxrwx---.
Though it is started as root, as of Jellybean, the installd employs the principle of least privilege. One of the first calls it makes is to drop_privileges(), which sets the uid/gid to AID_INSTALL. It also makes use of Linux capabilities (q.v. Chapter 21) to maintain chown(2), setuid(2)/setgid(2) and DAC override, as it needs them to deploy and remove packages owned by different user and group ids.
Finally, installd acquires the control socket (installd can only handle only one client at any given time). An interesting observation is the installd doesn't perform any verification of "caller id" on the socket, and relies on the socket being chmod()ed to system:system rw-------. Because only one client can be served at a time, there is the implicit assumption that it is held by the PackageManager. Note also, that installd performs no signature verification on APKs, assuming that its caller has done so already.
Commands
The framework uses the PackageManager, via the undocumented com.android.server.pm.Installer class, to provide a Dalvik-level API trusted applications can use in order to install or remove the various apps. The API methods are mapped to the commands sent over the socket (as strings, preceded by a two byte binary length),which are shown in table 4-instdcmds:
| Command | Arguments | Use |
|---|---|---|
| ping | Null command, used for connectivity | |
| install | pkgname uid gid seinfo | Install package specified by pkgname under uid/gid with SELinux context specified by seinfo |
| dexopt | apk_path uid is_public | Optimize dex file of APK, creating an .odex file |
| movedex | src dst | Rename DEX file specified by src to dst. |
| rmdex | pkg | Remove DEX file of package specified by pkg |
| remove | pkgname, userid | Remove package specified by pkg installed under uid. |
| rename | oldname newname | Rename package from oldname to newname |
| fixuid | pkgname uid gid | Fix package pkgname so it is owned by uid:gid |
| freecache | free_size | Free cache so it has free_size bytes left. |
| rmcache | pkgname uid | Remove package pkgname owned by uid from cache. |
| getsize | pkgdir uid apkpath | Return the size of the directory specified by apkpath |
| rmuserdata | pkgname uid | Remove user data used by package pkgname owned by uid. |
| movefiles | Execute scripts in /system/etc/updatecmds | |
| linklib | pkgname asecLibDir uid | Link native library to its real location |
| mkuserdata | pkgname uid userid | Creates data directory for package (owned by id for user userid), and installs symlinks |
| rmuser | uid | Remove user uid |
The phases of package installation are discussed in Chapter 6. The Installer service, which is the Java front-end to installd, is discussed in the next chapter.
keystore
The keystore service is, as its name implies, provides a storage service for keys. By design, it can provide storage for any arbitrary name-value pairs, though in practice it is only used for key storage. It is defined in
service keystore /system/bin/keystore /data/misc/keystore
class main
user keystore
group keystore drmrpc
The argument to the keystore daemon -
Unlike other daemons, keystore doesn't use a socket. It is accessible only via servicemanager, wherein it is published using the name android.security.keystore. The framework client of the keystore service is the java.security.KeyStore class. This is a developer-accessible class modeled after the Java standard, and is reasonably documented, but not fully so: There are quite a few other public methods available, which the Android documentation chooses to omit. There is also a keystore_cli command which allows command line native-level access to the keystore, though not all of the service methods are (at the time of writing) implemented. Table 4-keystorecmds shows the commands exposed by the class and service, showing the commands not implemented by the cli as grayed.
| 0 | test() | Test keystore daemon is active |
| 1 | byte[] get(String name) | Get value corresponding to name |
| 2 | insert(String name, | Insert a name/value combination into keystore belonging to uid, with flags |
| 3 | int del(String name, int uid) | Delete name (and value) from keystore belonging to uid |
| 4 | exist(String name, int uid) | Check if name exists in keystore belonging to uid |
| 5 | saw(String prefix, int uid) | List all keys beginning with prefix in uid's keystore |
| 6 | reset() | Reset (wipe) keystore |
| 7 | password(String password) | Change keystore password to password |
| 8 | lock() | Lock keystore, requiring password to unlock |
| 9 | unlock(String password) | Unlock previously locked keystore by supplying password. |
| 10 | zero() | Check if keystore is empty. |
| 11 | generate(String name, | Generate a private/public keypair in keystore owned by uid under name. The key can then be used to sign and verify, or retrieve the public key - but the private key will remain inaccessible. |
| 12 | import_key(String name, | Import key specified in data blob into keystore owned by uid into key name. |
| 13 | byte[] sign(String name, | sign data with key corresponding to name without actually retrieving key. |
| 14 | verify(String name, | Verify signature on data using key specified by name |
| 15 | byte[] get_pubkey(String name) | Get a public key associated with name |
| 16 | del_key(String name, int uid) | Delete key identified by name in keystore belonging to uid |
| 17 | grant(String name, int uid) | Grant uid access to key name |
| 18 | ungrant(String name, int uid) | Revoke access to key name to uid |
| 19 | long getmtime(String name) | Get modification time of name |
| 20 | duplicate(String srcKey, | Copy the key specified by srcKey in keystore belonging to srcUid to keystore owned by destUid under name destKey. |
| 21 | is_hardware_backed(String keyType) | Return integer specifying whether or not keyType is backed by a hardware keystore implementation |
| 22 | clear_uid(long uid) | @TODO |
Access to keys is governed by uid (hence its use as an argument), so each application effectively has its own private store.In addition, similar to the ACLs hard coded in init, the keystore daemon maintains a user_perms array of permissions, with specific exclusions for AID_SYSTEM (all access), AID_VPN and AID_WIFI (get, sign and verify only) and AID_ROOT (get only).
The keystore implementation, and cryptography in general, is discussed in Chapter 21.
debuggerd
Try as hard as developers will, their applications will inevitably face bugs, which will result in crashes. In order to fix those bugs, there must be an efficient mechanism to collect the crash data. On a desktop system, the crash results in a core dump - but that is simply not an option in a mobile device. Core dumps are often very large - in the hundreds of MB and sometimes more - and space is limited. What more, even if the core dump was saved, it's not a trivial matter to move such large files out of the device.
Similar to iOS's CrashReporter, Android introduces debuggerd. This small daemon is normally dormant, listening on its socket, until an application crashes. All processes on Android, by virtue of linkage with bionic, automatically install a signal handler for the lethal signals, shown in table 4-dbgrsig:
| Signal | Full Name | Meaning |
|---|---|---|
ILL | Illegal Instruction | Illegal machine opcode |
TRAP | Debugger Trap | Breakpoint |
ABRT | Voluntary Abort | Assertion failure |
BUS | Bus Error | MMU fault |
FPE | Floating Point Exception | Division by zero |
SEGV | Segmentation Violation | NULL pointer dereference |
PIPE | Broken Pipe | Termination of process on read end of pipe |
All signals use the same action, debuggerd_signal_handler, which establishes a connection to debuggerd over its socket, and sends it a message. The message wakes up the daemon, and makes it engrave a tombstone. A tombstone is, essentially, a crash report, which debuggerd generates by attaching to the failing process (using Linux's ptrace(2) APIs), catching its signal for it, and inspecting its memory. This way, rather than a full core dump, a tombstone can (hopefully) capture the essence of a crash and perform the basic crash processing. Tombstones are created in
If the debug.db.uid property is set to the uid of the crashing process, debuggerd freezes the process in its final death throes and waits for user to start gdbserver. It logs a message which can be easily seen in logcat:
I/DEBUG ( 24): ******************************************************** I/DEBUG ( 24): * Process pid has been suspended while crashing. To I/DEBUG ( 24): * attach gdbserver for a gdb connection on port 5039 I/DEBUG ( 24): * and start gdbclient: I/DEBUG ( 24): * I/DEBUG ( 24): * gdbclient app_process :5039 pid I/DEBUG ( 24): * I/DEBUG ( 24): * Wait for gdb to start, then press HOME or VOLUME DOWN key I/DEBUG ( 24): * to let the process continue crashing. I/DEBUG ( 24): ********************************************************
The debuggerd uses the low level Linux EV_* APIs (discussed in Chapter 11) to wait until the user presses one of the keys, and lights up the debug (red) led on the device to draw the user's attention. We discuss the debuggerd in general and tombstones in particular, in Chapter 7.
sdcard
Not all Android devices necessary support SDCards - but in those which do, the
zygote
Though last, both alphabetically and in this chapter, the zygote is hardly the least of all services. It provides the core support for all of the Android Framework Runtime services, in the form of an initialized empty Dalvik Virtual Machine, stopped just shy of the main class loading. The
service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
class main
socket zygote stream 660 root system
onrestart write /sys/android_power/request_state wake
onrestart write /sys/power/state on
onrestart restart media
onrestart restart netd
As the listing shows, zygote's "true name" is app_process. The name "zygote" however, is far more apt, as this process mimics, in some senses, its namesake. Just like the biological zygote, this process is full of unlimited potential - it can load any Dalvik class specified, and can become any user. This, however, is a one-way process (again, just like the biological parallel). The rest of the command line provides the arguments, all of which but the double-dashed get passed directly to the Dalvik VM. The last two arguments get processed by app_process itself, and result in the VM loading the Zygote class, and fork()ing to start the system_server process. The app_process can also accept a --nice-name argument to rename itself.
The system_server process goes on to load all of the Android runtime frameworks, whereas the Zygote binds its socket (fork() and load the classes - which will result in the creation of a new app. A new "Life" will be born. But all these apps, and indeed zygote itself, are, from the Linux perspective, merely instances of app_process, which renames itself accordingly (and you can verify with an ls -l /proc/pid/exe).
We discuss the internals of both Zygote and system_server in depth in Chapter 8.
Files discussed in this Chapter
| Section | File/Directory | Contains |
|---|---|---|
| adb | Implementation of adb, both client and server | |
USB Debugging Manager server, used by system_server | ||
| vold | The Mount Service Manager, used by system_server | |
| installd | Source of installd | |
| bootanimation | frameworks/base/cmds/bootanimation/BootAnimation.cpp | Bootanimation source |