installd守护进程分析

installd进程

pms分析前置守护进程启动,基于android Q源码解析

bp配置文件

platform/frameworks/native/cmds/installd/Android.bp

...
cc_binary {
    ...
    srcs: ["installd.cpp"],
    ...
    init_rc: ["installd.rc"],
    ...
}
...

Android.bp文件中指定这个模块装载时候解析的init.rc文件为installd.rc,对外部提供的加载文件为installd.cpp

rc文件解析

platform/frameworks/native/cmds/installd/installd.rc

service installd /system/bin/installd
    class main
....

启动时,以service形式启动

installd启动

platform/frameworks/native/cmds/installd/installd.cpp

int main(const int argc, char *argv[]) {
    return android::installd::installd_main(argc, argv);
}

可以看到调用的是installd这个命名空间的isstalld_main做初始化

static int installd_main(const int argc ATTRIBUTE_UNUSED, char *argv[]) {
    int ret;
    ...

    //初始化 /data 、/system目录
    if (!initialize_globals()) {
        ...
    }

    //初始化 /data/misc/user
    if (initialize_directories() < 0) {
        ...
    }
    //selinux校验
    if (selinux_enabled && selinux_status_open(true) < 0) {
        ...
    }
    ...

    //注册binder服务 InstalldNativeService
    if ((ret = InstalldNativeService::start()) != android::OK) {
        ...
    }

    IPCThreadState::self()->joinThreadPool();
    ...
    return 0;
}

这里首先调initialize_globals用初始化解析/data、/system下各种必要目录,然后通过initialize_directories去初始化/data/misc/user下用户组信息,这里会根据不同用户设置不同的id,默认只有一个用户则是0,之后会经过一个selinux的校验,最后会向binder注册一个InstalldNativeService类型的服务

全局初始化

platform/frameworks/native/cmds/installd/installd.cpp

static bool initialize_globals() {
    return init_globals_from_data_and_root();
}

init_globals_from_data_and_root定义在platform/frameworks/native/cmds/installd/globals.cpp下

bool init_globals_from_data_and_root() {
    //env查看,ANDROID_DATA->/data
    const char* data_path = getenv("ANDROID_DATA");
    ...
    //ANDROID_ROOT->/system
    const char* root_path = getenv("ANDROID_ROOT");
    ...
    return init_globals_from_data_and_root(data_path, root_path);
}

这里去拿环境变量ANDROID_DATA、ANDROID_ROOT目录,可以在adb shell环境下

env |grep ANDROID_

ANDROID_DATA=/data
ANDROID_ROOT=/system

对应的其实就是/data、/system,这里其实就是找到/data、/system然后通过调用init_globals_from_data_and_root去初始化各级目录,并复制给各个全局变量

namespace android{
namespace installd{
...
std::string android_app_dir;
std::string android_app_ephemeral_dir;
std::string android_app_lib_dir;
std::string android_app_private_dir;
std::string android_asec_dir;
std::string android_data_dir;
std::string android_media_dir;
std::string android_mnt_expand_dir;
std::string android_profiles_dir;
std::string android_root_dir;
std::string android_staging_dir;
std::vector<std::string> android_system_dirs;
...
bool init_globals_from_data_and_root(const char* data, const char* root) {
    // android_data_dir->/data/
    android_data_dir = ensure_trailing_slash(data);

    // android_root_dir->/system/
    android_root_dir = ensure_trailing_slash(root);

    // android_app_dir-> /data/app
    android_app_dir = android_data_dir + APP_SUBDIR;

    // android_app_private_dir-> /data/app-private
    android_app_private_dir = android_data_dir + PRIVATE_APP_SUBDIR;

    // android_app_ephemeral_dir-> /data/app-private/
    android_app_ephemeral_dir = android_data_dir + EPHEMERAL_APP_SUBDIR;

    // android_app_lib_dir->/data/app-lib/
    android_app_lib_dir = android_data_dir + APP_LIB_SUBDIR;

    // ASEC_MOUNTPOINT=/mnt/asec
    android_asec_dir = ensure_trailing_slash(getenv(ASEC_MOUNTPOINT_ENV_NAME));

    // android_media_dir->/data/media/
    android_media_dir = android_data_dir + MEDIA_SUBDIR;

    // Get the android external app directory.
    android_mnt_expand_dir = "/mnt/expand/";

    // android_profiles_dir->/data/misc/profiles
    android_profiles_dir = android_data_dir + PROFILES_SUBDIR;

    // android_staging_dir->/data/app-staging/
    android_staging_dir = android_data_dir + STAGING_SUBDIR;

    // Take note of the system and vendor directories.
    android_system_dirs.clear();
    android_system_dirs.push_back(android_root_dir + APP_SUBDIR);
    android_system_dirs.push_back(android_root_dir + PRIV_APP_SUBDIR);
    android_system_dirs.push_back("/vendor/app/");
    android_system_dirs.push_back("/oem/app/");

    return true;
}
...
}
}

ensure_trailing_slash这个函数只是对/data下添加了一个/
android_system_dirs存储的是一个dir数组

static std::string ensure_trailing_slash(const std::string& path) {
    if (path.rfind('/') != path.size() - 1) {
        return path + '/';
    } else {
        return path;
    }
}

执行完init_globals_from_data_and_root,android::installd::xxx下的变量就已经复制完毕,接下来我们看用户目录初始化

