Android开机流程-从Init进程启动到进入Android桌面

1. init进程启动流程

Android bootloader负责加载boot.img,将其内容放入内存,然后启动内核。内核接管之后,会解压并加载ramdisk到内存中,然后启动用户空间的第一个进程init

在Android系统启动过程中,ramdisk.img被内核直接解压到内存中并用作初始根文件系统。这一过程不是通过挂载块设备来实现的,而是通过解压cpio档案的内容到内存中的临时文件系统(tmpfs)来实现的。这种方式类似于Linux中的initramfs,并且在内核的初始化代码中处理。

boot.img包含内核镜像、ramdisk镜像和可能的设备树blob(DTB), 简单的可以通过file命令查看,或者可以解压boot.img查看里面的内容。

$ file boot.img 
boot.img: Android bootimg, kernel (0x20fecc), ramdisk (0x62c)

ramdisk包含的内容可以查看out目录中生成的ramdisk文件夹
请添加图片描述

通过 ps -ef | grep init查看init进程启动参数

barbet:/ # ps -ef | grep init
root             1     0 0 16:52:12 ?     00:00:54 init second_stage
root           554     1 0 16:52:13 ?     00:00:02 init subcontext u:r:vendor_init:s0 14

由于第一阶段 init 过程是序列化的,因此并行化启动过程的机会并不多。如果一个模块在完成第一阶段 init 时用不到,请将模块放入 vendor 或 vendor_dlkm 分区,从而将其移至第二阶段 init。
https://source.android.com/docs/core/architecture/kernel/boot-time-opt?hl=zh-cn

1.1 init源码结构

Android开机流程-从Init进程启动到进入Android桌面_第1张图片

这些源码会编译出两个init(./system/bin/init和./ramdisk/init),以及一个ueventd符号链接,ueventd链接到./system/bin/init, 有着同样的md5值。

out/target/product/barbet$ md5sum ./system/bin/init ./system/bin/ueventd ./ramdisk/init
741c9721502bb629d7329a69162689e5  ./system/bin/init
741c9721502bb629d7329a69162689e5  ./system/bin/ueventd
c3b8ba9312369c6307036369539cea74  ./ramdisk/init

init启动分5个阶段:

FirstStateMain->SetupSelinux->SecondStageMain->SubcontextMain->ueventd_main

Android开机流程-从Init进程启动到进入Android桌面_第2张图片

int main(int argc, char** argv) {
#if __has_feature(address_sanitizer)
    __asan_set_error_report_callback(AsanReportCallback);
#elif __has_feature(hwaddress_sanitizer)
    __hwasan_set_error_report_callback(AsanReportCallback);
#endif
    // Boost prio which will be restored later
    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 = GetBuiltinFunctionMap();

            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);
}

查看Android.bp, FirstStateMain对应的就是./ramdisk/init,作用是执行init第一阶段的业务。
所以如果我们需要调试init进程init_first_stage相关的源码,比如添加log,应该烧写boot.img,而不是push /system/bin/init文件,/system/bin/init对应的是init第二阶段和ueventd的业务代码。

./ramdisk/init: FirstStateMain
./system/bin/init: SetupSelinux, SecondStageMain, SubcontextMain, ueventd_main

cc_binary {
    name: "init_first_stage",
    stem: "init",
    defaults: ["init_first_stage_defaults"],

    srcs: [
        "block_dev_initializer.cpp",
        "devices.cpp",
        "first_stage_console.cpp",
        "first_stage_init.cpp",
        "first_stage_main.cpp",
        "first_stage_mount.cpp",
        "reboot_utils.cpp",
        "selabel.cpp",
        "service_utils.cpp",
        "snapuserd_transition.cpp",
        "switch_root.cpp",
        "uevent_listener.cpp",
        "util.cpp",
    ],

    static_libs: [
        "libc++fs",
        "libfs_avb",
        "libfs_mgr",
        "libfec",
        "libfec_rs",
        "libsquashfs_utils",
        "libcrypto_utils",
        "libavb",
        "liblp",
        "libcutils",
        "libbase",
        "liblog",
        "libcrypto_static",
        "libselinux",
        "libcap",
        "libgsi",
        "liblzma",
        "libunwindstack_no_dex",
        "libbacktrace_no_dex",
        "libmodprobe",
        "libext2_uuid",
        "libprotobuf-cpp-lite",
        "libsnapshot_cow",
        "libsnapshot_init",
        "update_metadata-protos",
        "libprocinfo",
    ],

    static_executable: true,
    system_shared_libs: [],

    cflags: [
        "-Wall",
        "-Wextra",
        "-Wno-unused-parameter",
        "-Werror",
        "-DALLOW_FIRST_STAGE_CONSOLE=0",
        "-DALLOW_LOCAL_PROP_OVERRIDE=0",
        "-DALLOW_PERMISSIVE_SELINUX=0",
        "-DREBOOT_BOOTLOADER_ON_PANIC=0",
        "-DWORLD_WRITABLE_KMSG=0",
        "-DDUMP_ON_UMOUNT_FAILURE=0",
        "-DSHUTDOWN_ZERO_TIMEOUT=0",
        "-DLOG_UEVENTS=0",
        "-DSEPOLICY_VERSION=30", // TODO(jiyong): externalize the version number
    ],

    product_variables: {
        debuggable: {
            cflags: [
                "-UALLOW_FIRST_STAGE_CONSOLE",
                "-DALLOW_FIRST_STAGE_CONSOLE=1",

                "-UALLOW_LOCAL_PROP_OVERRIDE",
                "-DALLOW_LOCAL_PROP_OVERRIDE=1",

                "-UALLOW_PERMISSIVE_SELINUX",
                "-DALLOW_PERMISSIVE_SELINUX=1",

                "-UREBOOT_BOOTLOADER_ON_PANIC",
                "-DREBOOT_BOOTLOADER_ON_PANIC=1",

                "-UWORLD_WRITABLE_KMSG",
                "-DWORLD_WRITABLE_KMSG=1",

                "-UDUMP_ON_UMOUNT_FAILURE",
                "-DDUMP_ON_UMOUNT_FAILURE=1",
            ],
        },

        eng: {
            cflags: [
                "-USHUTDOWN_ZERO_TIMEOUT",
                "-DSHUTDOWN_ZERO_TIMEOUT=1",
            ],
        },
    },

    sanitize: {
        misc_undefined: ["signed-integer-overflow"],

        // First stage init is weird: it may start without stdout/stderr, and no /proc.
        hwaddress: false,
    },

    // Install adb_debug.prop into debug ramdisk.
    // This allows adb root on a user build, when debug ramdisk is used.
    required: ["adb_debug.prop"],

    ramdisk: true,

    install_in_root: true,
}

init_second_stage对应./system/bin/init和 ./system/bin/ueventd

cc_binary {
    name: "init_second_stage",
    recovery_available: true,
    stem: "init",
    defaults: ["init_defaults"],
    static_libs: ["libinit"],
    srcs: ["main.cpp"],
    symlinks: ["ueventd"],
    target: {
        platform: {
            required: [
                "init.rc",
                "ueventd.rc",
                "e2fsdroid",
                "extra_free_kbytes.sh",
                "make_f2fs",
                "mke2fs",
                "sload_f2fs",
            ],
        },
        recovery: {
            cflags: ["-DRECOVERY"],
            exclude_static_libs: [
                "libxml2",
            ],
            exclude_shared_libs: [
                "libbinder",
                "libutils",
            ],
            required: [
                "init_recovery.rc",
                "ueventd.rc.recovery",
                "e2fsdroid.recovery",
                "make_f2fs.recovery",
                "mke2fs.recovery",
                "sload_f2fs.recovery",
            ],
        },
    },
    visibility: ["//packages/modules/Virtualization/microdroid"],
}

Android.bp中
stem:用于重命名生成文件的基名。
symlinks:用于创建符号链接。
phony:用于定义伪目标,用来建立依赖关系或执行特定构建步骤。

1.2 init启动阶段

1.2.1 FirstStateMain

FirstStageMain 函数在 Android 引导过程中执行了大量的初始化工作,包括环境变量设置、文件系统挂载、内核模块加载以及控制台和调试选项的处理。

环境变量设置,部分文件系统挂载
#define CHECKCALL(x) \
    if ((x) != 0) errors.emplace_back(#x " failed", errno);

    // Clear the umask.
    umask(0);

    CHECKCALL(clearenv());
    CHECKCALL(setenv("PATH", _PATH_DEFPATH, 1));
    // Get the basic filesystem setup we need put together in the initramdisk
    // on / and then we'll let the rc file figure out the rest.
    CHECKCALL(mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755"));
    CHECKCALL(mkdir("/dev/pts", 0755));
    CHECKCALL(mkdir("/dev/socket", 0755));
    CHECKCALL(mkdir("/dev/dm-user", 0755));
    CHECKCALL(mount("devpts", "/dev/pts", "devpts", 0, NULL));
#define MAKE_STR(x) __STRING(x)
    CHECKCALL(mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC)));
