android installd分析

 installd分析


android apk安裝最後使用服務installd來實現,源碼路徑:frameworks/base/cmds/installd
$ ls -l frameworks/base/cmds/installd
total 68
-rw-r--r-- 1 lizhiguo lizhiguo  2321 2011-11-15 17:06 Android.mk
-rw-r--r-- 1 lizhiguo lizhiguo 34863 2011-11-15 17:06 commands.c
-rw-r--r-- 1 lizhiguo lizhiguo  9742 2011-11-15 17:06 installd.c
-rw-r--r-- 1 lizhiguo lizhiguo  5586 2011-11-15 17:06 installd.h
-rw-r--r-- 1 lizhiguo lizhiguo  6902 2011-11-15 17:06 utils.c

frameworks/base/services/java/com/android/server/Installer.java該文件中實現了java的Installer類,負責和Installd通信,主要實現的功能包括:
connect,disconnect,execute,install,dexopt,movedex等,對應於installd裏面的功能:
struct cmdinfo cmds[] = {
    { "ping",                 0, do_ping },
    { "install",              4, do_install },
    { "dexopt",               3, do_dexopt },
    { "movedex",              2, do_move_dex },
    { "rmdex",                1, do_rm_dex },
    { "remove",               2, do_remove },
    { "rename",               3, do_rename },
    { "freecache",            1, do_free_cache },
    { "rmcache",              2, do_rm_cache },
    { "protect",              2, do_protect },
    { "getsize",              4, do_get_size },
    { "rmuserdata",           2, do_rm_user_data },
    { "movefiles",            0, do_movefiles },
    { "linklib",              2, do_linklib },
    { "unlinklib",            1, do_unlinklib },
};

@ frameworks/base/cmds/installd/installd.c
int main(const int argc, const char *argv[]) {   
    char buf[BUFFER_MAX];
    struct sockaddr addr;
    socklen_t alen;
    int lsocket, s, count;

    lsocket = android_get_control_socket(SOCKET_PATH); // #define SOCKET_PATH "installd"
    // frameworks/base/cmds/installd/installd.h
  // 獲得socket的描述符,見後註釋  
    ...
    if (listen(lsocket, 5)) {
        LOGE("Listen on socket failed: %s\n", strerror(errno));
        exit(1);
    }
    fcntl(lsocket, F_SETFD, FD_CLOEXEC);
    LOGI("new connection\n");
        for (;;) {
            unsigned short count;
            if (readx(s, &count, sizeof(count))) {
                LOGE("failed to read size\n");
                break;
            } // 讀數據大小
            if ((count < 1) || (count >= BUFFER_MAX)) {
                LOGE("invalid size %d\n", count);
                break;
            }
            if (readx(s, buf, count)) {
                LOGE("failed to read command\n");
                break;
            } // 讀實際數據
            buf[count] = 0;
            if (execute(s, buf)) break; // 執行命令
        }
        LOGI("closing connection\n");
        close(s);
    }

    return 0;
   
}

//////////////////////////////////////////////////////////////////////////
@ system/core/include/cutils/Sockets.h
#define ANDROID_SOCKET_ENV_PREFIX "ANDROID_SOCKET_"
#define ANDROID_SOCKET_DIR  "/dev/socket"

static inline int android_get_control_socket(const char *name)
{
 char key[64] = ANDROID_SOCKET_ENV_PREFIX; // ANDROID_SOCKET_
 const char *val;
 int fd;
 ...
 strncpy(key + sizeof(ANDROID_SOCKET_ENV_PREFIX) - 1, name, sizeof(key) - sizeof(ANDROID_SOCKET_ENV_PREFIX));
 key[sizeof(key)-1] = '\0';
 ...

 val = getenv(key); // 從環境變量中得到這個key值,那麼它什麼時候設置的呢?
 if (!val)
  return -1;

 errno = 0;
 fd = strtol(val, NULL, 10);
 if (errno)
  return -1;

 return fd;
}

