框架层的启动¶
我们在上一章结尾介绍了 init 命令,以及如何配置和使用它。然而,在描述默认的 init.rc 时,我仅仅简要暗示了 Android 框架层是如何通过 Zygote 启动的。这个话题当然有很多可以展开讨论的内容,我们稍后就会看到。之前章节中描述的许多内容可以很容易地与嵌入式 Linux 世界中存在的组件进行类比;但接下来的内容几乎没有任何可类比的对象。的确,Android 开发人员对移动世界的贡献,正是他们在 BSD/ASL 许可的嵌入式 Linux 等价物之上所构建的那一套技术栈。
不带框架层构建 AOSP¶
尽管听起来有些奇怪,但在某些情况下,你可能确实希望构建不带任何华丽的、基于 Java 的系统服务和应用的 AOSP——而这些正是 Android 最广为人知的东西。无论是为了在"无头"(headless)系统上运行"Android",还是仅仅因为正在进行主板移植工作,希望获得一个最小的 AOSP 构建来获取原生用户空间的基本工具和环境——有一个专门为此准备的 AOSP 构建版本:Tiny Android。
要使 AOSP 生成 Tiny Android,你只需进入 AOSP 的源码目录并输入以下命令:
$ BUILD_TINY_ANDROID=true make -j16
这将生成一组包含最小 Android 组件集的输出镜像,以便与内核一起运行一个功能性的原生 Android 用户空间。主要你会得到 Toolbox、Bionic、init、adbd、logcat、sh 以及其他一些关键的二进制文件和库。这些镜像中将不包含任何 Android 框架层的组件,比如系统服务或任何应用。
这是否还算"Android"确实值得商榷,但在某些情况下,这恰恰正是你想要的。至于最终结果是否应该被称为"Android",这完全取决于你自己。话说回来,美丑自在观者心中。
核心构建块¶
框架层的运行依赖于少数几个关键的构建块:服务管理器(Service Manager)、Android 运行时(Android Runtime)、Zygote 和 Dalvik。没有这些,就没有任何我们知道是 Android 的组件能够工作。我们之前在第 2 章已经介绍过它们各自在系统启动中的作用。我鼓励你回到那一章进行深入阅读,但在我们刚刚了解了 init 及其脚本之后,这里仍然值得回顾一下要点。事实上,在你阅读下面的解释时,你可能希望手边有附录 D 中关于主要 init.rc 文件的内容。
init 启动的首批服务之一是 servicemanager。正如我之前解释的,它是所有系统服务的"黄页"或目录。显然,在它启动时还没有任何系统服务启动,但它必须非常早地可用,以便启动的系统服务能够向它注册,从而对系统的其余部分可见。
如果 servicemanager 没有运行,任何系统服务都无法宣传自己,框架层根本就无法工作。因此,servicemanager 不是可选组件,它在 init.rc 文件中的顺序也不允许定制。你必须按照默认的方式将其保留在 main init.rc 文件中的原位置,以及为它指定的默认选项。
接下来启动的核心组件是 Zygote。以下是 init.rc 中的相关行:
service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
这简单的一行包含了很多内容。首先,请注意实际运行的是 app_process 命令。以下是它的正式参数列表:
Usage: app_process [java-options] cmd-dir start-class-name [options]
app_process 是一个鲜为人知但功能强大的命令。它允许你直接从命令行启动一个新的 Dalvik 虚拟机来运行 Android 代码。这并不意味着你可以用它从命令行启动常规的 Android 应用——事实上你不能——但你很快就会了解到一个确实可以做到这一点的命令:am。然而,某些关键的系统组件和工具必须从命令行启动,且不引用任何现有的 Dalvik 虚拟机实例。Zygote 就是其中之一,因为它是第一个运行的 Dalvik 进程;am 和 pm 是另外两个,我们稍后会介绍。
为了实现其功能,app_process 依赖 Android 运行时。作为共享库 libandroid_runtime.so 打包,Android 运行时能够为运行 Android 类型代码的目的启动和管理 Dalvik 虚拟机。此外,它还会预加载大量通常被任何依赖 Android API 的代码所使用的库,包括任何 Android 框架层 Java 代码所需的原生调用。这些都被注册到虚拟机中,这样每当 Android 框架层的 Java 代码调用某个原生函数时,虚拟机就能找到它们。
运行时还包括一些为所有在 Dalvik 上运行的 Android 类型应用提供便利操作的函数。事实上,你可以将 Dalvik 看作是一个非常原始、低级的虚拟机,它并不假设你在它之上运行 Android 类型的代码。为了在 Dalvik 上运行依赖 Android Java API 的 Java 代码,运行时需要用专门为其定制的参数来启动 Dalvik——无论是公开记录在开发者文档中并通过 SDK 提供的公共 API,还是仅在作为 AOSP 内部代码构建时才可用的内部 API。
此外,运行时还依赖许多原生用户空间功能。例如,它会考虑 init 维护的一些全局属性,以控制 Dalvik 虚拟机的启动,还会使用 Android 的日志函数来记录 Dalvik 虚拟机初始化的进度。除了设置用于启动运行 Java 代码的 Dalvik 虚拟机的参数外,运行时还会在调用代码的 main() 方法之前初始化 Java 和 Android 环境的一些关键方面。最重要的是,它为在新实例化虚拟机上运行的所有线程提供了一个默认的异常处理器。
请注意,运行时不会预加载类:那是 Zygote 在为运行 Android 应用设置系统时做的事情。而且,由于每次使用 app_process 命令都会导致启动一个独立的虚拟机,所有非 Zygote 的 Dalvik 实例都会按需加载类,而不是在你的代码开始运行之前就加载。
Dalvik 的全局属性¶
除了我们在上一章讨论的由 init 维护的全局属性之外,Dalvik 还通过 java.lang.System 继续提供属性系统。因此,如果你浏览一些系统服务的源码,可能会注意到对 System.getProperty() 或 System.setProperty() 的调用。请注意,这些调用及其底层属性集与 init 的全局属性完全独立。
例如,包管理器服务(Package Manager Service)在启动时会读取 java.boot.class.path。然而,如果你在命令行使用 getprop,你不会在 init 返回的属性列表中找到这个属性。相反,这类变量是在每个 Dalvik 实例中维护的,供运行的 Java 代码检索和/或使用。例如,特定的 java.boot.class.path 是在 dalvik/vm/Properties.c 中使用 init.rc 中设置的 BOOTCLASSPATH 变量来设置的。
你可以在 Java 官方文档中找到更多关于 Java 系统属性的信息。请注意,init 全局属性使用的变量名语义与 Java 系统属性使用的非常相似。
一旦启动,使用 app_process 启动的 Java 类就可以开始使用"常规"Android API 并与现有系统服务通信。如果它是作为 AOSP 的一部分构建的,它可以使用在构建时对其可用的许多 android.* 包。例如,am 和 pm 命令正是这样做的。因此,你同样可以编写自己的完全用 Java 编写的命令行工具,使用 Android API,并让它独立于 Zygote 以及 Zygote 导致启动的所有其他内容而启动和运行。
但这仍然不能让你编写一个由 app_process 启动的常规 Android 应用。Android 应用只能由活动管理器(Activity Manager)使用 intent 来启动,而活动管理器本身是在 Zygote 启动后作为其他系统服务的一部分启动的。这又把讨论带回到 Zygote 的启动。
为了让 Zygote 正确启动并让它启动系统服务器,你必须保持 init.rc 中相应的 app_process 行完好无损,并位于默认位置。关于 Zygote 的启动,没有什么是你可以配置的。不过,你可以通过修改一些系统全局属性来影响 Android 运行时启动任何 Dalvik 虚拟机的方式。你可以查看 2.3/Gingerbread 或 4.2/Jelly Bean 中 frameworks/base/core/jni/AndroidRuntime.cpp 里的 AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv) 函数,看看 Android 运行时在准备启动新虚拟机时读取了哪些全局属性。请注意,使用这些属性来影响 Dalvik 虚拟机设置很可能与特定版本相关。
一旦 Zygote 的虚拟机启动,就会调用 com.android.internal.os.ZygoteInit 类的 main() 函数,它会预加载整套 Android 包,然后启动系统服务器,接着开始循环监听来自活动管理器的连接请求——活动管理器请求 Zygote fork 并启动新的 Android 应用。同样,这里没有什么可以定制的,除非你能在 frameworks/base/core/java/com/android/internal/os/ZygoteInit.java 中 startSystemServer() 函数使用的参数列表中找到与你相关的内容。我的建议是,除非你对 Android 的内部结构有非常扎实的理解,否则请保持原样。
禁用 Zygote¶
虽然你无法配置 Zygote 在启动时做什么,但你仍然可以通过在 init.rc 中为其添加 disabled 选项来完全禁用它的启动。以下是 2.3/Gingerbread 中的做法:
service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
socket zygote stream 666
onrestart write /sys/android_power/request_state wake
onrestart write /sys/power/state on
onrestart restart media
onrestart restart netd
disabled
这将有效地阻止 init 在启动时启动 Zygote,因此 Android 框架层的任何部分都不会启动,包括系统服务器。如果你正在调试关键的系统错误或开发某个 HAL 模块,并且必须在关键系统服务启动之前手动设置调试工具、加载文件或监控系统行为,这可能会非常有用。
之后你可以手动启动 Zygote 及系统其余部分:
# start zygote