#undef MAKE_STR
    // Don't expose the raw commandline to unprivileged processes.
    CHECKCALL(chmod("/proc/cmdline", 0440));
    std::string cmdline;
    android::base::ReadFileToString("/proc/cmdline", &cmdline);
    // Don't expose the raw bootconfig to unprivileged processes.
    chmod("/proc/bootconfig", 0440);
    std::string bootconfig;
    android::base::ReadFileToString("/proc/bootconfig", &bootconfig);
    gid_t groups[] = {AID_READPROC};
    CHECKCALL(setgroups(arraysize(groups), groups));
    CHECKCALL(mount("sysfs", "/sys", "sysfs", 0, NULL));
    CHECKCALL(mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL));

    CHECKCALL(mknod("/dev/kmsg", S_IFCHR | 0600, makedev(1, 11)));

    if constexpr (WORLD_WRITABLE_KMSG) {
        CHECKCALL(mknod("/dev/kmsg_debug", S_IFCHR | 0622, makedev(1, 11)));
    }

    CHECKCALL(mknod("/dev/random", S_IFCHR | 0666, makedev(1, 8)));
    CHECKCALL(mknod("/dev/urandom", S_IFCHR | 0666, makedev(1, 9)));

    // This is needed for log wrapper, which gets called before ueventd runs.
    CHECKCALL(mknod("/dev/ptmx", S_IFCHR | 0666, makedev(5, 2)));
    CHECKCALL(mknod("/dev/null", S_IFCHR | 0666, makedev(1, 3)));

    // These below mounts are done in first stage init so that first stage mount can mount
    // subdirectories of /mnt/{vendor,product}/.  Other mounts, not required by first stage mount,
    // should be done in rc files.
    // Mount staging areas for devices managed by vold
    // See storage config details at http://source.android.com/devices/storage/
    CHECKCALL(mount("tmpfs", "/mnt", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
                    "mode=0755,uid=0,gid=1000"));
    // /mnt/vendor is used to mount vendor-specific partitions that can not be
    // part of the vendor partition, e.g. because they are mounted read-write.
    CHECKCALL(mkdir("/mnt/vendor", 0755));
    // /mnt/product is used to mount product-specific partitions that can not be
    // part of the product partition, e.g. because they are mounted read-write.
    CHECKCALL(mkdir("/mnt/product", 0755));

    // /debug_ramdisk is used to preserve additional files from the debug ramdisk
    CHECKCALL(mount("tmpfs", "/debug_ramdisk", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
                    "mode=0755,uid=0,gid=0"));

    // /second_stage_resources is used to preserve files from first to second
    // stage init
    CHECKCALL(mount("tmpfs", kSecondStageRes, "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
                    "mode=0755,uid=0,gid=0"))

    // First stage init stores Mainline sepolicy here.
    CHECKCALL(mkdir("/dev/selinux", 0744));
#undef CHECKCALL

调用 ,之后才能够打印kernel log。

InitKernelLogging(argv)
加载内核模块
boot_clock::time_point module_start_time = boot_clock::now();
    int module_count = 0;
    if (!LoadKernelModules(IsRecoveryMode() && !ForceNormalBoot(cmdline, bootconfig), want_console,
                           want_parallel, module_count)) {
        if (want_console != FirstStageConsoleParam::DISABLED) {
            LOG(ERROR) << "Failed to load kernel modules, starting console";
        } else {
            LOG(FATAL) << "Failed to load kernel modules";
        }
    }
    if (module_count > 0) {
        auto module_elapse_time = std::chrono::duration_cast(
                boot_clock::now() - module_start_time);
        setenv(kEnvInitModuleDurationMs, std::to_string(module_elapse_time.count()).c_str(), 1);
        LOG(INFO) << "Loaded " << module_count << " kernel modules took "
                  << module_elapse_time.count() << " ms";
    }

对应的log,dmesg或者logcat都可查看

行    590: 03-25 01:32:45.979     0     0 I         : c7      1 init: Loading module /lib/modules/msm_ipc_logging.ko with args ''
行    591: 03-25 01:32:45.980     0     0 I         : c7      1 init: Loaded kernel module /lib/modules/msm_ipc_logging.ko
行    592: 03-25 01:32:45.980     0     0 I         : c7      1 init: Loading module /lib/modules/qtee_shm_bridge.ko with args ''
行    595: 03-25 01:32:45.983     0     0 I         : c7      1 init: Loaded kernel module /lib/modules/qtee_shm_bridge.ko
行    596: 03-25 01:32:45.983     0     0 I         : c7      1 init: Loading module /lib/modules/qcom-cpufreq-hw.ko with args ''
行    602: 03-25 01:32:45.996     0     0 I         : c7      1 init: Loaded kernel module /lib/modules/qcom-cpufreq-hw.ko
行    603: 03-25 01:32:45.996     0     0 I         : c7      1 init: Loading module /lib/modules/qcom_hwspinlock.ko with args ''
行    604: 03-25 01:32:45.996     0     0 I         : c7      1 init: Loaded kernel module /lib/modules/qcom_hwspinlock.ko
行    605: 03-25 01:32:45.996     0     0 I         : c7      1 init: Loading module /lib/modules/smem.ko with args ''
行    606: 03-25 01:32:45.996     0     0 I         : c7      1 init: Loaded kernel module /lib/modules/smem.ko
行    607: 03-25 01:32:45.997     0     0 I         : c7      1 init: Loading module /lib/modules/msm_minidump.ko with args ''
行    609: 03-25 01:32:45.997     0     0 I         : c7      1 init: Loaded kernel module /lib/modules/msm_minidump.ko
行    610: 03-25 01:32:45.997     0     0 I         : c7      1 init: Loading module /lib/modules/watchdog_v2.ko with args ''
行    613: 03-25 01:32:45.997     0     0 I         : c7      1 init: Loaded kernel module /lib/modules/watchdog_v2.ko
行    614: 03-25 01:32:45.997     0     0 I         : c7      1 init: Loading module /lib/modules/early_random.ko with args ''
行    616: 03-25 01:32:45.998     0     0 I         : c7      1 init: Loaded kernel module /lib/modules/early_random.ko
行    617: 03-25 01:32:45.998     0     0 I         : c7      1 init: Loading module /lib/modules/qcom-pdc.ko with args ''
行    618: 03-25 01:32:45.998     0     0 I         : c7      1 init: Loaded kernel module /lib/modules/qcom-pdc.ko
行    619: 03-25 01:32:45.998     0     0 I         : c7      1 init: Loading module /lib/modules/cmd-db.ko with args ''
行    620: 03-25 01:32:45.998     0     0 I         : c7      1 init: Loaded kernel module /lib/modules/cmd-db.ko
行    621: 03-25 01:32:45.998     0     0 I         : c7      1 init: Loading module /lib/modules/qcom_rpmh.ko with args ''
行    680: 03-25 01:32:46.075     0     0 I         : c7      1 init: Loaded kernel module /lib/modules/qcom_rpmh.ko
行    681: 03-25 01:32:46.075     0     0 I         : c7      1 init: Loading module /lib/modules/phy-qcom-ufs.ko with args ''
行    682: 03-25 01:32:46.075     0     0 I         : c7      1 init: Loaded kernel module /lib/modules/phy-qcom-ufs.ko
行    683: 03-25 01:32:46.075     0     0 I         : c7      1 init: Loading module /lib/modules/phy-qcom-ufs-qrbtc-sdm845.ko with args ''
行    684: 03-25 01:32:46.076     0     0 I         : c7      1 init: Loaded kernel module /lib/modules/phy-qcom-ufs-qrbtc-sdm845.ko
...
...
...
行   2073: 03-25 01:32:46.667     0     0 I         : c7      1 init: Loaded kernel module /lib/modules/ebtables.ko
行   2074: 03-25 01:32:46.667     0     0 I         : c7      1 init: Loading module /lib/modules/ebtable_broute.ko with args ''
行   2075: 03-25 01:32:46.668     0     0 I         : c7      1 init: Loaded kernel module /lib/modules/ebtable_broute.ko
行   2076: 03-25 01:32:46.668     0     0 I         : c7      1 init: Loading module /lib/modules/sctp.ko with args ''
行   2078: 03-25 01:32:46.672     0     0 I         : c7      1 init: Loaded kernel module /lib/modules/sctp.ko
行   2079: 03-25 01:32:46.672     0     0 I         : c7      1 init: Loading module /lib/modules/sctp_diag.ko with args ''
行   2080: 03-25 01:32:46.673     0     0 I         : c7      1 init: Loaded kernel module /lib/modules/sctp_diag.ko
行   2081: 03-25 01:32:46.673     0     0 I         : c7      1 init: Loading module /lib/modules/qrtr-smd.ko with args ''
行   2082: 03-25 01:32:46.673     0     0 I         : c7      1 init: Loaded kernel module /lib/modules/qrtr-smd.ko
行   2083: 03-25 01:32:46.673     0     0 I         : c7      1 init: Loaded 220 kernel modules took 697 ms
挂载实际文件系统并切换根文件系统
if (ForceNormalBoot(cmdline, bootconfig)) {
        mkdir("/first_stage_ramdisk", 0755);
        PrepareSwitchRoot();
        // SwitchRoot() must be called with a mount point as the target, so we bind mount the
        // target directory to itself here.
        if (mount("/first_stage_ramdisk", "/first_stage_ramdisk", nullptr, MS_BIND, nullptr) != 0) {
            PLOG(FATAL) << "Could not bind mount /first_stage_ramdisk to itself";
        }
        SwitchRoot("/first_stage_ramdisk");
    }

    if (!DoFirstStageMount(!created_devices)) {
        LOG(FATAL) << "Failed to mount required partitions early ...";
    }

