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 /data partition, though no default services belong to it. In this section, we adopt the service class division, but - since most services are in "main" - further subcategorize by function.

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 init is different than that of Linux, with the most important differences being in its support of System Properties and using a particular set of rc files. Following the explanation of those two features, we piece together the flow of init: its Initialization and Run-Loop.

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 /sbin/init, which read the /etc/inittab file for a description of supported "run-levels", or runtime configurations (single user, multi-user, network file systems, etc), start-up processes, and ctrl-alt-del behavior. Android also uses an "init" binary, but most similarities end with the name. The following table shows the differences:

Linux /sbin/initAndroid /init
Config file/etc/inittab/init.rc and any imported file (commonly init.hardware.rc and init.usb.rc (sometimes init.hardware.usb.rc
Multiple configurationsSupported through the notion of "run-levels" (0: shutdown, 1, single user, 2-3 multi-user, etc). Each "run level" loads scripts from /etc/rcrunlevel.dNo run-levels, but offers configuration options through triggers and system properties
Watchdog functionalityYes: 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 processesYes: /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 PropertiesNo: 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 operationNo: Linux allows only very specific triggers, such as ctrl-alt-del and UPS power events, but does not allow arbitrary triggersYes: /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 ueventsNo: 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:

Table 4-props: Property Files in the Android file system
FileContains
/default.prop
(PROP_PATH_RAMDISK_DEFAULT)
Initial settings. Note this file is part of the initramfs, and not present on the device's flash partitions.
/system/build.prop (PROP_PATH_SYSTEM_BUILD)Settings generated by the Android build process
/system/default.prop
(PROP_PATH_SYSTEM_DEFAULT)
Settings usually added by vendor
/data/local.prop
(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 /data.
/data/properties/*
(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 /init.rc.
 

An additional property file, PROP_PATH_FACTORY (/factory/factory.prop) is #defined but no longer supported. Note the order of loading does matter, since setting the same property a second time will overwrite the previous value (unless the property is marked read-only).

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 /dev/__properties__, and 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 maps /proc entry:

# 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:

Figure 4-propstruct: Property Area memory layout (bionic/libc/bionic/system_properties.c)

The property_service

In order to service write requests, init opens up a dedicated UNIX domain socket - /dev/socket/property_service, which is world-writable (0666), so that any client may connect. It is then up to 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 /property_contexts, and explained in Chapter 21.

 
Table 4-propn: Property namespaces and their permissions
Namespace Owning UID Contains
net.rmnet0
net.gprs
net.ppp
net.qmi
net.ltr
net.cdma
AID_RADIO Network properties, used by rild
gsmGSM related settings
persist.radioPersistent radio settings
net.dnsDNS resolver settings (in loco /etc/resolv.conf)
sys.usb.config@TODO
netAID_SYSTEMAll network settings (including those owned by AID_RADIO)
devAll device settings
runtime@TODO
hwhardware related settings
[persist.]syssystem related settings
[persist.]serviceservice start/stop keys
persist.securitysecurity @TODO
wlanWireless LAN (WiFi) settings
selinuxSecurity Enhanced Linux settings
dhcpAID_SYSTEM
AID_DHCP
DHCP settings
debugAID_SYSTEM
AID_SHELL
Debug settings
logAID_SHELLLogging settings
service.adb.rootAID_SHELLUsed by ADB if running as root
service.adb.tcp.portAID_SHELLUsed by ADB if running over TCP/IP
sys.powerctlAID_SHELLPower Management Control
bluetoothAID_BLUETOOTHBluetooth settings
persist.service.bdroidBluetooth settings @TODO

Special namespace prefixes

init recognizes several special prefixes, which govern how it handles the properties:

  • The persist pseudo-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 ro pseudo-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 ctl prefix: is used to provide a convenient way to control init's services, by setting the ctl.start.service or ctl.stop.service properties (respectively) with the service name. A separate ACL is maintained in the control_perms array, to restrict services by UID/GID. As of KitKat, this list currently defines dumpstate (shell:log) and ril-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 system/core/include/cutils/properties.h:

 
  • 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 /init.rc, and a device-specific /init.hardware.rc, where hardware is obtained from the androidboot.hardware kernel argument, or /proc/cpuinfo. The default emulator hardware, for example, is "goldfish", and it is not uncommon to see /init.goldfish.rc on actual devices as well, probably due to most implementors copying the default filesystem without really paying attention to detail. The original idea might have been to have all Android devices use the same /init.rc, leaving the device-specific file for vendor customizations. In practice one finds quite often that implementors simply add more directives into /init.rc.

As of JB, the only hard-coded rc file is /init.rc, and the import directive is used to include additional rc files explicitly. JB's default /init.rc also includes /init.hardware.rc, (imported as /init.${ro.hardware}.rc, substituting the value of the property), and /init.usb.rc (or /init.${ro.hardware}.rc), which contains the USB related directives for 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)

Table 4-initst: The init boot stages
Init stageContents
early-initVery first stage of initialization. Used for SELinux and OOM settings
initCreates file systems, mount points, and writes kernel variables
early-fsRun just before filesystems are ready to be mounted
fsSpecifies which partitions to load
post-fsCommands to run after filesystems (other than /data) are mounted
post-fs-data/data decrypted (if necessary) and mounted
early-bootRun after property service has been initialized, but before booting rest
bootNormal boot commands
chargerCommands 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 /init.rc alongside reading this section.

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 keywords.h. Colors correspond to different versions:

Table 4-initcs: Init commands
Command syntaxNotes
chdir directoryas cd command (calls chdir(2))
chmod octal_perms fileChange octal_perms masks of file
chown user group fileSame as chown user:group file
chroot directoryas Linux chroot command (calls chroot(2))
class_reset service_classStop/Start all services associated with service_class
class_start service_classStart all services associated with service_class
class_stop service_classStop all services associated with service_class
copy src_file dst_fileSame as cp(1) command
domainname domainnameWrites domainname to /proc/sys/kernel/domainname
exec commandNo longer supported
export variable valueExport environment variable. Will be inherited by all children
hostname hostnameWrites hostnname to /proc/sys/kernel/hostname
ifup interfaceBring up an interface (same as ifconfig interface up)
insmod module.koLoad a kernel module
import filename.rcInclude an additional rc file
load_persist_props(Re)-Load all persistent properties from /data/properties
loglevel levelSet kernel loglevel (printk)
mkdir directoryCreate a directory (calls mkdir(2))
mount fstype fs pointMount a file system of fs_htype from fs on mount point
mount_allMount all file systems in /system/etc/fstab
powerctl shutdown/rebootshutdown/reboot wrapper
[re]start service_nameStart or restart service specified in service block matching service_name
restorecon pathRestore SELinux context for path
rm[dir] filenameRemove a file or directory (calls unlink(2)/rmdir(2), respectively)
setcon SEcontextSet (change) SELinux context. Init uses u:r:init:s0
setenforce [0|1]Toggle SELinux enforcement on/off
setkey table index valueSet key table
setprop key valueSet a system property
setsebool valueSet an SELinux boolean property. value can be 0/false/off or 1/true/on
setrlimit category min maxuse setrlimit(2) system call to enforce process (q.v. ulimit(1)
stop service_nameStop service specified in service block matching service_name
swapon_all..Activate all swap partitions in /system/etc/fstab
symlink target srcCreates a symbolic link (as ln -s - calls symlink(2))
sysclktz tzoffsetSet system clock timezone (using settimeofday(2)
trigger trigger_nameActivate a trigger (causing init to re-run corresponding commands)
wait file timeoutWait up to timeout seconds for file to be created.
write file valueWrites value to file. Same as echo value > file
 

If you look through your /init.rc files, you will likely see these commands used during the various boot stages to perform what one might expect during system startup: Setting up the directory structure, enforcing filesystem permissions, and setting up various kernel parameters via /proc or /sys. Once the boot stages are all defined, the rest of the file will deal with service definitions. As stated, service blocks are modified by options. These provide the parameters by means of which init determines how the services are to be run, and monitored. Table 4-initopts lists the available options.

Table 4-initopts: Init options
Option SyntaxNotes
capabilitySupports Linux capabilities(7) (or at least, will, at some point in the future)
classDefines the service to be part of a service group. Classes can then be manipulated together by the class_[start|stop|reset] commands.
consoleDefines the service as a console service. stdin/stdout/stderr linked to /dev/console.
criticalDefines 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
disabledIndicates service will not be started. Service can still be started manually
groupSpecifies the gid to start the service as. init will call setgid(2) for this.
ioprioSpecifies the I/O priority for the service. init will call ioprio_set
keycodesSpecifies a key chord that can trigger this service. (discussed below)
oneshotTells init to start the service, but not worry about it (that is, ignore its SIGCHLD).
onrestartLists which commands to invoke if/when the service needs to be restarted. This is commonly used to restart dependent services
seclabelSpecifies the SELinux label to apply to this service
setenvSet an environment variable prior to fork()ing and exec()ing the service. Unlike export, this environment variable will only be seen by the service
socketTells 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
userSpecifies 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 /dev/console to stdin/stdout/stderr, and for all others it "zaps" stdio. Though presently unsupported, 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 /system/usr/keylayout) and not the same codes as specified and used by the frameworks (i.e. the codes at frameworks/native/include/android/keycodes.h). The only default service tied to a keychord is 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' /init.hammerhead.rc:

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 dumpstate command will be detailed in Chapter 5; For the moment, however, note the service is disabled, meaning it has to be started manually, and its startup is tied to keycodes 114, 115 and 116. These, as you can verify by /system/usr/keylayout/Generic.kl are mapped to VOLUME_DOWN, VOLUME_UP and POWER, respectively.

For keychords to be supported, /dev/keychord must exist. This is a device node exported by the keychord kernel driver, if the kernel was compiled with 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 ueventd or (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, and sys, and mount them.
  • Touch (open and then close) /dev/.booting. This file is cleared once startup is complete (by check_startup (q.v. figure 4-initst).
  • open_devnull_stdio() "daemonizes" init by linking stdin, stdout and stderr to /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_cmdline reads /proc/cmdline and imports as properties any arguments beginning with androidboot as ro.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 androidboot kernel 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, init proceeds to load /default.prop.
  • init_parse_config_file() is called to parse /init.rc.
  • init enqueues the actions supplied in the init.rc sections (using action_for_each_trigger()) and the built-in actions (queue_builtin_action) on an action_queue. The resulting queue is shown in figure 4-initstfig.
 
Figure 4-initfig: The init boot stages (and built-in commands)
early-init Writes oom_adj, sets SELinux context, starts ueventd wait_for_coldboot_done Blocks until ueventd creates /dev/.coldboot_done mix_hwrng_into_linux_rng Copies entropy from /dev/hw_random (if present) to /dev/urandom. If not, skip keychord_init Opens /dev/keychord for service keycodes console_init Loads logo (/initlogo.rle) on graphics console (fb0) or displays "A N D R O I D" on 40x30 text console (tty0) init ro.bootmode != charger ro.bootmode == charger early-fs fs post-fs post-fs-data mix_hwrng_into_linux_rng Remixes entropy, in case random devices weren't available property_service_init Loads properties from files Initializes /dev/socket/property_service signal_init Create signal socketpair, registers SIGCHLD handler check_startup Verify sockets exist, unlink /dev/.booting ro.bootmode != charger ro.bootmode == charger early-boot charger boot queue_property_triggers Add all property triggers at end of action_queue bootchart_init (#if BOOTCHART) collect boot statistics

Eventually, the main loop iterates through at all the init.rc commands, and 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 /proc file system:

 
Figure 4-initfd: Init's file descriptors, as seen through /proc/1/fd:
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. The property_service code obtains the peer's credentials (using getsockopt(..SO_PEERCRED..)), and performs the permission checks on the property. If it can be set, any triggers or related services (for ctl.start/stop properties) 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 a socketpair, created to handle SIGCHLD from dead offspring. When the signal is received, the sigchld_handler writes data to the other end of the socketpair (signal_fd), making data available on the receiving end, and causing init to call wait_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 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 /ueventd.rc and /ueventd.hardware.rc, where hardware is obtained from /proc/cpuinfo, or the 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, ueventd functions like the traditional Linux udevd: It creates or removes /dev nodes corresponding to the devices.
  • firmware events: ueventd listens 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 /dev/watchdog at regular intervals. If the timeout value passes and 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 /dev/watchdog node, and 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 /init.rc, albeit disabled, and started on demand in /init.usb.rc, when the sys.usb.config property contains "adb" - which is what the well-known "USB Debugging" GUI option activates1:

Listing 4-adbrc: adb definitions in the rc files (KitKat)
# adbd is controlled via property triggers in init..usb.rc
service adbd /sbin/adbd
    class core
    socket adbd stream 660 system system
    disabled
    seclabel u:r:adbd:s0 # As of JB, ADB gets its own SELinux context

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:

Listing 4-adbm: The adb main startup function, showing privilege settings
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 /data/adb/adb-%Y-%m-%d-%H-%M-%S.

The adbd normally uses the /dev/socket/adb UNIX Domain socket, as set up by init, but also uses /dev/android_adb when connecting to the host over USB (i.e. not in the emulator). The latter is a device node which is created by the USB Gadget Driver (discussed in Chapter ###). The 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:

Figure 4-adbarch: The adb Architecture

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 /init.rc, as shown in Listing 4-svcmgrrc:

Listing 4-svcmgrrc: The servicemanager definition in /init.rc
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 /init.rc should make sense - it's not that the services actually require the 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:

Figure 4-hdsf: The flow of healthd
Process commandline Only supports -n: (no publish with servicemanager) healthd_board_init Loads configuration (sysfs file names for battery stats) wakealarm_init Sets timer for periodic chores uevent_init Opens a netlink multicast socket for uevents binder_init Set up binder fd Create/Init BatteryMonitor Construct and then initialize a BatteryMonitor object epoll_wait Set up three descriptors in epoll, and wait for events, or timeout after periodic chores interval handle events periodic_chores

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:

Table 4-hdfdt: The file descriptors held by healthd and their purpose
DescriptorTypePurpose
event_fdNetlinkReads 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_fdTimerFDTimer set to fire every periodic_chores_interval seconds. Upon wakeup, healthd runs periodic_chores.
binder_fd/dev/binderListener 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.

Experiment: Observing 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:

Output 4-hdstr: Using strace(1) on healthd
root@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 (/sys/class/power_supply/*) are standard - in practice they are symbolic links to the specific platform device nodes, which change between devices.

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 /sys/class/power_supply/usb/online (from 1 to 0 on disconnect, or vice versa on connect).

 

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 /dev/log/ files, implemented in kernel ring buffers), in a manner reminiscient of UN*X syslog. The service is defined in /init.rc as follows:

Listing 4-logdrc: The 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 the logd 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 /init.rc as follows:

Listing 4-lmkdrc: 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 /data. Listing 4-voldrc shows the definition of vold:

Listing 4-voldrc: vold definitions in /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 operations
  • NetlinkManager: Responsible for listening on kernel netlink events of the blocksubsystems, by passing them to the volumeManager
  • CommandListener: 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 vdc utility, which you can use to send these commands to vold yourself (as root). The utility is nothing more than a tiny UNIX domain socket client, whose source can be found in system/vold/vdc.c. The commands are shown in table 4-voldc:

 
Table 4-voldc: vold commands
CmdSubcmdArgumentsPurpose
dumpDumps loop, device mapper, and mounted filesystems
volumelistList mounted volumes
debugon|offToggle debug messages for formatting/unmounting
mountpathMount a file system
unmountpath[force[_and_revert]]Unmount a file system, possibly forcefully
[un]shareumsShare/unshare USB Mass Storage
sharedumsReturn share state (enabled/disabled) of USB Mass Storage
mkdirspathMake a directory/mount point
format[wipe] pathFormat a FAT volume, optionally erasing its contents first
storageusersList PIDs using a mounted volume (like fuser)
aseclistList all Android Secure Storage containers
createcid mb fstype key uidCreate new asec with cid, as a filesystem fstype of size mb
destroycid [force]Destroy the asec identified by cid, possibly forcefully
finalizecidFinalize container cid.
fixpermscid gid filenameFix permissions in container cid so as to be owned by gid.
mountcid key uidMount the container cid under app-id uid, with key.
unmountcid [force]Unmount the container cid, possibly forcefully if in use.
pathcidReturn the path to the container cid.
renameold_cid new_cidChange the name of old_cid to new_cid
fspathcidReturn file system path corresponding to cid
obblistList all mounted opaque binary blobs
mountfilename key ownerGidmount the opaque binary blob specified by filename for app ownerGid, with optional key
unmountsource [force]unmount the opaque binary blob specified by source filename
pathsource
xwarpenable/disabletoggle xwarp mirroring
statusreturn status (enabled/disabled)
cryptfsrestartSignal init to restart frameworks
cryptocompleteQuery if filesystem is fully encrypted
enablecryptoinplace|wipe passwordEncrypt filesystem, possibly erasing first
changepwold_passwd new_passwdChange encryption password
checkpwpasswdCheck if supplied password can mount encrypted fs
verifypwpasswdUsed by BackupManagerService
getfieldnameGet metadata field from cryptfs
setfieldname valueSet metadata field in cryptfs
fstrimdotrim

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 screen1, and uses the com.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:

Figure 4-voldch:The interaction between 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 /data (by setting 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 /data is a prerequisite for loading the UI frameworks, since those need to write various files. As a temporary filesystem, however, the /data mount holds no data. When the 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 /data unmountable, and forcing the user to do a recovery/reset). If the 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 /data can be unmounted. If the unmount is successful, vold remounts the (now unecrypted) /data partition, and again updates the 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 /data defined in /init.rc) and then to trigger_restart_framework, which makes init restart the frameworks. The properties must be defined in /init.rc to arm the approriate triggers, as shown in listing 4-initencact:

Listing 4-initencact: Actions in init.rc relating to encryption events
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 /data can be safely unmounted. 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 /init.rc:

Listing 4-netdrc: The 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 with vold, 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:
    CmdSubcommandPurpose
    interfacelistList all interfaces
    route iface default/secondary dest prefix gateway
    fwmark ...
    list_ttysList ttys used by the PPP daemon as interfaces
    ipfwdenable|disable|statusToggle IP Forwarding or query status
    tether
    natenable|disable
    pppdattach 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 ttyExecs /system/bin/pppd -detach
    softapstartap|stopap|statusToggle Access point (by exec()ing or kill()ing /system/bin/hostapd), or query status
    fwreload iface AP|P2P|STA
    set iface SSID hidden/* channel security keySet access point parameters. If any other word but "hidden" is specified, AP will be broadcast
    resolverSee table 4-resolvercCommands to control DNS resolution. See "DNS proxying" below
    bandwidth
    idletimer
    firewall
     
    /dev/socket/mdns MDnsSdListener mdnsd /dev/socket/dnsproxyd DNSProxyListener /dev/socket/netd CommandListener NetlinkManager Uevent socket Route Socket NF Quota Socket Resolver Controller NAT Controller Firewall Controller SecondaryTable Controller Bandwidth Controller Idletimer Controller Interface Controller Tether Controller Softap Controller PPP Controller Clatd Controller pppd clatd hostapd dnsmasqd /proc/sys/net/... /sys/class/net/... iptables/iptables6 command lines Kernel iptables facility
     

    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 iptables command line.

  • NetlinkManager: Responsible for listening on kernel netlink events through three separate sockets:
    • NETLINK_KOBJECT_UEVENT:
    • NETLINK_ROUTE: These events, from the "net" subsystem, notify netd of interface modifications (addition, removal, IP address changes, etc).
    • NETLINK_NFLOG.
    xt_idletimer, qlog, platform, backlight
  • Events broadcast by Netlink are intercepted by its internal NetlinkHandler class, which is responsbible for broadcasting them to clients connected to the /dev/socket/netd (i.e. to the CommandListener)

  • DnsProxyListener: A type of FrameworkListener responsible 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 the mdnsd (discussed later). Supported commands are shown in table 4-mdnssdcmds. All commands also have a stop- counterpart, accepting a request id, which has been omitted for brevity.
    Table 4-mdnssdcmds:
    CmdArgumentsPurpose
    discoverrId svcType..
    registerrId svcName ServiceType port..
    resolverId ifaceName svcName RegType Domain..
    start-serviceStarts the mdnsd daemon
    sethostnamerId hostname..
    getaddrinforId hostname..

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.

Table 4-resolverc:
CmdArgumentsPurpose
 

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, /init.rc defines the mDNS service as follows:

Listing 4-mdnsrc: The 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 /dev/socket/mdnsd (MDNS_UDS_SERVERPATH) for requests, and has another socket (/dev/socket/mdns) connected to netd.

Todo: Mention NsdService

The mDNS implementation is found in the external/mdnsresponder directory. It is largely the same as the open source mDNS project, with the Android specific modifications (such as the UNIX domain socket and Android logging) clearly marked by #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 /init.rc as follows:

Listing 4-mtprc: The 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.

Listing 4-racrc: The 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 /init.rc as follows:

Listing 4-rildrc: The 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 the debugCallback of 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 IssuedFunction
    0RIL_REQUEST_RESET_RADIOReset the radio
    1RIL_REQUEST_RADIO_POWERPower the radio off
    2RIL_UNSOL_RESPONSE_VOICE_NETWORK_STATE_CHANGEDCalls voice network change callback
    3RIL_REQUEST_OEM_HOOK_RAWEnable the QXDM log
    4Disable the QXDM log
    5RIL_REQUEST_RADIO_POWER
    RIL_REQUEST_SET_NETWORK_SELECTION_AUTOMATIC
    Power the radio on
    6RIL_REQUEST_SETUP_DATA_CALLSet up a data call
    7RIL_REQUEST_DEACTIVATE_DATA_CALLDeactivate (tear down) a data call
    8RIL_REQUEST_DIALDial a number
    9RIL_REQUEST_ANSWERAnswer an incoming call
    10RIL_REQUEST_HANGUPDisconnectr an incoming call

The daemon also has its own debug facility - radio - which results in a dedicated logging device - /dev/log/radio. Inspecting this log file using 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.

Figure 4-rildarch: A bird's eye view of the Radio Interface Layer architecture

If you look through the rild source, you might be suprised to find it consists of merely two files: the main rild.c and radiooptions.c. Most of the code is in 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 is a simple one:

Listing 4-sfrc:surfaceflinger 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.


* - As of Android L, a third flinger - 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 thus:

Listing 4-banimrc:bootanimation definition 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 the vold.decrypt property is set, indicating the file system is encrypted.
  • /data/local/bootanimation.zip: Allowing the (advanced) user to drop their own animation file via adb 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 - android-logo-[mask|shine].png, both hidden in the /assets/images folder in the /system/framework/framework-res.apk (you can easily see the png files yourself if you pull the framework-res.apk to your host and unzip the file).

Most device vendors will provide a bootanimation.zip with their logo or, in some cases, the carrier's logo. Likewise cyanogen and other Android "mods" deploy a zip of their own. Such zip files must contain a desc.txt, and an assortment of images which 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 /system/bin/samsungani and proprietary qmg files). Alternatively, they may change the implementation to look at other directories (e.g. HTC One M8, looking for hTC_bootup_one.zip and vendor_boot.zip in /system/customize/resource). The Kindle's "FireOS" is somewhere in between, retaining the bootanimation binary, but modifying it to display the "Kindle Fire" logo rather than that of Android. Like most of the default binaries in the Android build, however, your device is very likely to contain the binary, even if it is unused. A quick way to figure out what files bootanimation is using is shown in the following experiment:

Experiment: Unpacking bootanimation.zip on a Nexus 5

The Nexus 5's boot animation is in /system/media/bootanimation.zip. You can pull it to a host using adb, and inspect its contents like so:

Output 4-ba: bootanimation.zip example

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 desc.txt specifies, in order, the width, height and frames-per-second (fps) of the boot animation. As shown above, this is consistent with the dimensions of the individual .png files.

The book's website contains miscellaneous boot animations you can experiment with on your device. Try dropping them into /data/local (searched before /system/media and see how easy it is to replace the boot animation. When you do, make sure the zip file is readable (if running bootanimation from adb as user shell), or else the animation will default to the "A N D R O I D" console text.

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 (/dev/graphics/fb0) directly, which is why is is run under uid graphics (the owner of the device node). We will take a close look at the low level graphics calls in Chapter 14.

Using low level calls and direct write operations to the framebuffer also enables 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 init.rc as follows:

Listing 4-mediasrvrc: mediaserver definitions in /init.rc
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:

Table 4-mediasrv: The mediaserver services
ServiceServiceManager nameProvides
AudioFlingermedia.audio_flingerAudio playback. The service gets one or more PCM audio streams as input, and "flings" them into a merged stream.
AudioPolicyServicemedia.audio_policyAudio policy. The policy tells the AudioFlinger what the volume setting is, as well as which device is the target audio device
CameraServicemedia.cameraCamera services. Its main client is the camera app, whether the Android supplied one, or the vendor's
MediaPlayerServicemedia.playerPlaying 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 /init.rc like so:

Listing 4-drmsrvrc: drmserver definitions in /init.rc
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 /vendor/lib/drm, and /system/lib/drm. The 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.

Figure 4-drmlibs: Viewing DRM plugins on a Galaxy S3
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 IDrmEngine.h. The DRM message flow is shown in Figure 4-drmflow:

Figure 4-drmflow: The flow of DRM calls from frameworks to engine

The sources also include two plug-ins in frameworks/av/drm/libdrmframework/plugins: A sample "passthrough" DRM module, which can be used as a point of departure for plug-ins, and a "forward-lock" plug-in, implementing OMA V1 Forward Locking.

 

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 .apk directly, via Google Play or via 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 /init.rc service definition:

Listing 4-installdrc: installd definition in /init.rc
service installd /system/bin/installd
    class main
    socket installd stream 600 system system

Startup

startup of installd proceeds as shown in Figure s-ins:

Figure s-ins: Startup of the installd daemon:
initialize_globals Read environment variables and deduce directory structure from them initialize_directories Set up directory structure, migrate to multi-user drop_privileges Give up UID/GID 0, but maintain some capabilities android_get_control_socket Acquire socket from init listen Put into listening mode, backlog of 5 accept Block until a client requests a connection readx Read a request from a client execute Fulfill client's bidding, no questions asked

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 init to be /data. To this base, installd appends APP_SUBDIR (app/), PRIVATE_APP_SUBDIR (app-private/), APP_LIB_SUBDIR (app-lib) and MEDIA_SUBDIR (media/).

installd also obtains two other environment variables - ANDROID_ROOT (pointing to /system) and ASEC_MOUNTPOINT (pointing to /data/asec). Once it has deduced its directory structure, it proceeds to initialize the directories - making sure they exist (as /data initially starts up empty on factory default). As of Jellybean, 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 ownership system:system and mode rwx--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 are media:media rwxrwx---.

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 (/dev/socket/installd), and enters an accept loop, waiting for connections from client. Once a client forms a connection, an inner read/execute loop handles this connection until it closes (meaning that 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:

Table 4-instdcmds: Installd commands
CommandArgumentsUse
pingNull command, used for connectivity
installpkgname uid gid seinfoInstall package specified by pkgname under uid/gid with SELinux context specified by seinfo
dexoptapk_path uid is_publicOptimize dex file of APK, creating an .odex file
movedexsrc dstRename DEX file specified by src to dst.
rmdexpkgRemove DEX file of package specified by pkg
removepkgname, userid Remove package specified by pkg installed under uid.
renameoldname newnameRename package from oldname to newname
fixuidpkgname uid gid Fix package pkgname so it is owned by uid:gid
freecachefree_sizeFree cache so it has free_size bytes left.
rmcachepkgname uidRemove package pkgname owned by uid from cache.
getsizepkgdir uid apkpathReturn the size of the directory specified by apkpath
rmuserdatapkgname uidRemove user data used by package pkgname owned by uid.
movefilesExecute scripts in /system/etc/updatecmds
linklibpkgname asecLibDir uidLink native library to its real location
mkuserdatapkgname uid userid Creates data directory for package (owned by id for user userid), and installs symlinks
rmuseruidRemove 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 /init.rc as follows:

service keystore /system/bin/keystore /data/misc/keystore
    class main
    user keystore
    group keystore drmrpc

The argument to the keystore daemon - /data/misc/keystore - is the directory used to hold the various keystore files. As of Jellybean, each user has its own keystore directory, with the primary user using /data/misc/keystore/0.

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.

Table 4-keystorecmds: keystore commands, exported by java.security.KeyStore
0test() Test keystore daemon is active
1 byte[] get(String name) Get value corresponding to name
2insert(String name,
byte[] value,
int uid,
int flags)
Insert a name/value combination into keystore belonging to uid, with flags
3int del(String name, int uid)Delete name (and value) from keystore belonging to uid
4exist(String name, int uid)Check if name exists in keystore belonging to uid
5saw(String prefix, int uid)List all keys beginning with prefix in uid's keystore
6reset()Reset (wipe) keystore
7password(String password)Change keystore password to password
8lock()Lock keystore, requiring password to unlock
9unlock(String password)Unlock previously locked keystore by supplying password.
10zero()Check if keystore is empty.
11generate(String name,
int uid, int keyType,
int keySize, int flags,
byte[][] args)
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.
12import_key(String name,
byte[] data, int uid,
int flags)
Import key specified in data blob into keystore owned by uid into key name.
13byte[] sign(String name,
byte[] data)
sign data with key corresponding to name without actually retrieving key.
14verify(String name,
byte[] data,
byte[] signature)
Verify signature on data using key specified by name
15byte[] get_pubkey(String name)Get a public key associated with name
16del_key(String name, int uid)Delete key identified by name in keystore belonging to uid
17grant(String name, int uid) Grant uid access to key name
18ungrant(String name, int uid) Revoke access to key name to uid
19long getmtime(String name)Get modification time of name
20duplicate(String srcKey,
int srcUid, String destKey, int destUid)
Copy the key specified by srcKey in keystore belonging to srcUid to keystore owned by destUid under name destKey.
21is_hardware_backed(String keyType)Return integer specifying whether or not keyType is backed by a hardware keystore implementation
22clear_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:

Table 4-dbgrsig: Signals caught by debuggerd
SignalFull NameMeaning
ILLIllegal InstructionIllegal machine opcode
TRAPDebugger TrapBreakpoint
ABRTVoluntary AbortAssertion failure
BUSBus ErrorMMU fault
FPEFloating Point ExceptionDivision by zero
SEGVSegmentation ViolationNULL pointer dereference
PIPEBroken PipeTermination 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 /data/tombstones, and are discussed in detail in Chapter6.

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 sdcard daemon provides the user-mode support.

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 /init.rc definition is as follows:

Listing s-zyrc: Zygote definitions in /init.rc
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 (/dev/socket/zygote) to listen for incoming requests. When such requests will arrive, they will contain a class name to load, and Zygote will similarly 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

SectionFile/DirectoryContains
adbsystem/core/adb/Implementation of adb, both client and server
frameworks/base/services/java/com/android/server/usb/UsbDebuggingManager.javaUSB Debugging Manager server, used by system_server
voldbase/services/java/com/android/server/MountService.javaThe Mount Service Manager, used by system_server
installdframeworks/native/cmds/installdSource of installd
bootanimationframeworks/base/cmds/bootanimation/BootAnimation.cppBootanimation source

Questions to Reviewer:

  • Do you agree with the service category breakdown I used
  • Would it be better to break this chapter right before "Core Services", so that all the services go into their own chapter? I already use a separate chapter for framework services (the next one), and doing a split would break a ~27page chapter to 13 + 14. Note that my pages are substantially denser than the standard (I base on A4, no header/footer).
  • References

    [1] Android Explorations: http://nelenkov.blogspot.com/2013/02/secure-usb-debugging-in-android-422.html [2] Android Explorations: http://nelenkov.blogspot.com/2012/08/changing-androids-disk-encryption.html
    http://source.android.com/devices/tech/encryption/android_crypto_implementation.html