Android 12 S 系统开机流程分析 - SecondStageMain(三)

Android 12 S 系统开机流程分析-FirstStageMain(一)

Android 12 S 系统开机流程分析 - SetupSelinux(二)

本文接着上文开始往下讲 

1. SecondStageMain

此时就到了init的第二阶段。

int main(int argc, char** argv) {
...
//执行此处
        if (!strcmp(argv[1], "second_stage")) {
            return SecondStageMain(argc, argv);
        }
    }

    return FirstStageMain(argc, argv);
}
3.3.1  初始化log系统并设置环境变量

1)InstallRebootSignalHandlers中主要做了以下几件事:

       1. 初始化了一个自定义信号集,将其所有信号都填充满,即将信号集中的所有的标志位都置为1,使得这个集合包含所有可接受的信号,也就是阻塞所有信号。这个函数可以用于快速创建一个包含所有信号的信号集,然后可以根据需要删除其中的某些信号。

       2. init创建出来的子进程不做处理,直接exit;如果不是子进程,则代表是init进程,则执行InitFatalReboot

       3. 通过syscall向内核发送重启命令

       4. 捕获一些信号

2)初始化kernel log

int SecondStageMain(int argc, char** argv) {
    if (REBOOT_BOOTLOADER_ON_PANIC) {
        InstallRebootSignalHandlers();
    }

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

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

    SetStdioToDevNull(argv);
//初始化kernel log
    InitKernelLogging(argv);
    LOG(INFO) << "init second stage started!";

//设置环境变量
    if (setenv("PATH", _PATH_DEFPATH, 1) != 0) {
        PLOG(FATAL) << "Could not set $PATH to '" << _PATH_DEFPATH << "' in second stage";
    }
3.3.2 设置init和它的子进程的oom_adj
//设置init和它的子进程的 oom_adj. 防止被杀,adj越小越不容易被杀
//static const int MIN_OOM_SCORE_ADJUST = -1000;
//static const int MAX_OOM_SCORE_ADJUST = 1000;
//static const int DEFAULT_OOM_SCORE_ADJUST = MIN_OOM_SCORE_ADJUST;
    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();
    }
3.3.3 
    // 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));

    // 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;
//是debug版本+设备unlock
    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();
    }
3.3.4 初始化property
    PropertyInit();

该函数主要是加载以下prop文件:

/system/build.prop, /system_ext/etc/build.prop, /system_ext/etc/default.prop, /vendor/default.prop, /vendor/build.prop, /vendor_dlkm/etc/build.prop, /odm_dlkm/etc/build.prop
/odm/etc/build.prop, /odm/etc/default.prop, /product/etc/build.prop /product/etc/default.prop,/debug_ramdisk/adb_debug.prop

void PropertyInit() {
    selinux_callback cb;
    cb.func_audit = PropertyAuditCallback;
    selinux_set_callback(SELINUX_CB_AUDIT, cb);

    mkdir("/dev/__properties__", S_IRWXU | S_IXGRP | S_IXOTH);
    CreateSerializedPropertyInfo();
    if (__system_property_area_init()) {
        LOG(FATAL) << "Failed to initialize property area";
    }
    if (!property_info_area.LoadDefaultPath()) {
        LOG(FATAL) << "Failed to load serialized property info file";
    }

    // If arguments are passed both on the command line and in DT,
    // properties set in DT always have priority over the command-line ones.
    ProcessKernelDt();
    ProcessKernelCmdline();
    ProcessBootconfig();

    // Propagate the kernel variables to internal variables
    // used by init as well as the current required properties.
    ExportKernelBootProps();

    PropertyLoadBootDefaults();
}

 加载各个目录下的prop文件

