0% found this document useful (0 votes)
11 views49 pages

Dispensa Android

Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
11 views49 pages

Dispensa Android

Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 49

802 CASE STUDY 1: UNIX, LINUX, AND ANDROID CHAP.

10

from being stored in unencrypted form anywhere in the system. If the password is
correct, the login program looks in /etc/passwd to see the name of the user’s pre-
ferred shell, possibly bash, but possibly some other shell such as csh or ksh. The
login program then uses setuid and setgid to give itself the user’s UID and GID
(remember, it started out as SETUID root). Then it opens the keyboard for stan-
dard input (file descriptor 0), the screen for standard output (file descriptor 1), and
the screen for standard error (file descriptor 2). Finally, it executes the preferred
shell, thus terminating itself.
At this point the preferred shell is running with the correct UID and GID and
standard input, output, and error all set to their default devices. All processes that it
forks off (i.e., commands typed by the user) automatically inherit the shell’s UID
and GID, so they also will have the correct owner and group. All files they create
also get these values.
When any process attempts to open a file, the system first checks the protec-
tion bits in the file’s i-node against the caller’s effective UID and effective GID to
see if the access is permitted. If so, the file is opened and a file descriptor returned.
If not, the file is not opened and −1 is returned. No checks are made on subsequent
read or write calls. As a consequence, if the protection mode changes after a file is
already open, the new mode will not affect processes that already have the file
open.
The Linux security model and its implementation are essentially the same as in
most other traditional UNIX systems.

10.8 ANDROID
Android is a relatively new operating system designed to run on mobile de-
vices. It is based on the Linux kernel—Android introduces only a few new con-
cepts to the Linux kernel itself, using most of the Linux facilities you are already
familiar with (processes, user IDs, virtual memory, file systems, scheduling, etc.)
in sometimes very different ways than they were originally intended.
In the five years since its introduction, Android has grown to be one of the
most widely used smartphone operating systems. Its popularity has ridden the ex-
plosion of smartphones, and it is freely available for manufacturers of mobile de-
vices to use in their products. It is also an open-source platform, making it cus-
tomizable to a diverse variety of devices. It is popular not only for consumer-
centric devices where its third-party application ecosystem is advantageous (such
as tablets, televisions, game systems, and media players), but is increasingly used
as the embedded OS for dedicated devices that need a graphical user interface
(GUI) such as VOIP phones, smart watches, automotive dashboards, medical de-
vices, and home appliances.
A large amount of the Android operating system is written in a high-level lan-
guage, the Java programming language. The kernel and a large number of low-
SEC. 10.8 ANDROID 803

level libraries are written in C and C++. However a large amount of the system is
written in Java and, but for some small exceptions, the entire application API is
written and published in Java as well. The parts of Android written in Java tend to
follow a very object-oriented design as encouraged by that language.

10.8.1 Android and Google

Android is an unusual operating system in the way it combines open-source


code with closed-source third-party applications. The open-source part of Android
is called the Android Open Source Project (AOSP) and is completely open and
free to be used and modified by anyone.
An important goal of Android is to support a rich third-party application envi-
ronment, which requires having a stable implementation and API for applications
to run against. However, in an open-source world where every device manufac-
turer can customize the platform however it wants, compatibility issues quickly
arise. There needs to be some way to control this conflict.
Part of the solution to this for Android is the CDD (Compatibility Definition
Document), which describes the ways Android must behave to be compatible with
third party applications. This document by itself describes what is required to be a
compatible Android device. Without some way to enforce such compatibility, how-
ever, it will often be ignored; there needs to be some additional mechanism to do
this.
Android solves this by allowing additional proprietary services to be created
on top of the open-source platform, providing (typically cloud-based) services that
the platform cannot itself implement. Since these services are proprietary, they can
restrict which devices are allowed to include them, thus requiring CDD compatibil-
ity of those devices.
Google implemented Android to be able to support a wide variety of propri-
etary cloud services, with Google’s extensive set of services being representative
cases: Gmail, calendar and contacts sync, cloud-to-device messaging, and many
other services, some visible to the user, some not. When it comes to offering com-
patible apps, the most important service is Google Play.
Google Play is Google’s online store for Android apps. Generally when devel-
opers create Android applications, they will publish with Google Play. Since
Google Play (or any other application store) is the channel through which applica-
tions are delivered to an Android device, that proprietary service is responsible for
ensuring that applications will work on the devices it delivers them to.
Google Play uses two main mechanisms to ensure compatibility. The first and
most important is requiring that any device shipping with it must be a compatible
Android device as per the CDD. This ensures a baseline of behavior across all de-
vices. In addition, Google Play must know about any features of a device that an
application requires (such as there being a GPS for performing mapping naviga-
tion) so the application is not made available on devices that lack those features.
804 CASE STUDY 1: UNIX, LINUX, AND ANDROID CHAP. 10

10.8.2 History of Android

Google developed Android in the mid-2000s, after acquiring Android as a


startup company early in its development. Nearly all the development of the
Android platform that exists today was done under Google’s management.

Early Development

Android, Inc. was a software company founded to build software to create


smarter mobile devices. Originally looking at cameras, the vision soon switched to
smartphones due to their larger potential market. That initial goal grew to ad-
dressing the then-current difficulty in developing for mobile devices, by bringing
to them an open platform built on top of Linux that could be widely used.
During this time, prototypes for the platform’s user interface were imple-
mented to demonstrate the ideas behind it. The platform itself was targeting three
key languages, JavaScript, Java, and C++, in order to support a rich application-de-
velopment environment.
Google acquired Android in July 2005, providing the necessary resources and
cloud-service support to continue Android development as a complete product. A
fairly small group of engineers worked closely together during this time, starting to
develop the core infrastructure for the platform and foundations for higher-level
application development.
In early 2006, a significant shift in plan was made: instead of supporting multi-
ple programming languages, the platform would focus entirely on the Java pro-
gramming language for its application development. This was a difficult change,
as the original multilanguage approach superficially kept everyone happy with ‘‘the
best of all worlds’’; focusing on one language felt like a step backward to engineers
who preferred other languages.
Trying to make everyone happy, however, can easily make nobody happy.
Building out three different sets of language APIs would have required much more
effort than focusing on a single language, greatly reducing the quality of each one.
The decision to focus on the Java language was critical for the ultimate quality of
the platform and the development team’s ability to meet important deadlines.
As development progressed, the Android platform was developed closely with
the applications that would ultimately ship on top of it. Google already had a wide
variety of services—including Gmail, Maps, Calendar, YouTube, and of course
Search—that would be delivered on top of Android. Knowledge gained from im-
plementing these applications on top of the early platform was fed back into its de-
sign. This iterative process with the applications allowed many design flaws in the
platform to be addressed early in its development.
Most of the early application development was done with little of the underly-
ing platform actually available to the developers. The platform was usually run-
ning all inside one process, through a ‘‘simulator’’ that ran all of the system and
SEC. 10.8 ANDROID 805

applications as a single process on a host computer. In fact there are still some
remnants of this old implementation around today, with things like the Applica-
tion.onTerminate method still in the SDK (Software Development Kit), which
Android programmers use to write applications.
In June 2006, two hardware devices were selected as software-development
targets for planned products. The first, code-named ‘‘Sooner,’’ was based on an
existing smartphone with a QWERTY keyboard and screen without touch input.
The goal of this device was to get an initial product out as soon as possible, by
leveraging existing hardware. The second target device, code-named ‘‘Dream,’’
was designed specifically for Android, to run it as fully envisioned. It included a
large (for that time) touch screen, slide-out QWERTY keyboard, 3G radio (for fast-
er web browsing), accelerometer, GPS and compass (to support Google Maps), etc.
As the software schedule came better into focus, it became clear that the two
hardware schedules did not make sense. By the time it was possible to release
Sooner, that hardware would be well out of date, and the effort put on Sooner was
pushing out the more important Dream device. To address this, it was decided to
drop Sooner as a target device (though development on that hardware continued for
some time until the newer hardware was ready) and focus entirely on Dream.

Android 1.0

The first public availability of the Android platform was a preview SDK re-
leased in November 2007. This consisted of a hardware device emulator running a
full Android device system image and core applications, API documentation, and a
development environment. At this point the core design and implementation were
in place, and in most ways closely resembled the modern Android system architec-
ture we will be discussing. The announcement included video demos of the plat-
form running on top of both the Sooner and Dream hardware.
Early development of Android had been done under a series of quarterly demo
milestones to drive and show continued process. The SDK release was the first
more formal release for the platform. It required taking all the pieces that had been
put together so far for application development, cleaning them up, documenting
them, and creating a cohesive development environment for third-party developers.
Development now proceeded along two tracks: taking in feedback about the
SDK to further refine and finalize APIs, and finishing and stabilizing the imple-
mentation needed to ship the Dream device. A number of public updates to the
SDK occurred during this time, culminating in a 0.9 release in August 2008 that
contained the nearly final APIs.
The platform itself had been going through rapid development, and in the
spring of 2008 the focus was shifting to stabilization so that Dream could ship.
Android at this point contained a large amount of code that had never been shipped
as a commercial product, all the way from parts of the C library, through the
Dalvik interpreter (which runs the apps), system, and applications.
806 CASE STUDY 1: UNIX, LINUX, AND ANDROID CHAP. 10

Android also contained quite a few novel design ideas that had never been
done before, and it was not clear how they would pan out. This all needed to come
together as a stable product, and the team spent a few nail-biting months wonder-
ing if all of this stuff would actually come together and work as intended.
Finally, in August 2008, the software was stable and ready to ship. Builds
went to the factory and started being flashed onto devices. In September Android
1.0 was launched on the Dream device, now called the T-Mobile G1.

Continued Development

After Android’s 1.0 release, development continued at a rapid pace. There


were about 15 major updates to the platform over the following 5 years, adding a
large variety of new features and improvements from the initial 1.0 release.
The original Compatibility Definition Document basically allowed only for
compatible devices that were very much like the T-Mobile G1. Over the following
years, the range of compatible devices would greatly expand. Key points of this
process were:
1. During 2009, Android versions 1.5 through 2.0 introduced a soft
keyboard to remove a requirement for a physical keyboard, much
more extensive screen support (both size and pixel density) for lower-
end QVGA devices and new larger and higher density devices like the
WVGA Motorola Droid, and a new ‘‘system feature’’ facility for de-
vices to report what hardware features they support and applications
to indicate which hardware features they require. The latter is the key
mechanism Google Play uses to determine application compatibility
with a specific device.
2. During 2011, Android versions 3.0 through 4.0 introduced new core
support in the platform for 10-inch and larger tablets; the core plat-
form now fully supported device screen sizes everywhere from small
QVGA phones, through smartphones and larger ‘‘phablets,’’ 7-inch
tablets and larger tablets to beyond 10 inches.
3. As the platform provided built-in support for more diverse hardware,
not only larger screens but also nontouch devices with or without a
mouse, many more types of Android devices appeared. This included
TV devices such as Google TV, gaming devices, notebooks, cameras,
etc.
Significant development work also went into something not as visible: a
cleaner separation of Google’s proprietary services from the Android open-source
platform.
For Android 1.0, significant work had been put into having a clean third-party
application API and an open-source platform with no dependencies on proprietary
SEC. 10.8 ANDROID 807

