Android 源码 installd 启动流程分析

本文详细介绍了Android系统服务启动过程中,尤其是PackageManagerService如何通过installd进程进行交互。installd进程具有CAP_DAC_OVERRIDE特权,允许访问系统目录。PackageManagerService在启动时等待installd准备好,然后通过反射创建Installer服务,Installer服务通过LocalSocket与installd通信,确保关键目录的权限设置。installd服务在init.rc中配置,负责处理文件系统布局升级和命令执行。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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;
}
 

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值