installd 进程具有 CAP_DAC_OVERRIDE 特权(Privilege)。对于具有 CAP_DAC_OVERRIDE 特权的进程,Linux 会跳过文件的读、写和执行权限的检查。所以,installd 可以访问任何目录下的文件。system 用户则会受限。
而 PackageManagerService 属于系统进程,system 用户并没有访问应用程序目录的权限,这就需要委托给 installd 处理。
SystemServer 类 startBootstrapServices() 方法中我们在等待 installd 完成启动。首先调用 SystemServiceManager 类 startService() 方法来启动 Installer 服务。
frameworks/base/services/java/com/android/server/SystemServer.java
public final class SystemServer {
......
private PackageManagerService mPackageManagerService;
......
/**
* The main entry point from zygote.
*/
public static void main(String[] args) {
new SystemServer().run();
}
public SystemServer() {
// Check for factory test mode.
mFactoryTestMode = FactoryTest.getMode();
}
private void run() {
......
// Start services.
try {
startBootstrapServices();
......
} catch (Throwable ex) {
Slog.e("System", "******************************************");
Slog.e("System", "************ Failure starting system services", ex);
throw ex;
}
......
}
......
private void startBootstrapServices() {
......
// 等待 installd 完成启动,以便它有机会创建具有适当权限的关键目录,如/data/user。
// 我们需要在初始化其他服务之前完成此操作。
Installer installer = mSystemServiceManager.startService(Installer.class);
......
// Activity manager runs the show.
mActivityManagerService = mSystemServiceManager.startService(
ActivityManagerService.Lifecycle.class).getService();
mActivityManagerService.setSystemServiceManager(mSystemServiceManager);
// 此处 AMS 和 Installer 有了关联
mActivityManagerService.setInstaller(installer);
......
// Start the package manager.
Slog.i(TAG, "Package Manager");
mPackageManagerService = PackageManagerService.main(mSystemContext, installer,
mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);
......
}
......
}
startService 方法的作用是创建并启动系统服务。被启动的类必须是 com.android.server.SystemService 的子类,Installer 继承自 SystemService。
通过反射的方式来调用 Installer 类带 Context 入参的构造器,创建 Installer 对象
将 Installer 服务添加到接收生命周期事件的服务列表 mServices(ArrayList<SystemService>)
调用 Installer 类 onStart 方法实际启动
frameworks/base/services/core/java/com/android/server/SystemServiceManager.java
public class SystemServiceManager {
......
// 接收生命周期事件的服务列表
private final ArrayList<SystemService> mServices = new ArrayList<SystemService>();
......
@SuppressWarnings("unchecked")
public <T extends SystemService> T startService(Class<T> serviceClass) {
final String name = serviceClass.getName();
Slog.i(TAG, "Starting " + name);
// Create the service.
if (!SystemService.class.isAssignableFrom(serviceClass)) {
throw new RuntimeException("Failed to create " + name
+ ": service must extend " + SystemService.class.getName());
}
final T service;
try {
Constructor<T> constructor = serviceClass.getConstructor(Context.class);
service = constructor.newInstance(mContext);
} catch (InstantiationException ex) {
throw new RuntimeException("Failed to create service " + name
+ ": service could not be instantiated", ex);
} catch (IllegalAccessException ex) {
throw new RuntimeException("Failed to create service " + name
+ ": service must have a public constructor with a Context argument", ex);
} catch (NoSuchMethodException ex) {
throw new RuntimeException("Failed to create service " + name
+ ": service must have a public constructor with a Context argument", ex);
} catch (InvocationTargetException ex) {
throw new RuntimeException("Failed to create service " + name
+ ": service constructor threw an exception", ex);
}
// Register it.
mServices.add(service);
// Start it.
try {
service.onStart();
} catch (RuntimeException ex) {
throw new RuntimeException("Failed to start service " + name
+ ": onStart threw an exception", ex);
}
return service;
}
......
}
先上图梳理一下流程。
Installer 类构造函数内创建了 Installer 对象,Installer 类 onStart() 方法通过调用 InstallerConnection 类 waitForConnection() 等待 installd 准备就绪。
frameworks/base/services/core/java/com/android/server/pm/Installer.java
public final class Installer extends SystemService {
......
// 表示与 installd 的连接
private final InstallerConnection mInstaller;
public Installer(Context context) {
super(context);
mInstaller = new InstallerConnection();
}
@Override
public void onStart() {
Slog.i(TAG, "Waiting for installd to be ready.");
mInstaller.waitForConnection();
}
......
}
waitForConnection() 实现非常简单,不停的去执行 ping 命令(通过调用 execute(“ping”)),直到命令执行成功直接返回,否则每轮休息 1000 ms 继续 ping。
frameworks/base/core/java/com/android/internal/os/InstallerConnection.java
public class InstallerConnection {
......
public void waitForConnection() {
for (;;) {
if (execute("ping") >= 0) {
return;
}
Slog.w(TAG, "installd not ready");
SystemClock.sleep(1000);
}
}
......
}
execute(…) —— 负责执行命令。
transact(…) —— 负责发送命令和读回 installd 处理结果,如果没有连接 Socket,则先调用 connect() 方法进行连接,否则直接调用 writeCommand() 函数写入命令到 installd,最后调用 readReply() 等待读回结果。
connect() —— 创建 UNIX(AF_UNIX)domain 套接字构建和远程 installd 通信。关于 LocalSocket 建立连接可以参考《Android AOSP 6.0.1 Process start 流程分析(一)》一节中 LocalSocket 类 connect() 方法流程分析。
disconnect() —— 关闭 Socket 连接、关闭输入和输出流。
readFully(…) —— 读取流上指定长度的字节数组,也就是说如果声明了长度为 len 的字节数组,readFully(byte[] b)方法只有读取 len 长度个字节的时候才返回,否则阻塞等待,如果超时,则会抛出异常 EOFException。
readReply() —— 先读回长度,再根据长度读回反馈的具体内容。
writeCommand(…) —— 先写入命令长度,再写入命令具体内容。
frameworks/base/core/java/com/android/internal/os/InstallerConnection.java
public class InstallerConnection {
......
private InputStream mIn;
private OutputStream mOut;
private LocalSocket mSocket;
private final byte buf[] = new byte[1024];
......
public synchronized String transact(String cmd) {
if (!connect()) {
Slog.e(TAG, "connection failed");
return "-1";
}
if (!writeCommand(cmd)) {
/*
* 如果 installd 死掉并在后台重启(不太可能,但有可能),
* 我们将在写入时失败。放弃之前,尝试重新连接并再写命令一次。
*/
Slog.e(TAG, "write command failed? reconnect!");
if (!connect() || !writeCommand(cmd)) {
return "-1";
}
}
if (LOCAL_DEBUG) {
Slog.i(TAG, "send: '" + cmd + "'");
}
final int replyLength = readReply();
if (replyLength > 0) {
String s = new String(buf, 0, replyLength);
if (LOCAL_DEBUG) {
Slog.i(TAG, "recv: '" + s + "'");
}
return s;
} else {
if (LOCAL_DEBUG) {
Slog.i(TAG, "fail");
}
return "-1";
}
}
public int execute(String cmd) {
String res = transact(cmd);
try {
return Integer.parseInt(res);
} catch (NumberFormatException ex) {
return -1;
}
}
......
private boolean connect() {
if (mSocket != null) {
return true;
}
Slog.i(TAG, "connecting...");
try {
mSocket = new LocalSocket();
LocalSocketAddress address = new LocalSocketAddress("installd",
LocalSocketAddress.Namespace.RESERVED);
mSocket.connect(address);
mIn = mSocket.getInputStream();
mOut = mSocket.getOutputStream();
} catch (IOException ex) {
disconnect();
return false;
}
return true;
}
public void disconnect() {
Slog.i(TAG, "disconnecting...");
IoUtils.closeQuietly(mSocket);
IoUtils.closeQuietly(mIn);
IoUtils.closeQuietly(mOut);
mSocket = null;
mIn = null;
mOut = null;
}
private boolean readFully(byte[] buffer, int len) {
try {
Streams.readFully(mIn, buffer, 0, len);
} catch (IOException ioe) {
Slog.e(TAG, "read exception");
disconnect();
return false;
}
if (LOCAL_DEBUG) {
Slog.i(TAG, "read " + len + " bytes");
}
return true;
}
private int readReply() {
if (!readFully(buf, 2)) {
return -1;
}
final int len = (((int) buf[0]) & 0xff) | ((((int) buf[1]) & 0xff) << 8);
if ((len < 1) || (len > buf.length)) {
Slog.e(TAG, "invalid reply length (" + len + ")");
disconnect();
return -1;
}
if (!readFully(buf, len)) {
return -1;
}
return len;
}
private boolean writeCommand(String cmdString) {
final byte[] cmd = cmdString.getBytes();
final int len = cmd.length;
if ((len < 1) || (len > buf.length)) {
return false;
}
buf[0] = (byte) (len & 0xff);
buf[1] = (byte) ((len >> 8) & 0xff);
try {
mOut.write(buf, 0, 2);
mOut.write(cmd, 0, len);
} catch (IOException ex) {
Slog.e(TAG, "write error");
disconnect();
return false;
}
return true;
}
......
}
现在回到 installd 启动过程,它是在 init.rc 中配置的。
system/core/rootdir/init.rc
service installd /system/bin/installd
class main
socket installd stream 600 system system
下面是启动 log
......
[ 3.168143] init: service_for_each_class service name=installd
[ 3.168155] init: service_start_if_not_disabled name=installd
[ 3.168400] init: Starting service 'installd'...
......
[ 3.178336] init: Command 'class_start main' action=nonencrypted (/init.rc:496) returned 0 took 0.01s
......
从 log 不难看出执行 init.rc 脚本中 496 行的命令,最终启动了 installd service。
system/core/rootdir/init.rc
......
495 on nonencrypted
496 class_start main
497 class_start late_start
......
nonencrypted Action 中会启动所有 classname 声明为 main 的 service,当然也包括 installd service,启动 installd service 的时候同时会创建 socket 这构成了服务端 socket。
installd 服务源码位于 frameworks/native/cmds/installd/ 下。接着会运行它的 main 函数。
main 入口函数中主要完成以下工作:
调用 initialize_globals() 获取各种路径
调用 initialize_directories() 初始化各种目录
获取服务端 socket 文件描述符
监听客户端套接字连接
接受客户端套接字连接
读取客户端发来的数据长度
根据上一步获取的长度,读取客户端发来的具体内容
调用 execute 执行客户端发来的命令
命令处理结束,关闭客户端套接字
frameworks/native/cmds/installd/installd.cpp
int main(const int argc __unused, char *argv[]) {
char buf[BUFFER_MAX];
struct sockaddr addr;
socklen_t alen;
int lsocket, s;
int selinux_enabled = (is_selinux_enabled() > 0);
setenv("ANDROID_LOG_TAGS", "*:v", 1);
android::base::InitLogging(argv);
ALOGI("installd firing up\n");
// SELinux 相关略过
union selinux_callback cb;
cb.func_log = log_callback;
selinux_set_callback(SELINUX_CB_LOG, cb);
// 获取各种路径
if (initialize_globals() < 0) {
ALOGE("Could not initialize globals; exiting.\n");
exit(1);
}
// 初始化各种目录
if (initialize_directories() < 0) {
ALOGE("Could not create directories; exiting.\n");
exit(1);
}
// SELinux 相关略过
if (selinux_enabled && selinux_status_open(true) < 0) {
ALOGE("Could not open selinux status; exiting.\n");
exit(1);
}
// 获取服务端 socket fd
lsocket = android_get_control_socket(SOCKET_PATH);
if (lsocket < 0) {
ALOGE("Failed to get socket from environment: %s\n", strerror(errno));
exit(1);
}
// 监听客户端套接字连接
if (listen(lsocket, 5)) {
ALOGE("Listen on socket failed: %s\n", strerror(errno));
exit(1);
}
// FD_CLOEXEC 用来设置文件的 close-on-exec 状态标准。
// 在 exec() 调用后,close-on-exec 标志为 0 的情况,此文件不被关闭。
// 非零则在 exec() 后被关闭。默认 close-on-exec 状态为 0,需要通过 FD_CLOEXEC 设置。
fcntl(lsocket, F_SETFD, FD_CLOEXEC);
for (;;) {
alen = sizeof(addr);
// 接受客户端套接字连接
s = accept(lsocket, &addr, &alen);
if (s < 0) {
ALOGE("Accept failed: %s\n", strerror(errno));
continue;
}
fcntl(s, F_SETFD, FD_CLOEXEC);
ALOGI("new connection\n");
// 新连接
for (;;) {
unsigned short count;
// 读取客户端发来的数据长度
if (readx(s, &count, sizeof(count))) {
ALOGE("failed to read size\n");
break;
}
// 客户端发来的数据长度无效
if ((count < 1) || (count >= BUFFER_MAX)) {
ALOGE("invalid size %d\n", count);
break;
}
// 读取客户端发来的具体内容
if (readx(s, buf, count)) {
ALOGE("failed to read command\n");
break;
}
buf[count] = 0;
// SELinux 相关略过
if (selinux_enabled && selinux_status_updated() > 0) {
selinux_android_seapp_context_reload();
}
// 调用 execute 执行客户端发来的命令
if (execute(s, buf)) break;
}
ALOGI("closing connection\n");
// 命令处理结束,关闭客户端套接字
close(s);
}
return 0;
}
initialize_globals() 获取各种目录包括:
获取 Android 数据目录:/data/
获取 Android 应用目录:/data/app/
获取受 Android 保护的应用目录:/data/app-private/
获取 Android 应用 native library 目录:/data/app-lib/
获取 SD 卡 ASEC 挂载点:/mnt/asec
获取 Android 媒体目录:/data/media/
获取 Android 外部应用目录:/mnt/expand/
获取系统和供应商目录包括 /system/app-private/、/system/priv-app/、/vendor/app/ 和 /oem/app/
frameworks/native/cmds/installd/installd.cpp
int initialize_globals() {
// 获取 Android 数据目录:/data/
if (get_path_from_env(&android_data_dir, "ANDROID_DATA") < 0) {
return -1;
}
// 获取 Android 应用目录:/data/app/
if (copy_and_append(&android_app_dir, &android_data_dir, APP_SUBDIR) < 0) {
return -1;
}
// 获取受 Android 保护的应用目录:/data/app-private/
if (copy_and_append(&android_app_private_dir, &android_data_dir, PRIVATE_APP_SUBDIR) < 0) {
return -1;
}
// 获取 Android 应用 native library 目录:/data/app-lib/
if (copy_and_append(&android_app_lib_dir, &android_data_dir, APP_LIB_SUBDIR) < 0) {
return -1;
}
// 获取 SD 卡 ASEC 挂载点:/mnt/asec
if (get_path_from_env(&android_asec_dir, "ASEC_MOUNTPOINT") < 0) {
return -1;
}
// 获取 Android 媒体目录:/data/media/
if (copy_and_append(&android_media_dir, &android_data_dir, MEDIA_SUBDIR) < 0) {
return -1;
}
// 获取 Android 外部应用目录:/mnt/expand/
if (get_path_from_string(&android_mnt_expand_dir, "/mnt/expand/") < 0) {
return -1;
}
// 注意系统和供应商目录。
android_system_dirs.count = 4;
android_system_dirs.dirs = (dir_rec_t*) calloc(android_system_dirs.count, sizeof(dir_rec_t));
if (android_system_dirs.dirs == NULL) {
ALOGE("Couldn't allocate array for dirs; aborting\n");
return -1;
}
dir_rec_t android_root_dir;
// 获取 Android 根目录:/system
if (get_path_from_env(&android_root_dir, "ANDROID_ROOT") < 0) {
ALOGE("Missing ANDROID_ROOT; aborting\n");
return -1;
}
// 获取目录:/system/app-private/
android_system_dirs.dirs[0].path = build_string2(android_root_dir.path, APP_SUBDIR);
android_system_dirs.dirs[0].len = strlen(android_system_dirs.dirs[0].path);
// 获取目录:/system/priv-app/
android_system_dirs.dirs[1].path = build_string2(android_root_dir.path, PRIV_APP_SUBDIR);
android_system_dirs.dirs[1].len = strlen(android_system_dirs.dirs[1].path);
// 获取目录:/vendor/app/
android_system_dirs.dirs[2].path = strdup("/vendor/app/");
android_system_dirs.dirs[2].len = strlen(android_system_dirs.dirs[2].path);
// 获取目录:/oem/app/
android_system_dirs.dirs[3].path = strdup("/oem/app/");
android_system_dirs.dirs[3].len = strlen(android_system_dirs.dirs[3].path);
return 0;
}
initialize_directories() 大致做了以下工作:
读取当前文件系统布局版本(/data/.layout_version)
需要时创建文件夹:/data/user
如有必要,将 /data/user/0 符号链接到 /data/data
文件系统布局版本为 0:引入了多用户,(1)将 /data/media 内容迁移到 /data/media/0;(2)确保任何现有用户的媒体目录创建
文件系统布局版本为 1:引入 /data/media/obb 以便在用户之间共享 OBB;从所有者迁移任何现有的 OBB 文件
文件系统布局版本为 2:升级到 /data/misc/user 目录
frameworks/native/cmds/installd/installd.cpp
int initialize_directories() {
int res = -1;
// 读取当前文件系统布局版本以处理升级路径
char version_path[PATH_MAX];
//
snprintf(version_path, PATH_MAX, "%s.layout_version", android_data_dir.path);
int oldVersion;
if (fs_read_atomic_int(version_path, &oldVersion) == -1) {
oldVersion = 0;
}
int version = oldVersion;
// 构建路径:/data/user
char *user_data_dir = build_string2(android_data_dir.path, SECONDARY_USER_PREFIX);
// 构建路径:/data/data
char *legacy_data_dir = build_string2(android_data_dir.path, PRIMARY_USER_PREFIX);
// 构建路径:/data/user/0
char *primary_data_dir = build_string3(android_data_dir.path, SECONDARY_USER_PREFIX, "0");
if (!user_data_dir || !legacy_data_dir || !primary_data_dir) {
goto fail;
}
// 需要时创建文件夹:/data/user
if (access(user_data_dir, R_OK) < 0) {
if (mkdir(user_data_dir, 0711) < 0) {
goto fail;
}
if (chown(user_data_dir, AID_SYSTEM, AID_SYSTEM) < 0) {
goto fail;
}
if (chmod(user_data_dir, 0711) < 0) {
goto fail;
}
}
// 如有必要,将 /data/user/0 符号链接到 /data/data
if (access(primary_data_dir, R_OK) < 0) {
if (symlink(legacy_data_dir, primary_data_dir)) {
goto fail;
}
}
if (version == 0) {
// 引入了多用户,因此将 /data/media 内容迁移到 /data/media/0
ALOGD("Upgrading /data/media for multi-user");
// 准备 /data/media
if (fs_prepare_dir(android_media_dir.path, 0770, AID_MEDIA_RW, AID_MEDIA_RW) == -1) {
goto fail;
}
// /data/media.tmp
char media_tmp_dir[PATH_MAX];
snprintf(media_tmp_dir, PATH_MAX, "%smedia.tmp", android_data_dir.path);
// 仅在尚未进行升级时复制
if (access(media_tmp_dir, F_OK) == -1) {
if (rename(android_media_dir.path, media_tmp_dir) == -1) {
ALOGE("Failed to move legacy media path: %s", strerror(errno));
goto fail;
}
}
// 再次创建目录 /data/media
if (fs_prepare_dir(android_media_dir.path, 0770, AID_MEDIA_RW, AID_MEDIA_RW) == -1) {
goto fail;
}
if (selinux_android_restorecon(android_media_dir.path, 0)) {
goto fail;
}
// /data/media/0
char owner_media_dir[PATH_MAX];
snprintf(owner_media_dir, PATH_MAX, "%s0", android_media_dir.path);
// 将所有所有者数据移到适当位置
if (access(media_tmp_dir, F_OK) == 0) {
if (rename(media_tmp_dir, owner_media_dir) == -1) {
ALOGE("Failed to move owner media path: %s", strerror(errno));
goto fail;
}
}
// 确保任何现有用户的媒体目录
DIR *dir;
struct dirent *dirent;
char user_media_dir[PATH_MAX];
dir = opendir(user_data_dir);
if (dir != NULL) {
while ((dirent = readdir(dir))) {
if (dirent->d_type == DT_DIR) {
const char *name = dirent->d_name;
// 跳过 "." 和 ".."
if (name[0] == '.') {
if (name[1] == 0) continue;
if ((name[1] == '.') && (name[2] == 0)) continue;
}
// /data/media/<user_id>
snprintf(user_media_dir, PATH_MAX, "%s%s", android_media_dir.path, name);
if (fs_prepare_dir(user_media_dir, 0770, AID_MEDIA_RW, AID_MEDIA_RW) == -1) {
goto fail;
}
}
}
closedir(dir);
}
version = 1;
}
// /data/media/obb
char media_obb_dir[PATH_MAX];
snprintf(media_obb_dir, PATH_MAX, "%sobb", android_media_dir.path);
if (version == 1) {
// 引入 /data/media/obb 以便在用户之间共享 OBB;从所有者迁移任何现有的 OBB 文件。
ALOGD("Upgrading to shared /data/media/obb");
// /data/media/0/Android/obb
char owner_obb_path[PATH_MAX];
snprintf(owner_obb_path, PATH_MAX, "%s0/Android/obb", android_media_dir.path);
// 仅在目标不存在时移动
if (access(media_obb_dir, F_OK) != 0 && access(owner_obb_path, F_OK) == 0) {
if (rename(owner_obb_path, media_obb_dir) == -1) {
ALOGE("Failed to move OBB from owner: %s", strerror(errno));
goto fail;
}
}
version = 2;
}
if (ensure_media_user_dirs(nullptr, 0) == -1) {
ALOGE("Failed to setup media for user 0");
goto fail;
}
if (fs_prepare_dir(media_obb_dir, 0770, AID_MEDIA_RW, AID_MEDIA_RW) == -1) {
goto fail;
}
if (ensure_config_user_dirs(0) == -1) {
ALOGE("Failed to setup misc for user 0");
goto fail;
}
if (version == 2) {
// 升级到 /data/misc/user 目录
ALOGD("Upgrading to /data/misc/user directories");
char misc_dir[PATH_MAX];
// /data/misc
snprintf(misc_dir, PATH_MAX, "%smisc", android_data_dir.path);
char keychain_added_dir[PATH_MAX];
// /data/misc/keychain/cacerts-added
snprintf(keychain_added_dir, PATH_MAX, "%s/keychain/cacerts-added", misc_dir);
char keychain_removed_dir[PATH_MAX];
// /data/misc/keychain/cacerts-removed
snprintf(keychain_removed_dir, PATH_MAX, "%s/keychain/cacerts-removed", misc_dir);
DIR *dir;
struct dirent *dirent;
dir = opendir(user_data_dir);
if (dir != NULL) {
while ((dirent = readdir(dir))) {
const char *name = dirent->d_name;
// 跳过 "." 和 ".."
if (name[0] == '.') {
if (name[1] == 0) continue;
if ((name[1] == '.') && (name[2] == 0)) continue;
}
uint32_t user_id = atoi(name);
// /data/misc/user/<user_id>
if (ensure_config_user_dirs(user_id) == -1) {
goto fail;
}
char misc_added_dir[PATH_MAX];
// /data/misc/user/<user_id>/keychain/cacerts-added
snprintf(misc_added_dir, PATH_MAX, "%s/user/%s/cacerts-added", misc_dir, name);
char misc_removed_dir[PATH_MAX];
// /data/misc/user/<user_id>/keychain/cacerts-removed
snprintf(misc_removed_dir, PATH_MAX, "%s/user/%s/cacerts-removed", misc_dir, name);
uid_t uid = multiuser_get_uid(user_id, AID_SYSTEM);
gid_t gid = uid;
if (access(keychain_added_dir, F_OK) == 0) {
if (copy_dir_files(keychain_added_dir, misc_added_dir, uid, gid) != 0) {
ALOGE("Some files failed to copy");
}
}
if (access(keychain_removed_dir, F_OK) == 0) {
if (copy_dir_files(keychain_removed_dir, misc_removed_dir, uid, gid) != 0) {
ALOGE("Some files failed to copy");
}
}
}
closedir(dir);
if (access(keychain_added_dir, F_OK) == 0) {
delete_dir_contents(keychain_added_dir, 1, 0);
}
if (access(keychain_removed_dir, F_OK) == 0) {
delete_dir_contents(keychain_removed_dir, 1, 0);
}
}
version = 3;
}
// 持久化布局版本(如果更改)
if (version != oldVersion) {
if (fs_write_atomic_int(version_path, version) == -1) {
ALOGE("Failed to save version to %s: %s", version_path, strerror(errno));
goto fail;
}
}
// Success!
res = 0;
fail:
free(user_data_dir);
free(legacy_data_dir);
free(primary_data_dir);
return res;
}
最后来看 execute 函数执行客户端发来的命令做了些什么?
execute 函数的作用是:标记命令缓冲区,找到匹配的命令,确保提供了所需数量的参数,调用 function(),返回结果。
frameworks/native/cmds/installd/installd.cpp
static int execute(int s, char cmd[BUFFER_MAX])
{
char reply[REPLY_MAX];
char *arg[TOKEN_MAX+1];
unsigned i;
unsigned n = 0;
unsigned short count;
int ret = -1;
// ALOGI("execute('%s')\n", cmd);
/* 默认答复 "" */
reply[0] = 0;
/* n 是 arg 的数目(不包括 arg[0]) */
arg[0] = cmd;
while (*cmd) {
if (isspace(*cmd)) {
*cmd++ = 0;
n++;
arg[n] = cmd;
if (n == TOKEN_MAX) {
ALOGE("too many arguments\n");
goto done;
}
}
if (*cmd) {
cmd++;
}
}
for (i = 0; i < sizeof(cmds) / sizeof(cmds[0]); i++) {
if (!strcmp(cmds[i].name,arg[0])) {
if (n != cmds[i].numargs) {
ALOGE("%s requires %d arguments (%d given)\n",
cmds[i].name, cmds[i].numargs, n);
} else {
// 调用具体命令动作
ret = cmds[i].func(arg + 1, reply);
}
goto done;
}
}
ALOGE("unsupported command '%s'\n", arg[0]);
done:
if (reply[0]) {
n = snprintf(cmd, BUFFER_MAX, "%d %s", ret, reply);
} else {
n = snprintf(cmd, BUFFER_MAX, "%d", ret);
}
if (n > BUFFER_MAX) n = BUFFER_MAX;
count = n;
// ALOGI("reply: '%s'\n", cmd);
// 写入命名执行完后的答复
if (writex(s, &count, sizeof(count))) return -1;
if (writex(s, cmd, count)) return -1;
return 0;
}
以下是 cmds 数组定义,其每一项为一个 cmdinfo 结构体。cmdinfo 结构体定义很好理解,分别是命令名称、参数个数和具体执行的函数指针 func。
frameworks/native/cmds/installd/installd.cpp
struct cmdinfo {
const char *name;
unsigned numargs;
int (*func)(char **arg, char reply[REPLY_MAX]);
};
struct cmdinfo cmds[] = {
{ "ping", 0, do_ping },
{ "install", 5, do_install },
{ "dexopt", 10, do_dexopt },
{ "markbootcomplete", 1, do_mark_boot_complete },
{ "movedex", 3, do_move_dex },
{ "rmdex", 2, do_rm_dex },
{ "remove", 3, do_remove },
{ "rename", 2, do_rename },
{ "fixuid", 4, do_fixuid },
{ "freecache", 2, do_free_cache },
{ "rmcache", 3, do_rm_cache },
{ "rmcodecache", 3, do_rm_code_cache },
{ "getsize", 8, do_get_size },
{ "rmuserdata", 3, do_rm_user_data },
{ "cpcompleteapp", 6, do_cp_complete_app },
{ "movefiles", 0, do_movefiles },
{ "linklib", 4, do_linklib },
{ "mkuserdata", 5, do_mk_user_data },
{ "mkuserconfig", 1, do_mk_user_config },
{ "rmuser", 2, do_rm_user },
{ "idmap", 3, do_idmap },
{ "restorecondata", 4, do_restorecon_data },
{ "createoatdir", 2, do_create_oat_dir },
{ "rmpackagedir", 1, do_rm_package_dir },
{ "linkfile", 3, do_link_file }
};
回到客户端发送 ping 命令。服务端对应执行 do_ping 函数。此函数只是简单的返回了 0,客户端就能退出其 for 循环了,代表 installd 服务已启动。
frameworks/native/cmds/installd/installd.cpp
static int do_ping(char **arg __unused, char reply[REPLY_MAX] __unused)
{
return 0;
}