void PropertyLoadBootDefaults() {
    // We read the properties and their values into a map, in order to always allow properties
    // loaded in the later property files to override the properties in loaded in the earlier
    // property files, regardless of if they are "ro." properties or not.
    std::map properties;

    if (IsRecoveryMode()) {
        load_properties_from_file("/prop.default", nullptr, &properties);
    }
----------------------------------------------------------------------
//定义load_properties_from_partition 函数
    const auto load_properties_from_partition = [&properties](const std::string& partition,
                                                              int support_legacy_path_until) {
        auto path = "/" + partition + "/etc/build.prop";
        if (load_properties_from_file(path.c_str(), nullptr, &properties)) {
            return;
        }
        // To read ro..build.version.sdk, temporarily load the legacy paths into a
        // separate map. Then by comparing its value with legacy_version, we know that if the
        // partition is old enough so that we need to respect the legacy paths.
        std::map temp;
        auto legacy_path1 = "/" + partition + "/default.prop";
        auto legacy_path2 = "/" + partition + "/build.prop";
        load_properties_from_file(legacy_path1.c_str(), nullptr, &temp);
        load_properties_from_file(legacy_path2.c_str(), nullptr, &temp);
        bool support_legacy_path = false;
        auto version_prop_name = "ro." + partition + ".build.version.sdk";
        auto it = temp.find(version_prop_name);
        if (it == temp.end()) {
            // This is embarassing. Without the prop, we can't determine how old the partition is.
            // Let's be conservative by assuming it is very very old.
            support_legacy_path = true;
        } else if (int value;
                   ParseInt(it->second.c_str(), &value) && value <= support_legacy_path_until) {
            support_legacy_path = true;
        }
        if (support_legacy_path) {
            // We don't update temp into properties directly as it might skip any (future) logic
            // for resolving duplicates implemented in load_properties_from_file.  Instead, read
            // the files again into the properties map.
            load_properties_from_file(legacy_path1.c_str(), nullptr, &properties);
            load_properties_from_file(legacy_path2.c_str(), nullptr, &properties);
        } else {
            LOG(FATAL) << legacy_path1 << " and " << legacy_path2 << " were not loaded "
                       << "because " << version_prop_name << "(" << it->second << ") is newer "
                       << "than " << support_legacy_path_until;
        }
    };
--------------------------------------------------------------------------
    // Order matters here. The more the partition is specific to a product, the higher its
    // precedence is.
    LoadPropertiesFromSecondStageRes(&properties);
    load_properties_from_file("/system/build.prop", nullptr, &properties);
    load_properties_from_partition("system_ext", /* support_legacy_path_until */ 30);
    // TODO(b/117892318): uncomment the following condition when vendor.imgs for aosp_* targets are
    // all updated.
    // if (SelinuxGetVendorAndroidVersion() <= __ANDROID_API_R__) {
    load_properties_from_file("/vendor/default.prop", nullptr, &properties);
    // }
    load_properties_from_file("/vendor/build.prop", nullptr, &properties);
    load_properties_from_file("/vendor_dlkm/etc/build.prop", nullptr, &properties);
    load_properties_from_file("/odm_dlkm/etc/build.prop", nullptr, &properties);
//load /odm/etc/build.prop
    load_properties_from_partition("odm", /* support_legacy_path_until */ 28);
//load /product/etc/build.prop
    load_properties_from_partition("product", /* support_legacy_path_until */ 30);

    if (access(kDebugRamdiskProp, R_OK) == 0) {
        LOG(INFO) << "Loading " << kDebugRamdiskProp;
        load_properties_from_file(kDebugRamdiskProp, nullptr, &properties);
    }

    for (const auto& [name, value] : properties) {
        std::string error;
        if (PropertySet(name, value, &error) != PROP_SUCCESS) {
            LOG(ERROR) << "Could not set '" << name << "' to '" << value
                       << "' while loading .prop files" << error;
        }
    }

    property_initialize_ro_product_props();
    property_initialize_build_id();
    property_derive_build_fingerprint();
    property_derive_legacy_build_fingerprint();
    property_initialize_ro_cpu_abilist();

    update_sys_usb_config();
}
3.3.5 
    // 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();
3.3.6 Selinux标签restore
    // Now set up SELinux for second stage.
    SelinuxSetupKernelLogging();
    SelabelInitialize();
    SelinuxRestoreContext();

自启动的服务都是经过init进行域转换的,在转换之前标签是携带_exec的,在转化之后则不携带此后缀,所以是需要在sepolicy策略中增加init_daemon_domain的。

例如:如果/system/bin/testsh需要自启动,则需要在testsh_exec.te(此文件新增,根据selinux要求

创建,以标签名+.te文件命名)中添加如下内容

file_contexts

+ /system/bin/testsh0    u:object_r:testsh0_exec:s0

testsh_exec.te

+typeattribute testsh0 coredomain;

+type testsh0_exec, exec_type, file_type, system_file_type,;