/*
 * See also android.os.LocalSocketAddress.Namespace
 */
// Linux "abstract" (non-filesystem) namespace
#define ANDROID_SOCKET_NAMESPACE_ABSTRACT 0
// Android "reserved" (/dev/socket) namespace
#define ANDROID_SOCKET_NAMESPACE_RESERVED 1
// Normal filesystem namespace
#define ANDROID_SOCKET_NAMESPACE_FILESYSTEM 2


系統屬性區是在init進程啓動過程中初始化的,而起在init.rc中會發現如下的語句:
service installd /system/bin/installd
    socket installd stream 600 system system

service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
    socket zygote stream 666
    onrestart write /sys/android_power/request_state wake
    onrestart write /sys/power/state on
    onrestart restart media
    onrestart restart netd
在描述啓動服務的語句時,會跟上關於這個服務的所有options,而這裏socket就是其中一項option。

1. 系統屬性區和屬性服務的建立
android的每一個版本的init代碼都有一些差別,所以以下是android2.3的init代碼:
@ system/core/init/init.c
int main(int argc, char **argv)
{
...
queue_builtin_action(property_init_action, "property_init");
// 在這裏來來初始化屬性區域。
...

queue_builtin_action(property_service_init_action, "property_service_init");
// 開啓屬性服務,會導入其餘三個文件:/system/build.prop,/system/default.prop, /data/local.prop中的屬性和/data/property中的persist.開頭的屬性。
// 然後加上lcd density的屬性,ro.sf.lcd_density默認是160(MDPI), 從底層讀取像素值判斷是否是120(LDPI),240(HDPI).
// @ system/core/init/proerty_service.c
// @ system/core/init/property_patch.c
// 接着調用fd = create_socket(PROP_SERVICE_NAME, SOCK_STREAM, 0666, 0, 0);創建一個流套接字
// listen(fd, 8); property_set_fd = fd;
// 最後在init進去無限循環中,通過socket接收其他進程的屬性請求來處理。
...

}
static int property_init_action(int nargs, char **args)
{
    INFO("property init\n");
    property_init();
    return 0;
}

@ system/core/init/proerty_service.c
void property_init(void)
{
    init_property_area();
    load_properties_from_file(PROP_PATH_RAMDISK_DEFAULT);
    // 初始了屬性區域之後,首先導入編譯生成的屬性文件:
}
@ bionic/libc/include/sys/_system_properties.h
#define PROP_PATH_RAMDISK_DEFAULT  "/default.prop"
#define PROP_PATH_SYSTEM_BUILD     "/system/build.prop"
#define PROP_PATH_SYSTEM_DEFAULT   "/system/default.prop"
#define PROP_PATH_LOCAL_OVERRIDE   "/data/local.prop"

@ system/core/init/proerty_service.c
#define PA_COUNT_MAX  247
#define PA_INFO_START 1024
#define PA_SIZE       32768

static workspace pa_workspace;
static prop_info *pa_info_array;

extern prop_area *__system_property_area__;
static int init_property_area(void)
{
    prop_area *pa;

    if(pa_info_array)
        return -1;

    if(init_workspace(&pa_workspace, PA_SIZE))// 初始化工作區。
        return -1;

    fcntl(pa_workspace.fd, F_SETFD, FD_CLOEXEC);

    pa_info_array = (void*) (((char*) pa_workspace.data) + PA_INFO_START);

    pa = pa_workspace.data;
    memset(pa, 0, PA_SIZE);
    pa->magic = PROP_AREA_MAGIC;
    pa->version = PROP_AREA_VERSION;

    /* plug into the lib property services */
    __system_property_area__ = pa;
    property_area_inited = 1;  // 表明屬性區已經經過了初始化。
    return 0;
}
// 初始化工作區
static int init_workspace(workspace *w, size_t size)
{
    void *data;
    int fd;

    fd = open("/dev/__properties__", O_RDWR | O_CREAT, 0600);// 以讀寫方式打開設備文件/dev/__properties__
 ...
    data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    // 將該文件描述符映射到init進程用戶空間的虛擬地址,可讀可寫。
    ...
    close(fd);

    fd = open("/dev/__properties__", O_RDONLY);// 關閉之後重新已只讀打開這個設備文件。
 // 這樣的打開方式保證了只有init進程有對屬性區域寫的權限,而其餘進程只有讀的權限。
    unlink("/dev/__properties__");

    w->data = data;
    w->size = size;
    w->fd = fd;    // 將大小,文件描述符,虛擬地址保存在全局變量pa_workspace中。
    return 0;

out:
    close(fd);
    return -1;
}

