Android source code analysis directory

(Note: The following code analysis is based on Android-10.0.0_R30)

An overview of

Because the Android system is based on the Linux kernel, so the start of the Android system is from Linux, when we start the Android phone, will go through a series of calls, until the start of zygote process, is also the first process of our system

system/core/init/main.cpp
system/core/init/init.cpp
system/core/init/parser.cpp
system/core/init/epoll.h
system/core/init/epoll.cpp
system/core/rootdir/init.rc
system/core/rootdir/init.zygote64.rc
Copy the code

Two init

Initial startup, it is in the Android system catalog system/core/init/main. The main function that executes in a CPP

There are mainly several functions in main

  1. FirstStageMain: The first stage of system startup
  2. SetupSelinux: Loads SELinux rules
  3. SecondStageMain: The second stage of system startup

They are called by passing different arguments

[main.cpp]

int main(int argc, char** argv) { #if __has_feature(address_sanitizer) __asan_set_error_report_callback(AsanReportCallback); #endif setpriority(PRIO_PROCESS, 0, -20); #endif setpriority(PRIO_PROCESS, 0, -20) if (! strcmp(basename(argv[0]), "ueventd")) { return ueventd_main(argc, argv); } if (argc > 1) { if (! strcmp(argv[1], "subcontext")) { android::base::InitLogging(argv, &android::base::KernelLogger); const BuiltinFunctionMap function_map; // return SubcontextMain(argc, argv, &function_map); } if (! STRCMP (argv[1], "selinux_setup")) {return SetupSelinux(argv); } if (! STRCMP (argv[1], "second_stage") {return SecondStageMain(argc, argv); }} return FirstStageMain(argc, argv); }Copy the code

FirstStageMain

Init is the first step in starting the process. This step does several things

  1. Mount some partitions and create critical directories
  2. Example Initialize the Kernel log system
  3. Enter the Set Linux phase

[init.cpp]

Int FirstStageMain(int argc, char** argv) {if (REBOOT_BOOTLOADER_ON_PANIC) {// Start bootloader when init process crashes, Let developers positioning problem InstallRebootSignalHandlers (); } boot_clock::time_point start_time = boot_clock::now(); std::vector<std::pair<std::string, int>> errors; #define CHECKCALL(x) \ if ((x) ! = 0) errors.emplace_back(#x " failed", errno); // Clear the umask. umask(0); CHECKCALL(clearenv())); . #undef CHECKCALL // redirect standard input/output error output to dev/null, SetStdioToDevNull(argv) for both the first and second phases; InitKernelLogging(argv) is not enabled for Android logs. // Load some ramdisk... // Selinux_setup const char* path = "/system/bin/init"; const char* args[] = {path, "selinux_setup", nullptr}; auto fd = open("/dev/kmsg", O_WRONLY | O_CLOEXEC); dup2(fd, STDOUT_FILENO); dup2(fd, STDERR_FILENO); close(fd); execv(path, const_cast<char**>(args)); return 1; }Copy the code

SetupSelinux

SELinux, which stands for Security-Enhanced Linux, is a Linux-related Security subsystem that is entered here after performing these related operations

SecondStageMain phase

Int SetupSelinux(char** argv) {// Some selinux-related initializers... // Enter the second phase of the init process const char* path = "/system/bin/init"; const char* args[] = {path, "second_stage", nullptr}; execv(path, const_cast<char**>(args)); return 1; }Copy the code

SecondStageMain

Init is the second step to start the process, and this step does a lot of work

  1. The system will perform some Linux related Settings
  2. And then it initializes some property PropertyInit
  3. Do some SELinux Settings
  4. Create an epoll that accepts the signal and processes it
  5. Set the system property StartPropertyService
  6. Parse the rc file and start adding actions. These actions can be divided into four stages: early-init, init, early-boot, and boot
  7. Go into an infinite loop and execute these actions

[init.cpp]

Int SecondStageMain(int argc, char** argv) {if (REBOOT_BOOTLOADER_ON_PANIC) { Let developers positioning problem InstallRebootSignalHandlers (); } boot_clock::time_point start_time = boot_clock::now(); trigger_shutdown = [](const std::string& command) { shutdown_state.TriggerShutdown(command); }; Dev /null SetStdioToDevNull(argv); // As in the first step, redirect the standard input output error output to dev/null SetStdioToDevNull(argv); InitSecondStageLogging(argv); // There are some linux-related Settings, which are omitted here. // Initialize some properties PropertyInit(); //debug related if (load_debug_prop) {UmountDebugRamdisk(); } // mount filesystems MountExtraFilesystems(); / / set SELinux SelinuxSetupKernelLogging (); Selabel SelabelInitialize(); // Set Selinux and restore some context SelinuxRestoreContext(); // Create epoll epoll epoll; if (auto result = epoll.Open(); ! result.ok()) { PLOG(FATAL) << result.error(); } // Set InstallSignalFdHandler(&epoll); // Set the notification function for property changes InstallInitNotifier(&epoll); // Set the system property. StartPropertyService(&property_fd); // Make the time that init stages started available for bootstat to log. RecordStageBoottimes(start_time); // Set libavb if (const char* avb_version = getenv("INIT_AVB_VERSION"); avb_version ! = nullptr) { SetProperty("ro.boot.avb_version", avb_version); } unsetenv("INIT_AVB_VERSION"); fs_mgr_vendor_overlay_mount_all(); export_oem_lock_status(); MountHandler mount_handler(&epoll); SetUsbController(); const BuiltinFunctionMap& function_map = GetBuiltinFunctionMap(); Action::set_function_map(&function_map); if (! SetupMountNamespaces()) { PLOG(FATAL) << "SetupMountNamespaces failed"; } // Initialize the second-stage context InitializeSubcontext(); / / build ActionManager and ServiceList related objects, used to resolve the rc related documents of the action, service ActionManager & am = ActionManager: : GetInstance (); ServiceList& sm = ServiceList::GetInstance(); // Load the startup configuration file, that is, some rc files LoadBootScripts(am, sm); // Turning this on and letting the INFO logging be discarded adds 0.2s to // Nexus 9 boot time, so it's disabled by default. if (false) DumpState(); / / make sure gsi available auto is_running = android: : gsi: : IsGsiRunning ()? "1", "0"; SetProperty(gsi::kGsiBootedProp, is_running); auto is_installed = android::gsi::IsGsiInstalled() ? "1", "0"; SetProperty(gsi::kGsiInstalledProp, is_installed); QueueBuiltinAction(SetupCgroupsAction, "SetupCgroups"); am.QueueBuiltinAction(SetKptrRestrictAction, "SetKptrRestrict"); am.QueueBuiltinAction(TestPerfEventSelinuxAction, "TestPerfEventSelinux"); // Init has four stages: early-init, init, early-boot, boot // early-init am.QueueEventTrigger("early-init"); QueueBuiltinAction(wait_for_coldboot_done_action, "wait_for_coldboot_done"); / / need/dev action. Am QueueBuiltinAction (MixHwrngIntoLinuxRngAction, "MixHwrngIntoLinuxRng"); am.QueueBuiltinAction(SetMmapRndBitsAction, "SetMmapRndBits"); // Initialize the /dev/keychord device to debug the related Keychords Keychords; am.QueueBuiltinAction( [&epoll, &keychords](const BuiltinArguments& args) -> Result<void> { for (const auto& svc : ServiceList::GetInstance()) { keychords.Register(svc->keycodes()); } keychords.Start(&epoll, HandleKeychord); return {}; }, "KeychordInit"); // Phase 2, init phase am.queueEventTrigger ("init"); // If wait_for_coldboot_done is not ready, Just repeat. Mix_hwrng_into_linux_rng am QueueBuiltinAction (MixHwrngIntoLinuxRngAction, "MixHwrngIntoLinuxRng"); // Don't mount filesystems or start core system services in charger mode. std::string bootmode = GetProperty("ro.bootmode", ""); if (bootmode == "charger") { am.QueueEventTrigger("charger"); } else {// With filesystems in non-charger mode, start core system am.queueEventtrigger ("late-init"); QueueBuiltinAction(queue_property_triggerS_action, "queue_property_triggers"); // Restore priority setPriority (PRIO_PROCESS, 0, 0); While (true) {// This is an infinite loop, which is used to process events that have been added before. If the child dies, Will restart the corresponding child process auto epoll_timeout = STD: : optional < STD: : chrono: : milliseconds > {}; auto shutdown_command = shutdown_state.CheckShutdown(); if (shutdown_command) { LOG(INFO) << "Got shutdown_command '" << *shutdown_command << "' Calling HandlePowerctlMessage()"; HandlePowerctlMessage(*shutdown_command); shutdown_state.set_do_shutdown(false); } if (! (prop_waiter_state MightBeWaiting () | | Service: : is_exec_service_running ())) {/ / added to the action of the Command before implementation am.ExecuteOneCommand(); } if (! IsShuttingDown()) { auto next_process_action_time = HandleProcessActions(); // If the process needs to be restarted, Wakes the if (next_process_action_time) {epoll_timeout = STD: : chrono: : ceil < STD: : chrono: : milliseconds > ( *next_process_action_time - boot_clock::now()); if (*epoll_timeout < 0ms) epoll_timeout = 0ms; } } if (! (prop_waiter_state MightBeWaiting () | | Service: : is_exec_service_running ())) {/ / if didn't finish his work, If (am.hasmoreCommands ()) epoll_timeout = 0ms; } auto pending_functions = epoll.Wait(epoll_timeout); if (! pending_functions.ok()) { LOG(ERROR) << pending_functions.error(); } else if (! Pending_functions - > empty ()) {/ / before with other functions, to deal with children, avoid daemon see competition program exits and produce init ReapAnyOutstandingChildren (); For (const auto& function: *pending_functions) {(*function)(); } } if (! IsShuttingDown()) { HandleControlMessages(); SetUsbController(); } } return 0; }Copy the code

Three Epoll

About Epoll, its Baidu encyclopedia is as follows

Epoll is a poll modified by the Linux kernel for handling large numbers of file descriptors. It is an enhanced version of the SELECT/Poll multiplex IO interface under Linux, which can significantly improve the system CPU utilization of programs with only a small amount of activity in a large number of concurrent connections. Another reason is that when it gets an event, it doesn’t need to iterate through the entire set of listened descriptors, just the set of descriptors that have been asynchronously woken up by kernel IO events and added to the Ready queue. In addition to providing Level Triggered I/O events like SELECT /poll, epoll also provides Edge Triggered I/O events, which makes it possible for user-space programs to cache I/O state. Reduce epoll_WAIT /epoll_pwait calls to improve application efficiency.

The details are defined in system/core/init/epoll.h

class Epoll {
  public:
    Epoll();

    Result<void> Open();
    Result<void> RegisterHandler(int fd, std::function<void()> handler, uint32_t events = EPOLLIN);
    Result<void> UnregisterHandler(int fd);
    Result<std::vector<std::function<void()>*>> Wait(
            std::optional<std::chrono::milliseconds> timeout);

  private:
    android::base::unique_fd epoll_fd_;
    std::map<int, std::function<void()>> epoll_handlers_;
};
Copy the code

During initialization, two functions are called

InstallSignalFdHandler(&epoll); // Set InstallSignalFdHandler(&epoll); // Set the notification function for property changes InstallInitNotifier(&epoll);Copy the code

InstallSignalFdHandler

Register the child’s SIGCHLD signal to listen for the child’s health, and if the child hangs up, remove or restart the child to prevent it from becoming a zombie

[init.cpp]

static void InstallSignalFdHandler(Epoll* epoll) { ... // Register a handler to unblock signals in the child processes. const int result = pthread_atfork(nullptr, nullptr, &UnblockSignals); if (result ! = 0) { LOG(FATAL) << "Failed to register a fork handler: " << strerror(result); } signal_fd = signalfd(-1, &mask, SFD_CLOEXEC); if (signal_fd == -1) { PLOG(FATAL) << "failed to create signalfd"; If (auto result = epoll->RegisterHandler(signal_fd, HandleSignalFd); ! result.ok()) { LOG(FATAL) << result.error(); }}Copy the code

InstallInitNotifier

Register a listener for the wake_main_thread_fd event

[init.cpp]

static void InstallInitNotifier(Epoll* epoll) {
    wake_main_thread_fd = eventfd(0, EFD_CLOEXEC);
    if (wake_main_thread_fd == -1) {
        PLOG(FATAL) << "Failed to create eventfd for waking init";
    }
    auto clear_eventfd = [] {
        uint64_t counter;
        TEMP_FAILURE_RETRY(read(wake_main_thread_fd, &counter, sizeof(counter)));
    };

    if (auto result = epoll->RegisterHandler(wake_main_thread_fd, clear_eventfd); !result.ok()) {
        LOG(FATAL) << result.error();
    }
}
Copy the code

Four Android Init Language

In Android 10, there are multiple RC files. These rc files are written using the Android Init Language (AIL). The documentation for AIL is available here

Five zygote

LoadBootScripts

LoadBootScripts parses rc files using the Parser. Common examples include Action, Command, and import

[init.cpp]

static void LoadBootScripts(ActionManager& action_manager, ServiceList& service_list) { Parser parser = CreateParser(action_manager, service_list); Bootscript = GetProperty("ro.boot.init_rc", ""); STD ::string bootscript = GetProperty("ro.boot.init_rc", ""); if (bootscript.empty()) { parser.ParseConfig("/system/etc/init/hw/init.rc"); if (! parser.ParseConfig("/system/etc/init")) { late_import_paths.emplace_back("/system/etc/init"); } // late_import is available only in Q and earlier release. As we don't // have system_ext in those versions, skip late_import for system_ext. parser.ParseConfig("/system_ext/etc/init"); if (! parser.ParseConfig("/product/etc/init")) { late_import_paths.emplace_back("/product/etc/init"); } if (! parser.ParseConfig("/odm/etc/init")) { late_import_paths.emplace_back("/odm/etc/init"); } if (! parser.ParseConfig("/vendor/etc/init")) { late_import_paths.emplace_back("/vendor/etc/init"); } } else { parser.ParseConfig(bootscript); Parser CreateParser(ActionManager& Action_manager, ServiceList& Service_list) {Parser Parser; parser.AddSectionParser("service", std::make_unique<ServiceParser>( &service_list, GetSubcontext(), std::nullopt)); parser.AddSectionParser("on", std::make_unique<ActionParser>(&action_manager, GetSubcontext())); parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser)); return parser; }Copy the code

Rc related documents in the system/core/rootdir/init. Rc directory, because there are multiple zygote files, so it will be through the import import zygote file, and then start the zygote

[init.rc]

import /init.${ro.zygote}.rc
Copy the code

[init.zygote64.rc]

service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server
    class main
    priority -20
    user root
    group root readproc reserved_disk
    socket zygote stream 660 root system
    socket usap_pool_primary stream 660 root system
    onrestart write /sys/android_power/request_state wake
    onrestart write /sys/power/state on
    onrestart restart audioserver
    onrestart restart cameraserver
    onrestart restart media
    onrestart restart netd
    onrestart restart wificond
    writepid /dev/cpuset/foreground/tasks
Copy the code