对应log

行   2086: 03-25 01:32:46.674     0     0 I         : c7      1 init: Switching root to '/first_stage_ramdisk'
行   2088: 03-25 01:32:46.674     0     0 I         : c7      1 init: [libfs_mgr]ReadFstabFromDt(): failed to read fstab from dt
行   2089: 03-25 01:32:46.674     0     0 I         : c7      1 init: Using Android DT directory /proc/device-tree/firmware/android/
行   2090: 03-25 01:32:46.687     0     0 I         : c7      1 init: [libfs_mgr]superblock s_max_mnt_count:65535,/dev/block/by-name/metadata
行   2096: 03-25 01:32:46.689     0     0 I         : c7      1 init: [libfs_mgr]check_fs(): mount(/dev/block/by-name/metadata,/metadata,ext4)=0: Success
行   2097: 03-25 01:32:46.689     0     0 I         : c7      1 init: [libfs_mgr]umount_retry(): unmount(/metadata) succeeded
行   2098: 03-25 01:32:46.689     0     0 I         : c7      1 init: [libfs_mgr]Running /system/bin/e2fsck on /dev/block/sda15
行   2110: 03-25 01:32:46.699     0     0 I         : c7      1 init: [libfs_mgr]superblock s_max_mnt_count:65535,/dev/block/by-name/metadata
行   2113: 03-25 01:32:46.700     0     0 I         : c7      1 init: [libfs_mgr]__mount(source=/dev/block/by-name/metadata,target=/metadata,type=ext4)=0: Success
行   2114: 03-25 01:32:46.702     0     0 I         : c7      1 init: [libfs_mgr]Created logical partition system_a on device /dev/block/dm-0
行   2115: 03-25 01:32:46.703     0     0 I         : c7      1 init: [libfs_mgr]Created logical partition system_b on device /dev/block/dm-1
行   2116: 03-25 01:32:46.703     0     0 I         : c7      1 init: [libfs_mgr]Created logical partition product_a on device /dev/block/dm-2
行   2117: 03-25 01:32:46.703     0     0 I         : c7      1 init: [libfs_mgr]Created logical partition product_b on device /dev/block/dm-3
行   2118: 03-25 01:32:46.703     0     0 I         : c7      1 init: [libfs_mgr]Created logical partition system_ext_a on device /dev/block/dm-4
行   2119: 03-25 01:32:46.704     0     0 I         : c7      1 init: [libfs_mgr]Created logical partition system_ext_b on device /dev/block/dm-5
行   2120: 03-25 01:32:46.704     0     0 I         : c7      1 init: [libfs_mgr]Created logical partition vendor_a on device /dev/block/dm-6
行   2121: 03-25 01:32:46.704     0     0 I         : c7      1 init: [libfs_mgr]Created logical partition vendor_b on device /dev/block/dm-7
行   2122: 03-25 01:32:46.704     0     0 I         : c7      1 init: DSU not detected, proceeding with normal boot
行   2123: 03-25 01:32:46.705     0     0 E         : c7      1 init: [libfs_avb]Invalid hash size:
行   2124: 03-25 01:32:46.705     0     0 E         : c7      1 init: [libfs_avb]Failed to verify vbmeta digest
行   2125: 03-25 01:32:46.705     0     0 I         : c7      1 init: [libfs_avb]Returning avb_handle with status: VerificationDisabled
行   2126: 03-25 01:32:46.705     0     0 I         : c7      1 init: [libfs_avb]AVB HASHTREE disabled on: /system
行   2127: 03-25 01:32:46.706     0     0 I         : c7      1 init: [libfs_mgr]superblock s_max_mnt_count:65535,/dev/block/dm-0
行   2129: 03-25 01:32:46.707     0     0 I         : c7      1 init: [libfs_mgr]__mount(source=/dev/block/dm-0,target=/system,type=ext4)=0: Success
行   2130: 03-25 01:32:46.707     0     0 I         : c7      1 init: Switching root to '/system'
行   2131: 03-25 01:32:46.708     0     0 I         : c7      1 init: [libfs_avb]AVB HASHTREE disabled on: /system_ext
行   2132: 03-25 01:32:46.708     0     0 I         : c7      1 init: [libfs_mgr]superblock s_max_mnt_count:65535,/dev/block/dm-4
行   2134: 03-25 01:32:46.709     0     0 I         : c7      1 init: [libfs_mgr]__mount(source=/dev/block/dm-4,target=/system_ext,type=ext4)=0: Success
行   2135: 03-25 01:32:46.709     0     0 I         : c7      1 init: [libfs_avb]AVB HASHTREE disabled on: /vendor
行   2136: 03-25 01:32:46.709     0     0 I         : c7      1 init: [libfs_mgr]superblock s_max_mnt_count:65535,/dev/block/dm-6
行   2138: 03-25 01:32:46.711     0     0 I         : c7      1 init: [libfs_mgr]__mount(source=/dev/block/dm-6,target=/vendor,type=ext4)=0: Success
行   2139: 03-25 01:32:46.711     0     0 I         : c7      1 init: [libfs_avb]AVB HASHTREE disabled on: /product
行   2140: 03-25 01:32:46.711     0     0 I         : c7      1 init: [libfs_mgr]superblock s_max_mnt_count:65535,/dev/block/dm-2
行   2142: 03-25 01:32:46.712     0     0 I         : c7      1 init: [libfs_mgr]__mount(source=/dev/block/dm-2,target=/product,type=ext4)=0: Success
行   2143: 03-25 01:32:46.725     0     0 I         : c7      1 init: [libfs_mgr]Created logical partition scratch on device /dev/block/dm-8
行   2163: 03-25 01:32:46.766     0     0 I         : c7      1 init: [libfs_mgr]__mount(source=/dev/block/dm-8,target=/mnt/scratch,type=f2fs)=0: Success
行   2164: 03-25 01:32:46.767     0     0 I         : c7      1 init: [libfs_mgr]umount(/mnt/scratch)
行   2182: 03-25 01:32:46.773     0     0 I         : c7      1 init: [libfs_mgr]__mount(source=/dev/block/dm-8,target=/mnt/scratch,type=f2fs)=0: Success
行   2208: 03-25 01:32:46.779     0     0 I         : c7      1 init: [libfs_mgr]__mount(source=overlay,target=/system,type=overlay,upperdir=/mnt/scratch/overlay/system/upper)=0
行   2237: 03-25 01:32:46.784     0     0 I         : c6     81 [drm:sde_dbg_init:4755] evtlog_status: enable:11, panic:1, dump:2
行   2243: 03-25 01:32:46.784     0     0 I         : c6     81 dsi_panel_switch_init: Panel switch is not supported
行   2249: 03-25 01:32:46.785     0     0 I         : c7      1 init: [libfs_mgr]__mount(source=overlay,target=/system_ext,type=overlay,upperdir=/mnt/scratch/overlay/system_ext/upper)=0
行   2273: 03-25 01:32:46.791     0     0 I         : c7      1 init: [libfs_mgr]__mount(source=overlay,target=/vendor,type=overlay,upperdir=/mnt/scratch/overlay/vendor/upper)=0
行   2286: 03-25 01:32:46.797     0     0 I         : c7      1 init: [libfs_mgr]__mount(source=overlay,target=/product,type=overlay,upperdir=/mnt/scratch/overlay/product/upper)=0
调用第二阶段的 init 进程

挂载实际文件系统后,在FirstStageMain尾部调用第二阶段的 init 进程(/system/bin/init)。

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(args));

1.2.2 SetupSelinux

SetupSelinux 函数在 Android 引导过程中扮演了设置和加载 SELinux(Security-Enhanced Linux)策略的关键角色。SELinux 是一种强制访问控制(MAC)机制,用于增强系统安全性。在 Android 系统启动过程中,SetupSelinux 函数确保系统正确加载和应用 SELinux 策略,从而保护系统免受潜在的安全威胁。