Google code. However, the implementation of Google’s proprietary code was


often not yet cleaned up, having dependencies on internal parts of the platform.
Often the platform did not even have facilities that Google’s proprietary code need-
ed in order to integrate well with it. A series of projects were soon undertaken to
address these issues:
1. In 2009, Android version 2.0 introduced an architecture for third par-
ties to plug their own sync adapters into platform APIs like the con-
tacts database. Google’s code for syncing various data moved to this
well-defined SDK API.
2. In 2010, Android version 2.2 included work on the internal design
and implementation of Google’s proprietary code. This ‘‘great
unbundling’’ cleanly implemented many core Google services, from
delivering cloud-based system software updates to ‘‘cloud-to-device
messaging’’ and other background services, so that they could be de-
livered and updated separately from the platform.
3. In 2012, a new Google Play services application was delivered to de-
vices, containing updated and new features for Google’s proprietary
nonapplication services. This was the outgrowth of the unbundling
work in 2010, allowing proprietary APIs such as cloud-to-device mes-
saging and maps to be fully delivered and updated by Google.

10.8.3 Design Goals

A number of key design goals for the Android platform evolved during its de-
velopment:

1. Provide a complete open-source platform for mobile devices. The


open-source part of Android is a bottom-to-top operating system
stack, including a variety of applications, that can ship as a complete
product.
2. Strongly support proprietary third-party applications with a robust
and stable API. As previously discussed, it is challenging to maintain
a platform that is both truly open-source and also stable enough for
proprietary third-party applications. Android uses a mix of technical
solutions (specifying a very well-defined SDK and division between
public APIs and internal implementation) and policy requirements
(through the CDD) to address this.
3. Allow all third-party applications, including those from Google, to
compete on a level playing field. The Android open source code is
808 CASE STUDY 1: UNIX, LINUX, AND ANDROID CHAP. 10

designed to be neutral as much as possible to the higher-level system


features built on top of it, from access to cloud services (such as data
sync or cloud-to-device messaging APIs), to libraries (such as
Google’s mapping library) and rich services like application stores.
4. Provide an application security model in which users do not have to
deeply trust third-party applications. The operating system must pro-
tect the user from misbehavior of applications, not only buggy appli-
cations that can cause it to crash, but more subtle misuse of the device
and the user’s data on it. The less users need to trust applications, the
more freedom they have to try out and install them.
5. Support typical mobile user interaction: spending short amounts of
time in many apps. The mobile experience tends to involve brief
interactions with applications: glancing at new received email, receiv-
ing and sending an SMS message or IM, going to contacts to place a
call, etc. The system needs to optimize for these cases with fast app
launch and switch times; the goal for Android has generally been 200
msec to cold start a basic application up to the point of showing a full
interactive UI.
6. Manage application processes for users, simplifying the user experi-
ence around applications so that users do not have to worry about
closing applications when done with them. Mobile devices also tend
to run without the swap space that allows operating systems to fail
more gracefully when the current set of running applications requires
more RAM than is physically available. To address both of these re-
quirements, the system needs to take a more proactive stance about
managing processes and deciding when they should be started and
stopped.
7. Encourage applications to interoperate and collaborate in rich and
secure ways. Mobile applications are in some ways a return back to
shell commands: rather than the increasingly large monolithic design
of desktop applications, they are targeted and focused for specific
needs. To help support this, the operating system should provide new
types of facilities for these applications to collaborate together to cre-
ate a larger whole.
8. Create a full general-purpose operating system. Mobile devices are a
new expression of general purpose computing, not something simpler
than our traditional desktop operating systems. Android’s design
should be rich enough that it can grow to be at least as capable as a
traditional operating system.
SEC. 10.8 ANDROID 809

10.8.4 Android Architecture

Android is built on top of the standard Linux kernel, with only a few signifi-
cant extensions to the kernel itself that will be discussed later. Once in user space,
however, its implementation is quite different from a traditional Linux distribution
and uses many of the Linux features you already understand in very different ways.
As in a traditional Linux system, Android’s first user-space process is init,
which is the root of all other processes. The daemons Android’s init process starts
are different, however, focused more on low-level details (managing file systems
and hardware access) rather than higher-level user facilities like scheduling cron
jobs. Android also has an additional layer of processes, those running Dalvik’s
Java language environment, which are responsible for executing all parts of the
system implemented in Java.
Figure 10-39 illustrates the basic process structure of Android. First is the init
process, which spawns a number of low-level daemon processes. One of these is
zygote, which is the root of the higher-level Java language processes.

appN app2 app1


App
Dalvik Dalvik Dalvik processes

system_server phone
System
Dalvik Dalvik processes

zygote
installd servicemanager adbd Daemons
Dalvik

init

Kernel

Figure 10-39. Android process hierarchy.

Android’s init does not run a shell in the traditional way, since a typical
Android device does not have a local console for shell access. Instead, the daemon
process adbd listens for remote connections (such as over USB) that request shell
access, forking shell processes for them as needed.
Since most of Android is written in the Java language, the zygote daemon and
processes it starts are central to the system. The first process zygote always starts
810 CASE STUDY 1: UNIX, LINUX, AND ANDROID CHAP. 10

is called system server, which contains all of the core operating system services.
Key parts of this are the power manager, package manager, window manager, and
activity manager.
Other processes will be created from zygote as needed. Some of these are
‘‘persistent’’ processes that are part of the basic operating system, such as the tele-
phony stack in the phone process, which must remain always running. Additional
application processes will be created and stopped as needed while the system is
running.
Applications interact with the operating system through calls to libraries pro-
vided by it, which together compose the Android framework. Some of these li-
braries can perform their work within that process, but many will need to perform
interprocess communication with other processes, often services in the sys-
tem server process.
Figure 10-40 shows the typical design for Android framework APIs that inter-
act with system services, in this case the package manager. The package manager
provides a framework API for applications to call in their local process, here the
PackageManager class. Internally, this class must get a connection to the corres-
ponding service in the system server. To accomplish this, at boot time the sys-
tem server publishes each service under a well-defined name in the service man-
ager, a daemon started by init. The PackageManager in the application process
retrieves a connection from the service manager to its system service using that
same name.
Once the PackageManager has connected with its system service, it can make
calls on it. Most application calls to PackageManager are implemented as
interprocess communication using Android’s Binder IPC mechanism, in this case
making calls to the PackageManagerService implementation in the system server.
The implementation of PackageManagerService arbitrates interactions across all
client applications and maintains state that will be needed by multiple applications.

10.8.5 Linux Extensions

For the most part, Android includes a stock Linux kernel providing standard
Linux features. Most of the interesting aspects of Android as an operating system
are in how those existing Linux features are used. There are also, however,
serveral significant extensions to Linux that the Android system relies on.

Wake Locks

Power management on mobile devices is different than on traditional comput-


ing systems, so Android adds a new feature to Linux called wake locks (also called
suspend blockers) for managing how the system goes to sleep.
On a traditional computing system, the system can be in one of two power
states: running and ready for user input, or deeply asleep and unable to continue
SEC. 10.8 ANDROID 811

Application process System server

Application Code

PackageManager PackageManagerService
Binder IPC

Bind C
er IP er IP
C Bind

"package"

Service manager

Figure 10-40. Publishing and interacting with system services.

executing without an external interrupt such as pressing a power key. While run-
ning, secondary pieces of hardware may be turned on or off as needed, but the
CPU itself and core parts of the hardware must remain in a powered state to handle
incoming network traffic and other such events. Going into the lower-power sleep
state is something that happens relatively rarely: either through the user explicitly
putting the system to sleep, or its going to sleep itself due to a relatively long inter-
val of user inactivity. Coming out of this sleep state requires a hardware interrupt
from an external source, such as pressing a button on a keyboard, at which point
the device will wake up and turn on its screen.
Mobile device users have different expectations. Although the user can turn off
the screen in a way that looks like putting the device to sleep, the traditional sleep
state is not actually desired. While a device’s screen is off, the device still needs to
be able to do work: it needs to be able to receive phone calls, receive and process
data for incoming chat messages, and many other things.
The expectations around turning a mobile device’s screen on and off are also
much more demanding than on a traditional computer. Mobile interaction tends to
be in many short bursts throughout the day: you receive a message and turn on the
device to see it and perhaps send a one-sentence reply, you run into friends walking
812 CASE STUDY 1: UNIX, LINUX, AND ANDROID CHAP. 10

their new dog and turn on the device to take a picture of her. In this kind of typical
mobile usage, any delay from pulling the device out until it is ready for use has a
significant negative impact on the user experience.
Given these requirements, one solution would be to just not have the CPU go
to sleep when a device’s screen is turned off, so that it is always ready to turn back
on again. The kernel does, after all, know when there is no work scheduled for any
threads, and Linux (as well as most operating systems) will automatically make the
CPU idle and use less power in this situation.
An idle CPU, however, is not the same thing as true sleep. For example:
1. On many chipsets the idle state uses significantly more power than a
true sleep state.
2. An idle CPU can wake up at any moment if some work happens to
become available, even if that work is not important.
3. Just having the CPU idle does not tell you that you can turn off other
hardware that would not be needed in a true sleep.
Wake locks on Android allow the system to go in to a deeper sleep mode, with-
out being tied to an explicit user action like turning the screen off. The default
state of the system with wake locks is that the device is asleep. When the device is
running, to keep it from going back to sleep something needs to be holding a wake
lock.
While the screen is on, the system always holds a wake lock that prevents the
device from going to sleep, so it will stay running, as we expect.
When the screen is off, however, the system itself does not generally hold a
wake lock, so it will stay out of sleep only as long as something else is holding
one. When no more wake locks are held, the system goes to sleep, and it can come
out of sleep only due to a hardware interrupt.
Once the system has gone to sleep, a hardware interrupt will wake it up again,
as in a traditional operating system. Some sources of such an interrupt are time-
based alarms, events from the cellular radio (such as for an incoming call), incom-
ing network traffic, and presses on certain hardware buttons (such as the power
button). Interrupt handlers for these events require one change from standard
Linux: they need to aquire an initial wake lock to keep the system running after it
handles the interrupt.
The wake lock acquired by an interrupt handler must be held long enough to
transfer control up the stack to the driver in the kernel that will continue processing
the event. That kernel driver is then responsible for acquiring its own wake lock,
after which the interrupt wake lock can be safely released without risk of the sys-
tem going back to sleep.
If the driver is then going to deliver this event up to user space, a similar hand-
shake is needed. The driver must ensure that it continues to hold the wake lock un-
til it has delivered the event to a waiting user process and ensured there has been an
SEC. 10.8 ANDROID 813

opportunity there to acquire its own wake lock. This flow may continue across
subsystems in user space as well; as long as something is holding a wake lock, we
continue performing the desired processing to respond to the event. Once no more
wake locks are held, however, the entire system falls back to sleep and all proc-
essing stops.

Out-Of-Memory Killer

Linux includes an ‘‘out-of-memory killer’’ that attempts to recover when mem-


