This chapter is an overview that provides context for the rest of the book. It will present three different, high-level views of the Android system. It is divided into two parts, a conceptual section followed by a practical section.
The conceptual half of the chapter explores Android, metaphorically, from the side. This section models Android as a geologist might, as a stack of strata. Each layer in the model has its own significance, and each supports the layer above it. The exploration will reveal most of the major components of the Android system as well as several key architectural structures.
The second half of the chapter—the practical half—explores the AOSP source. It addresses acquiring the code, its organization, its customization, and some of the tools used to maintain it.
Putting Android in Its Place
You have almost certainly seen one of the many illustrations of Android as a layer cake before. Figure 3.1 is another version.
Figure 3.1 Android Layer Cake
Figure 3.1 shows the Android system in its context. The left half of the figure could be a generic Linux distribution such as Ubuntu or Mint. The right half illustrates the components specific to Android.
In the model, the system is divided into four major layers, each based on the one underneath it. Starting at the bottom, they are:
Hardware: This is the physical device; a piece of metal you can hold in your hand.
Linux Kernel: The kernel is software that creates the process and memory environments in which all applications run. As noted in Chapter 1, Android uses the very popular Linux kernel.
System Libraries: System libraries are software libraries, frequently supplied as opaque binaries by third-party providers that implement services used by multiple applications.
Applications and System Services: Applications use the environment provided by the lower layers to do work useful to the user. A set of special, long-running applications, called system services or sometimes “daemons,” perform periodic tasks and manage system state.
In this model, most Android developers work predominantly in the block labeled “Android Applications” at the top right of the figure. They develop the applications that run in the Android environment. These developers spend their time learning and using the environment provided by the surrounding Android system.
The rest of this book is about customizing Android itself to adapt it to new hardware. Its focus is the shaded areas outlined with the bold border, most of which is in the System Services layer of Figure 3.1. Before launching into the discussion of the Android-specific components, though, reviewing the rest of the environment in which those components run will be useful.
In this model, the bottom of the stack is the hardware. The hardware is the motivation: some device with cool new features. It is the part that you, the reader, bring to the party. The purpose of the entire software stack above the hardware is to enable the creation of applications that provide a human user with appropriate and intuitive control of some new, innovative hardware features.
Designing and bringing up hardware are dark arts that are well outside the scope of this book. Roger Ye’s book, Embedded Programming with Android (Ye, 2016), part of the Android Deep Dive Series, is an excellent introduction to “bare-metal” programming and the process of bringing up a device from scratch.
The Linux Kernel
As noted in Chapter 1, Android’s kernel is a variant of the Linux kernel. Linux is wildly popular, especially in embedded devices because it is open source and fairly easy to adapt to new hardware. Chapter 4 discusses it again in more detail.
The kernel is the Android system’s primary porting layer. The process of porting Android to a new device consists in large part of getting an Android variant of the Linux kernel running on the target device and then pulling the rest of the Android stack over on top of it.
Although a kernel is a key part of Android, building a kernel is not normally part of building an Android system. Instead, the Android build tools treat the kernel as a black box: a pre-built, third-party binary. As Chapter 4 will illustrate, it is a device developer’s responsibility to assemble the working kernel and to provide it for inclusion into the device-installable file system image that the Android build system creates.
You might be surprised to hear that the kernel is Android’s primary porting layer. Much of Android programming is done in the Java programming language, whose motto was “write once, run anywhere.” For Android, however, Java source code is compiled into instructions for Dalvik and ART, not instructions for the Java Virtual Machine (JVM). The purpose of the Android VM is to make applications portable across Android devices. Unlike the JVM, multi-platform and OS portability was not a goal at all in the design of Android’s virtual machine.
There are common capabilities and functions that are used by multiple applications. Some of these capabilities—cryptography, video and audio rendering, a web browser—are large and complex. Implementing them as libraries that can be used by multiple applications makes sense.
Most system libraries are included in the Android system, as is the kernel, as third-party, black box binaries. They are essential to Android but are neither built nor maintained as part of the Android source tree.
This is an important area of customization for the developer of a new device. If software already exists—and that definitely includes software written in a language other than Java—that can be used as part of the interface to a new device, it can be included as part of this layer.
Enterprises that already have extensive investments in software may be, quite understandably, reluctant to abandon that investment simply to move to the Android platform. Including the proprietary code as a system library and then plumbing it into the Android framework with Java bindings may preserve the investment at a reasonable cost.
One of the system libraries that is part of a standard Android system and that deserves particular attention is Bionic. Bionic is Android’s equivalent of the standard C library.
Most applications do not request kernel services directly. Instead, they use a standard library, the C library, to request them. The standard C library interface was specified originally as part of the ANSI C standard and subsequently accepted by ISO. The widely established POSIX C library standard is an incremental superset.
The C library standards are expressed as C header files (.h) against which applications are compiled. These header files define symbols that allow a compiler to emit code for macros, constants, variables, and functions in the application source code. Building an application requires only the header files. The actual implementations of the C library are not included in the compiled application.
Instead, at run time, a linker binds the compiled application dynamically to the implementation of the C library that is present on the system on which the application is running. Because the API definitions against which the application was compiled are (with luck!) identical to those actually implemented by the host system library, everything works. Usually, this just means that the C library is compiled using the exact same header files that the library clients use when they are compiled. A given piece of code can be binary compatible across multiple, similar platforms, as long as all the platforms have C libraries that implement the exact same API.
Bionic is Android’s version of the standard C library. As part of Android’s ongoing battle for frugality, it has been pruned relentlessly and is dramatically smaller than its BSD ancestors. It is so small, in fact, that it does not meet even the ANSI standard, let alone the ubiquitous POSIX standard. Applications that run perfectly well on other platforms may not run at all on Android, because Bionic does not support the functionality they require.
The implication that Bionic has its roots in BSD may come as a surprise. Most operating systems based on a Linux kernel use a version of the GNU C library, glibc. Instead, Bionic is derived from the BSD UNIX, libc. There are several reasons for this but the most obvious is that libc is licensed under the BSD license and is thus free of the constraints that its LGPL licensing imposes on glibc. One of the goals in Android’s design was to eliminate any possible impediment to its acceptance. That goal absolutely implies removing any possible licensing constraints.
Many existing applications, libraries, and utilities may be used on Android as long as they are recompiled against the Android platform. Obviously, some limits exist because of the extent of the pruning in Bionic. For example, existing code that uses System V IPC or certain functions in the kernel concurrency feature, pthreads, cannot be built or run on Android. Additionally, as this chapter will make clearer, the Android software stack is fundamentally different than a typical Linux stack, which sometimes makes the use of existing Linux programs difficult or impossible.
Augmenting an Android system by adding one of the standard C-library implementations is entirely feasible. This simple and very common augmentation makes it possible to run many standard Unix applications and libraries on an Android system.
System services are those special applications, typically with minimal user interfaces, that maintain various subsystems: indexing files for search, locating and connecting to Wi-Fi networks, mounting and unmounting removable media, and so on. One special system service, init, is the first application run on system startup. It is responsible for much of the system startup process, including bringing up the other system services. It is discussed in more detail in Chapter 6.
The Android system service environment is not the same as that on typical Linux systems. On a Linux system, running the command:
produces a list of currently running applications. On one of these systems, running this command will typically result in tens of lines of output describing many processes. Even if the system has just been finished booting and is not yet running many user applications, there are still likely to be quite a few applications running. These are the system services: the long running “daemons.”
Comparing the list of system services for a generic Linux system with a similar list from an Android system is instructive. Although the 30 to 40 system services that run as part of most common Linux distributions are fairly similar, the overlap with those running on an Android system is relatively small. The Android system has unfamiliar system services such as installd, rild, surfaceflinger, and vold instead of more common services such as udevd and syslogd. Although Chapter 6 addresses some of these daemons in a bit more detail, in-depth discussion of the differences between the daemons in the Android universe and those in the standard Linux universe are not the focus of this book. Linux daemons are well documented elsewhere. Although slightly dated, Karim Yaghmour’s excellent book Embedded Android (Yaghmour, 2013) is a fantastic resource for work in this area.
The Android Framework
Figure 3.1 illustrates some of the Android-specific components of an Android system and their positions within the broader model of a working Linux system. The right side of the figure shows the Android framework in relation to the larger OS. Android has components that operate within each of the layers of the system model. From the bottom up, the components of the Android system are
Binder and other kernel plug-ins: Android requires several non-standard kernel
capabilities to function. These extensions are implemented as standard kernel extension modules. Chief among these extensions is Binder. Binder is an Interprocess Communi-cations service and is, perhaps, the heart of Android.
HAL: The Hardware Abstraction Layer (HAL) is a system library that supports binary compatibility for the Android system across multiple hardware/driver platforms. The HAL, actually a group of libraries, serves as the interface between Android and certain generic types of hardware devices. Like the C library, the HAL is, essentially, a set of header (.h) files that define an API for each of several common categories of hardware. The HAL abstracts an interface between the underlying hardware and Android almost exactly as the C library abstracts the interface to the kernel and other common functionality. The HAL has evolved over Android’s lifetime. Newer versions of Android combine a library and a daemon/service to push the abstraction even farther. Most HAL code is written in C or C++ and compiled to device native binaries. Chapters 8 and 10 cover this in greater detail.
Dalvik, ART, and the Native Libraries: These are the special system libraries that comprise the virtual machine and runtime environment in which Android applications execute. ART (and Dalvik, which it replaced) are Android’s analog of the Java virtual machine and the libraries it provides. Both the runtime and many of the libraries that support it are written in C/C++ and compiled to native code for the device. Above this layer, however, nearly all source code is written in Java and compiled to virtual instructions. Chapter 7 discusses ART and Dalvik.
Android API Libraries: These are system libraries written in Java, compiled to dex virtual machine code, and translated to near-machine code (.oat files) during installation. They are bound to Android applications at runtime, almost exactly as the C Library is bound to native applications. The code they contain, though, cannot be executed without the help of the virtual machine and its runtime environment. These libraries are the APIs to Android services.
Android Services: The Android analog of a system service, these privileged Android applications written in Java provide essential Android functionality. The Zygote service, especially, plays a key role in an Android system. Zygote is covered in Chapter 6. The Android service model is covered in the next section and seen again in Chapters 10 and 11 as part of the binderized HAL.
Android Applications: These applications are developed for Android, compiled against the Android API, and run within the runtime environment. Building an Android application is unlike developing applications either for other varieties of Linux or for other mobile platforms. Many other resources are available to a developer building an application. Application building is discussed only peripherally in this book.