int SetupSelinux(char** argv) {
    SetStdioToDevNull(argv);
    InitKernelLogging(argv);

    if (REBOOT_BOOTLOADER_ON_PANIC) {
        InstallRebootSignalHandlers();
    }

    boot_clock::time_point start_time = boot_clock::now();

    MountMissingSystemPartitions();

    SelinuxSetupKernelLogging();

    LOG(INFO) << "Opening SELinux policy";

    PrepareApexSepolicy();

    // Read the policy before potentially killing snapuserd.
    std::string policy;
    ReadPolicy(&policy);
    CleanupApexSepolicy();

    auto snapuserd_helper = SnapuserdSelinuxHelper::CreateIfNeeded();
    if (snapuserd_helper) {
        // Kill the old snapused to avoid audit messages. After this we cannot
        // read from /system (or other dynamic partitions) until we call
        // FinishTransition().
        snapuserd_helper->StartTransition();
    }

    LoadSelinuxPolicy(policy);

    if (snapuserd_helper) {
        // Before enforcing, finish the pending snapuserd transition.
        snapuserd_helper->FinishTransition();
        snapuserd_helper = nullptr;
    }

    // This restorecon is intentionally done before SelinuxSetEnforcement because the permissions
    // needed to transition files from tmpfs to *_contexts_file context should not be granted to
    // any process after selinux is set into enforcing mode.
    if (selinux_android_restorecon("/dev/selinux/", SELINUX_ANDROID_RESTORECON_RECURSE) == -1) {
        PLOG(FATAL) << "restorecon failed of /dev/selinux failed";
    }

    SelinuxSetEnforcement();

    // We're in the kernel domain and want to transition to the init domain.  File systems that
    // store SELabels in their xattrs, such as ext4 do not need an explicit restorecon here,
    // but other file systems do.  In particular, this is needed for ramdisks such as the
    // recovery image for A/B devices.
    if (selinux_android_restorecon("/system/bin/init", 0) == -1) {
        PLOG(FATAL) << "restorecon failed of /system/bin/init failed";
    }

    setenv(kEnvSelinuxStartedAt, std::to_string(start_time.time_since_epoch().count()).c_str(), 1);

    const char* path = "/system/bin/init";
    const char* args[] = {path, "second_stage", nullptr};
    execv(path, const_cast(args));

    // execv() only returns if an error happened, in which case we
    // panic and never return from this function.
    PLOG(FATAL) << "execv(\"" << path << "\") failed";

    return 1;
}

最后是再调用进入 /system/bin/init,进入 second_stage

const char* path = "/system/bin/init";
    const char* args[] = {path, "second_stage", nullptr};
    execv(path, const_cast(args));

对应的log

行   2330: 03-25 01:32:46.870     0     0 I         : c2      1 init: Opening SELinux policy
行   2331: 03-25 01:32:46.873     0     0 I         : c2      1 init: Falling back to standard signature check. TODO implementent support for fsverity SEPolicy.
行   2332: 03-25 01:32:46.873     0     0 I         : c2      1 init: Error: Apex SEPolicy failed signature check
行   2333: 03-25 01:32:46.873     0     0 I         : c2      1 init: Loading APEX Sepolicy from /system/etc/selinux/apex/SEPolicy.zip
行   2334: 03-25 01:32:46.873     0     0 E         : c2      1 init: Failed to open package /system/etc/selinux/apex/SEPolicy.zip: No such file or directory
行   2335: 03-25 01:32:46.879     0     0 I         : c2      1 init: Loading SELinux policy

1.2.3 SecondStageMain

业务概述

SecondStageMain 函数在 Android 启动过程中负责初始化系统的第二阶段。它执行一系列关键操作来配置系统环境、加载必要的服务和文件系统,并设置 SELinux 安全策略。详细解释如下

int SecondStageMain(int argc, char** argv) {
    "安装重启信号处理程序"
    if (REBOOT_BOOTLOADER_ON_PANIC) {
        InstallRebootSignalHandlers();
    }

    // No threads should be spin up until signalfd
    // is registered. If the threads are indeed required,
    // each of these threads _should_ make sure SIGCHLD signal
    // is blocked. See b/223076262
    boot_clock::time_point start_time = boot_clock::now();

    trigger_shutdown = [](const std::string& command) { shutdown_state.TriggerShutdown(command); };

    "重定向标准输入输出和初始化内核日志"
    SetStdioToDevNull(argv);
    InitKernelLogging(argv);
    LOG(INFO) << "debugboot-1---init second stage started!";
    LOG(INFO) << "init second stage started!";

    // Update $PATH in the case the second stage init is newer than first stage init, where it is
    // first set.
    if (setenv("PATH", _PATH_DEFPATH, 1) != 0) {
        PLOG(FATAL) << "Could not set $PATH to '" << _PATH_DEFPATH << "' in second stage";
    }

    // Init should not crash because of a dependence on any other process, therefore we ignore
    // SIGPIPE and handle EPIPE at the call site directly.  Note that setting a signal to SIG_IGN
    // is inherited across exec, but custom signal handlers are not.  Since we do not want to
    // ignore SIGPIPE for child processes, we set a no-op function for the signal handler instead.
    {
        struct sigaction action = {.sa_flags = SA_RESTART};
        action.sa_handler = [](int) {};
        sigaction(SIGPIPE, &action, nullptr);
    }

    "设置 init 及其子进程的 OOM 分数调整"
    // Set init and its forked children's oom_adj.
    if (auto result =
                WriteFile("/proc/1/oom_score_adj", StringPrintf("%d", DEFAULT_OOM_SCORE_ADJUST));
        !result.ok()) {
        LOG(ERROR) << "Unable to write " << DEFAULT_OOM_SCORE_ADJUST
                   << " to /proc/1/oom_score_adj: " << result.error();
    }

    // Set up a session keyring that all processes will have access to. It
    // will hold things like FBE encryption keys. No process should override
    // its session keyring.
    keyctl_get_keyring_ID(KEY_SPEC_SESSION_KEYRING, 1);

    "创建一个文件以指示引导进程正在进行。"
    // Indicate that booting is in progress to background fw loaders, etc.
    close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));

    "处理调试属性,如果设备解锁并设置了调试属性,则加载调试属性,否则卸载调试 RAM 盘。"
    // See if need to load debug props to allow adb root, when the device is unlocked.
    const char* force_debuggable_env = getenv("INIT_FORCE_DEBUGGABLE");
    bool load_debug_prop = false;
    if (force_debuggable_env && AvbHandle::IsDeviceUnlocked()) {
        load_debug_prop = "true"s == force_debuggable_env;
    }
    unsetenv("INIT_FORCE_DEBUGGABLE");

    // Umount the debug ramdisk so property service doesn't read .prop files from there, when it
    // is not meant to.
    if (!load_debug_prop) {
        UmountDebugRamdisk();
    }

    "初始化属性服务。"
    PropertyInit();

    "卸载第二阶段资源和调试 RAM 盘"
    // Umount second stage resources after property service has read the .prop files.
    UmountSecondStageRes();

    // Umount the debug ramdisk after property service has read the .prop files when it means to.
    if (load_debug_prop) {
        UmountDebugRamdisk();
    }

    "挂载第二阶段初始化所需的额外文件系统。"
    // Mount extra filesystems required during second stage init
    MountExtraFilesystems();

    "设置 SELinux 内核日志记录,初始化 SELinux 标签并恢复 SELinux 上下文。"
    // Now set up SELinux for second stage.
    SelinuxSetupKernelLogging();
    SelabelInitialize();
    SelinuxRestoreContext();


    "初始化 Epoll 和信号处理"
    Epoll epoll;
    if (auto result = epoll.Open(); !result.ok()) {
        PLOG(FATAL) << result.error();
    }

    InstallSignalFdHandler(&epoll);
    InstallInitNotifier(&epoll);
    StartPropertyService(&property_fd);

    "记录启动阶段的时间。"
    // Make the time that init stages started available for bootstat to log.
    RecordStageBoottimes(start_time);

    "设置 AVB 版本属性并清除环境变量。"
    // Set libavb version for Framework-only OTA match in Treble build.
    if (const char* avb_version = getenv("INIT_AVB_VERSION"); avb_version != nullptr) {
        SetProperty("ro.boot.avb_version", avb_version);
    }
    unsetenv("INIT_AVB_VERSION");

    "挂载 vendor 覆盖文件系统,导出 OEM 锁定状态,初始化挂载处理程序,设置 USB 控制器和内核版本。"
    fs_mgr_vendor_overlay_mount_all();
    export_oem_lock_status();
    MountHandler mount_handler(&epoll);
    SetUsbController();
    SetKernelVersion();

    "加载内置函数映射并设置挂载命名空间,初始化子上下文。"
    const BuiltinFunctionMap& function_map = GetBuiltinFunctionMap();
    Action::set_function_map(&function_map);

    if (!SetupMountNamespaces()) {
        PLOG(FATAL) << "SetupMountNamespaces failed";
    }

    InitializeSubcontext();

    "加载启动脚本"
    ActionManager& am = ActionManager::GetInstance();
    ServiceList& sm = ServiceList::GetInstance();
    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();

    "设置 GSI(通用系统镜像)状态属性"
    // Make the GSI status available before scripts start running.
    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);

    "通过队列一些内置动作和事件触发来完成 Android 系统启动过程中的一系列关键配置和初始化任务"
    am.QueueBuiltinAction(SetupCgroupsAction, "SetupCgroups");
    am.QueueBuiltinAction(SetKptrRestrictAction, "SetKptrRestrict");
    am.QueueBuiltinAction(TestPerfEventSelinuxAction, "TestPerfEventSelinux");
    am.QueueBuiltinAction(ConnectEarlyStageSnapuserdAction, "ConnectEarlyStageSnapuserd");
    LOG(DEBUG) << "debugboot----trigger early-init";
    am.QueueEventTrigger("early-init");

    // Queue an action that waits for coldboot done so we know ueventd has set up all of /dev...
    am.QueueBuiltinAction(wait_for_coldboot_done_action, "wait_for_coldboot_done");
    // ... so that we can start queuing up actions that require stuff from /dev.
    am.QueueBuiltinAction(SetMmapRndBitsAction, "SetMmapRndBits");
    Keychords keychords;
    am.QueueBuiltinAction(
            [&epoll, &keychords](const BuiltinArguments& args) -> Result {
                for (const auto& svc : ServiceList::GetInstance()) {
                    keychords.Register(svc->keycodes());
                }
                keychords.Start(&epoll, HandleKeychord);
                return {};
            },
            "KeychordInit");

    LOG(DEBUG) << "debugboot----trigger init";
    // Trigger all the boot actions to get us started.
    am.QueueEventTrigger("init");

    // 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 {
        am.QueueEventTrigger("late-init");
    }

    // Run all property triggers based on current state of the properties.
    am.QueueBuiltinAction(queue_property_triggers_action, "queue_property_triggers");

    // Restore prio before main loop
    setpriority(PRIO_PROCESS, 0, 0);
    while (true) {
        // By default, sleep until something happens.
        auto epoll_timeout = std::optional{kDiagnosticTimeout};

        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())) {
            am.ExecuteOneCommand();
        }
        if (!IsShuttingDown()) {
            auto next_process_action_time = HandleProcessActions();

            // If there's a process that needs restarting, wake up in time for that.
            if (next_process_action_time) {
                epoll_timeout = std::chrono::ceil(
                        *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 there's more work to do, wake up again immediately.
            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()) {
            // We always reap children before responding to the other pending functions. This is to
            // prevent a race where other daemons see that a service has exited and ask init to
            // start it again via ctl.start before init has reaped it.
            ReapAnyOutstandingChildren();
            for (const auto& function : *pending_functions) {
                (*function)();
            }
        } else if (Service::is_exec_service_running()) {
            static bool dumped_diagnostics = false;
            std::chrono::duration waited =
                    std::chrono::steady_clock::now() - Service::exec_service_started();
            if (waited >= kDiagnosticTimeout) {
                LOG(ERROR) << "Exec service is hung? Waited " << waited.count()
                           << " without SIGCHLD";
                if (!dumped_diagnostics) {
                    DumpPidFds("exec service opened: ", Service::exec_service_pid());

                    std::string status_file =
                            "/proc/" + std::to_string(Service::exec_service_pid()) + "/status";
                    DumpFile("exec service: ", status_file);
                    dumped_diagnostics = true;

                    LOG(INFO) << "Attempting to handle any stuck SIGCHLDs...";
                    HandleSignalFd(true);
                }
            }
        }
        if (!IsShuttingDown()) {
            HandleControlMessages();
            SetUsbController();
        }
    }

    return 0;
}
加载启动脚本并启动相关进程