ory is extremely low. Out-of-memory situations on modern operating systems are
nebulous affairs. With paging and swap, it is rare for applications themselves to see
out-of-memory failures. However, the kernel can still get in to a situation where it
is unable to find available RAM pages when needed, not just for a new allocation,
but when swapping in or paging in some address range that is now being used.
In such a low-memory situation, the standard Linux out-of-memory killer is a
last resort to try to find RAM so that the kernel can continue with whatever it is
doing. This is done by assigning each process a ‘‘badness’’ level, and simply
killing the process that is considered the most bad. A process’s badness is based on
the amount of RAM being used by the process, how long it has been running, and
other factors; the goal is to kill large processes that are hopefully not critical.
Android puts special pressure on the out-of-memory killer. It does not have a
swap space, so it is much more common to be in out-of-memory situations: there is
no way to relieve memory pressure except by dropping clean RAM pages mapped
from storage that has been recently used. Even so, Android uses the standard
Linux configuration to over-commit memory—that is, allow address space to be al-
located in RAM without a guarantee that there is available RAM to back it. Over-
commit is an extremely important tool for optimizing memory use, since it is com-
mon to mmap large files (such as executables) where you will only be needing to
load into RAM small parts of the overall data in that file.
Given this situation, the stock Linux out-of-memory killer does not work well,
as it is intended more as a last resort and has a hard time correctly identifying good
processes to kill. In fact, as we will discuss later, Android relies extensively on the
out-of-memory killer running regularly to reap processes and make good choices
about which to select.
To address this, Android introduces its own out-of-memory killer to the kernel,
with different semantics and design goals. The Android out-of-memory killer runs
much more aggressively: whenever RAM is getting ‘‘low.’’ Low RAM is identified
by a tunable parameter indicating how much available free and cached RAM in the
kernel is acceptable. When the system goes below that limit, the out-of-memory
killer runs to release RAM from elsewhere. The goal is to ensure that the system
never gets into bad paging states, which can negatively impact the user experience
when foreground applications are competing for RAM, since their execution be-
comes much slower due to continual paging in and out.
814 CASE STUDY 1: UNIX, LINUX, AND ANDROID CHAP. 10

Instead of trying to guess which processes should be killed, the Android


out-of-memory killer relies very strictly on information provided to it by user
space. The traditional Linux out-of-memory killer has a per-process oom adj pa-
rameter that can be used to guide it toward the best process to kill by modifying the
process’ overall badness score. Android’s out-of-memory killer uses this same pa-
rameter, but as a strict ordering: processes with a higher oom adj will always be
killed before those with lower ones. We will discuss later how the Android system
decides to assign these scores.

10.8.6 Dalvik

Dalvik implements the Java language environment on Android that is responsi-


ble for running applications as well as most of its system code. Almost everything
in the system service process—from the package manager, through the window
manager, to the activity manager—is implemented with Java language code ex-
ecuted by Dalvik.
Android is not, however, a Java-language platform in the traditional sense.
Java code in an Android application is provided in Dalvik’s bytecode format, based
around a register machine rather than Java’s traditional stack-based bytecode.
Dalvik’s bytecode format allows for faster interpretation, while still supporting JIT
(Just-in-Time) compilation. Dalvik bytecode is also more space efficient, both on
disk and in RAM, through the use of string pooling and other techniques.
When writing Android applications, source code is written in Java and then
compiled into standard Java bytecode using traditional Java tools. Android then
introduces a new step: converting that Java bytecode into Dalvik’s more compact
bytecode representation. It is the Dalvik bytecode version of an application that is
packaged up as the final application binary and ultimately installed on the device.
Android’s system architecture leans heavily on Linux for system primitives, in-
cluding memory management, security, and communication across security bound-
aries. It does not use the Java language for core operating system concepts—there
is little attempt to abstract away these important aspects of the underlying Linux
operating system.
Of particular note is Android’s use of processes. Android’s design does not
rely on the Java language for isolation between applications and the system, but
rather takes the traditional operating system approach of process isolation. This
means that each application is running in its own Linux process with its own
Dalvik environment, as are the system server and other core parts of the platform
that are written in Java.
Using processes for this isolation allows Android to leverage all of Linux’s
features for managing processes, from memory isolation to cleaning up all of the
resources associated with a process when it goes away. In addition to processes,
instead of using Java’s SecurityManager architecture, Android relies exclusively on
Linux’s security features.
SEC. 10.8 ANDROID 815

The use of Linux processes and security greatly simplifies the Dalvik environ-
ment, since it is no longer responsible for these critical aspects of system stability
and robustness. Not incidentally, it also allows applications to freely use native
code in their implementation, which is especially important for games which are
usually built with C++-based engines.
Mixing processes and the Java language like this does introduce some chal-
lenges. Bringing up a fresh Java-language environment can take a second, even on
modern mobile hardware. Recall one of the design goals of Android, to be able to
quickly launch applications, with a target of 200 msec. Requiring that a fresh
Dalvik process be brought up for this new application would be well beyond that
budget. A 200-msec launch is hard to achieve on mobile hardware, even without
needing to initialize a new Java-language environment.
The solution to this problem is the zygote native daemon that we briefly men-
tioned previously. Zygote is responsible for bringing up and initializing Dalvik, to
the point where it is ready to start running system or application code written in
Java. All new Dalvik-based processes (system or application) are forked from
zygote, allowing them to start execution with the environment already ready to go.
It is not just Dalvik that zygote brings up. Zygote also preloads many parts of
the Android framework that are commonly used in the system and application, as
well as loading resources and other things that are often needed.
Note that creating a new process from zygote involves a Linux fork, but there is
no exec call. The new process is a replica of the original zygote process, with all
of its preinitialized state already set up and ready to go. Figure 10-41 illustrates
how a new Java application process is related to the original zygote process. After
the fork, the new process has its own separate Dalvik environment, though it is
sharing all of the preloaded and initialed data with zygote through copy-on-write
pages. All that now remains to have the new running process ready to go is to give
it the correct identity (UID etc.), finish any initialization of Dalvik that requires
starting threads, and loading the application or system code to be run.
In addition to launch speed, there is another benefit that zygote brings. Because
only a fork is used to create processes from it, the large number of dirty RAM
pages needed to initialize Dalvik and preload classes and resources can be shared
between zygote and all of its child processes. This sharing is especially important
for Android’s environment, where swap is not available; demand paging of clean
pages (such as executable code) from ‘‘disk’’ (flash memory) is available. However
any dirty pages must stay locked in RAM; they cannot be paged out to ‘‘disk.’’

10.8.7 Binder IPC

Android’s system design revolves significantly around process isolation, be-


tween applications as well as between different parts of the system itself. This re-
quires a large amount of interprocess-communication to coordinate between the
different processes, which can take a large amount of work to implement and get
816 CASE STUDY 1: UNIX, LINUX, AND ANDROID CHAP. 10

Zygote App process

Application classes
and resources

Preloaded resources Preloaded resources

Preloaded classes Copy-on-write Preloaded classes

Dalvik Dalvik

Figure 10-41. Creating a new Dalvik process from zygote.

right. Android’s Binder interprocess communication mechanism is a rich general-


purpose IPC facility that most of the Android system is built on top of.
The Binder architecture is divided into three layers, shown in Fig. 10-42. At
the bottom of the stack is a kernel module that implements the actual cross-process
interaction and exposes it through the kernel’s ioctl function. (ioctl is a gener-
al-purpose kernel call for sending custom commands to kernel drivers and mod-
ules.) On top of the kernel module is a basic object-oriented user-space API, al-
lowing applications to create and interact with IPC endpoints through the IBinder
and Binder classes. At the top is an interface-based programming model where ap-
plications declare their IPC interfaces and do not otherwise need to worry about
the details of how IPC happens in the lower layers.

Binder Kernel Module

Rather than use existing Linux IPC facilities such as pipes, Binder includes a
special kernel module that implements its own IPC mechanism. The Binder IPC
model is different enough from traditional Linux mechanisms that it cannot be ef-
ficiently implemented on top of them purely in user space. In addition, Android
does not support most of the System V primitives for cross-process interaction
(semaphores, shared memory segments, message queues) because they do not pro-
vide robust semantics for cleaning up their resources from buggy or malicious ap-
plications.
The basic IPC model Binder uses is the RPC (remote procedure call). That
is, the sending process is submitting a complete IPC operation to the kernel, which
SEC. 10.8 ANDROID 817

Platform / Application

Method calls

Ilnterface / aidl

Interface definitions

transact() onTransact()

IBinder / Binder

Binder user space

command Codes Result codes

ioctl()

Binder kernel module

Figure 10-42. Binder IPC architecture.

is executed in the receiving process; the sender may block while the receiver ex-
ecutes, allowing a result to be returned back from the call. (Senders optionally
may specify they should not block, continuing their execution in parallel with the
receiver.) Binder IPC is thus message based, like System V message queues, rath-
er than stream based as in Linux pipes. A message in Binder is referred to as a
transaction, and at a higher level can be viewed as a function call across proc-
esses.
Each transaction that user space submits to the kernel is a complete operation:
it identifies the target of the operation and identity of the sender as well as the
complete data being delivered. The kernel determines the appropriate process to
receive that transaction, delivering it to a waiting thread in the process.
Figure 10-43 illustrates the basic flow of a transaction. Any thread in the orig-
inating process may create a transaction identifying its target, and submit this to
the kernel. The kernel makes a copy of the transaction, adding to it the identity of
818 CASE STUDY 1: UNIX, LINUX, AND ANDROID CHAP. 10

the sender. It determines which process is responsible for the target of the transac-
tion and wakes up a thread in the process to receive it. Once the receiving process
is executing, it determines the appropriate target of the transaction and delivers it.
Process 1 Process 2

Transaction Transaction Object1


Ta
To: Object1
To: Object1 From: Process 1
(Data) (Data)
Kernel

Thread pool Transaction Thread pool

To: Object1
From: Process 1
T1 T2 T1 T2
(Data)

Figure 10-43. Basic Binder IPC transaction.

(For the discussion here, we are simplifying the the way transaction data
moves through the system as two copies, one to the kernel and one to the receiving
process’s address space. The actual implementation does this in one copy. For
each process that can receive transactions, the kernel creates a shared memory area
with it. When it is handling a transaction, it first determines the process that will
be receiving that transaction and copies the data directly into that shared address
space.)
Note that each process in Fig. 10-43 has a ‘‘thread pool.’’ This is one or more
threads created by user space to handle incoming transactions. The kernel will dis-
patch each incoming transaction to a thread currently waiting for work in that proc-
ess’s thread pool. Calls into the kernel from a sending process however do not
need to come from the thread pool—any thread in the process is free to initiate a
transaction, such as Ta in Fig. 10-43.
We have already seen that transactions given to the kernel identify a target ob-
ject; however, the kernel must determine the receiving process. To accomplish
this, the kernel keeps track of the available objects in each process and maps them
to other processes, as shown in Fig. 10-44. The objects we are looking at here are
simply locations in the address space of that process. The kernel only keeps track
of these object addresses, with no meaning attached to them; they may be the loca-
tion of a C data structure, C++ object, or anything else located in that process’s ad-
dress space.
References to objects in remote processes are identified by an integer handle,
which is much like a Linux file descriptor. For example, consider Object2a in
SEC. 10.8 ANDROID 819

