Android-6.0之PMS的守护进程installd

本文转载于:http://www.iloveandroid.net/2016/06/30/Android_PackageManagerService-11/

有一个名为installd的native 守护进程一直默默的为PMS服务。现在就来看看它究竟在做什么。


installd定义在init.rc中:

1

service installd /system/bin/installd
    class main
    socket installd stream 600 system system

installd架构相对比较简单,会监听一个本地的socket,这个socket通过在init.rc文件中指定服务属性的方式创建,创建的本地socket位于:

1
/dev/socket/installd

源码位置:

1
Android-6/frameworks/native/cmds/installd/Installd.cpp

入口点

自然就是main函数了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74

int main(const int argc unused, char ×argv[]) {
    ...................

    // 初始化全局变量
    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);
    }

    if (selinux_enabled && selinux_status_open(true) < 0) {
        ALOGE("Could not open selinux status; exiting.\n");
        exit(1);
    }
    // 从环境变量ANDROID_SOCKET_installd中获取用于监听的本地socket
    lsocket = android_get_control_socket(SOCKET_PATH);
    if (lsocket < 0) {
        ALOGE("Failed to get socket from environment: %s\n", strerror(errno));
        exit(1);
    }
    // 监听socket
    if (listen(lsocket, 5)) {
        ALOGE("Listen on socket failed: %s\n", strerror(errno));
        exit(1);
    }
    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);

        // 立即处理新的连接,这说明install一次只能处理一个请求
        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;
            if (selinux_enabled && selinux_status_updated() > 0) {
                selinux_android_seapp_context_reload();
            }
            // 执行命令
            if (execute(s, buf)) break;
        }
        ALOGI("closing connection\n");
        close(s);
    }

    return 0;
}

通过代码中的注释,已经可以大体知道他的工作流程了。

initialize_globals()是用来初始化全局变量的,当该方法执行完毕的时候,可以确定以下的全局变量的值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
dir_rec_t android_data_dir; // /data
dir_rec_t android_asec_dir; // /mnt/asec
dir_rec_t android_app_dir; // /data/app
dir_rec_t android_app_private_dir; // /data/priv-app
dir_rec_t android_app_lib_dir; // /data/app-lib
dir_rec_t android_media_dir;// /data/media
dir_rec_t android_mnt_expand_dir; // /mnt/expand

// android_system_dirs.count = 4;
// /system/app 
// /system/priv-app
// /vendor/app
// /oem/app

dir_rec_array_t android_system_dirs;// /

initialize_directories()负责保证上面的文件夹都被创建了,以及设置了正确的权限。

installd支持的命令

支持如下命令:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
struct cmdinfo cmds[] = {
    { "ping",                 0, do_ping }, // 用于测试的空操作
    { "install",              5, do_install }, // 安装应用
    { "dexopt",               9, do_dexopt }, //将dex转换为oat或者patchoat oat文件
    { "markbootcomplete",     1, do_mark_boot_complete },
    { "movedex",              3, do_move_dex },  //把apk文件从一个目录移动到另一个目录
    { "rmdex",                2, do_rm_dex }, // 删除apk文件
    { "remove",               3, do_remove }, // 卸载应用
    { "rename",               2, do_rename }, // 更改应用数据目录的名称
    { "fixuid",               4, do_fixuid }, // 更改应用数据目录的uid
    { "freecache",            2, do_free_cache }, // 清除/cache目录下的文件
    { "rmcache",              3, do_rm_cache }, // 删除/cache下某个应用的目录
    { "rmcodecache",          3, do_rm_code_cache }, // 删除数据目录中code_cache文件夹
    { "getsize",              8, do_get_size }, // 计算一个应用占用的空间大小,包括apk大小,数据目录,cache目录等
    { "rmuserdata",           3, do_rm_user_data },// 删除一个用户中某个app的应用数据
    { "cpcompleteapp",        6, do_cp_complete_app },
    { "movefiles",            0, do_movefiles },//执行/system/etc/updatecmds/中的脚本
    { "linklib",              4, do_linklib }, // 建立 jib连接
    { "mkuserdata",           5, do_mk_user_data },// 为某个用户创建应用数据目录
    { "mkuserconfig",         1, do_mk_user_config },// 创建/data/misc/user/userid/
    { "rmuser",               2, do_rm_user },// 删除一个user的所有文件
    { "idmap",                3, do_idmap },
    { "restorecondata",       4, do_restorecon_data },// 恢复目录的SEAndroid安全上下文
    { "createoatdir",         2, do_create_oat_dir }, // 创建 /data/app/包名/oat/<inst>文件夹
    { "rmpackagedir",         1, do_rm_package_dir },// 删除/data/app/包名
    { "linkfile",             3, do_link_file } // 创建软连接
};

其中do_install所做的安装工作,实际上就是创建应用数据目录,这里分为用户id为0和不为0两种情况。(应用安装在内部存储)

当用户ID为0:

1
/data/data/包名/

用户ID不为0:

1
/data/user/userid/包名

用户安装在外部存储时就一种情况:

1
/mnt/expand/volume_uuid/user/userid/包名

创建完文件夹之后,就修改权限为0751,然后SEAndroid上下文,以及uid和gid。

另外do_dexopt中既可以执行dex2oat命令,将dex转换为oat文件,也可以执行patchoat,为oat文件随机化内存加载地址。

还要了解的的是installd命令的格式:

先发命令长度,然后再发命令。

    原文作者:疾风-Bevis
    原文地址: https://blog.csdn.net/LOVE000520/article/details/70463896
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