system/core/init/init.cpp

static void LoadBootScripts(ActionManager& action_manager, ServiceList& service_list) {
    Parser parser = CreateParser(action_manager, service_list);

    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("/vendor/etc/init")) {
            late_import_paths.emplace_back("/vendor/etc/init");
        }
        if (!parser.ParseConfig("/odm/etc/init")) {
            late_import_paths.emplace_back("/odm/etc/init");
        }
        if (!parser.ParseConfig("/product/etc/init")) {
            late_import_paths.emplace_back("/product/etc/init");
        }
    } else {
        parser.ParseConfig(bootscript);
    }
}

通常位于/system/etc/init/hw/init.rc,/system/etc/init, vendor/etc/init,其他如system_ext,odm,product不一定包含rc文件,比如pixel5a Android13。

设备目录下查看相关目录
Android开机流程-从Init进程启动到进入Android桌面_第3张图片
pixel5a Android13源码out编译目录下vendor/etc/init是不存在的,因为vendor部分闭源,vendor.img也是预编译的镜像。

$ grep "qcrild.rc" ./vendor -R
Binary file ./vendor/google_devices/barbet/proprietary/vendor.img matches
$ md5sum vendor/google_devices/barbet/proprietary/vendor.img
d681d7d0d9ce0cf5f64a25b6344815cf  vendor/google_devices/barbet/proprietary/vendor.img
$ md5sum out/target/product/barbet/vendor.img 
d681d7d0d9ce0cf5f64a25b6344815cf  out/target/product/barbet/vendor.img

加载完所有启动脚本后,通过QueueEventTrigger来触发进程启动,按关系链启动所有进程。

int SecondStageMain(int argc, char** argv) {
   ...
   am.QueueEventTrigger("early-init");
   ...
   am.QueueEventTrigger("init");
   ...
   am.QueueEventTrigger("late-init");
   ...
}

on nonencrypted触发流程

on nonencrypted
class_start main
class_start late_start

/system/core/init/builtins.cpp

static Result queue_fs_event(int code, bool userdata_remount) {
    if (code == FS_MGR_MNTALL_DEV_NOT_ENCRYPTABLE) {
       ...
    } else if (code == FS_MGR_MNTALL_DEV_NEEDS_METADATA_ENCRYPTION) {
        ...
        ActionManager::GetInstance().QueueEventTrigger("nonencrypted");
        ...
    }
	...
    return Error() << "Invalid code: " << code;
}

1.2.4 SubcontextMain

SubcontextMain启动过程

init.cpp: SecondStageMain()->InitializeSubcontext()

void InitializeSubcontext() {
if (IsMicrodroid()) {
LOG(INFO) << "Not using subcontext for microdroid";
return;
}


if (SelinuxGetVendorAndroidVersion() >= __ANDROID_API_P__) {
    subcontext.reset(
            new Subcontext(std::vector{"/vendor", "/odm"}, kVendorContext));
}


}

subcontext.cpp: new Subcontext()

class Subcontext {
public:
Subcontext(std::vector[std::string](std::string) path_prefixes, std::string context, bool host = false)
: path_prefixes_(std::move(path_prefixes)), context_(std::move(context)), pid_(0) {
if (!host) {
Fork();
}
}

subcontext.cpp: Subcontext()->Fork ->fork() -> execv()

void Subcontext::Fork() {
unique_fd subcontext_socket;
if (!Socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, &socket_, &subcontext_socket)) {
LOG(FATAL) << "Could not create socket pair to communicate to subcontext";
return;
}


auto result = fork();

if (result == -1) {
    LOG(FATAL) << "Could not fork subcontext";
} else if (result == 0) {
    socket_.reset();

    // We explicitly do not use O_CLOEXEC here, such that we can reference this FD by number
    // in the subcontext process after we exec.
    int child_fd = dup(subcontext_socket);  // NOLINT(android-cloexec-dup)
    if (child_fd < 0) {
        PLOG(FATAL) << "Could not dup child_fd";
    }

    // We don't switch contexts if we're running the unit tests.  We don't use std::optional,
    // since we still need a real context string to pass to the builtin functions.
    if (context_ != kTestContext) {
        if (setexeccon(context_.c_str()) < 0) {
            PLOG(FATAL) << "Could not set execcon for '" << context_ << "'";
        }
    }


#if defined(__ANDROID__)
// subcontext init runs in "default" mount namespace
// so that it can access /apex/*
if (auto result = SwitchToMountNamespaceIfNeeded(NS_DEFAULT); !result.ok()) {
LOG(FATAL) << "Could not switch to \"default\" mount namespace: " << result.error();
}
#endif
auto init_path = GetExecutablePath();
auto child_fd_string = std::to_string(child_fd);
const char* args[] = {init_path.c_str(), "subcontext", context_.c_str(),
child_fd_string.c_str(), nullptr};
execv(init_path.data(), const_cast(args));


PLOG(FATAL) << "Could not execv subcontext init";
} else {
    subcontext_socket.reset();
    pid_ = result;
    LOG(INFO) << "Forked subcontext for '" << context_ << "' with pid " << pid_;
}


}

1.2.5 ueventd_main

ueventd 是 Android 系统中负责处理内核 uevent 消息的守护进程,负责处理由内核发出的 uevent 消息,uevent 消息用于通知用户空间硬件事件。 ueventd 能够创建设备节点、设置权限、管理符号链接,并在设备状态变化时进行相应的处理,如设备插入或移除。它在系统启动时执行冷启动,确保所有设备都被正确处理。这个进程在确保 Android 设备的硬件资源可用性和权限管理方面起着关键作用。