Process 2—this is known by the kernel to be associated with Process 2, and further
the kernel has assigned Handle 2 for it in Process 1. Process 1 can thus submit a
transaction to the kernel targeted to its Handle 2, and from that the kernel can de-
termine this is being sent to Process 2 and specifically Object2a in that process.
Process 1 Kernel Process 2

Process 1 Process 2
Object1a Object1a Object2a Object2a

Object1b Object2b
Object1b Object2b

Handle 1 Handle 1
Handle 2 Handle 2
Handle 2 Handle 2
Handle 3 Handle 3

Figure 10-44. Binder cross-process object mapping.

Also like file descriptors, the value of a handle in one process does not mean
the same thing as that value in another process. For example, in Fig. 10-44, we can
see that in Process 1, a handle value of 2 identifies Object2a; however, in Process
2, that same handle value of 2 identifies Object1a. Further, it is impossible for one
process to access an object in another process if the kernel has not assigned a hand-
le to it for that process. Again in Fig. 10-44, we can see that Process 2’s Object2b
is known by the kernel, but no handle has been assigned to it for Process 1. There
is thus no path for Process 1 to access that object, even if the kernel has assigned
handles to it for other processes.
How do these handle-to-object associations get set up in the first place?
Unlike Linux file descriptors, user processes do not directly ask for handles. In-
stead, the kernel assigns handles to processes as needed. This process is illustrated
in Fig. 10-45. Here we are looking at how the reference to Object1b from Process
2 to Process 1 in the previous figure may have come about. The key to this is how
a transaction flows through the system, from left to right at the bottom of the fig-
ure.
The key steps shown in Fig. 10-45 are:
1. Process 1 creates the initial transaction structure, which contains the
local address Object1b.
2. Process 1 submits the transaction to the kernel.
3. The kernel looks at the data in the transaction, finds the address Ob-
ject1b, and creates a new entry for it since it did not previously know
about this address.
820 CASE STUDY 1: UNIX, LINUX, AND ANDROID CHAP. 10

Process 1 Kernel Process 2

Process 1 Process 2
Object2a
Object1b 3
Object2a
Object1b

Handle 1 8
Handle 1
Handle 3
Handle 2
Handle 2
Handle 2
Handle 3 6

1 Transaction Transaction Transaction Transaction


To: Handle 2 5 To: Object2a To: Object2a
To: Handle 2
From: Process 1 From: Process 1 From: Process 1
2 4 7
Data Data Data Data
Object1b Object1b Handle 3 6 Handle 3
Data Data Data Data

Figure 10-45. Transferring Binder objects between processes.

4. The kernel uses the target of the transaction, Handle 2, to determine


that this is intended for Object2a which is in Process 2.
5. The kernel now rewrites the transaction header to be appropriate for
Process 2, changing its target to address Object2a.
6. The kernel likewise rewrites the transaction data for the target proc-
ess; here it finds that Object1b is not yet known by Process 2, so a
new Handle 3 is created for it.
7. The rewritten transaction is delivered to Process 2 for execution.
8. Upon receiving the transaction, the process discovers there is a new
Handle 3 and adds this to its table of available handles.

If an object within a transaction is already known to the receiving process, the


flow is similar, except that now the kernel only needs to rewrite the transaction so
that it contains the previously assigned handle or the receiving process’s local ob-
ject pointer. This means that sending the same object to a process multiple times
will always result in the same identity, unlike Linux file descriptors where opening
the same file multiple times will allocate a different descriptor each time. The
Binder IPC system maintains unique object identities as those objects move be-
tween processes.
The Binder architecture essentially introduces a capability-based security
model to Linux. Each Binder object is a capability. Sending an object to another
process grants that capability to the process. The receiving process may then make
use of whatever features the object provides. A process can send an object out to
another process, later receive an object from any process, and identify whether that
received object is exactly the same object it originally sent out.
SEC. 10.8 ANDROID 821

Binder User-Space API

Most user-space code does not directly interact with the Binder kernel module.
Instead, there is a user-space object-oriented library that provides a simpler API.
The first level of these user-space APIs maps fairly directly to the kernel concepts
we have covered so far, in the form of three classes:
1. IBinder is an abstract interface for a Binder object. Its key method is
transact, which submits a transaction to the object. The imple-
mentation receiving the transaction may be an object either in the
local process or in another process; if it is in another process, this will
be delivered to it through the Binder kernel module as previously dis-
cussed.
2. Binder is a concrete Binder object. Implementing a Binder subclass
gives you a class that can be called by other processes. Its key meth-
od is onTransact, which receives a transaction that was sent to it. The
main responsibility of a Binder subclass is to look at the transaction
data it receives here and perform the appropriate operation.
3. Parcel is a container for reading and writing data that is in a Binder
transaction. It has methods for reading and writing typed data—inte-
gers, strings, arrays—but most importantly it can read and write refer-
ences to any IBinder object, using the appropriate data structure for
the kernel to understand and transport that reference across processes.
Figure 10-46 depicts how these classes work together, modifying Fig. 10-44
that we previously looked at with the user-space classes that are used. Here we see
that Binder1b and Binder2a are instances of concrete Binder subclasses. To per-
form an IPC, a process now creates a Parcel containing the desired data, and sends
it through another class we have not yet seen, BinderProxy. This class is created
whenever a new handle appears in a process, thus providing an implementation of
IBinder whose transact method creates the appropriate transaction for the call and
submits it to the kernel.
The kernel transaction structure we had previously looked at is thus split apart
in the user-space APIs: the target is represented by a BinderProxy and its data is
held in a Parcel. The transaction flows through the kernel as we previously saw
and, upon appearing in user space in the receiving process, its target is used to de-
termine the appropriate receiving Binder object while a Parcel is constructed from
its data and delivered to that object’s onTransact method.
These three classes now make it fairly easy to write IPC code:
1. Subclass from Binder.
2. Implement onTransact to decode and execute incoming calls.
3. Implement corresponding code to create a Parcel that can be passed
to that object’s transact method.
822 CASE STUDY 1: UNIX, LINUX, AND ANDROID CHAP. 10

Process 1 Kernel Process 2

Process 1 Process 2
Binder1b
Binder1b Binder2b

Binder2a
Handle 1
Parcel Handle 1
Handle 2
Data
Handle 2 onTransact()
Binder1b
Handle 3
Data
BinderProxy
Transaction Transaction (Handle 3)
transact()
To: Handle 2 To: Binder2a Parcel
From: Process 1 From: Process 1 Data
BinderProxy
Handle 3
(Handle 2) Data Data Data
Binder1b Handle 3
Data Data

Figure 10-46. Binder user-space API.

The bulk of this work is in the last two steps. This is the unmarshalling and
marshalling code that is needed to turn how we’d prefer to program—using sim-
ple method calls—into the operations that are needed to execute an IPC. This is
boring and error-prone code to write, so we’d like to let the computer take care of
that for us.

Binder Interfaces and AIDL

The final piece of Binder IPC is the one that is most often used, a high-level in-
terface-based programming model. Instead of dealing with Binder objects and
Parcel data, here we get to think in terms of interfaces and methods.
The main piece of this layer is a command-line tool called AIDL (for Android
Interface Definition Language). This tool is an interface compiler, taking an ab-
stract description of an interface and generating from it the source code necessary
to define that interface and implement the appropriate marshalling and unmar-
shalling code needed to make remote calls with it.
Figure 10-47 shows a simple example of an interface defined in AIDL. This
interface is called IExample and contains a single method, print, which takes a sin-
gle String argument.

package com.example

interface IExample {
void print(String msg);
}

Figure 10-47. Simple interface described in AIDL.


SEC. 10.8 ANDROID 823

An interface description like that in Fig. 10-47 is compiled by AIDL to gener-


ate three Java-language classes illustrated in Fig. 10-48:
1. IExample supplies the Java-language interface definition.
2. IExample.Stub is the base class for implementations of this inter-
face. It inherits from Binder, meaning it can be the recipient of IPC
calls; it inherits from IExample, since this is the interface being im-
plemented. The purpose of this class is to perform unmarshalling:
turn incoming onTransact calls in to the appropriate method call of
IExample. A subclass of it is then responsible only for implementing
the IExample methods.
3. IExample.Proxy is the other side of an IPC call, responsible for per-
forming marshalling of the call. It is a concrete implementation of
IExample, implementing each method of it to transform the call into
the appropriate Parcel contents and send it off through a transact call
on an IBinder it is communicating with.

Binder IExample

IExample.Stub IExample.Proxy IBinder

Figure 10-48. Binder interface inheritance hierarchy.

With these classes in place, there is no longer any need to worry about the
mechanics of an IPC. Implementors of the IExample interface simply derive from
IExample.Stub and implement the interface methods as they normally would. Cal-
lers will receive an IExample interface that is implemented by IExample.Proxy, al-
lowing them to make regular calls on the interface.
The way these pieces work together to perform a complete IPC operation is
shown in Fig. 10-49. A simple print call on an IExample interface turns into:

1. IExample.Proxy marshals the method call into a Parcel, calling trans-


act on the underlying BinderProxy.
2. BinderProxy constructs a kernel transaction and delivers it to the ker-
nel through an ioctl call.
3. The kernel transfers the transaction to the intended process, delivering
it to a thread that is waiting in its own ioctl call.
824 CASE STUDY 1: UNIX, LINUX, AND ANDROID CHAP. 10

4. The transaction is decoded back into a Parcel and onTransact called


on the appropriate local object, here ExampleImpl (which is a sub-
class of IExample.Stub).
5. IExample.Stub decodes the Parcel into the appropriate method and
arguments to call, here calling print.
6. The concrete implementation of print in ExampleImpl finally ex-
ecutes.

Process 1 Process 2

Examplelmpl

print("hello")
IExample

IExample.Proxy print("hello")

Kernel IExample.Stub
transact({print hello})

onTransact({print hello})
ioctl()
BinderProxy ioctl()
binder_module Binder

Figure 10-49. Full path of an AIDL-based Binder IPC.

The bulk of Android’s IPC is written using this mechanism. Most services in
Android are defined through AIDL and implemented as shown here. Recall the
previous Fig. 10-40 showing how the implementation of the package manager in
the system server process uses IPC to publish itself with the service manager for
other processes to make calls to it. Two AIDL interfaces are involved here: one for
the service manager and one for the package manager. For example, Fig. 10-50
shows the basic AIDL description for the service manager; it contains the getSer-
vice method, which other processes use to retrieve the IBinder of system service
interfaces like the package manager.

10.8.8 Android Applications

Android provides an application model that is very different from the normal
command-line environment in the Linux shell or even applications launched from a
graphical user interface. An application is not an executable file with a main entry
point; it is a container of everything that makes up that app: its code, graphical re-
sources, declarations about what it is to the system, and other data.
SEC. 10.8 ANDROID 825

package android.os

interface IServiceManager {
IBinder getService(String name);
void addService(String name, IBinder binder);
}

Figure 10-50. Basic service manager AIDL interface.

An Android application by convention is a file with the apk extension, for


Android Package. This file is actually a normal zip archive, containing everything
about the application. The important contents of an apk are:

1. A manifest describing what the application is, what it does, and how
to run it. The manifest must provide a package name for the applica-
tion, a Java-style scoped string (such as com.android.app.calculator),
which uniquely identifies it.
2. Resources needed by the application, including strings it displays to
the user, XML data for layouts and other descriptions, graphical bit-
maps, etc.
3. The code itself, which may be Dalvik bytecode as well as native li-
brary code.
4. Signing information, securely identifying the author.

The key part of the application for our purposes here is its manifest, which ap-
pears as a precompiled XML file named AndroidManifest.xml in the root of the
apk’s zip namespace. A complete example manifest declaration for a hypothetical
email application is shown in Fig. 10-51: it allows you to view and compose emails
and also includes components needed for synchronizing its local email storage
with a server even when the user is not currently in the application.
Android applications do not have a simple main entry point which is executed
when the user launches them. Instead, they publish under the manifest’s <applica-
tion> tag a variety of entry points describing the various things the application can
do. These entry points are expressed as four distinct types, defining the core types
of behavior that applications can provide: activity, receiver, service, and content
provider. The example we have presented shows a few activities and one declara-
tion of the other component types, but an application may declare zero or more of
any of these.
Each of the different four component types an application can contain has dif-
ferent semantics and uses within the system. In all cases, the android:name attrib-
ute supplies the Java class name of the application code implementing that compo-
nent, which will be instantiated by the system when needed.
826 CASE STUDY 1: UNIX, LINUX, AND ANDROID CHAP. 10

<?xml version="1.0" encoding="utf-8"?>


<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.email">
<application>

<activity android:name="com.example.email.MailMainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<categor y android:name="android.intent.categor y.LAUNCHER" />
</intent-filter>
</activity>

<activity android:name="com.example.email.ComposeActivity">
<intent-filter>
<action android:name="android.intent.action.SEND" />
<categor y android:name="android.intent.categor y.DEFAULT" />
<data android:mimeType="*/*" />
</intent-filter>
</activity>

<ser vice android:name="com.example.email.SyncSer vice">


</ser vice>

<receiver android:name="com.example.email.SyncControlReceiver">
<intent-filter>
<action android:name="android.intent.action.DEVICE STORAGE LOW" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.DEVICE STORAGE OKAY" />
</intent-filter>
</receiver>

<provider android:name="com.example.email.EmailProvider"
android:authorities="com.example.email.provider.email">
</provider>

</application>
</manifest>

Figure 10-51. Basic structure of AndroidManifest.xml.

The package manager is the part of Android that keeps track of all application
packages. It parses every application’s manifest, collecting and indexing the infor-
mation it finds in them. With that information, it then provides facilities for clients
to query it about the currently installed applications and retrieve relevant infor-
mation about them. It is also responsible for installing applications (creating stor-
age space for the application and ensuring the integrity of the apk) as well as
everything needed to uninstall (cleaning up everything associated with a previously
installed app).
SEC. 10.8 ANDROID 827

Applications statically declare their entry points in their manifest so they do


not need to execute code at install time that registers them with the system. This
design makes the system more robust in many ways: installing an application does
not require running any application code, the top-level capabilities of the applica-
tion can always be determined by looking at the manifest, there is no need to keep
a separate database of this information about the application which can get out of
sync (such as across updates) with the application’s actual capabilities, and it guar-
antees no information about an application can be left around after it is uninstalled.
This decentralized approach was taken to avoid many of these types of problems
caused by Windows’ centralized Registry.
Breaking an application into finer-grained components also serves our design
goal of supporting interoperation and collaboration between applications. Applica-
tions can publish pieces of themselves that provide specific functionality, which
other applications can make use of either directly or indirectly. This will be illus-
trated as we look in more detail at the four kinds of components that can be pub-
lished.
Above the package manager sits another important system service, the activity
manager. While the package manager is responsible for maintaining static infor-
mation about all installed applications, the activity manager determines when,
where, and how those applications should run. Despite its name, it is actually
responsible for running all four types of application components and implementing
the appropriate behavior for each of them.

Activities

An activity is a part of the application that interacts directly with the user
through a user interface. When the user launches an application on their device,
this is actually an activity inside the application that has been designated as such a
main entry point. The application implements code in its activity that is responsi-
ble for interacting with the user.
The example email manifest shown in Fig. 10-51 contains two activities. The
first is the main mail user interface, allowing users to view their messages; the sec-
ond is a separate interface for composing a new message. The first mail activity is
declared as the main entry point for the application, that is, the activity that will be
started when the user launches it from the home screen.
Since the first activity is the main activity, it will be shown to users as an appli-
cation they can launch from the main application launcher. If they do so, the sys-
tem will be in the state shown in Fig. 10-52. Here the activity manager, on the left
side, has made an internal ActivityRecord instance in its process to keep track of
the activity. One or more of these activities are organized into containers called
tasks, which roughly correspond to what the user experiences as an application. At
this point the activity manager has started the email application’s process and an
instance of its MainMailActivity for displaying its main UI, which is associated
828 CASE STUDY 1: UNIX, LINUX, AND ANDROID CHAP. 10

with the appropriate ActivityRecord. This activity is in a state called resumed since
it is now in the foreground of the user interface.
Activity manager in system_server process Email app process

Task: Email
MailMainActivity
RESUMED

ActivityRecord
(MailMainActivity)

Figure 10-52. Starting an email application’s main activity.

If the user were now to switch away from the email application (not exiting it)
and launch a camera application to take a picture, we would be in the state shown
in Fig. 10-53. Note that we now have a new camera process running the camera’s
main activity, an associated ActivityRecord for it in the activity manager, and it is
now the resumed activity. Something interesting also happens to the previous
email activity: instead of being resumed, it is now stopped and the ActivityRecord
holds this activity’s saved state.
Activity manager in system_server process Camera app process

Task: Camera
CameraMainActivity
RESUMED

ActivityRecord
(CameraMainActivity)

Email app process

Task: Email
MailMainActivity
ActivityRecord
STOPPED

(MailMainActivity)
Saved state

Figure 10-53. Starting the camera application after email.

When an activity is no longer in the foreground, the system asks it to ‘‘save its
state.’’ This involves the application creating a minimal amount of state infor-
mation representing what the user currently sees, which it returns to the activity
SEC. 10.8 ANDROID 829

manager and stores in the system server process, in the ActivityRecord associated
with that activity. The saved state for an activity is generally small, containing for
example where you are scrolled in an email message, but not the message itself,
which will be stored elsewhere by the application in its persistent storage.
Recall that although Android does demand paging (it can page in and out clean
RAM that has been mapped from files on disk, such as code), it does not rely on
swap space. This means all dirty RAM pages in an application’s process must stay
in RAM. Having the email’s main activity state safely stored away in the activity
manager gives the system back some of the flexibility in dealing with memory that
swap provides.
For example, if the camera application starts to require a lot of RAM, the sys-
tem can simply get rid of the email process, as shown in Fig. 10-54. The Activi-
tyRecord, with its precious saved state, remains safely tucked away by the activity
manager in the system server process. Since the system server process hosts all of
Android’s core system services, it must always remain running, so the state saved
here will remain around for as long as we might need it.
Activity manager in system_server process Camera app process

Task: Camera
CameraMainActivity
RESUMED

ActivityRecord
(CameraMainActivity)

Task: Email

ActivityRecord
STOPPED

(MailMainActivity)
Saved state

Figure 10-54. Removing the email process to reclaim RAM for the camera.

Our example email application not only has an activity for its main UI, but in-
cludes another ComposeActivity. Applications can declare any number of activities
they want. This can help organize the implementation of an application, but more
importantly it can be used to implement cross-application interactions. For ex-
ample, this is the basis of Android’s cross-application sharing system, which the
ComposeActivity here is participating in. If the user, while in the camera applica-
tion, decides she wants to share a picture she took, our email application’s Com-
poseActivity is one of the sharing options she has. If it is selected, that activity will
830 CASE STUDY 1: UNIX, LINUX, AND ANDROID CHAP. 10

be started and given the picture to be shared. (Later we will see how the camera
application is able to find the email application’s ComposeActivity.)
Performing that share option while in the activity state seen in Fig. 10-54 will
lead to the new state in Fig. 10-55. There are a number of important things to note:
1. The email app’s process must be started again, to run its ComposeAc-
tivity.
2. However, the old MailMainActivity is not started at this point, since it
is not needed. This reduces RAM use.
3. The camera’s task now has two records: the original CameraMainAc-
tivity we had just been in, and the new ComposeActivity that is now
displayed. To the user, these are still one cohesive task: it is the cam-
era currently interacting with them to email a picture.
4. The new ComposeActivity is at the top, so it is resumed; the previous
CameraMainActivity is no longer at the top, so its state has been
saved. We can at this point safely quit its process if its RAM is need-
ed elsewhere.

Activity manager in system_server process Email app process

Task: Camera
ComposeActivity
RESUMED

ActivityRecord
(ComposeActivity)

Camera app process


ActivityRecord
STOPPED

(CameraMainActivity)
Saved state
CameraMainActivity

Task: Email

ActivityRecord
STOPPED

(MailMainActivity)
Saved state

Figure 10-55. Sharing a camera picture through the email application.

Finally let us look at would happen if the user left the camera task while in this
last state (that is, composing an email to share a picture) and returned to the email
SEC. 10.8 ANDROID 831

application. Figure 10-56 shows the new state the system will be in. Note that we
have brought the email task with its main activity back to the foreground. This
makes MailMainActivity the foreground activity, but there is currently no instance
of it running in the application’s process.
Activity manager in system_server process Email app process

Task: Email
MailMainActivity
RESUMED

ActivityRecord
(MailMainActivity)
ComposeActivity

Task: Camera

ActivityRecord
STOPPED

(ComposeActivity) Camera app process


Saved state

ActivityRecord
STOPPED

CameraMainActivity
(CameraMainActivity)
Saved state

Figure 10-56. Returning to the email application.

To return to the previous activity, the system makes a new instance, handing it
back the previously saved state the old instance had provided. This action of
restoring an activity from its saved state must be able to bring the activity back to
the same visual state as the user last left it. To accomplish this, the application will
look in its saved state for the message the user was in, load that message’s data
from its persistent storage, and then apply any scroll position or other user-inter-
face state that had been saved.

Services

A service has two distinct identities:

1. It can be a self-contained long-running background operation. Com-


mon examples of using services in this way are performing back-
ground music playback, maintaining an active network connection
(such as with an IRC server) while the user is in other applications,
downloading or uploading data in the background, etc.
832 CASE STUDY 1: UNIX, LINUX, AND ANDROID CHAP. 10

2. It can serve as a connection point for other applications or the system


to perform rich interaction with the application. This can be used by
applications to provide secure APIs for other applications, such as to
perform image or audio processing, provide a text to speech, etc.

The example email manifest shown in Fig. 10-51 contains a service that is used
to perform synchronization of the user’s mailbox. A common implementation
would schedule the service to run at a regular interval, such as every 15 minutes,
starting the service when it is time to run, and stopping itself when done.
This is a typical use of the first style of service, a long-running background op-
eration. Figure 10-57 shows the state of the system in this case, which is quite
simple. The activity manager has created a ServiceRecord to keep track of the ser-
vice, noting that it has been started, and thus created its SyncService instance in the
application’s process. While in this state the service is fully active (barring the en-
tire system going to sleep if not holding a wake lock) and free to do what it wants.
It is possible for the application’s process to go away while in this state, such as if
the process crashes, but the activity manager will continue to maintain its Ser-
viceRecord and can at that point decide to restart the service if desired.