2. 帶socket選項的服務啓動
解析init.rc中的服務:
init_parse_config_file("/init.rc")
--> parse_config(fn, data)
 --> parse_new_section(&state, kw, nargs, args)
  --> parse_line_service()
...
case K_socket: {/* name type perm [ uid gid ] */
        struct socketinfo *si;
        if (nargs < 4) {
            parse_error(state, "socket option requires name, type, perm arguments\n");
            break;
        }
        if (strcmp(args[2],"dgram") && strcmp(args[2],"stream")
                && strcmp(args[2],"seqpacket")) {
            parse_error(state, "socket type must be 'dgram', 'stream' or 'seqpacket'\n");
            // android只支持這三種形式的socket:數據報文套接字(UDP),流套接字(TCP),seqpacket(使用SCTP協議)
            break;
        }
        si = calloc(1, sizeof(*si)); // 分配socketinfo的內存
        if (!si) {
            parse_error(state, "out of memory\n");
            break;
        }
        // installd stream 600 system system
        si->name = args[1]; // installd
        si->type = args[2]; // stream
        si->perm = strtoul(args[3], 0, 8); // 600
        if (nargs > 4)
            si->uid = decode_uid(args[4]); // system
        if (nargs > 5)
            si->gid = decode_uid(args[5]); // system
        si->next = svc->sockets;
        svc->sockets = si; // 保存到當前服務中的sockets鏈表中,該服務啓動的時候會創建這些socket。
        break;
    }
...
開啓服務函數:service_start()
void service_start(struct service *svc, const char *dynamic_args)
{
 ...
 NOTICE("starting '%s'\n", svc->name);
    pid = fork();
 
 if (pid == 0) {
  ...
  if (properties_inited()) {
            get_property_workspace(&fd, &sz);
            sprintf(tmp, "%d,%d", dup(fd), sz);
            add_environment("ANDROID_PROPERTY_WORKSPACE", tmp);
        }// 添加系統屬性區的描述符和大小打環境變量中,ANDROID_PROPERTY_WORKSPACE
        ...
        for (si = svc->sockets; si; si = si->next) {
            int socket_type = (
                    !strcmp(si->type, "stream") ? SOCK_STREAM :
                        (!strcmp(si->type, "dgram") ? SOCK_DGRAM : SOCK_SEQPACKET));
            int s = create_socket(si->name, socket_type, si->perm, si->uid, si->gid);// 創建socket
            if (s >= 0) {
                publish_socket(si->name, s);// 將socket添加到環境變量中
            }
        }
  ...
 }
 
}
static void publish_socket(const char *name, int fd)
{
    char key[64] = ANDROID_SOCKET_ENV_PREFIX;
    char val[64];

    strlcpy(key + sizeof(ANDROID_SOCKET_ENV_PREFIX) - 1,
            name,
            sizeof(key) - sizeof(ANDROID_SOCKET_ENV_PREFIX));
    snprintf(val, sizeof(val), "%d", fd);
    add_environment(key, val);// 添加至環境變量

    /* make sure we don't close-on-exec */
    fcntl(fd, F_SETFD, 0);
}


 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章