Android Framework P1 - 低配学习 Framework 方案、开机启动 Init 进程

低配学习 Framework 方案

  1. 在线阅读源码:http://xrefandroid.com/,这是一个国内访问速度不错的第三方镜像站

  2. 打开网址,Android Source 下选择 Android - 15.0.0_r1,目录结构如下

    art
    bionic
    bootable
    build
    cts
    dalvik
    developers
    development
    device
    external
    frameworks
    hardware
    kernel
    libcore
    libnativehelper
    packages
    pdk
    platform_testing
    sdk
    system
    test
    toolchain
    tools
    trusty

  3. 其中,frameworks 是 Framework 核心,要学习的主要内容

  4. 推荐起点(Android 10+):/frameworks/base/services/core/java/com/android/server/wm/ActivityStarter.java

    https://xrefandroid.com/android-15.0.0_r1/xref/frameworks/base/services/core/java/com/android/server/wm/ActivityStarter.java

  5. Android 从 10 版本开始,Google 将 ActivityStarter.java 这个负责 Activity 启动决策的核心类,从原来的 am 目录迁移到了 wm 目录

  6. Android 9 之前是 /frameworks/base/services/core/java/com/android/server/am/ActivityStarter.java

    https://xrefandroid.com/android-9.0.0_r61/xref/frameworks/base/services/core/java/com/android/server/am/ActivityStarter.java


HAL

  • HAL(硬件抽象层) 的作用是隔离操作系统与具体硬件
  1. 没有 HAL:Android 系统要直接调用不同厂商(例如,高通、联发科、海思等)的摄像头、传感器、音频等硬件的驱动,代码会充满 if-else 判断,臃肿且无法跨设备运行

  2. 有 HAL:Android 系统只调用 HAL 定义的标准接口,厂商只需按标准实现 HAL 里的函数,调用自己底层的 Linux 驱动

  • 核心价值是解耦,上层不用关心硬件怎么实现,下层改动不影响上层

Android HAL 与 STM32 HAL 库

  • Android HAL 是系统架构的"战略"分层,STM32 HAL 库是芯片编程的"战术"工具包
  1. Android HAL 是一个位于操作系统内部的软件层,它承上启下,位于 Android Framework 和 Linux Kernel 之间

    Android Application
    Android Framework
    Android HAL
    Linux Kernel
    Hardware

  2. STM32 HAL 库是一套软件代码库,运行在裸机或 RTOS(实时操作系统)之上,直接与 STM32 硬件交互,它更像一个位于用户代码和硬件寄存器之间的辅助工具

    User Application
    HAL Library
    STM32 Hardware


刷成砖头

  1. 刷成砖头是指设备无法开机、无法充电、无法进入任何系统界面,像一块砖头

  2. 嵌入式设备刷机可能刷成砖头,一样的概念


exec 函数族

  • exec 函数族包括 execl、execlp、execle、execv、execvp 等函数。这些函数的调用成功后,将不会返回,而是直接开始执行新程序

开机启动 Init 进程

  1. Android 系统基于 Linux 系统,但是由于 Android 属于移动设备,并没有像 PC 那样的 BIOS 程序,取而代之的是【BootLoader】(系统启动加载器)

  2. 【Idle 进程】是 0 号进程(可以不用关注),【Init 进程】是 1 号进程

  3. Init 进程源码位于 /system/core/init/init.cpp,其中,main 函数中,klog_init(); 是初始化日志系统

  4. init.rc 文件】是 Android 系统中一个至关重要的启动配置文件,由 Init 进程在启动时解析并执行,init.rc 文件位于 /system/core/rootdir/init.rc

    init.rc 文件中有 3 种元素:import、on、service

    通过一个函数,将 init.rc 文件解析成装着这 3 个结构体的列表(import_list、on_list、service_list)

    比较重要的

    import /init.${ro.zygote}.rc

    service servicemanager /system/bin/servicemanager

    service surfaceflinger /system/bin/surfaceflinger

    ...

  5. Init 进程解析 init.rc 文件

cpp 复制代码
// /system/core/init/init.cpp

init_parse_config_file("/init.rc");
cpp 复制代码
// /system/core/init/init.cpp

