支持守护进程¶
虽然 Android 的大部分智能是在系统服务中实现的,但有些情况下系统服务充当中间人,实际进行关键操作的原生守护进程。这样做的主要原因有两个:安全性和可靠性。
正如我在第 1 章解释的,Android 的权限模型要求需要调用特权操作的应用开发者在构建时请求特定的权限。通常,这些权限在应用的清单文件中类似于这样:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
显然,这样的权限远不止这些。请查看 app 开发者文档中可用的完整权限列表。没有这些权限,应用就无法进行一些最关键的 Android 操作。主要原因是应用作为非特权用户运行,不能——例如——调用需要 root 权限的系统调用或访问 /dev 中的大多数关键设备。相反,应用必须请求系统服务代表它们执行操作,而系统服务在跟进任何请求之前会检查应用的权限。
然而,系统服务本身并不是以 root 运行的。相反,system_server 进程以 system 身份运行;mediaserver 进程以 media 身份运行;Phone 应用以 radio 身份运行。如果你在 /dev 中检查,你会看到一些条目专门属于其中一些用户。你还会看到不少属于 root 用户的条目。因此,与应用一样,系统服务通常不能使用需要 root 权限的系统调用,也不能访问 /dev 中的关键设备。
相反,许多关键操作需要系统服务通过 /dev/socket/ 中的 Unix 域套接字与以 root 或特定用户身份运行的原生守护进程通信,以执行特权操作。其中许多守护进程是 Android 特有的,尽管有些,如 4.2/Jelly Bean 之前的 bluetoothd,我们之前在第 6 章中介绍过是遗留的 Linux 守护进程。
在某些特定情况下,例如 rild(负责与基带处理器通信),选择作为独立进程运行的原因似乎更可能与可靠性有关。确实,智能手机的电话功能是如此关键,以至于确保其操作独立于可能影响 system_server 进程中托管的系统服务的任何潜在问题是非常值得的。
让我们看看系统服务使用的主要支持守护进程,它们的配置以及相关的命令行工具。请注意,我们不会涵盖我们之前介绍过的守护进程,如 Zygote;或不与系统服务绑定的守护进程,如 ueventd 和 dumpsys;或非 Android 特有的守护进程,如 bluetoothd 或 wpa_supplicant。
installd
虽然包管理器服务的职责是处理 .apk 文件的管理,但它没有适当的权限来执行设置应用运行所需的许多操作和/或处理。相反,它依赖 installd——在 2.3/Gingerbread 中以 root 身份运行,在 4.2/Jelly Bean 中以 install 用户身份运行——来执行关键的文件系统操作和命令。例如,在安装时,生成 Dalvik 的 JIT 优化 .dex 文件就是在包管理器的请求下由 installd 完成的。
在 2.3/Gingerbread 中(4.2/Jelly Bean 做了一些相当类似的事情),installd 由 init.rc 的以下部分启动:
service installd /system/bin/installd
socket installd stream 600 system system
然后它打开 /dev/socket/installd 并监听连接,此后监听来自包管理器的命令。它没有配置文件,也不接受任何命令行参数。因此,也没有命令行工具可以独立于包管理器与之通信。因此,从命令行激活 installd 的唯一方法是使用 pm 命令,该命令将与包管理器通信,包管理器将在需要时与 installd 通信。
vold
vold 负责挂载服务所需的许多关键操作,如挂载和格式化卷。与 installd 不同,vold 在 2.3/Gingerbread 和 4.2/Jelly Bean 中都以 root 身份运行,而挂载服务是系统服务器的一部分。
与这里介绍的其他支持守护进程不同,vold 实际上有一个配置文件:/etc/vold.fstab。以下是 system/core/rootdir/etc/ 中默认 vold.fstab 的相关片段,描述了文件的语义:
#######################
## Regular device mount
##
## Format: dev_mount <label> <mount_point> <part> <sysfs_path1...>
## label - Label for the volume
## mount_point - Where the volume will be mounted
## part - Partition # (1 based), or 'auto' for first usable partition.
## <sysfs_path> - List of sysfs paths to source devices
######################
以下是描述模拟器中 SD 卡的相关部分:
dev_mount sdcard /mnt/sdcard auto /devices/platform/goldfish_mmc.0 /devices/platform/msm_sdcc.2/mmc_host/mmc1
当 vold 启动时,它解析此文件,然后打开 /dev/socket/vold 来监听连接和命令。与 installd 不同,有一个命令行工具可以直接与 vold 通信:
Usage: vdc <monitor>|<cmd> [arg1] [arg2...]
vdc 在命令行上期望的实际参数与挂载服务通过指定套接字连接时期望的相同。不幸的是,没有文档或在线帮助描述完整的命令集。相反,你必须查看 system/vold/ 中的 CommandListener.cpp 文件来了解 vold 命令集的语义。
你可以通过 vdc 请求 vold 状态信息:
# vdc dump
000 Dumping loop status
...
200 dump complete
在某些情况下,vdc 实际上提供在线帮助:
# vdc volume format
500 Usage: volume format <path>
netd¶
网络管理服务依赖 netd 进行关键的网络配置操作,如配置网络接口、设置网络共享(tethering)和运行 pppd。在这种情况下,netd 也以 root 身份运行,而网络管理服务是系统服务器的一部分。在 2.3/Gingerbread 中,netd 由 init.rc 的以下部分启动:
service netd /system/bin/netd
socket netd stream 0660 root system
然而在 4.2/Jelly Bean 中,声明已更改:
service netd /system/bin/netd
class main
socket netd stream 0660 root system
socket dnsproxyd stream 0660 root inet
socket mdns stream 0660 root system
netd 打开 /dev/socket/netd 并监听连接和命令。它不接受任何命令行参数,也不依赖任何配置文件。然而,像 vold 一样,它有一个命令行工具与之通信。以下是 2.3/Gingerbread 中该命令的在线帮助:
# ndc
Usage: ndc <monitor>|<cmd> [arg1] [arg2...]
vold 和 netd 的命令集
vold 和 netd 都使用 libsutils 提供的相同 C++ 机制构建,并依赖 CommandListener.cpp 来解析和分派发送给它们的命令。要了解每个接受的特定命令,请查看 CommandListener.cpp 中的构造函数:
CommandListener::CommandListener() :
FrameworkListener("...") {
...
每个都包含对 registerCmd() 的调用,该调用注册在文件中更下方定义的对象。
rild
托管在 Phone 应用中的 Phone 系统服务使用 rild 与基带处理器通信。rild 本身使用 dlopen() 加载特定于基带的 .so 来接口到实际的基带硬件。如我之前提到的,rild 的存在可能是为了确保电话端的系统即使在栈的其余部分出现问题时也能保持活跃。
在模拟器的情况下,在 2.3/Gingerbread 的 init.rc 文件中,rild 由以下部分启动(4.2/Jelly Bean 的版本几乎相同):
service ril-daemon /system/bin/rild
socket rild stream 660 root radio
socket rild-debug stream 660 radio system
user root
group radio cache inet misc audio sdcard_rw
rild 本身可以接受一些命令行参数:
Usage: rild -l <ril impl library> [-- <args for impl library>]
rild 使用两个 Unix 域套接字:/dev/socket/rild(由 Phone 系统服务使用)和 /dev/socket/rild-debug(可用于 radiooptions 命令进行交互)。后者是一个命令行工具,用于与 rild 通信:
Usage: radiooptions [option] [extra_socket_args]
0 - RADIO_RESET,
1 - RADIO_OFF,
2 - UNSOL_NETWORK_STATE_CHANGE,
3 - QXDM_ENABLE,
4 - QXDM_DISABLE,
5 - RADIO_ON,
6 apn- SETUP_PDP apn,
7 - DEACTIVE_PDP,
8 number - DIAL_CALL number,
9 - ANSWER_CALL,
10 - END_CALL
keystore
与迄今为止呈现的其他守护进程不同,keystore 实际上不服务任何系统服务。相反,它被系统的各个部分用于存储和检索密钥对。它维护的值主要是用于连接到网络或网络基础设施(如接入点和 VPN)的安全密钥,而保护这些值的方法是用户定义的密码。很明显,将这些信息的存储和检索分离为一个单独的守护进程是为了提高系统的整体安全性。
其他支持守护进程¶
还有一些更次要角色的额外守护进程,我们不会在这里介绍,例如 mtpd 和 racoon。前者用于 VPN,位于 external/mtpd/,后者用于 IPsec,位于 external/ipsec-tools/。
当然,可能还有其他守护进程在你的系统上运行以用于特定目的,和/或你可能想要添加你自己的自定义守护进程。回到第 4 章,获取有关将你自己的自定义二进制文件添加到 AOSP 构建系统的说明。请记住,如果你希望守护进程由 init 在启动时启动,你需要在一个设备特定的 init.<device_name>.rc 中为其添加服务声明。