ueventd进程启动流程

init.rc 中start ueventd

on early-init
    ...
    start ueventd
    ...
    exec_start apexd-bootstrap
	...
service ueventd /system/bin/ueventd
    class core
    critical
    onrestart restart healthd
    shutdown critical

/system/bin/ueventd软链接到system/bin/init

barbet:/ # ls -la  system/bin/ueventd                                                                                                                                                                                                                                  
lrwxr-xr-x 1 root shell 4 2024-06-19 20:05 system/bin/ueventd -> init

init.cpp中调用ueventd_main

DeviceHandler::HandleUevent 函数在接收到 uevent 事件后,首先根据事件类型修复系统权限,然后根据子系统类型设置设备路径和符号链接,并创建相应的设备节点。对于特殊设备(如 /dev/ashmem),进行额外处理。这是 ueventd 在 Android 系统中管理设备节点和处理硬件事件的核心部分。

system/core/init/ueventd.cpp

int ueventd_main(int argc, char** argv) {
    // 设置 umask 为 000 以确保创建文件时不受 umask 影响
    umask(000);

    // 初始化日志记录
    android::base::InitLogging(argv, &android::base::KernelLogger);

    // 设置 SELinux 日志记录
    SelinuxSetupKernelLogging();
    // 初始化 SELinux 标签
    SelabelInitialize();

    // 创建 uevent 处理器列表
    std::vector> uevent_handlers;

    // 获取 ueventd 配置
    auto ueventd_configuration = GetConfiguration();

    // 添加设备处理器
    uevent_handlers.emplace_back(std::make_unique(
        std::move(ueventd_configuration.dev_permissions),
        std::move(ueventd_configuration.sysfs_permissions),
        std::move(ueventd_configuration.subsystems), android::fs_mgr::GetBootDevices(), true));
    // 添加固件处理器
    uevent_handlers.emplace_back(std::make_unique(
        std::move(ueventd_configuration.firmware_directories),
        std::move(ueventd_configuration.external_firmware_handlers)));

    // 如果配置启用了 modalias 处理,则添加 modalias 处理器
    if (ueventd_configuration.enable_modalias_handling) {
        std::vector base_paths = {"/odm/lib/modules", "/vendor/lib/modules"};
        uevent_handlers.emplace_back(std::make_unique(base_paths));
    }

    // 创建 uevent 监听器
    UeventListener uevent_listener(ueventd_configuration.uevent_socket_rcvbuf_size);

    // 如果未完成冷启动,则进行冷启动
    if (!android::base::GetBoolProperty(kColdBootDoneProp, false)) {
        ColdBoot cold_boot(uevent_listener, uevent_handlers,
                           ueventd_configuration.enable_parallel_restorecon,
                           ueventd_configuration.parallel_restorecon_dirs);
        cold_boot.Run();
    }

    // 冷启动完成后的处理
    for (auto& uevent_handler : uevent_handlers) {
        uevent_handler->ColdbootDone();
    }

    // 忽略 SIGCHLD 信号并处理任何挂起的子进程
    signal(SIGCHLD, SIG_IGN);
    while (waitpid(-1, nullptr, WNOHANG) > 0) {
    }

    // 恢复进程优先级
    setpriority(PRIO_PROCESS, 0, 0);

    // 进入 uevent 监听循环,处理 uevent 消息
    uevent_listener.Poll([&uevent_handlers](const Uevent& uevent) {
        for (auto& uevent_handler : uevent_handlers) {
            uevent_handler->HandleUevent(uevent);
        }
        return ListenerAction::kContinue;
    });

    return 0;
}
static UeventdConfiguration GetConfiguration() {
    auto hardware = android::base::GetProperty("ro.hardware", "");
    std::vector legacy_paths{"/vendor/ueventd.rc", "/odm/ueventd.rc",
                                          "/ueventd." + hardware + ".rc"};

    std::vector canonical{"/system/etc/ueventd.rc"};

    if (android::base::GetIntProperty("ro.product.first_api_level", 10000) < __ANDROID_API_T__) {
        // TODO: Remove these legacy paths once Android S is no longer supported.
        canonical.insert(canonical.end(), legacy_paths.begin(), legacy_paths.end());
    } else {
        // Warn if newer device is using legacy paths.
        for (const auto& path : legacy_paths) {
            if (access(path.c_str(), F_OK) == 0) {
                LOG(FATAL_WITHOUT_ABORT)
                        << "Legacy ueventd configuration file detected and will not be parsed: "
                        << path;
            }
        }
    }

    return ParseConfig(canonical);
}

system/core/init/devices.cpp

void DeviceHandler::HandleUevent(const Uevent& uevent) {
  if (uevent.action == "add" || uevent.action == "change" ||
      uevent.action == "bind" || uevent.action == "online") {
    FixupSysPermissions(uevent.path, uevent.subsystem);
  }

    // if it's not a /dev device, nothing to do
    if (uevent.major < 0 || uevent.minor < 0) return;

    std::string devpath;
    std::vector links;
    bool block = false;

    if (uevent.subsystem == "block") {
        block = true;
        devpath = "/dev/block/" + Basename(uevent.path);

        if (StartsWith(uevent.path, "/devices")) {
            links = GetBlockDeviceSymlinks(uevent);
        }
    } else if (const auto subsystem =
                   std::find(subsystems_.cbegin(), subsystems_.cend(), uevent.subsystem);
               subsystem != subsystems_.cend()) {
        devpath = subsystem->ParseDevPath(uevent);
    } else if (uevent.subsystem == "usb") {
        if (!uevent.device_name.empty()) {
            devpath = "/dev/" + uevent.device_name;
        } else {
            // This imitates the file system that would be created
            // if we were using devfs instead.
            // Minors are broken up into groups of 128, starting at "001"
            int bus_id = uevent.minor / 128 + 1;
            int device_id = uevent.minor % 128 + 1;
            devpath = StringPrintf("/dev/bus/usb/%03d/%03d", bus_id, device_id);
        }
    } else if (StartsWith(uevent.subsystem, "usb")) {
        // ignore other USB events
        return;
    } else if (uevent.subsystem == "misc" && StartsWith(uevent.device_name, "dm-user/")) {
        devpath = "/dev/dm-user/" + uevent.device_name.substr(8);
    } else {
        devpath = "/dev/" + Basename(uevent.path);
    }

    mkdir_recursive(Dirname(devpath), 0755);

    HandleDevice(uevent.action, devpath, block, uevent.major, uevent.minor, links);

    // Duplicate /dev/ashmem device and name it /dev/ashmem.
    // TODO(b/111903542): remove once all users of /dev/ashmem are migrated to libcutils API.
    HandleAshmemUevent(uevent);
}

2. Zygote启动流程

Zygote 是 Android 系统中的关键进程,负责启动 Java 虚拟机 (JVM) 并创建系统服务进程和应用进程。Zygote 启动流程复杂且涉及多个组件,以下是详细的 Zygote 启动流程及其主要步骤。

2.1 init 进程启动 Zygote

system/etc/init/hw/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 exec_background - system system -- /system/bin/vdc volume abort_fuse
    onrestart write /sys/power/state on
    # NOTE: If the wakelock name here is changed, then also
    # update it in SystemSuspend.cpp
    onrestart write /sys/power/wake_lock zygote_kwl
    onrestart restart audioserver
    onrestart restart cameraserver
    onrestart restart media
    onrestart restart media.tuner
    onrestart restart netd
    onrestart restart wificond
    task_profiles ProcessCapacityHigh
    critical window=${zygote.critical_window.minute:-off} target=zygote-fatal

2.2 app_process 启动 ZygoteInit

  • init 进程执行 /system/bin/app_process,这个二进制文件是启动 Zygote 的入口点。
  • app_process 在启动时,解析参数并调用 com.android.internal.os.ZygoteInit 类的 main 方法。

frameworks/base/cmds/app_process/app_main.cpp call AndroidRuntime.cpp runtime.start("com.android.internal.os.ZygoteInit", args, zygote);

int main(int argc, char* const argv[])
{
    ...
    while (i < argc) {
        const char* arg = argv[i++];
        if (strcmp(arg, "--zygote") == 0) {
            zygote = true;
            niceName = ZYGOTE_NICE_NAME;
        } else if (strcmp(arg, "--start-system-server") == 0) {
            startSystemServer = true;
        }
    }
    ...

    if (zygote) {
        runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
    } else if (className) {
        runtime.start("com.android.internal.os.RuntimeInit", args, zygote);
    } 
    ...
}

frameworks/base/core/jni/AndroidRuntime.cpp中的start 方法