int init_parse_config_file(const char* path) {
    INFO("Parsing %s...\n", path);
    Timer t;
    std::string data;
    if (!read_file(path, &data)) {
        return -1;
    }

    data.push_back('\n'); // TODO: fix parse_config.
    parse_config(path, data);
    dump_parser_state();

    NOTICE("(Parsing %s took %.2fs.)\n", path, t.duration());
    return 0;
}
cpp 复制代码
// /system/core/init/init_parser.cpp

static void parse_config(const char *fn, const std::string& data)
{
    struct listnode import_list;
    struct listnode *node;
    char *args[INIT_PARSER_MAXARGS];

    int nargs = 0;

    parse_state state;
    state.filename = fn;
    state.line = 0;
    state.ptr = strdup(data.c_str());  // TODO: fix this code!
    state.nexttoken = 0;
    state.parse_line = parse_line_no_op;

    list_init(&import_list);
    state.priv = &import_list;

    for (;;) {
        switch (next_token(&state)) {
        case T_EOF:
            state.parse_line(&state, 0, 0);
            goto parser_done;
        case T_NEWLINE:
            state.line++;
            if (nargs) {
                int kw = lookup_keyword(args[0]);
                if (kw_is(kw, SECTION)) {
                    state.parse_line(&state, 0, 0);
                    parse_new_section(&state, kw, nargs, args);
                } else {
                    state.parse_line(&state, nargs, args);
                }
                nargs = 0;
            }
            break;
        case T_TEXT:
            if (nargs < INIT_PARSER_MAXARGS) {
                args[nargs++] = state.text;
            }
            break;
        }
    }

parser_done:
    list_for_each(node, &import_list) {
         struct import *import = node_to_item(node, struct import, list);
         int ret;

         ret = init_parse_config_file(import->filename);
         if (ret)
             ERROR("could not import file '%s' from '%s'\n",
                   import->filename, fn);
    }
}
  1. 将命令添加到可执行队列中
cpp 复制代码
// /system/core/init/init.cpp

action_for_each_trigger("early-init", action_add_queue_tail);

// Queue an action that waits for coldboot done so we know ueventd has set up all of /dev...
queue_builtin_action(wait_for_coldboot_done_action, "wait_for_coldboot_done");
// ... so that we can start queuing up actions that require stuff from /dev.
queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
queue_builtin_action(keychord_init_action, "keychord_init");
queue_builtin_action(console_init_action, "console_init");

// Trigger all the boot actions to get us started.
action_for_each_trigger("init", action_add_queue_tail);

// Repeat mix_hwrng_into_linux_rng in case /dev/hw_random or /dev/random
// wasn't ready immediately after wait_for_coldboot_done
queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");

// Don't mount filesystems or start core system services in charger mode.
char bootmode[PROP_VALUE_MAX];
if (property_get("ro.bootmode", bootmode) > 0 && strcmp(bootmode, "charger") == 0) {
    action_for_each_trigger("charger", action_add_queue_tail);
} else {
    action_for_each_trigger("late-init", action_add_queue_tail);
}

// Run all property triggers based on current state of the properties.
queue_builtin_action(queue_property_triggers_action, "queue_property_triggers");
cpp 复制代码
// /system/core/init/init.cpp

void action_for_each_trigger(const char *trigger,
                             void (*func)(struct action *act))
{
    struct listnode *node, *node2;
    struct action *act;
    struct trigger *cur_trigger;

    list_for_each(node, &action_list) {
        act = node_to_item(node, struct action, alist);
        list_for_each(node2, &act->triggers) {
            cur_trigger = node_to_item(node2, struct trigger, nlist);
            if (!strcmp(cur_trigger->name, trigger)) {
                func(act);
            }
        }
    }
}
  1. 执行命令
cpp 复制代码
while (true) {
    if (!waiting_for_exec) {
        execute_one_command();
        restart_processes();
    }

    int timeout = -1;
    if (process_needs_restart) {
        timeout = (process_needs_restart - gettime()) * 1000;
        if (timeout < 0)
            timeout = 0;
    }

    if (!action_queue_empty() || cur_action) {
        timeout = 0;
    }

    bootchart_sample(&timeout);

    epoll_event ev;
    int nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1, timeout));
    if (nr == -1) {
        ERROR("epoll_wait failed: %s\n", strerror(errno));
    } else if (nr == 1) {
        ((void (*)()) ev.data.ptr)();
    }
}
  1. 启动解析的服务,通过【fork + exec】

    fork 会创建一个新的子进程。如果子进程成功创建,父进程会返回子进程的 PID,子进程会返回 0

    fork 后面需要判断返回值是否为 0。如果不判断,fork 后面的代码在父子进程都会执行

    exec 执行可执行文件。exec 一旦执行,就不会再往下执行(被新程序替换)