Activity manager in system_server process Email app process


STARTED

ServiceRecord
(SyncService) SyncService

Figure 10-57. Starting an application service.

To see how one can use a service as a connection point for interaction with
other applications, let us say that we want to extend our existing SyncService to
have an API that allows other applications to control its sync interval. We will
need to define an AIDL interface for this API, like the one shown in Fig. 10-58.
package com.example.email

interface ISyncControl {
int getSyncInterval();
void setSyncInterval(int seconds);
}

Figure 10-58. Interface for controlling a sync service’s sync interval.

To use this, another process can bind to our application service, getting access
to its interface. This creates a connection between the two applications, shown in
Fig. 10-59. The steps of this process are:
SEC. 10.8 ANDROID 833

1. The client application tells the activity manager that it would like to
bind to the service.
2. If the service is not already created, the activity manager creates it in
the service application’s process.
3. The service returns the IBinder for its interface back to the activity
manager, which now holds that IBinder in its ServiceRecord.
4. Now that the activity manager has the service IBinder, it can be sent
back to the original client application.
5. The client application now having the service’s IBinder may proceed
to make any direct calls it would like on its interface.

Activity manager in system_server process Email app process

2. Create
ServiceRecord
STOPPED

(SyncService) SyncService
3. Return
IBinder IBinder
IBinder

5. Call service

4. Send
IBinder
IBinder

1. Bind
Client app process

Figure 10-59. Binding to an application service.

Receivers

A receiver is the recipient of (typically external) events that happen, generally


in the background and outside of normal user interaction. Receivers conceptually
are the same as an application explicitly registering for a callback when something
interesting happens (an alarm goes off, data connectivity changes, etc), but do not
require that the application be running in order to receive the event.
The example email manifest shown in Fig. 10-51 contains a receiver for the
application to find out when the device’s storage becomes low in order for it to
stop synchronizing email (which may consume more storage). When the device’s
storage becomes low, the system will send a broadcast with the low storage code,
to be delivered to all receivers interested in the event.
Figure 10-60 illustrates how such a broadcast is processed by the activity man-
ager in order to deliver it to interested receivers. It first asks the package manager
834 CASE STUDY 1: UNIX, LINUX, AND ANDROID CHAP. 10

for a list of all receivers interested in the event, which is placed in a Broadcast-
Record representing that broadcast. The activity manager will then proceed to step
through each entry in the list, having each associated application’s process create
and execute the appropriate receiver class.
Activity manager in system_server process Calendar app process

BroadcastRecord SyncControlReceiver
DEVICE_STORAGE_LOW

SyncControlReceiver
(Calendar app) Email app process

SyncControlReceiver SyncControlReceiver
(Email app)

CleanupReceiver Browser app process


(Browser app)

CleanupReceiver

Figure 10-60. Sending a broadcast to application receivers.

Receivers only run as one-shot operations. When an event happens, the system
finds any receivers interested in it, delivers that event to them, and once they have
consumed the event they are done. There is no ReceiverRecord like those we have
seen for other application components, because a particular receiver is only a tran-
sient entity for the duration of a single broadcast. Each time a new broadcast is
sent to a receiver component, a new instance of that receiver’s class is created.

Content Providers

Our last application component, the content provider, is the primary mechan-
ism that applications use to exchange data with each other. All interactions with a
content provider are through URIs using a content: scheme; the authority of the
URI is used to find the correct content-provider implementation to interact with.
For example, in our email application from Fig. 10-51, the content provider
specifies that its authority is com.example.email.provider.email. Thus URIs operat-
ing on this content provider would start with
content://com.example.email.provider.email/
The suffix to that URI is interpreted by the provider itself to determine which data
within it is being accessed. In the example here, a common convention would be
that the URI
SEC. 10.8 ANDROID 835

content://com.example.email.provider.email/messages
means the list of all email messages, while
content://com.example.email.provider.email/messages/1
provides access to a single message at key number 1.
To interact with a content provider, applications always go through a system
API called ContentResolver, where most methods have an initial URI argument
indicating the data to operate on. One of the most often used ContentResolver
methods is query, which performs a database query on a given URI and returns a
Cursor for retrieving the structured results. For example, retrieving a summary of
all of the available email messages would look something like:
quer y("content://com.example.email.provider.email/messages")
Though this does not look like it to applications, what is actually going on
when they use content providers has many similarities to binding to services. Fig-
ure 10-61 illustrates how the system handles our query example:

1. The application calls ContentResolver.query to initiate the operation.


2. The URI’s authority is handed to the activity manager for it to find
(via the package manager) the appropriate content provider.
3. If the content provider is not already running, it is created.
4. Once created, the content provider returns to the activity manager its
IBinder implementing the system’s IContentProvider interface.
5. The content provider’s Binder is returned to the ContentResolver.
6. The content resolver can now complete the initial query operation by
calling the appropriate method on the AIDL interface, returning the
Cursor result.

Content providers are one of the key mechanisms for performing interactions
across applications. For example, if we return to the cross-application sharing sys-
tem previously described in Fig. 10-55, content providers are the way data is ac-
tually transferred. The full flow for this operation is:

1. A share request that includes the URI of the data to be shared is creat-
ed and is submitted to the system.
2. The system asks the ContentResolver for the MIME type of the data
behind that URI; this works much like the query method we just dis-
cussed, but asks the content provider to return a MIME-type string for
the URI.
836 CASE STUDY 1: UNIX, LINUX, AND ANDROID CHAP. 10

Activity manager in system_server process Email app process

3. Create
ProviderRecord
(EmailProvider) EmailProvider
4. Return
IBinder IContentProvider.Stub
IBinder
6. query()

5. Return IContentProvider.Proxy
IBinder ContentResolver
2. Look up
Authority 1. query()

Client app process

Figure 10-61. Interacting with a content provider.

3. The system finds all activities that can receive data of the identified
MIME type.
4. A user interface is shown for the user to select one of the possible re-
cipients.
5. When one of these activities is selected, the system launches it.
6. The share-handling activity receives the URI of the data to be shared,
retrieves its data through ContentResolver, and performs its ap-
propriate operation: creates an email, stores it, etc..

10.8.9 Intents

A detail that we have not yet discussed in the application manifest shown in
Fig. 10-51 is the <intent-filter> tags included with the activities and receiver decla-
rations. This is part of the intent feature in Android, which is the cornerstone for
how different applications identify each other in order to be able to interact and
work together.
An intent is the mechanism Android uses to discover and identify activities,
receivers, and services. It is similar in some ways to the Linux shell’s search path,
which the shell uses to look through multiple possible directories in order to find
an executable matching command names given to it.
There are two major types of intents: explicit and implicit. An explicit intent
is one that directly identifies a single specific application component; in Linux
shell terms it is the equivalent to supplying an absolute path to a command. The
SEC. 10.8 ANDROID 837

most important part of such an intent is a pair of strings naming the component: the
package name of the target application and class name of the component within
that application. Now referring back to the activity of Fig. 10-52 in application
Fig. 10-51, an explicit intent for this component would be one with package name
com.example.email and class name com.example.email.MailMainActivity.
The package and class name of an explicit intent are enough information to
uniquely identify a target component, such as the main email activity in Fig. 10-52.
From the package name, the package manager can return everything needed about
the application, such as where to find its code. From the class name, we know
which part of that code to execute.
An implicit intent is one that describes characteristics of the desired compo-
nent, but not the component itself; in Linux shell terms this is the equivalent to
supplying a single command name to the shell, which it uses with its search path to
find a concrete command to be run. This process of finding the component match-
ing an implicit intent is called intent resolution.
Android’s general sharing facility, as we previously saw in Fig. 10-55’s illus-
tration of sharing a photo the user took from the camera through the email applica-
tion, is a good example of implicit intents. Here the camera application builds an
intent describing the action to be done, and the system finds all activities that can
potentially perform that action. A share is requested through the intent action
android.intent.action.SEND, and we can see in Fig. 10-51 that the email applica-
tion’s compose activity declares that it can perform this action.
There can be three outcomes to an intent resolution: (1) no match is found, (2)
a single unique match is found, or (3) there are multiple activities that can handle
the intent. An empty match will result in either an empty result or an exception,
depending on the expectations of the caller at that point. If the match is unique,
then the system can immediately proceed to launching the now explicit intent. If
the match is not unique, we need to somehow resolve it in another way to a single
result.
If the intent resolves to multiple possible activities, we cannot just launch all of
them; we need to pick a single one to be launched. This is accomplished through a
trick in the package manager. If the package manager is asked to resolve an intent
down to a single activity, but it finds there are multiple matches, it instead resolves
the intent to a special activity built into the system called the ResolverActivity.
This activity, when launched, simply takes the original intent, asks the package
manager for a list of all matching activities, and displays these for the user to select
a single desired action. When one is selected, it creates a new explicit intent from
the original intent and the selected activity, calling the system to have that new
activity started.
Android has another similarity with the Linux shell: Android’s graphical shell,
the launcher, runs in user space like any other application. An Android launcher
performs calls on the package manager to find the available activities and launch
them when selected by the user.
838 CASE STUDY 1: UNIX, LINUX, AND ANDROID CHAP. 10

10.8.10 Application Sandboxes

Traditionally in operating systems, applications are seen as code executing as


the user, on the user’s behalf. This behavior has been inherited from the command
line, where you run the ls command and expect that to run as your identity (UID),
with the same access rights as you have on the system. In the same way, when you
use a graphical user interface to launch a game you want to play, that game will ef-
fectively run as your identity, with access to your files and many other things it
may not actually need.
This is not, however, how we mostly use computers today. We run applica-
tions we acquired from some less trusted third-party source, that have sweeping
functionality, which will do a wide variety of things in their environment that we
have little control over. There is a disconnect between the application model sup-
ported by the operating system and the one actually in use. This may be mitigated
by strategies such as distinguishing between normal and ‘‘admin’’ user privileges
and warning the first time they are running an application, but those do not really
address the underlying disconnect.
In other words, traditional operating systems are very good at protecting users
from other users, but not in protecting users from themselves. All programs run
with the power of the user and, if any of them misbehaves, it can do all the damage
the user can do. Think about it: how much damage could you do in, say, a UNIX
environment? You could leak all information accessible to the user. You could
perform rm -rf * to give yourself a nice, empty home directory. And if the program
is not just buggy, but also malicious, it could encrypt all your files for ransom.
Running everything with ‘‘the power of you’’ is dangerous!
Android attempts to address this with a core premise: that an application is ac-
tually the developer of that application running as a guest on the user’s device.
Thus an application is not trusted with anything sensitive that is not explicitly
approved by the user.
In Android’s implementation, this philosophy is rather directly expressed
through user IDs. When an Android application is installed, a new unique Linux
user ID (or UID) is created for it, and all of its code runs as that ‘‘user.’’ Linux user
IDs thus create a sandbox for each application, with their own isolated area of the
file system, just as they create sandboxes for users on a desktop system. In other
words, Android uses an existing feature in Linux, but in a novel way. The result is
better isolation.