void AndroidRuntime::start(const char* className, const Vector& options) {
    ...
    JniInvocation jni_invocation;
    jni_invocation.Init(NULL);
    JNIEnv* env;
    if (startVm(&mJavaVM, &env, zygote) != 0) {
        return;
    }
    onVmCreated(env);
    ...
    char* slashClassName = toSlashClassName(className);
    jclass startClass = env->FindClass(slashClassName);
    if (startClass == NULL) {
        ...
    } else {
        jmethodID startMeth = env->GetStaticMethodID(startClass, "main", "([Ljava/lang/String;)V");
        if (startMeth == NULL) {
            ...
        } else {
            env->CallStaticVoidMethod(startClass, startMeth, strArray);
            if (env->ExceptionCheck()) {
                ...
            }
        }
    }
    ...
}

2.3 ZygoteInit 类初始化

  • ZygoteInit 类的 main 方法是 Zygote 进程的真正入口。
  • main 方法中,首先进行一些初始化操作,如设置性能参数、启动本地服务等。
  • 代码位置:frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
public static void main(String argv[]) {
    ...
    // Preload classes and resources
    preload();
    // Start SystemServer
    if (startSystemServer) {
        forkSystemServer(abiList, zygoteSocketName, zygoteServer);
    }
    // Accept socket connections
    caller = zygoteServer.runSelectLoop(abiList);
    ...
}

2.4 预加载类和资源

  • 在 ZygoteInit 的 main 方法中,调用 preload 方法预加载一些类和资源,以提高后续应用进程启动的效率。
  • 代码位置:frameworks/base/core/java/com/android/internal/os/ZygoteInit.java 中的 preload 方法
static void preload(TimingsTraceLog bootTimingsTraceLog) {
        ...
        beginPreload();
        preloadClasses();
        cacheNonBootClasspathClassLoaders();
        preloadResources();
        nativePreloadAppProcessHALs();
        maybePreloadGraphicsDriver();
        preloadSharedLibraries();
        preloadTextResources();
        WebViewFactory.prepareWebViewInZygote();
        endPreload();
        ...
    }

2.5 启动 SystemServer

  • Zygote 启动完成后,调用 forkSystemServer 方法启动 SystemServer,这是一个关键的系统服务进程,负责启动其他系统服务。
  • 代码位置:frameworks/base/core/java/com/android/internal/os/ZygoteInit.java 中的 forkSystemServer 方法
private static Runnable forkSystemServer(String abiList, String socketName,
		ZygoteServer zygoteServer) {
	...
	pid = Zygote.forkSystemServer(
			parsedArgs.mUid, parsedArgs.mGid,
			parsedArgs.mGids,
			parsedArgs.mRuntimeFlags,
			null,
			parsedArgs.mPermittedCapabilities,
			parsedArgs.mEffectiveCapabilities);
	...
}

2.6 进入 socket 监听循环,等待应用进程创建请求

  • Zygote 初始化和 SystemServer 启动完成后,进入 socket 监听循环,等待 fork 新的应用进程的请求。
  • 代码位置:frameworks/base/core/java/com/android/internal/os/ZygoteInit.java 中的 runSelectLoop 方法
Runnable runSelectLoop(String abiList) {
	ArrayList socketFDs = new ArrayList<>();
	ArrayList peers = new ArrayList<>();
	...
	if (pollIndex == 0) {
		// Zygote server socket
		ZygoteConnection newPeer = acceptCommandPeer(abiList);
		peers.add(newPeer);
		socketFDs.add(newPeer.getFileDescriptor());
	} else if (pollIndex < usapPoolEventFDIndex) {
		// Session socket accepted from the Zygote server socket

		try {
			ZygoteConnection connection = peers.get(pollIndex);
			boolean multipleForksOK = !isUsapPoolEnabled() && ZygoteHooks.isIndefiniteThreadSuspensionSafe();
			final Runnable command = connection.processCommand(this, multipleForksOK);
		} 
	}
	...
}

至此,zygote可以接受应用进程创建请求。

2.7 Zygote 启动应用进程

2.7.1 应用启动请求

当用户点击应用图标或系统有其他启动应用的需求时,会触发启动应用的请求。这一请求通常由 ActivityManagerService (AMS) 处理。AMS 会与 Zygote 进程通信,要求它 fork 出一个新的应用进程。

2.7.2 Zygote 进程接受请求

AMS 通过 ZygoteProcess 类与 Zygote 进程通信。ZygoteProcess 通过 socket 向 Zygote 发送启动新应用的请求。以下是相关代码片段:

framework/base/core/java/android/os/ZygoteProcess.java

private Process.ProcessStartResult startViaZygote(...) {
    // 向 argsForZygote 添加启动应用所需的参数
    ...
	synchronized(mLock) {
		// The USAP pool can not be used if the application will not use the systems graphics
		// driver.  If that driver is requested use the Zygote application start path.
        // 通过 Zygote socket 向 Zygote 进程发送启动请求
		return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi),
										  zygotePolicyFlags,
										  argsForZygote);
	...
	}
}

2.7.3 Zygote 进程处理请求

Zygote 进程的 runSelectLoop 方法在接收到新请求后,会调用 ZygoteConnection 类的 processOneCommand 方法处理请求。

framework/base/core/java/com/android/internal/os/ZygoteServer.java

Runnable runSelectLoop(String abiList) {
	ArrayList socketFDs = new ArrayList<>();
	ArrayList peers = new ArrayList<>();
	...
	if (pollIndex == 0) {
		// Zygote server socket
		ZygoteConnection newPeer = acceptCommandPeer(abiList);
		peers.add(newPeer);
		socketFDs.add(newPeer.getFileDescriptor());
	} else if (pollIndex < usapPoolEventFDIndex) {
		// Session socket accepted from the Zygote server socket

		try {
			ZygoteConnection connection = peers.get(pollIndex);
			boolean multipleForksOK = !isUsapPoolEnabled() && ZygoteHooks.isIndefiniteThreadSuspensionSafe();
			final Runnable command = connection.processCommand(this, multipleForksOK);
		} 
	}
	...
}

2.7.4 fork 新进程

ZygoteConnection 类中,processOneCommand 方法会根据 AMS 传递的参数 fork 一个新的进程。

framework/base/core/java/com/android/internal/os/ZygoteConnection.java

Runnable processCommand(ZygoteServer zygoteServer, boolean multipleOK) {
    // fork 新进程
	if (parsedArgs.mInvokeWith != null || parsedArgs.mStartChildZygote
			|| !multipleOK || peer.getUid() != Process.SYSTEM_UID) {
		// Continue using old code for now. TODO: Handle these cases in the other path.
		pid = Zygote.forkAndSpecialize(parsedArgs.mUid, parsedArgs.mGid,
				parsedArgs.mGids, parsedArgs.mRuntimeFlags, rlimits,
				parsedArgs.mMountExternal, parsedArgs.mSeInfo, parsedArgs.mNiceName,
				fdsToClose, fdsToIgnore, parsedArgs.mStartChildZygote,
				parsedArgs.mInstructionSet, parsedArgs.mAppDataDir,
				parsedArgs.mIsTopApp, parsedArgs.mPkgDataInfoList,
				parsedArgs.mAllowlistedDataInfoList, parsedArgs.mBindMountAppDataDirs,
				parsedArgs.mBindMountAppStorageDirs);

		try {
			if (pid == 0) {
				// in child
                // 子进程逻辑,启动应用 
				return handleChildProc(parsedArgs, childPipeFd,
						parsedArgs.mStartChildZygote);
			} else {
				// In the parent. A pid < 0 indicates a failure and will be handled in
				// handleParentProc.
                // 父进程逻辑,返回子进程 pid 给 AMS
				handleParentProc(pid, serverPipeFd);
				return null;
			}
		} 
	} 
}

2.7.5 子进程启动应用

在子进程中,handleChildProc 方法会启动应用的入口类 ActivityThread

private Runnable handleChildProc(ZygoteArguments parsedArgs,
            FileDescriptor pipeFd, boolean isZygote) {
        /*
         * By the time we get here, the native code has closed the two actual Zygote
         * socket connections, and substituted /dev/null in their place.  The LocalSocket
         * objects still need to be closed properly.
         */

        closeSocket();

        Zygote.setAppProcessName(parsedArgs, TAG);

        // End of the postFork event.
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        if (parsedArgs.mInvokeWith != null) {
            WrapperInit.execApplication(parsedArgs.mInvokeWith,
                    parsedArgs.mNiceName, parsedArgs.mTargetSdkVersion,
                    VMRuntime.getCurrentInstructionSet(),
                    pipeFd, parsedArgs.mRemainingArgs);

            // Should not get here.
            throw new IllegalStateException("WrapperInit.execApplication unexpectedly returned");
        } else {
            if (!isZygote) {
				// 启动应用
                return ZygoteInit.zygoteInit(parsedArgs.mTargetSdkVersion,
                        parsedArgs.mDisabledCompatChanges,
                        parsedArgs.mRemainingArgs, null /* classLoader */);
            } else {
                return ZygoteInit.childZygoteInit(
                        parsedArgs.mRemainingArgs  /* classLoader */);
            }
        }
    }

2.7.6 应用初始化

ZygoteInit.zygoteInit 方法会调用 RuntimeInit 类初始化应用的运行环境,然后调用 ActivityThreadmain 方法启动应用。