cpp 复制代码
void service_start(struct service *svc, const char *dynamic_args)
{
    // Starting a service removes it from the disabled or reset state and
    // immediately takes it out of the restarting state if it was in there.
    svc->flags &= (~(SVC_DISABLED|SVC_RESTARTING|SVC_RESET|SVC_RESTART|SVC_DISABLED_START));
    svc->time_started = 0;

    // Running processes require no additional work --- if they're in the
    // process of exiting, we've ensured that they will immediately restart
    // on exit, unless they are ONESHOT.
    if (svc->flags & SVC_RUNNING) {
        return;
    }

    bool needs_console = (svc->flags & SVC_CONSOLE);
    if (needs_console && !have_console) {
        ERROR("service '%s' requires console\n", svc->name);
        svc->flags |= SVC_DISABLED;
        return;
    }

    struct stat s;
    if (stat(svc->args[0], &s) != 0) {
        ERROR("cannot find '%s', disabling '%s'\n", svc->args[0], svc->name);
        svc->flags |= SVC_DISABLED;
        return;
    }

    if ((!(svc->flags & SVC_ONESHOT)) && dynamic_args) {
        ERROR("service '%s' must be one-shot to use dynamic args, disabling\n",
               svc->args[0]);
        svc->flags |= SVC_DISABLED;
        return;
    }

    char* scon = NULL;
    if (is_selinux_enabled() > 0) {
        if (svc->seclabel) {
            scon = strdup(svc->seclabel);
            if (!scon) {
                ERROR("Out of memory while starting '%s'\n", svc->name);
                return;
            }
        } else {
            char *mycon = NULL, *fcon = NULL;

            INFO("computing context for service '%s'\n", svc->args[0]);
            int rc = getcon(&mycon);
            if (rc < 0) {
                ERROR("could not get context while starting '%s'\n", svc->name);
                return;
            }

            rc = getfilecon(svc->args[0], &fcon);
            if (rc < 0) {
                ERROR("could not get context while starting '%s'\n", svc->name);
                freecon(mycon);
                return;
            }

            rc = security_compute_create(mycon, fcon, string_to_security_class("process"), &scon);
            if (rc == 0 && !strcmp(scon, mycon)) {
                ERROR("Warning!  Service %s needs a SELinux domain defined; please fix!\n", svc->name);
            }
            freecon(mycon);
            freecon(fcon);
            if (rc < 0) {
                ERROR("could not get context while starting '%s'\n", svc->name);
                return;
            }
        }
    }

    NOTICE("Starting service '%s'...\n", svc->name);

    pid_t pid = fork();
    if (pid == 0) {
        struct socketinfo *si;
        struct svcenvinfo *ei;
        char tmp[32];
        int fd, sz;

        umask(077);
        if (properties_initialized()) {
            get_property_workspace(&fd, &sz);
            snprintf(tmp, sizeof(tmp), "%d,%d", dup(fd), sz);
            add_environment("ANDROID_PROPERTY_WORKSPACE", tmp);
        }

        for (ei = svc->envvars; ei; ei = ei->next)
            add_environment(ei->name, ei->value);

        for (si = svc->sockets; si; si = si->next) {
            int socket_type = (
                    !strcmp(si->type, "stream") ? SOCK_STREAM :
                        (!strcmp(si->type, "dgram") ? SOCK_DGRAM : SOCK_SEQPACKET));
            int s = create_socket(si->name, socket_type,
                                  si->perm, si->uid, si->gid, si->socketcon ?: scon);
            if (s >= 0) {
                publish_socket(si->name, s);
            }
        }

        freecon(scon);
        scon = NULL;

        if (svc->writepid_files_) {
            std::string pid_str = android::base::StringPrintf("%d", pid);
            for (auto& file : *svc->writepid_files_) {
                if (!android::base::WriteStringToFile(pid_str, file)) {
                    ERROR("couldn't write %s to %s: %s\n",
                          pid_str.c_str(), file.c_str(), strerror(errno));
                }
            }
        }

        if (svc->ioprio_class != IoSchedClass_NONE) {
            if (android_set_ioprio(getpid(), svc->ioprio_class, svc->ioprio_pri)) {
                ERROR("Failed to set pid %d ioprio = %d,%d: %s\n",
                      getpid(), svc->ioprio_class, svc->ioprio_pri, strerror(errno));
            }
        }

        if (needs_console) {
            setsid();
            open_console();
        } else {
            zap_stdio();
        }

        if (false) {
            for (size_t n = 0; svc->args[n]; n++) {
                INFO("args[%zu] = '%s'\n", n, svc->args[n]);
            }
            for (size_t n = 0; ENV[n]; n++) {
                INFO("env[%zu] = '%s'\n", n, ENV[n]);
            }
        }

        setpgid(0, getpid());

        // As requested, set our gid, supplemental gids, and uid.
        if (svc->gid) {
            if (setgid(svc->gid) != 0) {
                ERROR("setgid failed: %s\n", strerror(errno));
                _exit(127);
            }
        }
        if (svc->nr_supp_gids) {
            if (setgroups(svc->nr_supp_gids, svc->supp_gids) != 0) {
                ERROR("setgroups failed: %s\n", strerror(errno));
                _exit(127);
            }
        }
        if (svc->uid) {
            if (setuid(svc->uid) != 0) {
                ERROR("setuid failed: %s\n", strerror(errno));
                _exit(127);
            }
        }
        if (svc->seclabel) {
            if (is_selinux_enabled() > 0 && setexeccon(svc->seclabel) < 0) {
                ERROR("cannot setexeccon('%s'): %s\n", svc->seclabel, strerror(errno));
                _exit(127);
            }
        }

        if (!dynamic_args) {
            if (execve(svc->args[0], (char**) svc->args, (char**) ENV) < 0) {
                ERROR("cannot execve('%s'): %s\n", svc->args[0], strerror(errno));
            }
        } else {
            char *arg_ptrs[INIT_PARSER_MAXARGS+1];
            int arg_idx = svc->nargs;
            char *tmp = strdup(dynamic_args);
            char *next = tmp;
            char *bword;

            /* Copy the static arguments */
            memcpy(arg_ptrs, svc->args, (svc->nargs * sizeof(char *)));

            while((bword = strsep(&next, " "))) {
                arg_ptrs[arg_idx++] = bword;
                if (arg_idx == INIT_PARSER_MAXARGS)
                    break;
            }
            arg_ptrs[arg_idx] = NULL;
            execve(svc->args[0], (char**) arg_ptrs, (char**) ENV);
        }
        _exit(127);
    }

    freecon(scon);

    if (pid < 0) {
        ERROR("failed to start '%s'\n", svc->name);
        svc->pid = 0;
        return;
    }

    svc->time_started = gettime();
    svc->pid = pid;
    svc->flags |= SVC_RUNNING;

    if ((svc->flags & SVC_EXEC) != 0) {
        INFO("SVC_EXEC pid %d (uid %d gid %d+%zu context %s) started; waiting...\n",
             svc->pid, svc->uid, svc->gid, svc->nr_supp_gids,
             svc->seclabel ? : "default");
        waiting_for_exec = true;
    }

    svc->NotifyStateChange("running");
}
  1. 守护解析的服务
相关推荐
为何创造硅基生物1 小时前
C语言 char
c语言
许长安1 小时前
互斥锁、自旋锁、读写锁使用场景以及底层实现
c++·经验分享·笔记
aqi001 小时前
FFmpeg开发笔记(一百零二)国产的音视频移动开源工具FFmpegAndroid
android·ffmpeg·kotlin·音视频·直播·流媒体
Season4501 小时前
C++11并发支持库(condition_variable | future全家桶)
java·jvm·c++
星间都市山脉1 小时前
Android 谷歌 CTS 完整测试
android
落羽的落羽1 小时前
【项目】C++从零实现JsonRpc框架——项目引入
linux·服务器·开发语言·c++·人工智能·算法·机器学习
nianniannnn1 小时前
快应用day2项目架构
android·快应用
Andy1 小时前
C++ 容器适配器_栈_队列_双端队列
开发语言·网络·c++
思麟呀2 小时前
在C++基础上理解Csharp-2
开发语言·jvm·c++·c#