Embedded Android
Embedded Android
Porting, Extending, and Customizing
by Karim Yaghmour.
Chapter 2 Internals Primer
App Developer’s View
https://developer.android.com/guide/
Android Concepts
Android apps are built as a combination of components that can be invoked individually.
- Components There are four main types of components:
- Activities An activity is the entry point for interacting with the user. It represents a single screen with a user interface. For example, an email app might have several activities: one activity that shows a list of new emails, another activity to compose an email, and another activity for reading emails. Although the activities work together to form a cohesive user experience in the email app, each one is independent of the others.
- Services A service is a general-purpose entry point for keeping an app running in the background to perform long-running operations or to perform work for remote processes.
- Broadcast receivers A broadcast receiver is a component that enables the system to deliver events to the app outside of a regular user flow, allowing the app to respond to system-wide broadcast announcements.
- Content providers A content provider manages a shared set of app data that you can store in the file system, in a SQLite database, on the web, or on any other persistent storage location that your app can access. Through the content provider, other apps can query or modify the data if the content provider allows it.
- Intents An Intent is a messaging object you can use to request an action from another app component. Three of the four component types — activities, services, and broadcast receivers—are activated by an asynchronous message called an intent. An intent is created with an Intent object, which defines a message to activate either a specific component (explicit intent) or a specific type of component (implicit intent).
Components can be declared as capable of dealing with given intent types using filters in the manifest file. Your app must declare all its components in this file, AndroidManifest.xml, which must be at the root of the app project directory.
The manifest does a number of things:
- Declaring components
- Declaring component capabilities
- Declaring app requirements
You must declare all app components using the following elements in the manifest file:
- <activity> elements for activities.
- <service> elements for services.
- <receiver> elements for broadcast receivers.
- <provider> elements for content providers.
An Android app requires resources that are separate from the source code, such as images, audio files, and anything relating to the visual presentation of the app.
For every resource that you include in your Android project, the SDK build tools define a unique integer ID, which you can use to reference the resource from your app code or from other resources defined in XML.
For example, the image file res/drawable/logo.png, the SDK tools generate a resource ID named R.drawable.logo. This ID maps to an app-specific integer, which you can use to reference the image and insert it in your user interface.
The Android SDK tools compile your code along with any data and resource files into an APK, an Android package, which is an archive file with an .apk suffix.
Each Android app lives in its own security sandbox, protected by the following Android security features:
- The Android operating system is a multi-user Linux system in which each app is a different user.
- By default, the system assigns each app a unique Linux user ID (the ID is used only by the system and is unknown to the app). The system sets permissions for all the files in an app so that only the user ID assigned to that app can access them.
- Each process has its own virtual machine (VM), so an app's code runs in isolation from other apps.
Framework Intro
App Development Tools
Native Development
Overall Architecture
Linux Kernel
Wakelocks
Instead of letting the system be put to sleep at the user’s behest, an Androidized kernel is made to go to sleep as soon and as often as possible.
Wakelocks are provided to keep the system awake so that the important processing is being done.
Low-Memory Killer
Android’s low-memory killer applies the policies described in the app development documentation, weeding out processes hosting components that haven’t been used in a long time and are not high priority.Android’s low-memory killer is based on the OOM adjustments mechanism available in Linux that enables the enforcement of different OOM kill priorities for different processes.
Android therefore attributes different OOM adjustment levels to different types of processes, the user-space policies are themselves applied by the init process at startup, and readjusted and partly enforced at runtime by the Activity Manager Service.
Binder
Android’s Binder mechanism is inspired by the OpenBinder code which is an RPC/IPC mechanism.
Binder attempts to provide remote object invocation capabilities without having to deal with a new OS.
The remote object can therefore be implemented in any desired language and may share the same process space as other remote services or have its own separate process.
Binder is a cornerstone of Android’s architecture. It’s what allows apps to talk the System Server, and it’s what apps use to talk to each others’ service components, app developers don’t actually talk to the Binder directly. Instead, they use the interfaces and stubs generated by the aidl tool.
The in-kernel driver part of the Binder mechanism is a character driver accessible through /dev/binder.
Anonymous Shared Memory (ashmem)
Ashmem is described as being similar to POSIX SHM “but with different behavior.”- It uses reference counting to destroy memory regions when all processes referring to them have exited
- It will shrink mapped regions if the system is in need of memory.
Alarm
Android’s alarm driver is actually layered on top of the kernel’s existing Real-Time Clock (RTC) and High-Resolution Timers (HRT) functionalities.
In old versions of Linux, timers are only supported at a resolution of 1 jiffy. The length of a jiffy is dependent on the value of HZ in the Linux kernel, and is 1 millisecond on i386 and some other platforms, and 10 milliseconds on most embedded platforms.
Higher resolution timers are needed to allow the system to wake up and process data at more accurate intervals: timers with accuracy better than 1 jiffy.
In order to use high resolution timers, you need to enable the kernel support with "CONFIG_HIGH_RES_TIMERS=y".
Most computers have one or more hardware clocks which record the current "wall clock" time. These are called "Real Time Clocks" (RTCs).
RTCs should not be confused with the system clock, which is a software clock maintained by the kernel.
The system clock reports seconds and microseconds since a start point, defined to be the POSIX Epoch: 1970-01-01 00:00:00 +0000 (UTC). (One common implementation counts timer interrupts, once per "jiffy", at a frequency of 100, 250, or 1000 Hz.)
The system clock can only report time since system boot, the system clock will often be set to the current wall clock time using an RTC.
Linux application developers typically call the setitimer() system call to get a signal when a given time value expires, however, it doesn’t work when the system is suspended.
The kernel’s RTC driver is accessible through /dev/rtc and enables its users to use an ioctl() to, among other things, set an alarm that will be activated by the RTC hardware device in the system. That alarm will fire off whether the system is suspended or not
Android’s alarm driver uses the kernel’s HRT functionality to provide alarms to its users, if the system is about to suspend itself, it programs the RTC so that the system gets woken up at the appropriate time.
Logger
Most Linux distributions include two logging systems:
- the kernel’s own log typically accessed through the dmesg command,
- the system logs typically stored in files in the /var/log directory.
Each buffer it manages is exposed as a separate entry within /dev/log/.
All user-space component rely on liblog, which provides a number of different logging functions.
The logcat utility is also relies on liblog.
It requires every event being logged to have a priority, a tag, and data.
When you type adb logcat on the host, the daemon actually launches the logcat command locally on the target to dump its “main” buffer and then transfers that back to the host to be shown on the terminal.
Hardware Support
The Linux Approach
The usual way to provide support for new hardware in Linux is to create device drivers and the corresponding hardware is thereafter generally accessible in user-space through entries in /dev.
Android’s General Approach
The Android stack typically relies on shared libraries provided by manufacturers to interact with hardware.The hardware manufacture providing the shared library to implement the support for the HAL(Hardware Abstraction Layer).
Generally speaking, you can consider the HAL layer as being the hardware library loader .
Android does not in fact specify how the shared library and the driver or kernel subsystem should interact. Only the API provided by the shared library to the upper layers is specified by the HAL.
A system service corresponding to the type of hardware is typically responsible for loading and interfacing with the shared library.
To add the support for a given type of hardware, you need to understand the internals of the system service corresponding to your hardware.
The system service will be split in two parts: one part in Java and another part in C/C++.
Loading and Interfacing Methods
There are various ways in which system services and Android in general interact with the shared libraries implementing hardware support and hardware devices:
- libhardware in HAL exposed a function hw_get_module() which relies on the classic dlopen() to load specific hardware- supporting shared library (a.k.a. a “module” in HAL terminology) libraries dynamically.
- system services are simply linked against a given .so file at build time.
- system services invoke dlopen() directly(the HAL is not used)
- Sockets are sometimes used by system services or framework components to talk to a remote daemon or service that actually interacts with the hardware.
- Some entries in sysfs (/sys) can be used to control the behavior of hardware and/or kernel subsystems
- In some corner cases AOSP components directly access device nodes.
- D-bus
Device Support Details
Hardware | System Service | Interface to User-Space Hardware | Interface to Hardware |
---|---|---|---|
Audio | Audio Flinger | Linker-loadeda libaudio.so | Up to hardware manufacturer, though ALSA is typical |
Bluetooth | Bluetooth Service | Socket/D-Bus to BlueZ | BlueZ stack |
Camera | Camera Service | Linker-loadedc libcamera.so | Up to hardware manufacturer, sometimes Video4Linux |
Display | Surface Flinger | HAL-loaded gralloc module | Up to hardware manufacturer, /dev/fb0 or /dev/graphics/fb0 |
GPS | Location Manager | HAL-loaded gps module | Up to hardware manufacturer |
Input | Input Manager | Native libui.so library | Entries in /dev/input/ |
Lights | Lights Service | HAL-loaded lights module | Up to hardware manufacturer |
Media | N/A, StageFright framework within Media Service | dlopen on libstagefrighthw.so | Up to hardware manufacturer |
Network interfaces | Network Management Service | Socket to netd | ioctl() on interfaces |
Power management | Power Manager Service | Linker-loaded libhardware_legacy.so | Entries in /sys/power/ or, in older days, /sys/ android_power/ |
Radio (Phone) | Phone Service | Socket to rild, which itself does a dlopen()on manufacturer- provided .so | Up to hardware manufacturer |
Storage | Mount Service | Socket to vold | System calls and /sys entries |
Sensors | Sensor Service | HAL-loaded sensors module | Up to hardware manufacturer |
Vibrator | Vibrator Service | Linker-loaded libhardware_legacy.so | Up to hardware manufacturer |
WiFi | Wifi Service | Linker-loaded libhardware_legacy.so | Classic wpa_supplicant in most cases |
Dalvik and Android’s Java
The code you write in Java gets compiled by a Java compiler into architecture-independent byte-code that is executed at runtime by a byte-code interpreter, also commonly referred to as a “virtual machine.”
Dalvik is Android’s Java virtual machine.
Android runtime (ART) is the managed runtime used by applications and some system services on Android.
ART and its predecessor Dalvik are compatible runtimes running Dex bytecode, so apps developed for Dalvik should work when running with ART.
The Java Development Kit (JDK) provides:
- the Java compiler
- the Java byte-code interpreter(JVM)
- the Java libraries
For more information about the features and internals of Dalvik, you can also just go to YouTube and search for “Dan Bornstein Dalvik.”
JNI
It’s essentially a call bridge to other languages such as C and C++.
The AOSP relies massively on JNI to enable Java-coded services and components to interface with An‐ droid’s low-level functionality, which is mostly written in C and C++.
System Services
You cane use the service utility on an Android.
Chapter 3 AOSP Jump-Start
Inside the AOSP
A summary of the AOSP’s top-level directory:
- art
- bionic Android’s custom C library
- bootable OTA, recovery mechanism and reference bootloader
- build Build system
- cts Comptability Test Suite
- dalvik Dalvik VM
- developers
- development Development tools
- device Device-specific files and components
- docs Content of http://source.android.com
- external External projects imported into the AOSP
- frameworks Core components such as system services
- hardware HAL and hardware support libraries
- libcore Apache Harmony
- libnativehelper Helper functions for use with JNI
- log
- out
- packages Stock Android apps, providers, and IMEs
- pdk Platform Development Kit
- platform_testing
- prebuilts Prebuilt binaries, including toolchains
- sdk Software Development Kit
- system “Embedded Linux” platform that houses Android
- test
- toolchain
- tools Various IDE tools
- vendor
Directories prebuilt and external are mostly made up of content from other open source projects and include things like various GNU toolchain versions, kernel images, common libraries, and frameworks such as OpenSSL and WebKit, etc.
A lot of key components come from frameworks/base/, which is where the bulk of Android’s “brains” are located.
Using the Android Debug Bridge (ADB)
You can shell into the running emulator, or any real device connected through USB for that matter, using the adb tool:
$ adb shell
To exit an ADB shell session, all you need to do is type Ctrl-D.When you start adb for the first time on the host, it starts a server in the background whose job is to manage the connections to all Android devices connected to the host.
You can actually ask that daemon what devices it sees:
$ adb devices
If there are multiple devices connected, you can tell it which device you want to talk to using the -s flag to identify the serial number of the device:
$ adb -s 0000021459584822 shell
adb can do a lot more than just give you a shell, you can run "adb" to get a list of commands. For ex., use adb to dump the data contained in the main logger buffer:
$ adb logcat
You can also copy files to and from the device:
$ adb push data.txt /data/local 1 KB/s (87 bytes in 0.043s)
$ adb pull /proc/config.gz
95 KB/s (7087 bytes in 0.072s)
adb might just hang on because of the device connected, to kill the host-side daemon:
$ adb kill-server
Mastering the Emulator
Android Emulator is included with Android Studio.
The Android Emulator simulates various Android phone, tablet, Wear OS, and Android TV devices on your computer. It comes with predefined configurations for popular device types and can transfer data faster than a device connected over USB.
Each instance of the Android Emulator uses an Android Virtual Device (AVD) to configure its size, form factor, Android version, and various hardware characteristics. To effectively test your app, you should create an AVD that models each device type that your app is designed to support.
The AVD Manager is an interface you can launch from Android Studio that helps you create and manage AVDs.
To open the AVD Manager, click AVD Manager icon in the toolbar:
- Click Create Virtual Device, at the bottom of the AVD Manager dialog. The Select Hardware page appears.
- Select a hardware profile, and then click Next. If you don't see the hardware profile you want, you can create or import a hardware profile. The System Image page appears.
- Select the system image for a particular API level You need to Download the selected image, and then click Next. The API level of the target device is important, because your app won't be able to run on a system image with an API level that's less than that required by your app, as specified in the minSdkVersion attribute of the app manifest file. For more information about the relationship between system API level and minSdkVersion, see Versioning Your Apps. The Verify Configuration page appears.
- Change AVD properties as needed, and then click Finish.
- To run an emulator that uses an AVD double-click the AVD. Or click the Launch button.
- To stop a running emulator right-click an AVD and select Stop. Or click Menu and select Stop.
- To clear the data for an emulator and return it to the same state as when it was first defined right-click an AVD and select Wipe Data. Or click Menu and select Wipe Data.
Chapter 4 The Build System
Within the context of Android’s build system, a “module” is any component of the AOSP that needs to be built. This might be a binary, an app package, a library, etc., and it might have to be built for the target or the host, but it’s still a “module” with regards to the build system.
The Android.mk files that specify how the local “module” is built.
The build system doesn’t generate object files(*.o) or any sort of intermediate output within the same location as the source files.
None of the existing AOSP directories are used in any of the output. Instead, the build system creates an out/ directory where it stores everything it generates.
Hence, a make clean is very much the same thing as an rm -rf out/.
Android uses a single global makefile for building the entire project based on module-provided .mk files.
Architecture
The entry point of the build system is the file build/core/main.mk.
Android’s build system pulls everything into a single makefile, each .mk file you see eventually becomes part of a single huge makefile that contains the rules for building all the pieces in the system.
Every time you type make, the aggregation of the .mk files happens.
What it’s doing is incorporating every Android.mk file it can find in the AOSP.
In build/core/main.mk:
subdir_makefiles := $(SOONG_ANDROID_MK) $(call first-makefiles-under,$(TOP)
Configuration
The build configuration is through the inclusion of build/make/core/config.mk.
This file sets up standard variables based on the current configuration and platform, it includes buildspec.mk at the top-level directory.
The build configuration can be configured either by the use of the envsetup.sh and lunch commands.
In addition to selecting which parts of the AOSP to build and which options to build them with, a BoardConfig.mk file describes the target to be built, such as the command line to be provided to the kernel, the base address at which the kernel should be loaded.
- build/target/board/generic_$(ARCH)/BoardConfig.mk
- device/VENDOR/*/TARGET_DEVICE/BoardConfig.mk
envsetup.sh
Mainly, it defines a series of shell commands that are useful.
You can use hmm to get the available commands.
- lunch [product_name]-[build_variant]
- croot Changes directory to the top of the tree.
- godir Go to the directory containing a file.
- m Make (build) from the top level regardless of where you are
- mm Just make(build) the modules found in the current directory. Note that mm doesn’t rebuild the entire tree and, therefore, won’t regenerate AOSP images even if a dependent module has changed.
This is how you end up with the "lunch" menu you saw. Note that the menu asks you to choose a combo. Essentially, this is a combination of a TARGET_PRODUCT and TARGET_BUILD_VARIANT
You can skip envsetup.sh and lunch if you copy the build/buildspec.mk.default into the top-level directory, rename it to buildspec.mk
Function Definitions
Because the build system is fairly large, he build system defines a large number of functions in the build/make/core/definitions.mk file.
Functions offer a variety of operations, including file lookup (e.g., all- makefiles-under and all-c-files-under), transformation (e.g., transform-c-to-o and transform-java-to-classes.jar), copying (e.g., copy-file-to-target), and utility (e.g., my-dir.)
Not only are these functions used throughout the rest of the build system’s components, acting as its core library, but they’re sometimes also directly used in modules’ Android.mk files.
Try grep define build/core/definitions.mk for an exhaustive list.
Call them via:
$(call FUNCTION, PARAM-1, PARAM-2)
or without parameters:
$(call FUNCTION)
Here are some possibly interesting functions:- print-vars print all Makefile variables, for debugging (not their values).
- emit-line output a line during building, to a file
- dump-words-to-file output a list of words to a file
- copy-one-file copy a file from one place to another (dest on target?)
- all-subdir-makefiles Recursively calls all Android.mk files, beginning in the current directory (Usage: include $(call all-subdir-makefiles)).
Build Variables
- $(ANDROID_BUILD_TOP) AOSP file system root folder
- $(LOCAL_PATH) Usually the current directory. Gets set by the developer/user in every Android.mk file. It get's overwritten in other Android.mk files down the file-tree (E.g. when you use include $(call all-subdir-makefiles)).
SAVED_LOCAL_PATH := $(call my-dir)
include $(call all-subdir-makefiles)
LOCAL_PATH:= $(SAVED_LOCAL_PATH)
Add a file directly to the output area
You can copy a file directly to the output area, without building anything, using the add-prebuilt-files function. The following line, extracted from prebuilt/android-arm/gdbserver/Android.mk copies a list of files to the EXECUTABLES directory in the output area:
$(call add-prebuilt-files, EXECUTABLES, $(prebuilt_files))
Steps for adding a new program to the Android source tree
- make a directory under 'external' e.g. ANDROID/external/myprogram
- create your C/cpp files.
- create Android.mk as clone of external/ping/Android.mk
- Change the names ping.c and ping to match your C/cpp files and program name
- add the directory name in ANDROID/build/core/main.mk after external/zlib as external/myprogram
- make from the root of the source tree
- your files will show up in the build output area, and in system images. You can copy your file from the build output area, under out/target/product/..., if you want to copy it individually to the target (not do a whole install)
Grep macros and functions
- cgrep PATTERN Greps on all local C/C++ files.
- ggrep PATTERN Greps on all local Gradle files.
- jgrep PATTERN Greps on all local Java files.
- resgrep PATTERN Greps on all local res/*.xml files.
- mangrep PATTERN Greps on all local AndroidManifest.xml files.
- mgrep PATTERN Greps on all local Makefiles files.
- sepgrep PATTERN Greps on all local sepolicy files.
- sgrep PATTERN Greps on all local source files.
- godir filename Go to the directory containing a file
Main Make Recipes
build/core/Makefile:- boot image
# #################################################################
# Targets for boot/OS images
# #################################################################
ifneq ($(strip $(TARGET_NO_BOOTLOADER)),true)
INSTALLED_BOOTLOADER_MODULE := $(PRODUCT_OUT)/bootloader
ifeq ($(strip $(TARGET_BOOTLOADER_IS_2ND)),true)
INSTALLED_2NDBOOTLOADER_TARGET := $(PRODUCT_OUT)/2ndbootloader
else
INSTALLED_2NDBOOTLOADER_TARGET :=
endif
else
INSTALLED_BOOTLOADER_MODULE :=
INSTALLED_2NDBOOTLOADER_TARGET :=
endif # TARGET_NO_BOOTLOADER
ifneq ($(strip $(TARGET_NO_KERNEL)),true)
INSTALLED_KERNEL_TARGET := $(PRODUCT_OUT)/kernel
else
INSTALLED_KERNEL_TARGET :=
endif
- Kernel images There is no kernel part of the official AOSP releases, you need to find an Androidized kernel for your target, build it separately from the AOSP, and feed it to the AOSP. Some platforms put the kernel build script in the *.mk file under device/VENDOR.
- NDK The NDK’s build system is in ndk/build/
- CTS The rules for building the CTS are in build/core/tasks/cts.mk
Cleaning
The clean target itself is defined in build/core/main.mk. build/core/cleanbuild.mk defines the installcleantarget which is automatically invoked whenever you change TARGET_PRODUCT, TARGET_BUILD_VARIANT or PRODUCT_LOCALES.Module Build Templates
Android is built from a top-down perspective, down to the level of AOSP modules’ Android.mk files. The Android build system looks for the first makefile in a hierarchy and doesn’t look in any subdirectories underneath the directory where one was found. Each template is tailored for a specific type of module, and module authors can use a set of documented variables, all prefixed by LOCAL_, to modulate the templates’ behavior and output. The build templates are provided in build/core/*.mk. Android.mk gets access to them through the include directive. For ex.,
include $(BUILD_PACKAGE)
Android.mk files don’t actually include the .mk templates by name. Instead, they include a variable that is set to the corresponding .mk file. Variable | Template |
---|---|
BUILD_EXECUTABLE | executable.mk |
. | . |
BUILD_PACKAGE | package.mk |
BUILD_KEY_CHAR_MAP | key_char_map.mk |
Output
The build output operates in three stages and in two modes(host, target):Build Recipes
The Default droid Build
When you run plain make, it’s as if you had typed:
$ make droid
droid is in fact the default target as defined in build/make/core/main.mk. Here is a list of different make targets you can use to build different parts of the system: - make sdk build the tools that are part of an SDK (adb, fastboot, etc.)
- make snod build the system image from the current software binaries
- make services
- make runtime
- make droid make droid is the normal build.
- make all make everything, whether it is included in the product definition or not
- make clean remove all built files (prepare for a new build). Same as rm -rf out/
- make modules shows a list of submodules that can be built (List of all LOCAL_MODULE definitions)
- make [local_module] make a specific module (note that this is not the same as directory name. It is the LOCAL_MODULE definition in the Android.mk file)
- make clean-[local_module] clean a specific module
- make bootimage TARGET_PREBUILT_KERNEL=/path/to/bzImage create a new boot image with custom bzImage
Seeing the Build Commands
If you want to see actual commands used to build the AOSP, like the gcc command lines for example, add the showcommands target to the build command line.Building the SDK for Linux and Mac OS
Building the CTS
Building the NDK
Building a Single Module
Use the Launcher2 module (i.e., the Home screen) as an example:
- build individual module
$ make Launcher2
$ make clean-Launcher2
$ make Launcher2 snod
If you’d ever like to build code against the AOSP and its Bionic library but don’t want to incorporate that into the AOSP, you can use a makefile such as the following to get the job done:
# Paths and settings
TARGET_PRODUCT = generic
ANDROID_ROOT = /home/karim/android/aosp-2.3.x
BIONIC_LIBC = $(ANDROID_ROOT)/bionic/libc
PRODUCT_OUT = $(ANDROID_ROOT)/out/target/product/$(TARGET_PRODUCT)
CROSS_COMPILE = $(ANDROID_ROOT)/prebuilt/linux-x86/toolchain/arm-eabi-4.4.3/bin/arm-eabi-
# Tool names
AS = $(CROSS_COMPILE)as
AR = $(CROSS_COMPILE)ar
CC = $(CROSS_COMPILE)gcc
CPP = $(CC) -E
LD = $(CROSS_COMPILE)ld
NM = $(CROSS_COMPILE)nm
OBJCOPY = $(CROSS_COMPILE)objcopy
OBJDUMP = $(CROSS_COMPILE)objdump
RANLIB = $(CROSS_COMPILE)ranlib
READELF = $(CROSS_COMPILE)readelf
SIZE = $(CROSS_COMPILE)size
STRINGS = $(CROSS_COMPILE)strings
STRIP = $(CROSS_COMPILE)strip
export AS AR CC CPP LD NM OBJCOPY OBJDUMP RANLIB READELF SIZE STRINGS STRIP
# Build settings
CFLAGS = -O2 -Wall -fno-short-enums
HEADER_OPS = -I$(BIONIC_LIBC)/arch-arm/include \
-I$(BIONIC_LIBC)/kernel/common \
-I$(BIONIC_LIBC)/kernel/arch-arm
LDFLAGS = -nostdlib -Wl,-dynamic-linker,/system/bin/linker \
$(PRODUCT_OUT)/obj/lib/crtbegin_dynamic.o \
$(PRODUCT_OUT)/obj/lib/crtend_android.o \
-L$(PRODUCT_OUT)/obj/lib -lc -ldl
# Installation variables
EXEC_NAME = example-app
INSTALL = install
INSTALL_DIR = $(PRODUCT_OUT)/system/bin
# Files needed for the build
OBJS = example-app.o
# Make rules
all: example-app
.c.o:
$(CC) $(CFLAGS) $(HEADER_OPS) -c $<
example-app: ${OBJS}
$(CC) -o $(EXEC_NAME) ${OBJS} $(LDFLAGS)
install: example-app
test -d $(INSTALL_DIR) || $(INSTALL) -d -m 755 $(INSTALL_DIR)
$(INSTALL) -m 755 $(EXEC_NAME) $(INSTALL_DIR)
clean:
rm -f *.o $(EXEC_NAME) core
distclean:
rm -f *~
rm -f *.o $(EXEC_NAME) core
You don’t need to care about either envsetup.sh or lunch, you can just go ahead:
make
This won’t add your binary to any of the images generated by the AOSP. Its value is only during debuggingBuilding Recursively, In-Tree
Signing Builds for Release
- Release keys The Android tree includes test-keys under build/target/product/security. Building an Android OS image using make will sign all .apk files using the test-keys. Since the test-keys are publicly known, anybody can sign their own .apk files with the same keys. For this reason it is critical to sign any publicly released or deployed Android OS image with a special set of release-keys that only you have access to.
To generate your own unique set of release-keys, run these commands from the root of your Android tree:
subject='/C=US/ST=California/L=Mountain View/O=Android/OU=Android/CN=Android/emailAddress=android@android.com'
mkdir ~/.android-certs
for x in releasekey platform shared media; do \
./development/tools/make_key ~/.android-certs/$x "$subject"; \
done
To generate a release image, use:
make dist
./build/tools/releasetools/sign_target_files_apks \
-o \ # explained in the next section
-d ~/.android-certs out/dist/*-target_files-*.zip \
signed-target_files.zip
Basic AOSP Hacks
To hack the AOSP to fit your needs.Adding a Device to the build system
Assume you work for a company called VENDOR and try to add the support for the DEVICE.Add the new device to the build system:
- creating an entry for our new device in device mkdir -p device/VENDOR/DEVICE
- adding an device/VENDOR/DEVICE/AndroidProducts.mk file to describe this AOSP product
PRODUC_MAKEFILES := $(LOCAL_DIR)/DEVICE.mk
and it’s described in device/VENDOR/DEVICE/DEVICE.mk:
$(call inherit-product, device/VENDOR/common/languages_full.mk)
$(call inherit-product, build/make/target/product/product_launched_with_n.mk)
DEVICE_PACKAGE_OVERLAYS :=
PRODUCT_PACKAGES +=
PRODUCT_COPY_FILES +=
PRODUCT_NAME := DEVICE_name
PRODUCT_DEVICE := DEVICE
PRODUCT_MODEL := Full Android on the device
- the inherit-product function tell the build system to pull in other product descriptions as the basis of ours. languages_full.mk will pull in a vast number of locales, and product_launched_with_n.mk will make sure the API level. If you have several products sharing the same set of packages, you may want to create a device/VENDOR/common/ directory containing the shared packages.
- DEVICE_PACKAGE_OVERLAYS
- PRODUCT_PACKAGES Allows us to specify packages we’d like to have this product include in addition to those specified in the products we’re already inheriting from.
- PRODUCT_COPY_FILES Allows us to list specific files we’d like to see copied to the target’s filesystem and the location where they need to be copied.
- PRODUCT_NAME TARGET_PRODUCT derives from this variable. It's part of the combo parameter to lunch.
- PRODUCT_DEVICE TARGET_DEVICE derives from this variable. PRODUCT_DEVICE has to match an entry in device/VENDOR/, since that’s where the build looks for the corresponding BoardConfig.mk.
- PRODUCT_MODEL The name of this product as provided in the “Model number”. This variable actually gets stored as the ro.prod uct.model global property accessible on the device.
ifneq ($(filter coyotepad,$(TARGET_DEVICE)),)
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
include $(call all-makefiles-under,$(LOCAL_PATH))
endif
add_lunch_combo PRODUCT_NAME-eng
and make sure that it’s executable:
$ chmod 755 vendorsetup.sh
After the building is don, you can check the file out/target/product/DEVICE/system/build.prop.It contains various global properties that will be available at runtime on the target and that relate to our configuration and build.
Adding an App
Creating a HelloWorld! app :
- device/VENDOR/DEVICE/HelloWorld
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_PACKAGE_NAME := HelloWorld
include $(BUILD_PACKAGE)
Given that we’re tagging this module as optional, it won’t be included by default in the AOSP build. Adding an App Overlay
Overlays are a mech‐ anism included in the AOSP to allow device manufacturers to change the resources provided (such as for apps), without actually modifying the original resources included in the AOSP.
To use this capability, you must create an overlay tree and tell the build system about it.
EVICE_PACKAGE_OVERLAYS := device/VENDOR/DEVICE/overlay
So long as you put the files in the same hierarchy as they are found in the AOSP but within device/VENDOR/DEVICE/overlay/, they’ll be taken into account by the build system.Adding a Native Tool or Daemon
You can add your custom native tools and daemons as subdirectories of device/VENDOR/DEVICE/. You’ll need to provide an Android.mk in the directory containing the code to build that module:
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := hello-world
LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES := hello-world.cpp
LOCAL_SHARED_LIBRARIES := liblog
include $(BUILD_EXECUTABLE)
Adding a Native Library
You’ll need an Android.mk in the directory containing the code to build your library:
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := libmylib
LOCAL_MODULE_TAGS := optional
LOCAL_PRELINK_MODULE := false
LOCAL_SRC_FILES := $(call all-c-files-under,.)
include $(BUILD_SHARED_LIBRARY)
To use this library, you must add it to the libraries listed by the Android.mk file of whichever binaries depend on it:
LOCAL_SHARED_LIBRARIES := libmylib
CHAPTER 5 Hardware Primer
An SoC comprises a CPU and a bunch of peripheral controllers all on the same integrated circuit (IC) die.
Android essentially runs on that SoC and therefore controls and/ or accesses everything on the board from that vantage point.
The Baseband Processor
You might wonder why there are two separate processors instead of just one: Application Processor (AP) and the Baseband Processor (BP).
- In US, the software controlling the radio may not be modified without authorization.
- there are hard real-time constraints on the operation of the radio functions
Notice that the BP has its own flash and RAM. This guarantees that the certified software running on the BP is isolated from the software running on the AP, and that the real-time OS (RTOS) running on the BP is focused on running a single thing: the radio’s operation. The BP, for instance, runs software implementing the GSM stack. Notice also that the SIM card and an RF transceiver are connected to the BP. The transceiver takes care of the actual RF transmission and reception with the tower, while the SIM card is used to identify the handset user with the mobile network operator (MNO).
Core Components
SoC chips have the required modules to do basic reads and boot directly from a partition on the eMMC.
“Internal” storage typically designates the onboard eMMC, while “external” storage designates the user-removable SD card attached to the phone or tablet.
The PMIC(Power Management IC) is connected to the SoC through SPI, I2C, and/or GPIO. It can generate interrupts for such things as low battery or the charger being attached.
Real-World Interaction
As suggested by its Compatibility Definition Document (CDD), a system built with Android should allow rich user interaction
Memory Layout and Mapping
The mapping between applications and devices works because the CPU manages two entirely separate address spaces through its memory management unit (MMU). Using its MMU, the CPU can present a virtual address space to applications running on it and still use a physical address space to communicate with components connected to it through its address bus.The flash storage is divided in two.
The first part contains the bootloader and starts at the lowest memory address available.
The rest of flash is occupied by the system.
Upon startup, the bootloader reads the kernel from the system part into RAM and jumps to the kernel's start routine. From there on, the startup is carried out by the kernel.
After the initialization task is completed by the kernel, the application processes are started
The OS collaborates with the MMU to implement a virtual address space wherein each process gets a similar view.
Virtual addresses eventually map to actual physical addresses, but the conversion is automatically handled by the MMU based on OS-maintained page tables.
The kernel is always seen as occupying an address range starting at 0xC000 0000 as its low address(kernel space). Android apps, on the other hand, occupy the entire address space below that address(user space).
Chapter 6 Native User-Space
“Native user-space” means all the user-space components that run outside the Dalvik virtual machine.
Such binaries usually have direct access to the root filesystem and the native libraries included in the system. Their capabilities are restricted only by the filesystem rights granted to them and their effective UID and GID. They aren’t subject to any of the restrictions imposed on a typical Android app by the Android Framework because they are running outside it.
Filesystem Layout
The kernel mounts the RAM disk image generated by the build system as its root filesystem and launches the init process found in that image. That init process’s configuration files will cause a number of additional images and virtual filesystems to be mounted onto existing directory entries in the root filesystem.
The RAM disk image is meant to be as small as possible, and its sole purpose is to provide the initial skeleton required to get the system going.
The two main directories in which Android operates are /system and /data. These directories are not used for standard Linux distributions that it might be possible to host Android side by side with a common Linux distribution on the same root filesystem.
Prepare the tools simg2img and make_ext4fs
- Download the https://android.googlesource.com/platform/system/extras repository:
git clone https://android.googlesource.com/platform/system/extras
cd extras
git checkout android-4.1.1_r1
cd ext4_utils
gcc -o simg2img -lz sparse_crc32.c simg2img.c
cd extras/ext4_utils
gcc -o make_ext4fs -lz make_ext4fs_main.c make_ext4fs.c ext4fixup.c ext4_utils.c allocate.c backed_block.c output_file.c contents.c extent.c indirect.c uuid.c sha1.c sparse_crc32.c wipe.c
Modifying .img Files
You would need to go through the process: unzip -modify - rezip
ramdisk.img
- Decompress the image
mkdir root
cd root
gunzip -c ramdisk.img | cpio -i
/out/host/linux-x86/bin/mkbootfs root/ | ./out/host/linux-x86/bin/minigzip > ramdisk.img.new
system.img
- Decompress the image
out/host/linux-x86/bin/simg2img system.img system.img.raw
# mount to directory "mnt-system"
mkdir mnt-system
sudo mount -t ext4 -o loop system.img.raw mnt-system
sudo out/host/linux-x86/bin/make_ext4fs -s -T -1 -S file_contexts -L system -l 512M -a system system.img.new mnt-system/
sudo umount mnt-system/
Instead of having to reflash the whole big system.img, one can selective update any binary in /system folder on running target:
adb remount
adb push out/target/product/elfin/system/priv-app/TVLauncher/TVLauncher.apk /system/priv-app/TVLauncher/TVLauncher.apk
adn sync
userdata.img
- Decompress the image
out/host/linux-x86/bin/simg2img userdata.img userdata.img.raw
# mount to directory "mnt-userdata"
mkdir mnt-userdata
sudo mount -t ext4 -o loop userdata.img.raw mnt-userdata
sudo out/host/linux-x86/bin/make_ext4fs -s -l 512M -a userdata userdata.img.new mnt-userdata/
sudo umount mnt-system/
cache.img
This is empty ext4 fs image.
Make the new image,
mkdir mnt-point/
sudo out/host/linux-x86/bin/make_ext4fs -s -l 256M -a cache cache.img mnt-cache/
boot.img
boot.img = zImage + ramdisk.img
zImage = kernel image
ramdisk.img = out/target/product/elfin/root/
The boot image is generated by:
out/host/linux-x86/bin/mkbootimg --kernel zImage --ramdisk ramdisk.img --base adress --cmdline "kernel_arguments" --board board -o boot.img.new
vendor.img
Libraries
Android relies on about 100 dynamically loaded libraries, all stored in the /system/lib directory.Init
After the kernel finished initializing itself and the drivers it contains, the kernel starts just one user-space process, the init process.
This process is then responsible for spawning all other processes and services in the system.
In embedded Linux systems, the classic package that provides init is BusyBox.
Android introduces its own custom init.
Configuration language
Android’s init defines its own configuration semantics and relies on changes to global properties to trigger the execution of specific instructions.
- The main configuration file for init is usually stored as /init.rc.
- The device-specific configuration file stored as /init.(device_name).rc.
Global properties
Some of the properties set at build time are stored in the /system/build.prop file and include the build date and build system details.
Use the getprop command to get the current list of properties and their values.
udev events
udev relies on runtime events generated by the kernel every time hardware is added or removed from the system.
In Android, these events are handled by the ueventd daemon built as part of Android’s init and accessed through a symbolic link from /sbin/ueventd to /init. To know which entries to create in /dev, ueventd relies on the /ueventd.rc and /ueventd.(device_name).rc files.
Toolbox
Busybox is a package which all commands are then made to be symbolic links to the busybox command.Android doesn’t use BusyBox but includes its own tool, Toolbox, that basically functions in the very same way, using symbolic links to the toolbox command.
Daemons
Android’s init starts a few key daemons that continue to run throughout the lifetime of the system.
- ueventd
- servicemanager
- vold
- netd
- debuggerd
- rild
- Zygote
- mediaserver
- dbus-daemon
- bluetoothd
- installd
- keystore
- system_server
- adbd
Command-Line Utilities
/system/bin contains the majority of them, but some “extras” are in /system/xbin, and a handful are in /sbin.
adb
Its two most important components are:- the adb server running on the host The adb server is started automatically whenever the adb command is invoked on the command line. The adb server opens socket 5037 on the host and listens for connections.
- the adbd daemon running on the target
Basic Local Commands
- adb start-server
- adb kill-server
- adb version
- adb devices
- adb connect host[:port]
- adb disconnect host[:port]
- adb wait-for-device
- adb -d get-serialno
- adb -s serial_no get-state
- adb shell [command]
- adb -d logcat [-b log_buffer_name]
- adb -d bugreport
Filesystem Commands
- adb push filename /data/local
- adb pull filename /data/local
- adb remount remount filesystems in read-write mode
- adb install app.apk Install a new app, this will invoke the pm (short for “package manager”) command on the target.
- adb uninstall full_app_package_name
State-Altering Commands
- adb reboot This actually invokes the re boot() system call on the target’s kernel
- adb reboot bootloader reboot in the bootloader
- adb reboot recovery reboot in the recovery mode
- user adbd won’t run as root
- userdebug With this build, you can ask adb to restart as root by typing:
$ adb root
To use TCP/IP instead of USB:
adb -s serial_no tcpip 7878
this will set the service.adb.tcp.port global property on the target to 7878 and restart the adbd daemon. You can then connect to it like above:
$ adb connect 192.168.202.79:7878
To switch it back to USB, you can type this:
$ adb -s 192.168.172.79:7878 usb
Toolbox:
- cat
- chmod
- chown
- cmp
- cp
- date
- dd
- df
- dmesg
- du
- grep
- hd
- id
- ifconfig
- iftop
- insmod
- ionice
- ln
- md5
- kill
- ls
- lsmod
- lsof
- mkdir
- mount
- mv
- netstat
- printenv
- ps ps -A is listing all processes in android-8.0-O release.
# ps --help
usage: ps [-AadefLlnwZ] [-gG GROUP,] [-k FIELD,] [-o FIELD,] [-p PID,] [-t TTY,] [-uU USER,]
List processes.
Which processes to show (selections may be comma separated lists):
-A All processes
-a Processes with terminals that aren't session leaders
-d All processes that aren't session leaders
-e Same as -A
-g Belonging to GROUPs
-G Belonging to real GROUPs (before sgid)
-p PIDs (--pid)
-P Parent PIDs (--ppid)
-s In session IDs
-t Attached to selected TTYs
-T Show threads
-u Owned by USERs
-U Owned by real USERs (before suid)
Output modifiers:
-k Sort FIELDs in +increasing or -decreasting order (--sort)
-M Measure field widths (expanding as necessary)
-n Show numeric USER and GROUP
-w Wide output (don't truncate fields)
Which FIELDs to show. (Default = -o PID,TTY,TIME,CMD)
-f Full listing (-o USER:12=UID,PID,PPID,C,STIME,TTY,TIME,ARGS=CMD)
-l Long listing (-o F,S,UID,PID,PPID,C,PRI,NI,ADDR,SZ,WCHAN,TTY,TIME,CMD)
-o Output FIELDs instead of defaults, each with optional :size and =title
-O Add FIELDS to defaults
-Z Include LABEL
Command line -o fields:
ARGS CMDLINE minus initial path CMD Command (thread) name (stat[2])
CMDLINE Command line (argv[]) COMM Command filename (/proc/$PID/exe)
COMMAND Command file (/proc/$PID/exe) NAME Process name (argv[0] of $PID)
Process attribute -o FIELDs:
ADDR Instruction pointer BIT Is this process 32 or 64 bits
CPU Which processor running on ETIME Elapsed time since PID start
F Flags (1=FORKNOEXEC 4=SUPERPRIV) GID Group id
GROUP Group name LABEL Security label
MAJFL Major page faults MINFL Minor page faults
NI Niceness (lower is faster)
PCPU Percentage of CPU time used PCY Android scheduling policy
PGID Process Group ID
PID Process ID PPID Parent Process ID
PRI Priority (higher is faster) PSR Processor last executed on
RGID Real (before sgid) group ID RGROUP Real (before sgid) group name
RSS Resident Set Size (pages in use) RTPRIO Realtime priority
RUID Real (before suid) user ID RUSER Real (before suid) user name
S Process state:
R (running) S (sleeping) D (device I/O) T (stopped) t (traced)
Z (zombie) X (deader) x (dead) K (wakekill) W (waking)
SCHED Scheduling policy (0=other, 1=fifo, 2=rr, 3=batch, 4=iso, 5=idle)
STAT Process state (S) plus:
< high priority N low priority L locked memory
s session leader + foreground l multithreaded
STIME Start time of process in hh:mm (size :19 shows yyyy-mm-dd hh:mm:ss)
SZ Memory Size (4k pages needed to completely swap out process)
TCNT Thread count TID Thread ID
TIME CPU time consumed TTY Controlling terminal
UID User id USER User name
VSZ Virtual memory size (1k units) %VSZ VSZ as % of physical memory
WCHAN What are we waiting in kernel for
Boot timing
init records some boot timing information in system properties:
- ro.boottime.init Time after boot in ns (via the CLOCK_BOOTTIME clock) at which the first stage of init started.
- ro.boottime.init.selinux How long it took the first stage to initialize SELinux.
- ro.boottime.init.cold_boot_wait How long init waited for ueventd's coldboot phase to end.
- ro.boottime.service-name Time after boot in ns (via the CLOCK_BOOTTIME clock) that the service was first started. For ex.,
getprop ro.boottime.bootanim
14573890508
This is the sorted list for starting time of services:
[ro.boottime.init.cold_boot_wait]: [343]
[ro.boottime.init.mount_all.default]: [999]
[ro.boottime.init.selinux]: [1811]
[ro.boottime.init]: [2690]
[ro.boottime.ueventd]: [4985370514]
[ro.boottime.vndservicemanager]: [6413975291]
[ro.boottime.hwservicemanager]: [6409472513]
[ro.boottime.servicemanager]: [6407880031]
[ro.boottime.logd]: [6406522920]
[ro.boottime.nxserver]: [6982565846]
[ro.boottime.nxssd]: [6985401735]
[ro.boottime.vendor.boot-hal-1-0]: [10064207363]
[ro.boottime.vendor.keymaster-3-0]: [10072800251]
[ro.boottime.vold]: [10086374881]
[ro.boottime.update_verifier_nonencrypted]: [10472909807]
[ro.boottime.hwcbinder]: [10463487325]
[ro.boottime.netd]: [10546403548]
[ro.boottime.zygote]: [10548066251]
[ro.boottime.logd-reinit]: [10739392621]
[ro.boottime.vendor.drm-hal-1-0]: [10842572658]
[ro.boottime.vendor.configstore-hal]: [10833104066]
[ro.boottime.vendor.drm-clearkey-hal-1-1]: [10877488844]
[ro.boottime.vendor.camera-provider-2-4-ext]: [10813270547]
[ro.boottime.vendor.cas-hal-1-0]: [10827322214]
[ro.boottime.vendor.audio-hal-2-0]: [10806470621]
[ro.boottime.vendor.bluetooth-1-0]: [10809773103]
[ro.boottime.healthd]: [10802831621]
[ro.boottime.hidl_memory]: [10800248214]
[ro.boottime.vendor.drm-playready-hal-1-1]: [11057987510]
[ro.boottime.vendor.drm-widevine-hal-1-1]: [11312583806]
[ro.boottime.vendor.hwcomposer-2-1]: [11413200028]
[ro.boottime.vendor.health-hal-2-0]: [11418875954]
[ro.boottime.vendor.gralloc-2-0]: [11408103436]
[ro.boottime.vendor.gatekeeper-1-0]: [11405293806]
[ro.boottime.vendor.light-hal-2-0]: [11420836103]
[ro.boottime.vendor.memtrack-hal-1-0]: [11425082732]
[ro.boottime.vendor.power-hal-1-0]: [11436459547]
[ro.boottime.vendor.thermal-hal-1-0]: [11450939473]
[ro.boottime.vendor.tv-input-1-0]: [11596201436]
[ro.boottime.vendor.cec-hal-1-0]: [11515599917]
[ro.boottime.dpthak-hal-1-0]: [11610388139]
[ro.boottime.dspscvext-hal-1-0]: [11624504547]
[ro.boottime.prdysc-hal-1-0]: [11640132214]
[ro.boottime.sdbhak-hal-1-0]: [11650355399]
[ro.boottime.vendor.wifi_hal_legacy]: [11605604806]
[ro.boottime.vendor.sfhak-hal-1-0]: [11704580288]
[ro.boottime.audioserver]: [11715227695]
[ro.boottime.surfaceflinger]: [11781523843]
[ro.boottime.thermalservice]: [11795234213]
[ro.boottime.lmkd]: [11716943065]
[ro.boottime.console]: [12235263398]
[ro.boottime.media]: [13371257361]
[ro.boottime.mediadrm]: [13365624583]
[ro.boottime.mediaextractor]: [13367349212]
[ro.boottime.mediametrics]: [13369200138]
[ro.boottime.adbd]: [13344453620]
[ro.boottime.wificond]: [13376630472]
[ro.boottime.cameraserver]: [13355411027]
[ro.boottime.traced_probes]: [13353444324]
[ro.boottime.drm]: [13357061101]
[ro.boottime.incidentd]: [13358782768]
[ro.boottime.installd]: [13360544398]
[ro.boottime.keystore]: [13362261250]
[ro.boottime.traced]: [13347719213]
[ro.boottime.statsd]: [13372931879]
[ro.boottime.storaged]: [13374700027]
[ro.boottime.vendor.media.omx]: [13506928101]
[ro.boottime.usbd]: [13526911694]
[ro.boottime.update_engine]: [13525073953]
[ro.boottime.gatekeeperd]: [13509408620]
[ro.boottime.tombstoned]: [13518850286]
[ro.boottime.perfprofd]: [13510986768]
[ro.boottime.bootanim]: [14573890508]
Boot events can be found via:
adb logcat -b events | grep boot
01-01 00:00:14.862 2959 2959 I boot_progress_start: 12711
01-01 00:00:18.217 2959 2959 I sysui_histogram: [boot_zygote_init,16066]
01-01 00:00:18.218 2959 2959 I sysui_multi_action: [757,804,799,boot_zygote_init,801,16066,802,1]
01-01 00:00:18.236 2959 2959 I boot_progress_preload_start: 16086
01-01 00:00:21.093 2959 2959 I boot_progress_preload_end: 18942
01-01 00:00:21.856 3206 3206 I boot_progress_system_run: 19705
01-01 00:00:21.858 3206 3206 I sysui_histogram: [boot_system_server_init,19705]
01-01 00:00:21.858 3206 3206 I sysui_multi_action: [757,804,799,boot_system_server_init,801,19705,802,1]
01-01 00:00:22.725 3206 3206 I sysui_histogram: [boot_package_manager_init_start,20575]
01-01 00:00:22.725 3206 3206 I sysui_multi_action: [757,804,799,boot_package_manager_init_start,801,20575,802,1]
01-01 00:00:22.760 3206 3206 I boot_progress_pms_start: 20610
01-01 00:00:22.897 3206 3206 I boot_progress_pms_system_scan_start: 20747
01-01 00:00:23.052 3206 3206 I boot_progress_pms_data_scan_start: 20902
01-01 00:00:23.060 3206 3206 I boot_progress_pms_scan_end: 20910
01-01 00:00:23.251 3206 3206 I boot_progress_pms_ready: 21101
01-01 00:00:23.334 3206 3206 I sysui_histogram: [boot_package_manager_init_ready,21183]
01-01 00:00:23.334 3206 3206 I sysui_multi_action: [757,804,799,boot_package_manager_init_ready,801,21183,802,1]
03-12 08:07:15.118 3206 3206 I boot_progress_ams_ready: 34268
03-12 08:07:17.017 3206 3206 I sysui_histogram: [boot_system_server_ready,36167]
03-12 08:07:17.017 3206 3206 I sysui_multi_action: [757,804,799,boot_system_server_ready,801,36167,802,1]
03-12 08:07:18.790 3206 3223 I boot_progress_enable_screen: 37940
03-12 08:07:19.185 3029 3544 I sf_stop_bootanim: 38335
03-12 08:07:19.187 3206 3231 I wm_boot_animation_done: 38338
03-12 08:07:19.209 3206 3231 I sysui_histogram: [framework_locked_boot_completed,38]
03-12 08:07:19.209 3206 3231 I sysui_multi_action: [757,804,799,framework_locked_boot_completed,801,38,802,1]
03-12 08:07:19.397 3571 3571 I sysui_multi_action: [757,804,799,ro.boottime.init,801,2690,802,1]
03-12 08:07:19.397 3571 3571 I sysui_multi_action: [757,804,799,boot_reason,801,1,802,1]
03-12 08:07:19.397 3571 3571 I sysui_multi_action: [757,804,799,ro.boottime.init.selinux,801,1811,802,1]
03-12 08:07:19.397 3571 3571 I sysui_multi_action: [757,804,799,boottime.bootloader.total,801,0,802,1]
03-12 08:07:19.397 3571 3571 I sysui_multi_action: [757,804,799,factory_reset_boot_complete,801,58,802,1]
03-12 08:07:19.397 3571 3571 I sysui_multi_action: [757,804,799,system_boot_reason,801,67,802,1]
03-12 08:07:19.397 3571 3571 I sysui_multi_action: [757,804,799,boot_complete,801,38,802,1]
03-12 08:07:19.397 3571 3571 I sysui_multi_action: [757,804,799,last_boot_time_utc,801,1552378039,802,1]
03-12 08:07:19.397 3571 3571 I sysui_multi_action: [757,804,799,time_since_last_boot,801,-883,802,1]
03-12 08:07:19.398 3571 3571 I sysui_multi_action: [757,804,799,boot_complete_no_encryption,801,38,802,1]
03-12 08:07:19.398 3571 3571 I sysui_multi_action: [757,804,799,ro.boottime.init.cold_boot_wait,801,343,802,1]
03-12 08:07:19.398 3571 3571 I sysui_multi_action: [757,804,799,absolute_boot_time,801,38,802,1]
03-12 08:07:19.398 3571 3571 I sysui_multi_action: [757,804,799,factory_reset_boot_complete_no_encryption,801,58,802,1]
03-12 08:07:19.607 3206 3223 I sysui_histogram: [framework_boot_completed,38]
03-12 08:07:19.607 3206 3223 I sysui_multi_action: [757,804,799,framework_boot_completed,801,38,802,1]
03-12 08:07:23.338 3206 3772 I sysui_histogram: [boot_mount_all_duration_default,999]
03-12 08:07:23.338 3206 3772 I sysui_multi_action: [757,804,799,boot_mount_all_duration_default,801,999,802,1]
03-12 08:07:23.341 3206 3772 I sysui_histogram: [boot_fs_stat_userdata,11]
03-12 08:07:23.341 3206 3772 I sysui_multi_action: [757,804,799,boot_fs_stat_userdata,801,11,802,1]
03-12 08:07:23.342 3206 3772 I sysui_histogram: [boot_fs_stat_cache,1035]
03-12 08:07:23.342 3206 3772 I sysui_multi_action: [757,804,799,boot_fs_stat_c
Bootcharting
This version of init contains code to perform “bootcharting”: generating log files that can be later processed by the tools provided by http://www.bootchart.org/.
Bootchart is a tool for performance analysis and visualization of the GNU/Linux boot process. Resource utilization and process information are collected during the boot process and are later rendered in a PNG, SVG or EPS encoded chart.
To activate bootchart on a device:
adb shell 'touch /data/bootchart/enabled'
Don‘t forget to delete this file when you’re done collecting data!The log files are written to /data/bootchart/. A script is provided to retrieve them and create a bootchart.tgz file that can be used with the bootchart command-line utility:
$ sudo apt-get install pybootchartgui
$ # grab-bootchart.sh uses $ANDROID_SERIAL.
$ adb devices
serialNo device
$ export ANDROID_SERIAL=serialNo
$ $ANDROID_BUILD_TOP/system/core/init/grab-bootchart.sh
The bootchart will show init as if it started running at 0s.
A handy script named compare-bootcharts.py can be used to compare the start/end time of selected processes.
For ex.,
system/core/init/compare-bootcharts.py base-bootchart-dir exp-bootchart-dir
process: baseline experiment (delta) - Unit is ms (a jiffy is 10 ms on the system)
------------------------------------
/init: 50 40 (-10)
/system/bin/surfaceflinger: 4320 4470 (+150)
/system/bin/bootanimation: 6980 6990 (+10)
zygote64: 10410 10640 (+230)
zygote: 10410 10640 (+230)
system_server: 15350 15150 (-200)
bootanimation ends at: 33790 31230 (-2560)
Optimizing Boot Times
- Optimizing I/O efficiency When a process reads sequentially a file the kernel starts reading some data in advance to reduce the amount of time that a process have to wait for data to be available, so this parameter "Readahead" sets the maximum amount of data that the kernel reads ahead for a single file. In order to avoid processes waiting for data to be available, this parameter sets the maximum amount of data that the kernel reads ahead for a single file. Default: 128KB
echo 640 > /sys/block/mmcblk0/queue/read_ahead_kb
Try set it on Android via /init.rc:
on late-fs
# boot time fs tune
# boot time fs tune
write /sys/block/sda/queue/iostats 0
write /sys/block/sda/queue/scheduler cfq
write /sys/block/sda/queue/iosched/slice_idle 0
write /sys/block/sda/queue/read_ahead_kb 2048
write /sys/block/sda/queue/nr_requests 256
write /sys/block/dm-0/queue/read_ahead_kb 2048
write /sys/block/dm-1/queue/read_ahead_kb 2048
on property:sys.boot_completed=1
# end boot time fs tune
write /sys/block/sda/queue/read_ahead_kb 512
...
Improving I/O efficiency is critical to making boot time faster, and reading anything not necessary should be deferred until after boot. fsck check is typically forced during reboot on your Linux installation.
- Skip fsck check once To skip it once, you simply need to add fastboot to kernel boot options. "fastboot" was added in 2.6.29 and removed again in 2.6.30.
- To permanently disable fsck check on a hard disk partition You need to edit the /etc/fstab file to change dump and pass. Setting both to 0 will tell fsck to skip checking the partitions. The format used in /etc/fstab:
<file system> <dir> <type> <options> <dump> <pass>
<dump> - Is used by the dump utility to decide when to make a backup. fsck reads the <pass> number and determines in which order/priority (0,1,2) the file systems should be checked. The root file system should have the highest priority, 1, all other file systems you want to have checked should get a 2. File systems with a - In the early phase, mount only the partitions (such as system/ and vendor/) that don't require run checks, then start boot animation services and its dependencies (such as servicemanager and surfaceflinger).
- In the second phase, mount partitions (such as data/) that do require run checks.
Debugging init
If it is required to debug a service from its very start, the sigstop service option is added. This option will send SIGSTOP to a service immediately before calling exec.
stop logd
setprop ctl.sigstop_on logd
start logd
ps -e | grep logd
> logd 4343 1 18156 1684 do_signal_stop 538280 T init
To resume the service,
kill -SIGCONT 4343
Global properties
A set of global properties that can be accessed from anywhere in the system.
- getprop list all the properties with their current values
- setprop prop val
- getprop prop
- watchprops monitor properties being changed in real-time
For ex.,
In that case, a file with the property’s full name will be created in /data/property containing the property’s value.Input events
Android relies heavily on Linux’s input layer to get the user’s input events.
- getevent getevent continuously displays events as they come in until you type Ctrl-C. The output format is event type, event code, and event value.
- sendevent Note, getevent’s output is hexadecimal, sendevent’s input is decimal.
Controlling services
You can start and stop services using the following:
start (servicename)
stop (servicename)
Logging
You can add your own events to Android’s logger:
log [-p (prioritychar)] [-t (tag)] (message)
prioritychar should be one of:
v,d,i,w,e
ioctl
Toolbox provides a utility to enable developers to invoke drivers’ ioctl() functions.
ioctl [-l ] [-a ] [-rdh]
-l Length of io buffer
-a Size of each argument (1-8)
-r Open device in read only mode
-d Direct argument (no iobuffer)
-h Print help
this will be highly driver-specific. Wiping the device
Toolbox’s wipe command:
wipe
system means '/system'
data means '/data'
Android-specific commands
- nandread This utility is for reading the contents of a NAND flash device to a file:
nandread [-d ] [-f ] [-s ] [-vh]
-d Read from
-f Write to
-s Number of spare bytes in file (default 64)
-R Raw mode
-S Start offset (default 0)
-L -v
-h
Length (default 0)
Print info
Print help
newfs_msdos [ -options ] device [disktype]
where the options are:
-@ create file system at specified offset
-B get bootstrap from file
-C create image file with specified size
-F FAT type (12, 16, or 32)
-I volume ID
-L volume label
-N don't create file system: just print out parameters
-O OEM string
-S bytes/sector
-a sectors/FAT
-b block size
-c sectors/cluster
-e root directory entries
-f standard format
-h drive heads
-i file system info sector
-k backup boot sector
-m media descriptor
-n number of FATs
-o hidden sectors
-r reserved sectors
-s file system size (sectors)
-u sectors/track
schedtop [-d delay] [-bitamun]
-d refresh every seconds
-b batch - continuous prints instead of refresh
-i hide idle tasks
-t show threads
-a use alternate screen
-m use millisecond precision
-u use microsecond precision
-n use nanosecond precision
# logcat --help
Usage: logcat [options] [filterspecs]
You can run logcat as an adb command or directly in a shell prompt of your connected device.
adb logcat
Options: - -b buffer The default used log buffers are: main, system, crash. The Android logging system keeps multiple circular buffers for log messages, and not all of the log messages are sent to the default circular buffer. To see additional log messages, you can run the logcat command with the -b option, to request viewing of an alternate circular buffer.
- radio: View the buffer that contains radio/telephony related messages.
- events: View the interpreted binary system event buffer messages.
- main: View the main log buffer (default) does not contain system and crash log messages.
- system: View the system log buffer (default).
- crash: View the crash log buffer (default).
- all: View all buffers.
- default: Reports main, system, and crash buffers.
- -c, --clear Clear (flush) the selected buffers and exit. The default buffer set is main, system and crash. To clear all of the buffers, use -b all -c.
- -d Dump the log to the screen and exits.
- -e expr, --regex=expr Only print lines where the log message matches expr where expr is a regular expression.
- -n count Set the maximum number of rotated logs to count.
- -t count Print only the most recent number of lines. This option includes -d functionality.
- -t 'time' Print the most recent lines since the specified time.
adb logcat -t '01-26 20:52:41.820'
priority/tag
The priority is one of the following character values, ordered from lowest to highest priority: - V: Verbose (lowest priority)
- D: Debug
- I: Info
- W: Warning
- E: Error
- F: Fatal
- S: Silent (highest priority, on which nothing is ever printed)
tag:priority ...
Messages for that tag at or above the specified priority are written to the log. You can supply any number of tag:priority specifications in a single filter expression. The following example suppresses all log messages except those with the tag "ActivityManager", at priority "Info" or above, and all log messages with tag "MyApp", with priority "Debug" or above:
adb logcat ActivityManager:I MyApp:D *:S
logcat [-v format]
You can specify one of the supported output formats listed below: - brief: Display priority, tag, and PID of the process issuing the message.
- long: Display all metadata fields and separate messages with blank lines.
- process: Display PID only.
- raw: Display the raw log message with no other metadata fields.
- tag: Display the priority and tag only.
- thread: A legacy format that shows priority, PID, and TID of the thread issuing the message.
- threadtime (default): Display the date, invocation time, priority, tag, PID, and TID of the thread issuing the message.
- time: Display the date, invocation time, priority, tag, and PID of the process issuing the message.
- verbose Log.v(tag, String)
- debug Log.d(tag, String)
- information Log.i(tag, String)
- warning Log.w(tag, String)
- error Log.e(tag, String)
Log.i("MyActivity", "MyClass.getView() — get item number " + position);
netcfg [interface {dhcp|up|down}]
# check_prereq 1336847591
current build time: [1336162137] new build time: [1336847591]
Init
After init is started by the kernel, it essentially reads its configuration files, prints out a boot logo or text to the screen, opens a socket for its property service, and starts all the daemons and services that bring up the entire Android user-space.
init includes an implementation of the udev hotplug events handler.
One of the first things init does is check whether it was invoked as ueventd.
init checks the command-line that was used to invoke it, and if it was invoked through the /sbin/ueventd symbolic link to /init, then init immediately runs as ueventd.
The next thing init does is create and mount /dev, /proc, and /sys.
then reads the /init.rc and /init.device_name.rc files, parses their content into its internal structures, and proceeds to initialize the system based on a mix of its configuration files and built-in rules.
Once all initialization is done, init then enters an infinite loop:
- restarts any services that might have exited and that need restarting
- polls file descriptors it needs to handle Such as the property service’s socket (this is how setprop property setting requests are serviced), input events.
Configuration Files
The main way to control init’s behavior is through its configuration files.
The root directory (/) includes the actual init binary itself and its two configurations files: init.rc and init.device_name.rc. The device_name is extracted from /proc/cpuinfo.
init reads both files before it executes any of the instructions.
init’s .rc files contain a series of declarations that fall in one of two types: actions and services. Each declarative section starts with a keyword identifying the type of declaration: on for an action and service for a service.
Only an action, however, results in the execution of commands. Service declarations serve only to describe services; they don’t actually start anything. The services are typically started or stopped when an action is triggered.
Android Init Language
The init language is used in plain text files that take the .rc file extension.
The Android Init Language consists of 5 classes of statements: Actions, Commands, Services, Options, and Imports.
- All of these are line-oriented, consisting of tokens separated by whitespace.
- Double quotes may also be used to prevent whitespace from breaking text into multiple tokens
- The backslash, when it is the last character on a line, may be used for line-folding.
- Lines which start with a # (leading whitespace allowed) are comments.
- System properties can be expanded using the syntax ${property.name}.
- Actions and Services implicitly declare a new section. All commands or options belong to the section most recently declared. Commands or options before the first section are ignored.
- Services have unique names.
/init.rc is the primary .rc file and is loaded by the init executable at the beginning of its execution.
All of the files contained within the /{system,vendor,odm}/etc/init/ directories are loaded immediately after loading the primary /init.rc .
The intention of these directories is:
- /system/etc/init/ is for core system items such as SurfaceFlinger, MediaService, and logcatd.
- /vendor/etc/init/ is for SoC vendor items such as actions or daemons needed for core SoC functionality.
- /odm/etc/init/ is for device manufacturer items such as actions or daemons needed for motion sensor or other peripheral functionality.
Imports
import path
Parse an init config file, extending the current configuration. If path is a directory, each file in the directory is parsed as a config file. It is not recursive, nested directories will not be parsed.Actions
Actions are named sequences of commands. Actions have a trigger which is used to determine when the action is executed. When an event occurs which matches an action's trigger, that action is added to the tail of a to-be-executed queue.
on trigger [&& trigger]*
command
command
...
Actions are added to the queue and executed based on the order that the file that contains them was parsed.For example if a file contains:
on boot
setprop a 1
setprop b 2
on boot && property:true=true
setprop c 1
setprop d 2
on boot
setprop e 1
setprop f 2
Then when the boot trigger occurs and assuming the property true equals true, then the order of the commands executed will be:
setprop a 1
setprop b 2
setprop c 1
setprop d 2
setprop e 1
setprop f 2
The commands are defined in system/core/init/builtins.cpp:
// Builtin-function-map start
const BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const {
constexpr std::size_t kMax = std::numeric_limits::max();
// clang-format off
static const Map builtin_functions = {
{"bootchart", {1, 1, {false, do_bootchart}}},
...
{"setprop", {2, 2, {true, do_setprop}}},
...
{"write", {2, 2, {true, do_write}}},
};
// clang-format on
return builtin_functions;
}
static Result do_setprop(const BuiltinArguments& args) {
property_set(args[1], args[2]);
return Success();
}
Triggers
Triggers are strings which can be used to match certain kinds of events and used to cause an action(a sequence of commands) to occur.
Triggers are subdivided into:
- event triggers Event triggers are strings triggered by the ‘trigger’ command or pre-defined by the QueueEventTrigger() function within the init executable(init.cpp):(built-in actions defined)
- early-init Built-in actions are executed:
- coldboot: Check that ueventd has populated /dev.
- Initialize property service’s internal data structures.
- Set up handler for keychords.
- Initialize the console and display startup text or image.
- init
- charger
- late-init
- property triggers After all pre-defined trigger commnds are executed, init runs all property-triggered commands based on current property values. Property triggers are strings triggered when a named property changes value to a given new value(‘property:=value’) or when a named property changes value to any new value(‘property:=*’). Actions can also be taken based on property value changes:
on property:name=value
A very good example of this is the default init.rc’s starting or stopping the adbd daemon based on the toggling of the “USB debugging” checkbox in Settings:
on property:persist.service.adb.enable=1
start adbd
on property:persist.service.adb.enable=0
stop adbd
Here’s the list of predefined and property triggers that is executed in order by the init configuration file: - early-init
- init
- charger
- late-init
- early-fs
- fs
- post-fs
- early-boot
- boot
Commands used for actions:
- chmod octal-mode path Change file access permissions.
- chown owner group path Change file owner and group.
- class_start serviceclass Start all services of the specified class if they are not already running. See the start entry for more information on starting services.
- class_stop serviceclass Stop and disable all services of the specified class if they are currently running.
- class_reset serviceclass Stop all services of the specified class if they are currently running, without disabling them. They can be restarted later using class_start.
- class_restart serviceclass Restarts all services of the specified class.
- copy src dst Copies a file. Similar to write, but useful for binary/large amounts of data. Regarding to the src file, copying from symbolic link file and world-writable or group-writable files are not allowed. Regarding to the dst file, the default mode created is 0600 if it does not exist. And it will be truncated if dst file is a normal regular file and already exists.
- domainname name Set the domain name.
- enable servicename Turns a disabled service into an enabled one as if the service did not specify disabled. If the service is supposed to be running, it will be started now. Typically used when the bootloader sets a variable that indicates a specific service should be started when needed. E.g.
on property:ro.boot.myfancyhardware=1
enable my_fancy_service_for_my_fancy_hardware
Services
Services are programs which init launches and (optionally) restarts when they exit.
service name pathname [ argument ]*
option
option
option
...
Service options are modifiers to services. They affect how and when init runs the service.
- class name [ name\* ] Specify class names for the service. All services in a named class may be started or stopped together.
- console [console] This service needs a console.
- critical This is a device-critical service. If it exits more than four times in four minutes, the device will reboot into recovery mode.
- disabled This service will not automatically start with its class. It must be explicitly started by name or by interface name.
- group groupname [ groupname\* ] Run this service under the given group(s).
- ioprio class priority Sets the IO priority and IO priority class for this service via the SYS_ioprio_set syscall. class must be one of “rt”, “be”, or “idle”. priority must be an integer in the range 0 - 7.
- keycodes keycode [ keycode\* ] Start the service whenever the given keycodes are activated.
- oneshot Do not restart the service when it exits.
- onrestart Execute a Command when service restarts.
- seclabel seclabel Change to ‘seclabel’ before exec'ing this service.
- setenv name value Set the environment variable name to value in the launched process.
- socket name type perm [ user [ group [ seclabel ] ] ] Create a unix domain socket named /dev/socket/name and pass its fd to the launched process. type must be “dgram”, “stream” or “seqpacket”.
- user username Change to ‘username’ before exec'ing this service.
Global Properties
Android’s global properties serve as a trivial way of sharing important values globally among all parts of the stack.
Property service is part of init process, there are two ways that this property service is exposed to the rest of the system:
- /dev/socket/property_service This is a Unix domain socket that processes can open to talk to the property service and have it set and/or change the value of global properties.
- /dev/__properties__ This is an “invisible” file that is created within the tmpfs-mounted /dev and that is memory-mapped into the address space. It’s through this mapped region that descendants of init (i.e., all user-space processes in the system) can read global properties via the memory-mapped file.
The specific permissions required to set certain global properties(through the /dev/socket/property_service) are hardcoded in system/core/init/property_service.c:
/* White list of permissions for setting property services. */
struct {
const char *prefix;
unsigned int uid;
unsigned int gid;
} property_perms[] = {
{ "net.rmnet0.", AID_RADIO, 0 },
...
{ "ril.", AID_RADIO, 0 },
{ "gsm.", AID_RADIO, 0 },
...
{ "hw.", AID_SYSTEM, 0 },
{ "sys.", AID_SYSTEM, 0 },
...
{ "persist.security.", AID_SYSTEM, 0 },
{ NULL, 0, 0 }
}
For instance, the above says that :- only processes running as the system user can change properties that start with sys. or hw.
- only processes running as the radio user can change properties that start with ril. or gsm.
#define AID_ROOT 0 /* traditional unix root user *
...
static const struct android_id_info android_ids[] = {
{ "root",AID_ROOT, }
...
}
Note that:- processes running as root can change any property they wish.
- properties whose names starts with ro. can be set only once. (these three characters "ro." are stripped from the name before permissions are checked)
To interact with the property service:
- the command line You can use getprop, setprop, and watchprops. You should use getprop after the initial boot of your system to get your device’s base list of properties.
- in Java Look at the frameworks/base/core/java/android/os/SystemProperties.java class and use this class,
import android.os.SystemProper
#include <cutils/properties.h>
Properties follow a certain naming convention and are processed differently based on their prefixes:
- ro.* Properties that start with this prefix are meant to be read-only. Hence, they can be set only once in the system’s lifetime. The only way to change their value is to change the source of the information to which they are set and reboot the system. For ex., hardware and ro.build.id
- persist.* Properties marked with this prefix are committed to persistent storage each time they are set. For ex., persist.service.adb.enable
- ctl.* There’s a ctl.start and a ctl.stop, and setting them doesn’t actually result in any property being saved. When the property service receives a request to set a ctl.* property, it starts/stops the service whose name is provided as the value for the property. The Surface Flinger, for instance, does this as part of its startup:
property_set("ctl.start", "bootanim");
This effectively results in the bootanim service being started by init. The bootanim service and its options are described in the init.rc file.
Several parts are involved in creating the final set of global properties found in any single Android device:
- The build system You can find them in the root/ and system/ subdirectories of out/target/product/PRODUCT_DEVICE/:
- /system/build.prop This one contains information about the build itself. Most of the content of these files is generated by the core AOSP build code(makefile) in build/core/. For ex.,
PRODUCT_PROPERTY_OVERRIDES += \
wifi.interface=eth0 \
wifi.supplicant_scan_interval=15 \
dalvik.vm.heapsize=32m
SystemProperties.set("net.hostname", name);
ueventd
ueventd is one of the very first services started by the default init.rc.
Based on the events ueventd receives and its configuration files, it automatically creates device node entries in /dev.
These entries under /dev are re-created based on ueventd’s configuration files,
/dev/node mode use group
When a uevent corresponding to node occurs, ueventd creates /dev/node with access permissions set to mode and assigns the entry to user/group.Boot Logo
Android devices’ screens typically go through 4 stages during boot:
- Kernel boot screen The kernel might either maintain the screen black until init starts, or it might display a static logo, built as part of the kernel image, to the framebuffer.
- Init boot logo This is a text string or an image displayed very early by init while it initializes the console.
- Boot animation This is a series of animated images, possibly a loop, that displays during the Surface Flinger’s start up.
- frameworks/native/include/ui/ISurfaceComposer.h
class BnSurfaceComposer : public BnInterface<ISurfaceComposer>
{
public:
enum {
// Note: BOOT_FINISHED must remain this value, it is called from
// Java by ActivityManagerService.
BOOT_FINISHED = IBinder::FIRST_CALL_TRANSACTION,
...
}
virtual void bootFinished()
{
Parcel data, reply;
data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
remote()->transact(BnSurfaceComposer::BOOT_FINISHED, data, &reply);
}
status_t BnSurfaceComposer::onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
switch(code) {
...
case BOOT_FINISHED: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
bootFinished();
} break;
...
}
status_t SurfaceFlinger::readyToRun()
{
ALOGI( "SurfaceFlinger's main thread ready to run. "
"Initializing graphics H/W...");
...
// start boot animation
startBootAnim();
return NO_ERROR;
}
void SurfaceFlinger::startBootAnim() {
// start boot animation
property_set("service.bootanim.exit", "0");
property_set("ctl.start", "bootanim");
}
void SurfaceFlinger::bootFinished()
{
const nsecs_t now = systemTime();
const nsecs_t duration = now - mBootTime;
ALOGI("Boot is finished (%ld ms)", long(ns2ms(duration)) );
mBootFinished = true;
// wait patiently for the window manager death
const String16 name("window");
sp window(defaultServiceManager()->getService(name));
if (window != 0) {
window->linkToDeath(static_cast(this));
}
// stop boot animation
// formerly we would just kill the process, but we now ask it to exit so it
// can choose where to stop the animation.
property_set("service.bootanim.exit", "1");
}
...
LOCAL_MODULE:= bootanimation
LOCAL_INIT_RC := bootanim.rc
...
service bootanim /system/bin/bootanimation
class core animation
user graphics
group graphics audio
disabled
oneshot
writepid /dev/stune/top-app/tasks
static const char EXIT_PROP_NAME[] = "service.bootanim.exit";
void BootAnimation::checkExit() {
// Allow surface flinger to gracefully request shutdown
char value[PROPERTY_VALUE_MAX];
property_get(EXIT_PROP_NAME, value, "0");
int exitnow = atoi(value);
if (exitnow) {
requestExit();
mCallbacks->shutdown();
}
}
bool BootAnimation::playAnimation(const Animation& animation)
{
...
nsecs_t now = systemTime();
nsecs_t delay = frameDuration - (now - lastFrame);
...
if (delay > 0) {
struct timespec spec;
spec.tv_sec = (now + delay) / 1000000000;
spec.tv_nsec = (now + delay) % 1000000000;
int err;
do {
err = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &spec, NULL);
} while (err<0 && errno == EINTR);
}
checkExit();
}
usleep(part.pause * ns2us(frameDuration));
}
CHAPTER 7 Android Framework
The Android Framework includes:
- the android.* packages
- the System Services
- the Android Runtime
- some of the native daemons
Practically, it can be thought as everything that runs on top of native user-space.
Kick-Starting the Framework
We can make the AOSP to generate Tiny Android, go to the AOSP’s source directory and type this:
$ BUILD_TINY_ANDROID=true make -j16
you’ll get Tool‐ box, Bionic, init, adbd, logcat, sh, and a few other key binaries and libraries.Core Building Blocks
One of the first services started by init is the servicemanager.
If the servicemanager isn’t running, none of the system services will be able to advertise themselves, and the Framework simply will not work.
The next core component to get started is the Zygote declared in root/init.zygote32.rc:
service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
init runs the C++ program /system/bin/app_process, and gives the resulting process the name "zygote".source for 'app_process is in frameworks/base/cmds/app_process/app_main.cpp.
app_process start a new Dalvik VM for running Android code straight from the command line.
Usage: app_process [java-options] cmd-dir start-class-name [options]
app_process has two main responsibilities:- initialize the Android runtime app_process does a runtime.start("com.android.internal.os.ZygoteInit", startSystemServer), the startSystemServer flag is passed as a parameter on to app_process, to indicate whether to start the system server.
- use the runtime to start zygote via its main method com.android.internal.os.ZygoteInit:main() starts executing, source is at frameworks/base/core/java/com/android/internal/os/ZygoteInit.java .
- If startSystemServer is set, then the system server is started via Zygote.forkSystemServer() Source for the SystemServer is in frameworks/base/services/java/com/android/server/SystemServer.java. The libandroid_servers.so library is loaded, which contains the JNI parts required by some of the system services and then invokes native code in frameworks/base/cmds/system_server/library/system_init.cpp, which starts C-coded system services that run in the system_server process. In the "ServerThread::run()" method, lots of critical services are started.
- zygote runs in "select loop mode", where a single process spins waiting for communication to start subsequent apps
runtime is an instance of class AppRuntime, which is sub-classed from AndroidRuntime, AndroidRuntime is the main class for starting the Dalvik runtime environment. source is in frameworks/base/core/jni/AndroidRuntime.cpp, the start() method starts the Dalvik virtual machine via startVM().
When you start an Android application, the Zygote is forked with another VM. Every android application runs in a separate process and has its own Dalvik VM.
You can disable Zygoye's startup entirely by adding the disabled option to its section in init.rc.
service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server ... disabledThis will effectively prevent init from starting the Zygote at boot time, so none of the Android Framework’s parts will start, including the System Server.
System Services
Android has quite a few daemons running in the background.
The Android init is different than that of Linux, with the most important differences being in its support of System Properties and using a particular set of rc files.
The System Server is started as part of the Zygote’s startup.
Apps Startup
Utilities and Commands
General-Purpose Utilities:
- service
- service list
- dumpsys service_name Call the dump() method implemented by a service. For ex., to get the CPU usage:
adb shell dumpsys cpuinfo
Service-Specific Utilities:
- am The Activity Manager. am is in fact a shell script, as you can see in frameworks/base/cmds/am/am/:
# Script to start "am" on the device, which has a very rudimentary
# shell.
#
base=/system
export CLASSPATH=$base/framework/am.jar
exec app_process $base/bin com.android.commands.am.Am "$@"
The script uses app_process to start Java code that implements am’s functionality. All parameters passed on the command line are actually passed on to the Java code as is.
# Script to start "pm" on the device, which has a very rudimentary
# shell.
#
base=/system
export CLASSPATH=$base/framework/pm.jar
exec app_process $base/bin com.android.commands.pm.Pm "$@"
- Listing the installed packages
pm list packages
pm install appName.apk
pm uninstall full.package.name
- power Control the power manager
usage: svc power stayon [true|false|usb|ac|wireless]
Set the 'keep awake while plugged in' setting.
svc power reboot [reason]
Perform a runtime shutdown and reboot device with specified reason.
svc power shutdown
Perform a runtime shutdown and power off the device.
usage: svc data [enable|disable]
Turn mobile data on or off.
usage: svc wifi [enable|disable]
Turn Wi-Fi on or off.
usage: ime list [-a] [-s]
ime enable ID
ime disable ID
ime set ID
The list command prints all enabled input methods. Use
the -a option to see all input methods. Use
the -s option to see only a single summary line of each.
The enable command allows the given input method ID to be used.
The disable command disallows the given input method ID from use.
The set command switches to the given input method ID.
Usage: input [source] command [arg...]
The sources are:
dpad
keyboard
mouse
touchpad
gamepad
touchnavigation
joystick
touchscreen
stylus
trackball
The commands and default sources are:
text string (Default: touchscreen)
keyevent [--longpress] key code number or name ... (Default: keyboard)
tap x y (Default: touchscreen)
swipe x1 y1 x2 y2 [duration(ms)] (Default: touchscreen)
draganddrop x1 y1 x2 y2 [duration(ms)] (Default: touchscreen)
press (Default: trackball)
roll dx dy (Default: trackball)
usage: monkey [-p ALLOWED_PACKAGE [-p ALLOWED_PACKAGE] ...]
[-c MAIN_CATEGORY [-c MAIN_CATEGORY] ...]
[--ignore-crashes] [--ignore-timeouts]
[--ignore-security-exceptions]
[--monitor-native-crashes] [--ignore-native-crashes]
[--kill-process-after-error] [--hprof]
[--match-description TEXT]
[--pct-touch PERCENT] [--pct-motion PERCENT]
[--pct-trackball PERCENT] [--pct-syskeys PERCENT]
[--pct-nav PERCENT] [--pct-majornav PERCENT]
[--pct-appswitch PERCENT] [--pct-flip PERCENT]
[--pct-anyevent PERCENT] [--pct-pinchzoom PERCENT]
[--pct-permission PERCENT]
[--pkg-blacklist-file PACKAGE_BLACKLIST_FILE]
[--pkg-whitelist-file PACKAGE_WHITELIST_FILE]
[--wait-dbg] [--dbg-no-events]
[--setup scriptfile] [-f scriptfile [-f scriptfile] ...]
[--port port]
[-s SEED] [-v [-v] ...]
[--throttle MILLISEC] [--randomize-throttle]
[--profile-wait MILLISEC]
[--device-sleep-time MILLISEC]
[--randomize-script]
[--script-log]
[--bugreport]
[--periodic-bugreport]
[--permission-target-system]
COUNT
Here is a more typical command line, which will launch your application and send 500 pseudo-random events to it:
$ adb shell monkey -p your.package.name -v 500
Most interestingly, you can provide a script to monkey for running a predefined set of input instead of providing random input. For a detailed understanding of the scripting language understood by monkey, take a look at monkey’s script interpreting code in development/cmds/monkey/src/com/android/commands/ monkey/MonkeySourceScript.java and look for EVENT_KEYWORD_.
usage: bmgr [backup|restore|list|transport|run]
bmgr backup PACKAGE
bmgr enable BOOL
bmgr enabled
bmgr list transports [-c]
bmgr list sets
bmgr transport WHICH|-c WHICH_COMPONENT
bmgr restore TOKEN
bmgr restore TOKEN PACKAGE...
bmgr restore PACKAGE
bmgr run
bmgr wipe TRANSPORT PACKAGE
bmgr fullbackup PACKAGE...
bmgr backupnow --all|PACKAGE...
bmgr cancel backups
The 'backup' command schedules a backup pass for the named package.
Note that the backup pass will effectively be a no-op if the package
does not actually have changed data to store.
The 'enable' command enables or disables the entire backup mechanism.
If the argument is 'true' it will be enabled, otherwise it will be
disabled. When disabled, neither backup or restore operations will
be performed.
The 'enabled' command reports the current enabled/disabled state of
the backup mechanism.
The 'list transports' command reports the names of the backup transports
BackupManager is currently bound to. These names can be passed as arguments
to the 'transport' and 'wipe' commands. The currently active transport
is indicated with a '*' character. If -c flag is used, all available
transport components on the device are listed. These can be used with
the component variant of 'transport' command.
The 'list sets' command reports the token and name of each restore set
available to the device via the currently active transport.
The 'transport' command designates the named transport as the currently
active one. This setting is persistent across reboots. If -c flag is
specified, the following string is treated as a component name.
The 'restore' command when given just a restore token initiates a full-system
restore operation from the currently active transport. It will deliver
the restore set designated by the TOKEN argument to each application
that had contributed data to that restore set.
The 'restore' command when given a token and one or more package names
initiates a restore operation of just those given packages from the restore
set designated by the TOKEN argument. It is effectively the same as the
'restore' operation supplying only a token, but applies a filter to the
set of applications to be restored.
The 'restore' command when given just a package name intiates a restore of
just that one package according to the restore set selection algorithm
used by the RestoreSession.restorePackage() method.
The 'run' command causes any scheduled backup operation to be initiated
immediately, without the usual waiting period for batching together
data changes.
The 'wipe' command causes all backed-up data for the given package to be
erased from the given transport's storage. The next backup operation
that the given application performs will rewrite its entire data set.
Transport names to use here are those reported by 'list transports'.
The 'fullbackup' command induces a full-data stream backup for one or more
packages. The data is sent via the currently active transport.
The 'backupnow' command runs an immediate backup for one or more packages.
--all flag runs backup for all eligible packages.
For each package it will run key/value or full data backup
depending on the package's manifest declarations.
The data is sent via the currently active transport.
The 'cancel backups' command cancels all running backups.
usage: media [subcommand] [options]
media dispatch KEY
media list-sessions
media monitor tag
media volume [options]
media dispatch: dispatch a media key to the system.
KEY may be: play, pause, play-pause, mute, headsethook,
stop, next, previous, rewind, record, fast-forword.
media list-sessions: print a list of the current sessions.
media monitor: monitor updates to the specified session.
Use the tag from list-sessions.
media volume: the options are as follows:
--stream STREAM selects the stream to control, see AudioManager.STREAM_*
controls AudioManager.STREAM_MUSIC if no stream is specified
--set INDEX sets the volume index value
--adj DIRECTION adjusts the volume, use raise|same|lower for the direction
--get outputs the current volume
--show shows the UI during the volume change
examples:
adb shell media volume --show --stream 3 --set 11
adb shell media volume --stream 0 --adj lower
adb shell media volume --stream 3 --get
Dalvik Utilities
- dalvikvm dalvikvm is actually a raw Dalvik VM without any connection to “Android”, it start just a Dalvik VM without any Android-specific functionality
- dexdump To reverse-engineer Android apps or JAR files.
Support Daemons
Apps run as unprivileged users that can’t, for instance, invoke any system call that requires root privileges or access most of the key devices in /dev. Apps must ask system services to act on their behalf.
System services don’t, however, themselves run as root. Instead, many key operations require system services to communicate through Unix domain sockets in /dev/socket/ with native daemons running as either root or as a specific user to conduct privileged operations.
vold
External storage is managed by a combination of the vold init service and StorageManagerService system service.
vold takes care of many of the key operations required by the Mount Service, such as mounting and formatting volumes.
Mounting of physical external storage volumes is handled by vold.
vold is started by init.rc, while the Mount Service is part of the System Server.
vold actually has a configuration file,
- /etc/vold.fstab for Android 4.2.2 and earlier
- /etc/fstab.device for Android releases 4.3 and later
src mnt_point type mnt_flags fs_mgr_flags
where:- src A path under sysfs (usually mounted at /sys) to the device that can provide the mount point. The path must start with /.
- mount_point Filesystem path where the volume should be mounted.
- type The type of the filesystem on the volume. For external cards, this is usually vfat.
- mnt_flags Vold ignores this field and it should be set to defaults
- fs_mgr_flags : Vold ignores any lines in the unified fstab that do not include the voldmanaged= flag in this field. This flag must be followed by a label describing the card, and a partition number or the word auto. Here is an example: voldmanaged=sdcard:auto. Other possible flags are nonremovable, encryptable=sdcard, noemulatedsd, and encryptable=userdata.
healthd
The "health daemon" is meant to service general "device health" tasks periodically, currently, the only tasks are battery related.
Like most daemons, healthd sets up an initial configuration, and then enters a run loop.
Android 9 includes android.hardware.health HAL 2.0, a major version upgrade from health@1.0 HAL.
Devices launching with Android 9 must provide the 2.0 HAL (and must not provide the 1.0 HAL).
Terminology:
- health@1.0 abbreviation of android.hardware.health@1.0. Refers to health HIDL HAL version 1.0 released in Android 8.0.
- health@2.0 abbreviation of android.hardware.health@2.0. Refers to health HIDL HAL version 2.0 released in Android 9.
- charger executable running in off-mode charging that displays the phone-charging animation.
- recovery executable running in recovery mode that must retrieve battery information.
- healthd legacy daemon running in Android that retrieves health-related information and provides it to framework.
- storaged daemon running in Android that retrieves storage information and provides it to framework.
The framework attempts to retrieve health@2.0 service from hwservicemanager. If it fails, it calls into health@1.0 (in Android 8.x). The legacy code path is kept so the Android 9 system image is compatible with the Android 8.x vendor image. The framework does not retrieve information from both HALs because only one service version (1.0 or 2.0) can exist on the device.
health@2.0 has the following clients:
- charger The usage of libbatterymonitor and healthd_common code is wrapped in health@2.0-impl.
- recovery
- BatteryManager
- BatteryService
- Storaged
Upgrading from health@1.0 HAL
Hardware Abstraction Layer
Android relies on a Hardware Abstraction Layer (HAL) to interface with hardware.
System services almost never interact with devices through /dev entries directly. Instead, they go through HAL modules, typically shared libraries, to talk to hardware.
The definitions of the interfaces between the Framework and the HAL modules are in header files under:
- hardware/libhardware/include/hardware/
- hardware/libhardware_legacy/include/hardware_legacy/
留言