10.8.11 Security

Application security in Android revolves around UIDs. In Linux, each process


runs as a specific UID, and Android uses the UID to identify and protect security
barriers. The only way to interact across processes is through some IPC mechan-
ism, which generally carries with it enough information to identify the UID of the
SEC. 10.8 ANDROID 839

caller. Binder IPC explicitly includes this information in every transaction deliv-
ered across processes so a recipient of the IPC can easily ask for the UID of the
caller.
Android predefines a number of standard UIDs for the lower-level parts of the
system, but most applications are dynamically assigned a UID, at first boot or in-
stall time, from a range of ‘‘application UIDs.’’ Figure 10-62 illustrates some com-
mon mappings of UID values to their meanings. UIDs below 10000 are fixed
assignments within the system for dedicated hardware or other specific parts of the
implementation; some typical values in this range are shown here. In the range
10000–19999 are UIDs dynamically assigned to applications by the package man-
ager when it installs them; this means at most 10,000 applications can be installed
on the system. Also note the range starting at 100000, which is used to implement
a traditional multiuser model for Android: an application that is granted UID
10002 as its identity would be identified as 110002 when running as a second user.

UID Purpose
0 Root
1000 Core system (system ser ver process)
1001 Telephony services
1013 Low-level media processes
2000 Command line shell access
10000–19999 Dynamically assigned application UIDs
100000 Start of secondary users

Figure 10-62. Common UID assignments in Android

When an application is first assigned a UID, a new storage directory is created


for it, with the files there owned by its UID. The application gets free access to its
private files there, but cannot access the files of other applications, nor can the
other applications touch its own files. This makes content providers, as discussed
in the earlier section on applications, especially important, as they are one of the
few mechanisms that can transfer data between applications.
Even the system itself, running as UID 1000, cannot touch the files of applica-
tions. This is why the installd daemon exists: it runs with special privileges to be
able to access and create files and directories for other applications. There is a
very restricted API installd provides to the package manager for it to create and
manage the data directories of applications as needed.
In their base state, Android’s application sandboxes must disallow any
cross-application interactions that can violate security between them. This may be
for robustness (preventing one app from crashing another app), but most often it is
about information access.
Consider our camera application. When the user takes a picture, the camera
application stores that picture in its private data space. No other applications can
840 CASE STUDY 1: UNIX, LINUX, AND ANDROID CHAP. 10

access that data, which is what we want since the pictures there may be sensitive
data to the user.
After the user has taken a picture, she may want to email it to a friend. Email
is a separate application, in its own sandbox, with no access to the pictures in the
camera application. How can the email application get access to the pictures in the
camera application’s sandbox?
The best-known form of access control in Android is application permissions.
Permissions are specific well-defined abilities that can be granted to an application
at install time. The application lists the permissions it needs in its manifest, and
prior to installing the application the user is informed of what it will be allowed to
do based on them.
Figure 10-63 shows how our email application could make use of permissions
to access pictures in the camera application. In this case, the camera application
has associated the READ PICTURES permission with its pictures, saying that any
application holding that permission can access its picture data. The email applica-
tion declares in its manifest that it requires this permission. The email application
can now access a URI owned by the camera, such as content://pics/1; upon receiv-
ing the request for this URI, the camera app’s content provider asks the package
manager whether the caller holds the necessary permission. If it does, the call suc-
ceeds and appropriate data is returned to the application.
Package manager in system_server process

Camera app process


Email package UID
Allow
Granted permissions PicturesProvider
Authority: "pics"
READ_CONTACTS
READ_PICTURES
INTERNET Check
Open Receive
content://pics/1 data

Browser package UID

ComposeActivity
Granted permissions

INTERNET
Email app process

Figure 10-63. Requesting and using a permission.

Permissions are not tied to content providers; any IPC into the system may be
protected by a permission through the system’s asking the package manager if the
caller holds the required permission. Recall that application sandboxing is based
SEC. 10.8 ANDROID 841

on processes and UIDs, so a security barrier always happens at a process boundary,


and permissions themselves are associated with UIDs. Given this, a permission
check can be performed by retrieving the UID associated with the incoming IPC
and asking the package manager whether that UID has been granted the correspon-
ding permission. For example, permissions for accessing the user’s location are
enforced by the system’s location manager service when applications call in to it.
Figure 10-64 illustrates what happens when an application does not hold a per-
mission needed for an operation it is performing. Here the browser application is
trying to directly access the user’s pictures, but the only permission it holds is one
for network operations over the Internet. In this case the PicturesProvider is told
by the package manager that the calling process does not hold the needed
READ PICTURES permission, and as a result throws a SecurityException back to
it.

Package manager in system_server process


Camera app process
Email package UID
Deny
Granted permissions PicturesProvider
Authority: "pics"
READ_CONTACTS
READ_PICTURES
INTERNET Check
Open Security
content://pics/1 exception

Browser package UID

BrowserMainActivity
Granted permissions

INTERNET
Browser app process

Figure 10-64. Accessing data without a permission.

Permissions provide broad, unrestricted access to classes of operations and


data. They work well when an application’s functionality is centered around those
operations, such as our email application requiring the INTERNET permission to
send and receive email. However, does it make sense for the email application to
hold a READ PICTURES permission? There is nothing about an email application
that is directly related to reading your pictures, and no reason for an email applica-
tion to have access to all of your pictures.
There is another issue with this use of permissions, which we can see by re-
turning to Fig. 10-55. Recall how we can launch the email application’s Com-
poseActivity to share a picture from the camera application. The email application
842 CASE STUDY 1: UNIX, LINUX, AND ANDROID CHAP. 10

receives a URI of the data to share, but does not know where it came from—in the
figure here it comes from the camera, but any other application could use this to let
the user email its data, from audio files to word-processing documents. The email
application only needs to read that URI as a byte stream to add it as an attachment.
However, with permissions it would also have to specify up-front the permissions
for all of the data of all of the applications it may be asked to send an email from.
We have two problems to solve. First, we do not want to give applications ac-
cess to wide swaths of data that they do not really need. Second, they need to be
given access to any data sources, even ones they do not have a priori knowledge
about.
There is an important observation to make: the act of emailing a picture is ac-
tually a user interaction where the user has expressed a clear intent to use a specific
picture with a specific application. As long as the operating system is involved in
the interaction, it can use this to identify a specific hole to open in the sandboxes
between the two applications, allowing that data through.
Android supports this kind of implicit secure data access through intents and
content providers. Figure 10-65 illustrates how this situation works for our picture
emailing example. The camera application at the bottom-left has created an intent
asking to share one of its images, content://pics/1. In addition to starting the email
compose application as we had seen before, this also adds an entry to a list of
‘‘granted URIs,’’ noting that the new ComposeActivity now has access to this URI.
Now when ComposeActivity looks to open and read the data from the URI it has
been given, the camera application’s PicturesProvider that owns the data behind the
URI can ask the activity manager if the calling email application has access to the
data, which it does, so the picture is returned.
This fine-grained URI access control can also operate the other way. There is
another intent action, android.intent.action.GET CONTENT, which an application
can use to ask the user to pick some data and return to it. This would be used in
our email application, for example, to operate the other way around: the user while
in the email application can ask to add an attachment, which will launch an activity
in the camera application for them to select one.
Figure 10-66 illustrates this new flow. It is almost identical to Fig. 10-65, the
only difference being in the way the activities of the two applications are com-
posed, with the email application starting the appropriate picture-selection activity
in the camera application. Once an image is selected, its URI is returned back to
the email application, and at this point our URI grant is recorded by the activity
manager.
This approach is extremely powerful, since it allows the system to maintain
tight control over per-application data, granting specific access to data where need-
ed, without the user needing to be aware that this is happening. Many other user
interactions can also benefit from it. An obvious one is drag and drop to create a
similar URI grant, but Android also takes advantage of other information such as
current window focus to determine the kinds of interactions applications can have.
SEC. 10.8 ANDROID 843

Activity manager in system_server process Camera app process

Granted URIs Allow


PicturesProvider
Authority: "pics"
To: ComposeActivity
URI: content://pics/1
Check

Open Receive
content://pics/1 data
Task: Pictures
RESUMED

ActivityRecord
(ComposeActivity) ComposeActivity

SEND
content://pics/1 Email app process

ActivityRecord
STOPPED

(CameraActivity)
Saved state

Figure 10-65. Sharing a picture using a content provider.

A final common security method Android uses is explicit user interfaces for al-
lowing/removing specific types of access. In this approach, there is some way an
application indicates it can optionally provide some functionally, and a sys-
tem-supplied trusted user interface that provides control over this access.
A typical example of this approach is Android’s input-method architecture.
An input method is a specific service supplied by a third-party application that al-
lows the user to provide input to applications, typically in the form of an on-screen
keyboard. This is a highly sensitive interaction in the system, since a lot of person-
al data will go through the input-method application, including passwords the user
types.
An application indicates it can be an input method by declaring a service in its
manifest with an intent filter matching the action for the system’s input-method
protocol. This does not, however, automatically allow it to become an input meth-
od, and unless something else happens the application’s sandbox has no ability to
operate like one.
Android’s system settings include a user interface for selecting input methods.
This interface shows all available input methods of the currently installed applica-
tions and whether or not they are enabled. If the user wants to use a new input
method after they have installed its application, they must go to this system settings
interface and enable it. When doing that, the system can also inform the user of
the kinds of things this will allow the application to do.
844 CASE STUDY 1: UNIX, LINUX, AND ANDROID CHAP. 10

Activity manager in system_server process Camera app process

Allow
Granted URls
PicturesProvider
To: ComposeActivity Authority: "pics"
URI: content://pics/1
Check

Open Receive
content://pics/1 data
Task: Pictures
RESUMED

ActivityRecord
(PicturePickerActivity) ComposeActivity

RECEIVE
GET
content://pics/1 Email app process

ActivityRecord
STOPPED

(ComposeActivity)
Saved state

Figure 10-66. Adding a picture attachment using a content provider.

Even once an application is enabled as an input method, Android uses fine-


grained access-control techniques to limit its impact. For example, only the appli-
cation that is being used as the current input method can actually have any special
interaction; if the user has enabled multiple input methods (such as a soft keyboard
and voice input), only the one that is currently in active use will have those features
available in its sandbox. Even the current input method is restricted in what it can
do, through additional policies such as only allowing it to interact with the window
that currently has input focus.

10.8.12 Process Model

The traditional process model in Linux is a fork to create a new process, fol-
lowed by an exec to initialize that process with the code to be run and then start its
execution. The shell is responsible for driving this execution, forking and execut-
ing processes as needed to run shell commands. When those commands exit, the
process is removed by Linux.
Android uses processes somewhat differently. As discussed in the previous
section on applications, the activity manager is the part of Android responsible for
managing running applications. It coordinates the launching of new application
processes, determines what will run in them, and when they are no longer needed.
SEC. 10.8 ANDROID 845

Starting Processes

