低配学习 Framework 方案
-
在线阅读源码:
http://xrefandroid.com/,这是一个国内访问速度不错的第三方镜像站 -
打开网址,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 -
其中,frameworks 是 Framework 核心,要学习的主要内容
-
推荐起点(Android 10+):
/frameworks/base/services/core/java/com/android/server/wm/ActivityStarter.java -
Android 从 10 版本开始,Google 将
ActivityStarter.java这个负责 Activity 启动决策的核心类,从原来的 am 目录迁移到了 wm 目录 -
Android 9 之前是
/frameworks/base/services/core/java/com/android/server/am/ActivityStarter.java
HAL
- HAL(硬件抽象层) 的作用是隔离操作系统与具体硬件
-
没有 HAL:Android 系统要直接调用不同厂商(例如,高通、联发科、海思等)的摄像头、传感器、音频等硬件的驱动,代码会充满 if-else 判断,臃肿且无法跨设备运行
-
有 HAL:Android 系统只调用 HAL 定义的标准接口,厂商只需按标准实现 HAL 里的函数,调用自己底层的 Linux 驱动
- 核心价值是解耦,上层不用关心硬件怎么实现,下层改动不影响上层
Android HAL 与 STM32 HAL 库
- Android HAL 是系统架构的"战略"分层,STM32 HAL 库是芯片编程的"战术"工具包
-
Android HAL 是一个位于操作系统内部的软件层,它承上启下,位于 Android Framework 和 Linux Kernel 之间
Android Application
Android Framework
Android HAL
Linux Kernel
Hardware -
STM32 HAL 库是一套软件代码库,运行在裸机或 RTOS(实时操作系统)之上,直接与 STM32 硬件交互,它更像一个位于用户代码和硬件寄存器之间的辅助工具
User Application
HAL Library
STM32 Hardware
刷成砖头
-
刷成砖头是指设备无法开机、无法充电、无法进入任何系统界面,像一块砖头
-
嵌入式设备刷机可能刷成砖头,一样的概念
exec 函数族
- exec 函数族包括 execl、execlp、execle、execv、execvp 等函数。这些函数的调用成功后,将不会返回,而是直接开始执行新程序
开机启动 Init 进程
-
Android 系统基于 Linux 系统,但是由于 Android 属于移动设备,并没有像 PC 那样的 BIOS 程序,取而代之的是【BootLoader】(系统启动加载器)
-
【Idle 进程】是 0 号进程(可以不用关注),【Init 进程】是 1 号进程
-
Init 进程源码位于
/system/core/init/init.cpp,其中,main 函数中,klog_init();是初始化日志系统 -
【
init.rc文件】是 Android 系统中一个至关重要的启动配置文件,由 Init 进程在启动时解析并执行,init.rc文件位于/system/core/rootdir/init.rcinit.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
...
-
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);
}
}
- 将命令添加到可执行队列中
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);
}
}
}
}
- 执行命令
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)();
}
}
-
启动解析的服务,通过【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");
}
- 守护解析的服务