Android boot sequence

To fully understand all Android internals, we are going to learn how the whole boot sequence works: from the power-on to the actual Android system boot. The Android boot sequence is similar to any other embedded system based on Linux: in a very abstract way, after the power-on, the system initializes the hardware, loads the kernel, and finally the Android framework. Any Linux-based system undergoes a similar process during its boot sequence: your Ubuntu computer or even your home DSL router.

In the next sections, we are going to dive deeper in to these steps to fully comprehend the operating system we love so much.

Internal ROM – bios

When you press the power button on your device, the system loads a tiny amount of code, stored inside a ROM memory. You can think about this as an equivalent of the BIOS software you have in your PC. This software is in charge of setting up all the parameters for CPU clock and running the RAM memory check. After this, the system loads the bootloader into memory and launches it.

An overview of bootloader

So far, the bootloader has been loaded into the RAM memory and started. The bootloader is in charge of loading the system kernel into the RAM memory and launching it, to continue the boot sequence.

The most popular bootloader software for Android devices is U-Boot, the Universal Bootloader. U-Boot is widely used in all kinds of embedded systems: DSL routers, smart TVs, infotainment systems, for example. U-boot is open source software and its flexibility to be customized for any device is definitely one of the reasons for its popularity.

U-boot's main task is to read the kernel image from the boot partition, load it into the RAM memory, and run it. From this moment on, the kernel is in charge of finishing the boot sequence.

You could think about U-boot on Android like GRUB on your Ubuntu system: it reads the kernel image, decompresses it, loads it into the RAM memory, and executes it. The following diagram gives you a graphical representation of the whole boot sequence as on an embedded Linux system, an Android system, and a Linux PC:

An overview of bootloader

The kernel

After the bootloader loads the kernel, the kernel's first task is to initialize the hardware. With all the necessary hardware properly set up, the kernel mounts the ramdisk from boot.img and launches init.

The Init process

In a standard Linux system, the init process takes care of starting all the core services needed to boot the system. The final goal is to complete the boot sequence and start the graphical interface or the command line to make the system available to the user. This whole process is based on a specific sequence of system scripts, executed in a rigorous order to assure system integrity and proper configuration.

Android follows the same philosophy, but it acts in a different way. In a standard Android system, the ramdisk, contained in the boot.img, provides the init script and all the scripts necessary for the boot.

The Android init process consists of two main files:

  • init.rc
  • init.${ro.hardware}.rc

The init.rc file is the first initialization script of the system. It takes care of initializing those aspects that are common to all Android systems. The second file is very hardware specific. As you can guess, ${ro.hardware} is a placeholder for the reference of a particular hardware where the boot sequence is happening. For instance, ${ro.hardware} is replaced with goldfinsh in the emulator boot configuration.

In a standard Linux system, the init sequence executes a set of bash scripts. These bash scripts start a set of system services. Bash scripting is a common solution for a lot of Linux systems, because it is very standardized and quite popular.

Android systems use a different language to deal with the initialization sequence: Android Init Language.

The Android init language

The Android team chose to not use Bash for Android init scripts, but to create its own language to perform configurations and services launches.

The Android Init Language is based on five classes of statements:

  • Actions
  • Commands
  • Services
  • Options
  • Imports

Every statement is line-oriented and is based on specific tokens, separated by white spaces. Comment lines start with a # symbol.

Actions

An Action is a sequence of commands bound to a specific trigger that's used to execute the particular action at a specific moment. When the desired event happens, the Action is placed in an execution queue, ready to be performed.

This snippet shows an example of an Action statement:

on <trigger> [&& <trigger>]*
  <command>
  <command>
  <command>

Actions have unique names. If a second Action is created with the same name in the same file, its set of commands is added to the first Action commands, set and executed as a single action.

Services

Services are programs that the init sequence will execute during the boot. These services can also be monitored and restarted if it's mandatory they stay up. The following snippet shows an example of a service statement:

service <name> <pathname> [ <argument> ]*
  <option>
  <option>
  ...

Services have unique names. If in the same file, a service with a nonunique name exists, only the first one is evaluated as valid; the second one is ignored and the developer is notified with an error message.

Options

Options statements are coupled with services. They are meant to influence how and when init manages a specific service.