public static Runnable zygoteInit(int targetSdkVersion, long[] disabledCompatChanges,
		String[] argv, ClassLoader classLoader) {
	if (RuntimeInit.DEBUG) {
		Slog.d(RuntimeInit.TAG, "RuntimeInit: Starting application from zygote");
	}

	Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ZygoteInit");
	RuntimeInit.redirectLogStreams();

	RuntimeInit.commonInit();
	ZygoteInit.nativeZygoteInit();
	return RuntimeInit.applicationInit(targetSdkVersion, disabledCompatChanges, argv,
			classLoader);
}

/framework/base/core/java/com/android/internal/os/RuntimeInit.java

protected static Runnable applicationInit(int targetSdkVersion, long[] disabledCompatChanges,
            String[] argv, ClassLoader classLoader) {
        // If the application calls System.exit(), terminate the process
        // immediately without running any shutdown hooks.  It is not possible to
        // shutdown an Android application gracefully.  Among other things, the
        // Android runtime shutdown hooks close the Binder driver, which can cause
        // leftover running threads to crash before the process actually exits.
        nativeSetExitWithoutCleanup(true);

        VMRuntime.getRuntime().setTargetSdkVersion(targetSdkVersion);
        VMRuntime.getRuntime().setDisabledCompatChanges(disabledCompatChanges);

        final Arguments args = new Arguments(argv);

        // The end of of the RuntimeInit event (see #zygoteInit).
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);

        // Remaining arguments are passed to the start class's static main
        return findStaticMain(args.startClass, args.startArgs, classLoader);
    }
protected static Runnable findStaticMain(String className, String[] argv,
            ClassLoader classLoader) {
        ...
        try {
            m = cl.getMethod("main", new Class[] { String[].class });
        } catch (NoSuchMethodException ex) {
            throw new RuntimeException(
                    "Missing static main on " + className, ex);
        } catch (SecurityException ex) {
            throw new RuntimeException(
                    "Problem getting static main on " + className, ex);
        }
        ...
    }

2.7.7 应用主线程运行

最终,ActivityThread.main 方法被调用,应用进入正常的运行状态。

framework/base/core/java/android/app/ActivityThread.java

public static void main(String[] args) {
    // 初始化应用的主线程
    ActivityThread thread = new ActivityThread();
    thread.attach(false);
}

3 SystemServer启动流程

3.1 forkSystemServer

frameworks/base/core/java/com/android/internal/os/ZygoteInit.javaforkSystemServer

private static Runnable forkSystemServer(String abiList, String socketName,
		ZygoteServer zygoteServer) {
	String[] args = {
			"--setuid=1000",
			"--setgid=1000",
			"--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1021,1023,"
					+ "1024,1032,1065,3001,3002,3003,3005,3006,3007,3009,3010,3011,3012",
			"--capabilities=" + capabilities + "," + capabilities,
			"--nice-name=system_server",
			"--runtime-args",
			"--target-sdk-version=" + VMRuntime.SDK_VERSION_CUR_DEVELOPMENT,
			"com.android.server.SystemServer",
	};

	...
	pid = Zygote.forkSystemServer(
			parsedArgs.mUid, parsedArgs.mGid,
			parsedArgs.mGids,
			parsedArgs.mRuntimeFlags,
			null,
			parsedArgs.mPermittedCapabilities,
			parsedArgs.mEffectiveCapabilities);
	...
    /* For child process */
	if (pid == 0) {
		if (hasSecondZygote(abiList)) {
			waitForSecondaryZygote(socketName);
		}

		zygoteServer.closeServerSocket();
		return handleSystemServerProcess(parsedArgs);
	}

}

framework/base/core/java/com/android/internal/os/Zygote.java

static int forkSystemServer(int uid, int gid, int[] gids, int runtimeFlags,
            int[][] rlimits, long permittedCapabilities, long effectiveCapabilities) {
        ZygoteHooks.preFork();

        int pid = nativeForkSystemServer(
                uid, gid, gids, runtimeFlags, rlimits,
                permittedCapabilities, effectiveCapabilities);

        // Set the Java Language thread priority to the default value for new apps.
        Thread.currentThread().setPriority(Thread.NORM_PRIORITY);

        ZygoteHooks.postForkCommon();
        return pid;
    }

nativeForkSystemServer

framework/base/core/jni/com_android_internal_os_Zygote.cpp

static jint com_android_internal_os_Zygote_nativeForkSystemServer(
        JNIEnv* env, jclass, uid_t uid, gid_t gid, jintArray gids,
        jint runtime_flags, jobjectArray rlimits, jlong permitted_capabilities,
        jlong effective_capabilities) {
  ...
  pid_t pid = zygote::ForkCommon(env, true, fds_to_close, fds_to_ignore,true);
  ...
}

ForkCommon

pid_t zygote::ForkCommon(JNIEnv* env, bool is_system_server,
                         const std::vector& fds_to_close,
                         const std::vector& fds_to_ignore,
                         bool is_priority_fork,
                         bool purge) {
  ...
  pid_t pid = fork();
  ...
  return pid;
}

3.2 handleSystemServerProcess

执行forkSystemServer fork 出来的子进程中,handleSystemServerProcess 方法负责启动 SystemServer

frameworks/base/core/java/com/android/internal/os/ZygoteInit.javahandleSystemServerProcess

private static Runnable forkSystemServer(String abiList, String socketName,
		ZygoteServer zygoteServer) {
	String[] args = {
			"--setuid=1000",
			"--setgid=1000",
			"--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1021,1023,"
					+ "1024,1032,1065,3001,3002,3003,3005,3006,3007,3009,3010,3011,3012",
			"--capabilities=" + capabilities + "," + capabilities,
			"--nice-name=system_server",
			"--runtime-args",
			"--target-sdk-version=" + VMRuntime.SDK_VERSION_CUR_DEVELOPMENT,
			"com.android.server.SystemServer",
	};

	...
	pid = Zygote.forkSystemServer(
			parsedArgs.mUid, parsedArgs.mGid,
			parsedArgs.mGids,
			parsedArgs.mRuntimeFlags,
			null,
			parsedArgs.mPermittedCapabilities,
			parsedArgs.mEffectiveCapabilities);
	...
    /* For child process */
	if (pid == 0) {
		if (hasSecondZygote(abiList)) {
			waitForSecondaryZygote(socketName);
		}

		zygoteServer.closeServerSocket();
		return handleSystemServerProcess(parsedArgs);
	}

}

handleSystemServerProcess

private static Runnable handleSystemServerProcess(ZygoteArguments parsedArgs) {
        ...

            /*
             * Pass the remaining arguments to SystemServer.
             */
            return ZygoteInit.zygoteInit(parsedArgs.mTargetSdkVersion,
                    parsedArgs.mDisabledCompatChanges,
                    parsedArgs.mRemainingArgs, cl);

        /* should never reach here */
    }

zygoteInit 方法最终会调用 RuntimeInit.applicationInit 方法,该方法会根据传递的参数启动 SystemServer

protected static Runnable applicationInit(int targetSdkVersion, long[] disabledCompatChanges,
            String[] argv, ClassLoader classLoader) {
        // If the application calls System.exit(), terminate the process
        // immediately without running any shutdown hooks.  It is not possible to
        // shutdown an Android application gracefully.  Among other things, the
        // Android runtime shutdown hooks close the Binder driver, which can cause
        // leftover running threads to crash before the process actually exits.
        nativeSetExitWithoutCleanup(true);

        VMRuntime.getRuntime().setTargetSdkVersion(targetSdkVersion);
        VMRuntime.getRuntime().setDisabledCompatChanges(disabledCompatChanges);

        final Arguments args = new Arguments(argv);

        // The end of of the RuntimeInit event (see #zygoteInit).
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);

        // Remaining arguments are passed to the start class's static main
        return findStaticMain(args.startClass, args.startArgs, classLoader);
    }

findStaticMain

protected static Runnable findStaticMain(String className, String[] argv,
            ClassLoader classLoader) {
        ...
        try {
            m = cl.getMethod("main", new Class[] { String[].class });
        } catch (NoSuchMethodException ex) {
            throw new RuntimeException(
                    "Missing static main on " + className, ex);
        } catch (SecurityException ex) {
            throw new RuntimeException(
                    "Problem getting static main on " + className, ex);
        }
        ...
    }

3.3 SystemServer 运行

SystemServer 类的 main 方法是 system_server 进程的入口点:

public static void main(String[] args) {
    new SystemServer().run();
}

private void run() {
    ...
    // 初始化系统上下文
    createSystemContext();
    // 启动引导服务,包括 Installer、PowerManager 等
    startBootstrapServices();
    //启动核心服务,包括 BatteryService、UsageStatsService 等
    startCoreServices();
    //启动其他服务,包括 WindowManagerService、ActivityManagerService 等
    startOtherServices();
    ...
}

你可能感兴趣的:(Android基础架构系列,android,java,机器人)