用户初始化

platform/frameworks/native/cmds/installd/installd.cpp

static int initialize_directories() {
    ...
    char version_path[PATH_MAX];
    //version_path => /data/.layout_version
    snprintf(version_path, PATH_MAX, "%s.layout_version", android_data_dir.c_str());
    int oldVersion;
    //读.layout_version存储的值
    if (fs_read_atomic_int(version_path, &oldVersion) == -1) {
        oldVersion = 0;
    }
    int version = oldVersion;
    if (version < 2) {
        ...
        version = 2;
    }
    // /data/misc/user/0
    if (ensure_config_user_dirs(0) == -1) {
        ...
        goto fail;
    }
    ...
    // /data/misc/user/0
    if (ensure_config_user_dirs(0) == -1) {
        ...
        goto fail;
    }
    if (version == 2) {
        ...
        char misc_dir[PATH_MAX];
        //misc_dir => /data/misc
        snprintf(misc_dir, PATH_MAX, "%smisc", android_data_dir.c_str());

        char keychain_added_dir[PATH_MAX];
        // keychain_added_dir => /data/msic/keychain/cacerts-added
        snprintf(keychain_added_dir, PATH_MAX, "%s/keychain/cacerts-added", misc_dir);

        char keychain_removed_dir[PATH_MAX];
        // keychain_added_dir => /data/msic/keychain/cacerts-removed
        snprintf(keychain_removed_dir, PATH_MAX, "%s/keychain/cacerts-removed", misc_dir);
        ...
        DIR *dir;
        struct dirent *dirent;
        dir = opendir("/data/user");
        if (dir != nullptr) {
            while ((dirent = readdir(dir))) {
                const char *name = dirent->d_name;
                ...
                uint32_t user_id = std::stoi(name);

                // /data/misc/user/<user_id>
                if (ensure_config_user_dirs(user_id) == -1) {
                    goto fail;
                }
                char misc_added_dir[PATH_MAX];
                // misc_added_dir => /data/msic/user/0/cacerts-added
                snprintf(misc_added_dir, PATH_MAX, 
                    "%s/user/%s/cacerts-added", misc_dir, name);

                char misc_removed_dir[PATH_MAX];
                // misc_removed_dir => /data/msic/user/0/cacerts-removed
                snprintf(misc_removed_dir, PATH_MAX, 
                    "%s/user/%s/cacerts-removed", misc_dir, name);
                //获取uid
                uid_t uid = multiuser_get_uid(user_id, AID_SYSTEM);
                gid_t gid = uid;
                //文件目录copy
                if (access(keychain_added_dir, F_OK) == 0) {
                    if (copy_dir_files(keychain_added_dir, 
                        misc_added_dir, uid, gid) != 0) {
                        ...
                    }
                }
                //文件目录copy
                if (access(keychain_removed_dir, F_OK) == 0) {
                    if (copy_dir_files(keychain_removed_dir, 
                     misc_removed_dir, uid, gid) != 0) {
                        ...
                    }
                }
            }
            //关闭dir
            closedir(dir);
            //删除临时文件
            if (access(keychain_added_dir, F_OK) == 0) {
                delete_dir_contents(keychain_added_dir, 1, nullptr);
            }
            if (access(keychain_removed_dir, F_OK) == 0) {
                delete_dir_contents(keychain_removed_dir, 1, nullptr);
            }
        }
        version = 3;
    }

    if (version != oldVersion) {
        if (fs_write_atomic_int(version_path, version) == -1) {
            ...
            goto fail;
        }
    }
}

-initialize_directories首先将/data/.layout_version复制给version_path,这里其实.layout_version就是简单的存储了个version值,其实是为了防止多次初始化
-之后读该文件的value,如果读取失败返回-1,否则返回0
-之后version==2时候,则会/data/misc/user/0,并将/data/misc/keychain/cacerts-added、/data/misc/keychain/cacerts-removed下的dir拷贝到/data/misc/user/uid/cacerts-added、/data/misc/user/uid/cacerts-removed下
-最后会校验版本,如果不一致则,回想.layout_version文件写入3,那么已经完成installd初始化的.layout_version一定是记录为3
adb shell验证如下:

download.png

InstalldNativeService初始化

最后我们来分析service的注册,InstalldNativeService#start定义如下
platform/frameworks/native/cmds/installd/InstalldNativeService.cpp

status_t InstalldNativeService::start() {
    IPCThreadState::self()->disableBackgroundScheduling(true);
    status_t ret = BinderService<InstalldNativeService>::publish();
    if (ret != android::OK) {
        return ret;
    }
    sp<ProcessState> ps(ProcessState::self());
    ps->startThreadPool();
    ps->giveThreadPoolName();
    return android::OK;
}

这里很常规的进程初始化+binder服务注册,BinderService::publish()这个是个模板函数,定义在/frameworks/native/libs/binder/include/binder/BinderService.h展开后伪代码如下:

static status_t publish(bool allowIsolated = false,
                            int dumpFlags = IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT) {
     sp<IServiceManager> sm(defaultServiceManager());
     return sm->addService(String16(InstalldNativeService::getServiceName())
      , new InstalldNativeService(), allowIsolated,dumpFlags);
}

这里实际上就是通过servce_manager去注册一个binder服务,详情可以参考我的另一篇binder解析

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345

推荐阅读更多精彩内容