+init_daemon_domain(testsh0)

init_daemon_domain定义如下

define(`init_daemon_domain', `
domain_auto_trans(init, $1_exec, $1)
')
------------------------------------------------------------
define(`domain_auto_trans', `
# Allow the necessary permissions.
domain_trans($1,$2,$3)
# Make the transition occur by default.
type_transition $1 $2:process $3;
')
------------------------------------------------------------
define(`domain_trans', `
# Old domain may exec the file and transition to the new domain.
allow $1 $2:file { getattr open read execute map };
allow $1 $3:process transition;
# New domain is entered by executing the file.
allow $3 $2:file { entrypoint open read execute getattr map };
# New domain can send SIGCHLD to its caller.
ifelse($1, `init', `', `allow $3 $1:process sigchld;')
# Enable AT_SECURE, i.e. libc secure mode.
dontaudit $1 $3:process noatsecure;
# XXX dontaudit candidate but requires further study.
allow $1 $3:process { siginh rlimitinh };
')

从定义就可以清楚的知道init在这其中所起到的作用了,以及自启动的bin是怎样将自己的标签转化的。

有一个知识点,比如新增了一个Selinux标签,并将相应文件push进了手机:

比如新增如下内容

file_contexts

+ /system/bin/testsh    u:object_r:testsh_exec:s0

file.te

+ type testsh_exec, exec_type, file_type, system_file_type,;

执行make selinux_policy编译selinux模块

adb push system/etc/selinux  /system_ext/etc/

adb reboot

如果新增了文件的标签,会在手机的/system/etc/selinux目录下的system_file_contexts中可以查看新增的标签。但是去/system/bin下执行命令ls -l -Z会发现,新增的标签并没有效果,此时如果在system/bin下执行

restorecon testsh
即
restorecon + 目录名

然后再执行ls -l -Z就会发现新增的标签生效了,这个restorecon的在此时作用就是重新加载一下selinux的标签。对于节点和文件系统的新增标签都可以执行此命令,如果不执行不会重新加载,重启也不会重新加载,此命令如果遇到重启,需要再重新执行一次。当然,如果能够编译boot.img镜像或者刷机就不用执行以上步骤了,此步骤只针对push的场景。

restorecon还有很多其他的功能,如果想了解,可以去百度一下。

3.3.7 启动属性服务
    Epoll epoll;
    if (auto result = epoll.Open(); !result.ok()) {
        PLOG(FATAL) << result.error();
    }

    InstallSignalFdHandler(&epoll);
    InstallInitNotifier(&epoll);
    StartPropertyService(&property_fd);
void StartPropertyService(int* epoll_socket) {
    InitPropertySet("ro.property_service.version", "2");

    int sockets[2];
    if (socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, sockets) != 0) {
        PLOG(FATAL) << "Failed to socketpair() between property_service and init";
    }
    *epoll_socket = from_init_socket = sockets[0];
    init_socket = sockets[1];
    StartSendingMessages();
//创建property service的socket
    if (auto result = CreateSocket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
                                   false, 0666, 0, 0, {});
        result.ok()) {
        property_set_fd = *result;
    } else {
        LOG(FATAL) << "start_property_service socket creation failed: " << result.error();
    }

    listen(property_set_fd, 8);

    auto new_thread = std::thread{PropertyServiceThread};
    property_service_thread.swap(new_thread);
}
 3.3.8 
    // Make the time that init stages started available for bootstat to log.
    RecordStageBoottimes(start_time);

    // 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");

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

    InitializeSubcontext();
3.3.9 加载rc文件
    ActionManager& am = ActionManager::GetInstance();
    ServiceList& sm = ServiceList::GetInstance();
//加载init.rc文件
    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 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);

    am.QueueBuiltinAction(SetupCgroupsAction, "SetupCgroups");
    am.QueueBuiltinAction(SetKptrRestrictAction, "SetKptrRestrict");
    am.QueueBuiltinAction(TestPerfEventSelinuxAction, "TestPerfEventSelinux");
    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");

    // 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");
//解析/system/etc/init/hw/init.rc
//将/system/etc/init,/system_ext/etc/init,/vendor/etc/init,/odm/etc/init
//和/product/etc/init存入late_import_paths
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);
    }
}

你可能感兴趣的:(Android,Android系统,android)