Android provides quite an amount of possible options statements:

  • critical: This specifies a device-critical service. The service will be constantly monitored and if it dies more than four times in four minutes, the device will be rebooted in Recovery Mode.
  • disabled: This service will be in a default stopped state. init won't launch it. A disabled service can only be launched manually, specifying it by name.
  • setenv <name> <value>: This sets an environment variable using name and value.
  • socket <name> <type> <perm> [ <user> [ <group> [ <seclabel> ] ] ]: This command creates a Unix socket, with a specified name, (/dev/socket/<name>) and provides its file descriptor the specified service. <type> specifies the type of socket: dgram, stream, or seqpacket. Default <user> and <group> are 0. <seclabel> specifies the SELinx security context for the created socket.
  • user <username>: This changes the username before the service is executed. The default username is root.
  • group <groupname> [ <groupname> ]*: This changes the group name before the service is executed.
  • seclabel <seclabel>: This changes the SELinux level before launching the service.
  • oneshot: This disables the service monitoring and the service won't be restarted when it terminates.
  • class <name>: This specifies a service class. Classes of services can be launched or stopped at the same time. A service with an unspecified class value will be associated to the default class.
  • onrestart: This executes a command when the service is restarted.
  • writepid <file...>: When a services forks, this option will write the process ID (PID) in a specified file.

Triggers

Triggers specify a condition that has to be satisfied to execute a particular action. They can be event triggers or property triggers. Event triggers can be fired by the trigger command or by the QueueEventTrigger() function. The example event triggers are boot and late-init. Property triggers can be fired when an observed property changes value. Every Action can have multiple Property triggers, but only one Event trigger; refer to the following code for instance:

on boot && property:a=b

This Action will be executed when the boot event is triggered and the property a is equal to b.

Commands

The Command statement specifies a command that can be executed during the boot sequence, placing it in the init.rc file. Most of these commands are common Linux system commands. The list is quite extensive. Let's look at them in detail:

  • bootchart_init: This starts bootchart if it is properly configured. Bootchart is a performance monitor and can provide insights about the boot performance of a device.
  • chmod <octal-mode-permissions> <filename>: This changes file permissions.
  • chown <owner> <group> <filename>: This changes the owner and the group for the specified file.
  • class_start <serviceclass>: This starts a service specified by its class name.
  • class_stop <serviceclass>: This stops and disables a service specified by its class name.
  • class_reset <serviceclass>: This stops a service specified by its class name. It doesn't disable the service.
  • copy <src> <dst>: This copies a source file to a new destination file.
  • domainname <name>: This sets the domain name.
  • enable <servicename>: This starts a service by its name. If the service is already queued to be started, then it starts the service immediately.
  • exec [<seclabel>[<user>[<group> ]* ]] -- <command> [ <argument> ]*: This forks and executes the specified command. The execution is blocking: no other command can be executed in the meantime.
  • export <name> <value>: This sets and exports an environment variable.
  • hostname <name>: This sets the hostname.
  • ifup <interface>: This enables the specified network interface.
  • insmod <path>: This loads the specified kernel module.
  • load_all_props: This loads all the system properties.
  • load_persist_props: This loads the persistent properties, after the successful decryption of the /data partition.
  • loglevel <level>: This sets the kernel log level.
  • mkdir <path> [mode] [owner] [group]: This creates a folder with the specified name, permissions, owner, and group. The defaults are 755 as permissions, and root as owner and group.
  • mount_all <fstab>: This mounts all the partitions in the fstab file.
  • mount <type> <device> <dir> [ <flag> ]* [<options>]: This mounts a specific device in a specific folder. A few mount flags are available: rw, ro, remount, noatime, and all the common Linux mount flags.
  • powerctl: This is used to react to changes of the sys.powerctl system parameter, critically important for the implementation of the reboot routing.
  • restart <service>: This restarts the specified service.
  • rm <filename>: This deletes the specified file.
  • rmdir <foldername>: This deletes the specified folder.
  • setpropr <name> <value>: This sets the system property with the specified name with the specified value.
  • start <service>: This starts a service.
  • stop <service>: This stops a service.
  • swapon_all <fstab>: This enables the swap partitions specified in the fstab file.
  • symlink <target> <path>: This creates a symbolic link from the target file to the destination path.
  • sysclktz <mins_west_of_gtm>: This sets the system clock.
  • trigger <event>: This programmatically triggers the specified event.
  • wait <filename > [ <timeout> ]: This monitors a path for a file to appear. A timeout can be specified. If not, the default timeout value is 5 seconds.
  • write <filename> <content>: This writes the specified content to the specified file. If the file doesn't exist, it creates the file. If the file already exists, it won't append the content, but it will override the whole file.

Imports

Imports specify all the external files that are needed in the current file and imports them:

import <path>

The previous snippet is an example of how the current init script can be extended, importing an external init script. path can be a single file or even a folder. In case path is a folder, all the files that exists in the first level of the specified folder will be imported. The command doesn't act recursively on folders: nested folders must be imported programmatically one by one.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.191.147.77