In order to launch new processes, the activity manager must communicate with
the zygote. When the activity manager first starts, it creates a dedicated socket
with zygote, through which it sends a command when it needs to start a process.
The command primarily describes the sandbox to be created: the UID that the new
process should run as and any other security restrictions that will apply to it.
Zygote thus must run as root: when it forks, it does the appropriate setup for the
UID it will run as, finally dropping root privileges and changing the process to the
desired UID.
Recall in our previous discussion about Android applications that the activity
manager maintains dynamic information about the execution of activities (in
Fig. 10-52), services (Fig. 10-57), broadcasts (to receivers as in Fig. 10-60), and
content providers (Fig. 10-61). It uses this information to drive the creation and
management of application processes. For example, when the application launcher
calls in to the system with a new intent to start an activity as we saw in Fig. 10-52,
it is the activity manager that is responsible for making that new application run.
The flow for starting an activity in a new process is shown in Fig. 10-67. The
details of each step in the illustration are:

1. Some existing process (such as the app launcher) calls in to the activ-
ity manager with an intent describing the new activity it would like to
have started.
2. Activity manager asks the package manager to resolve the intent to an
explicit component.
3. Activity manager determines that the application’s process is not al-
ready running, and then asks zygote for a new process of the ap-
propriate UID.
4. Zygote performs a fork, creating a new process that is a clone of itself,
drops privileges and sets its UID appropriately for the application’s
sandbox, and finishes initialization of Dalvik in that process so that
the Java runtime is fully executing. For example, it must start threads
like the garbage collector after it forks.
5. The new process, now a clone of zygote with the Java environment
fully up and running, calls back to the activity manager, asking
‘‘What am I supposed to do?’’
6. Activity manager returns back the full information about the applica-
tion it is starting, such as where to find its code.
7. New process loads the code for the application being run.
846 CASE STUDY 1: UNIX, LINUX, AND ANDROID CHAP. 10

8. Activity manager sends to the new process any pending operations, in


this case ‘‘start activity X.’’
9. New process receives the command to start an activity, instantiates the
appropriate Java class, and executes it.

System_server process Application process

PackageManagerService Activity instance

2 Resolve Intent 9

ss
cla
sta

s
thi Application code
rtA

te
cti

1 a
nti
vit

ta
y()

8 Ins 7

ActivityManagerService 6 Load this app s code


Android framework
5 "Who am I?"

ess
proc
a new
C reate Zygote process

Figure 10-67. Steps in launching a new application process.

Note that when we started this activity, the application’s process may already
have been running. In that case, the activity manager will simply skip to the end,
sending a new command to the process telling it to instantiate and run the ap-
propriate component. This can result in an additional activity instance running in
the application, if appropriate, as we saw previously in Fig. 10-56.

Process Lifecycle

The activity manager is also responsible for determining when processes are
no longer needed. It keeps track of all activities, receivers, services, and content
providers running in a process; from this it can determine how important (or not)
the process is.
Recall that Android’s out-of-memory killer in the kernel uses a process’s
oom adj as a strict ordering to determine which processes it should kill first. The
activity manager is responsible for setting each process’s oom adj appropriately
SEC. 10.8 ANDROID 847

based on the state of that process, by classifying them into major categories of use.
Figure 10-68 shows the main categories, with the most important category first.
The last column shows a typical oom adj value that is assigned to processes of this
type.

Category Description oom adj


SYSTEM The system and daemon processes −16
PERSISTENT Always-running application processes −12
FOREGROUND Currently interacting with user 0
VISIBLE Visible to user 1
PERCEPTIBLE Something the user is aware of 2
SERVICE Running background services 3
HOME The home/launcher process 4
CACHED Processes not in use 5

Figure 10-68. Process importance categories.

Now, when RAM is getting low, the system has configured the processes so
that the out-of-memory killer will first kill cached processes to try to reclaim
enough needed RAM, followed by home, service, and on up. Within a specific
oom adj level, it will kill processes with a larger RAM footprint before smaller
ones.
We’ve now seen how Android decides when to start processes and how it cate-
gorizes those processes in importance. Now we need to decide when to have proc-
esses exit, right? Or do we really need to do anything more here? The answer is,
we do not. On Android, application processes never cleanly exit. The system just
leaves unneeded processes around, relying on the kernel to reap them as needed.
Cached processes in many ways take the place of the swap space that Android
lacks. As RAM is needed elsewhere, cached processes can be thrown out of active
RAM. If an application later needs to run again, a new process can be created,
restoring any previous state needed to return it to how the user last left it. Behind
the scenes, the operating system is launching, killing, and relaunching processes as
needed so the important foreground operations remain running and cached proc-
esses are kept around as long as their RAM would not be better used elsewhere.

Process Dependencies

We at this point have a good overview of how individual Android processes are
managed. There is a further complication to this, however: dependencies between
processes.
As an example, consider our previous camera application holding the pictures
that have been taken. These pictures are not part of the operating system; they are
848 CASE STUDY 1: UNIX, LINUX, AND ANDROID CHAP. 10

implemented by a content provider in the camera application. Other applications


may want to access that picture data, becoming a client of the camera application.
Dependencies between processes can happen with both content providers
(through simple access to the provider) and services (by binding to a service). In
either case, the operating system must keep track of these dependencies and man-
age the processes appropriately.
Process dependencies impact two key things: when processes will be created
(and the components created inside of them), and what the oom adj importance of
the process will be. Recall that the importance of a process is that of the most im-
portant component in it. Its importance is also that of the most important process
that is dependent on it.
For example, in the case of the camera application, its process and thus its con-
tent provider is not normally running. It will be created when some other process
needs to access that content provider. While the camera’s content provider is being
accessed, the camera process will be considered at least as important as the process
that is using it.
To compute the final importance of every process, the system needs to main-
tain a dependency graph between those processes. Each process has a list of all
services and content providers currently running in it. Each service and content
provider itself has a list of each process using it. (These lists are maintained in
records inside the activity manager, so it is not possible for applications to lie about
them.) Walking the dependency graph for a process involves walking through all
of its content providers and services and the processes using them.
Figure 10-69 illustrates a typical state processes can be in, taking into account
dependencies between them. This example contains two dependencies, based on
using a camera-content provider to add a picture attachment to an email as dis-
cussed in Fig. 10-66. First is the current foreground email application, which is
making use of the camera application to load an attachment. This raises the cam-
era process up to the same importance as the email app. Second is a similar situa-
tion, the music application is playing music in the background with a service, and
while doing so has a dependency on the media process for accessing the user’s mu-
sic media.
Consider what happens if the state of Fig. 10-69 changes so that the email ap-
plication is done loading the attachment, and no longer uses the camera content
provider. Figure 10-70 illustrates how the process state will change. Note that the
camera application is no longer needed, so it has dropped out of the foreground
importance, and down to the cached level. Making the camera cached has also
pushed the old maps application one step down in the cached LRU list.
These two examples give a final illustration of the importance of cached proc-
esses. If the email application again needs to use the camera provider, the pro-
vider’s process will typically already be left as a cached process. Using it again is
then just a matter of setting the process back to the foreground and reconnecting
with the content provider that is already sitting there with its database initialized.
SEC. 10.9 SUMMARY 849

Process State Importance


system Core par t of operating system SYSTEM
phone Always running for telephony stack PERSISTENT
email Current foreground application FOREGROUND
camera In use by email to load attachment FOREGROUND
music Running background service playing music PERCEPTIBLE
media In use by music app for accessing user’s music PERCEPTIBLE
download Downloading a file for the user SERVICE
launcher App launcher not current in use HOME
maps Previously used mapping application CACHED

Figure 10-69. Typical state of process importance

Process State Importance


system Core par t of operating system SYSTEM
phone Always running for telephony stack PERSISTENT
email Current foreground application FOREGROUND
music Running background service playing music PERCEPTIBLE
media In-use by music app for accessing user’s music PERCEPTIBLE
download Downloading a file for the user SERVICE
launcher App launcher not current in use HOME
camera Previously used by email CACHED
maps Previously used mapping application CACHED+1

Figure 10-70. Process state after email stops using camera

10.9 SUMMARY
Linux began its life as an open-source, full-production UNIX clone, and is now
used on machines ranging from smartphones and notebook computers to
supercomputers. Three main interfaces to it exist: the shell, the C library, and the
system calls themselves. In addition, a graphical user interface is often used to sim-
plify user interaction with the system. The shell allows users to type commands for
execution. These may be simple commands, pipelines, or more complex struc-
tures. Input and output may be redirected. The C library contains the system calls
and also many enhanced calls, such as printf for writing formatted output to files.
The actual system call interface is architecture dependent, and on x86 platforms
consists of roughly 250 calls, each of which does what is needed and no more.
The key concepts in Linux include the process, the memory model, I/O, and
the file system. Processes may fork off subprocesses, leading to a tree of processes.
850 CASE STUDY 1: UNIX, LINUX, AND ANDROID CHAP. 10

Process management in Linux is different compared to other UNIX systems in that


Linux views each execution entity—a single-threaded process, or each thread with-
in a multithreaded process or the kernel—as a distinguishable task. A process, or a
single task in general, is then represented via two key components, the task struc-
ture and the additional information describing the user address space. The former
is always in memory, but the latter data can be paged in and out of memory. Proc-
ess creation is done by duplicating the process task structure, and then setting the
memory-image information to point to the parent’s memory image. Actual copies
of the memory-image pages are created only if sharing is not allowed and a memo-
ry modification is required. This mechanism is called copy on write. Scheduling is
done using a weighted fair queueing algorithm that uses a red-black tree for the
tasks’ queue management.
The memory model consists of three segments per process: text, data, and
stack. Memory management is done by paging. An in-memory map keeps track of
the state of each page, and the page daemon uses a modified dual-hand clock algo-
rithm to keep enough free pages around.
I/O devices are accessed using special files, each having a major device num-
ber and a minor device number. Block device I/O uses the main memory to cache
disk blocks and reduce the number of disk accesses. Character I/O can be done in
raw mode, or character streams can be modified via line disciplines. Networking
devices are treated somewhat differently, by associating entire network protocol
modules to process the network packets stream to and from the user process.
The file system is hierarchical with files and directories. All disks are mounted
into a single directory tree starting at a unique root. Individual files can be linked
into a directory from elsewhere in the file system. To use a file, it must be first
opened, which yields a file descriptor for use in reading and writing the file. Inter-
nally, the file system uses three main tables: the file descriptor table, the
open-file-description table, and the i-node table. The i-node table is the most im-
portant of these, containing all the administrative information about a file and the
location of its blocks. Directories and devices are also represented as files, along
with other special files.
Protection is based on controlling read, write, and execute access for the
owner, group, and others. For directories, the execute bit means search permission.
Android is a platform for allowing apps to run on mobile devices. It is based
on the Linux kernel, but consists of a large body of software on top of Linux, plus
a small number of changes to the Linux kernel. Most of Android is written in Java.
Apps are also written in Java, then translated to Java bytecode and then to Dalvik
bytecode. Android apps communicate by a form of protected message passing call-
ed transactions. A special Linux kernel model called the Binder handles the IPC.
Android packages are self contained and have a manifest desccribing what is in
the package. Packages contain activities, receivers, content providers, and intents.
The Android security model is different from the Linux model and carefully sand-
boxes each app because all apps are regarded as untrustworthy.

You might also like