android 调用 dexopt 的位置

dexopt 是 android dalvik 虚拟机使用的优化程序,它负责把dex文件优化成odex。


位置1 安装的时候调用

  1. PackageManagerService 中的 Installer.java 里执行 dexopt 方法

    public int dexopt(String apkPath, int uid, boolean isPublic)     {
        StringBuilder builder = new StringBuilder("dexopt");
        builder.append(' ');
        builder.append(apkPath);
        builder.append(' ');
        builder.append(uid);
        builder.append(isPublic ? " 1" : " 0");
        return execute(builder.toString());
    }

    其中会调用 execute 函数

  2. execute 函数会调用 transaction 函数

    private int execute(String cmd) {
        String res = transaction(cmd);
        try {
            return Integer.parseInt(res);
        } catch (NumberFormatException ex) {
            return -1;
        }
    }
  3. transaction 函数最终通过socket接口向 /system/bin/installd 进程发送安装消息

    private synchronized String transaction(String cmd) {
        if (!connect()) {
            Slog.e(TAG, "connection failed");
            return "-1";
        }
    
        if (!writeCommand(cmd)) {
            /*
             * If installd died and restarted in the background (unlikely but
             * possible) we'll fail on the next write (this one). Try to
             * reconnect and write the command one more time before giving up.
             */
            Slog.e(TAG, "write command failed? reconnect!");
            if (!connect() || !writeCommand(cmd)) {
                return "-1";
            }
        }
        if (LOCAL_DEBUG) {
            Slog.i(TAG, "send: '" + cmd + "'");
        }
        if (readReply()) {
            String s = new String(buf, 0, buflen);
            if (LOCAL_DEBUG) {
                Slog.i(TAG, "recv: '" + s + "'");
            }
            return s;
        } else {
            if (LOCAL_DEBUG) {
                Slog.i(TAG, "fail");
            }
            return "-1";
        }
    }
  4. /system/bin/installd 进程由init.rc创建

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

    它启动后会监听一个本地的socket端口,供其他程序调用
    并且它在main函数中循环监听该端口

    int main(const int argc, const char *argv[]) {
        char buf[BUFFER_MAX];
        struct sockaddr addr;
        socklen_t alen;
        int lsocket, s, count;
    
        if (initialize_globals() < 0) {
            LOGE("Could not initialize globals; exiting.\n");
            exit(1);
        }
    
        if (initialize_directories() < 0) {
            LOGE("Could not create directories; exiting.\n");
            exit(1);
        }
    
        lsocket = android_get_control_socket(SOCKET_PATH);
        if (lsocket < 0) {
            LOGE("Failed to get socket from environment: %s\n", strerror(errno));
            exit(1);
        }
        if (listen(lsocket, 5)) {
            LOGE("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) {
                LOGE("Accept failed: %s\n", strerror(errno));
                continue;
            }
            fcntl(s, 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;
    }
  5. 当它收到消息后它会调用 execute 函数

    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;
    
    //    LOGI("execute('%s')\n", cmd);
    
            /* default reply is "" */
        reply[0] = 0;
    
            /* n is number of args (not counting arg[0]) */
        arg[0] = cmd;
        while (*cmd) {
            if (isspace(*cmd)) {
                *cmd++ = 0;
                n++;
                arg[n] = cmd;
                if (n == TOKEN_MAX) {
                    LOGE("too many arguments\n");
                    goto done;
                }
            }
            cmd++;
        }
    
        for (i = 0; i < sizeof(cmds) / sizeof(cmds[0]); i++) {
            if (!strcmp(cmds[i].name,arg[0])) {
                if (n != cmds[i].numargs) {
                    LOGE("%s requires %d arguments (%d given)\n",
                         cmds[i].name, cmds[i].numargs, n);
                } else {
                    ret = cmds[i].func(arg + 1, reply);
                }
                goto done;
            }
        }
        LOGE("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;
    
    //    LOGI("reply: '%s'\n", cmd);
        if (writex(s, &count, sizeof(count))) return -1;
        if (writex(s, cmd, count)) return -1;
        return 0;
    }

    这个函数会从这个数组里找到这个消息对应的处理函数

    struct cmdinfo cmds[] = {
        { "ping",                 0, do_ping },
        { "install",              3, do_install },
        { "dexopt",               3, do_dexopt },
        { "movedex",              2, do_move_dex },
        { "rmdex",                1, do_rm_dex },
        { "remove",               2, do_remove },
        { "rename",               2, do_rename },
        { "freecache",            1, do_free_cache },
        { "rmcache",              1, 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 },
        { "mkuserdata",           3, do_mk_user_data },
        { "rmuser",               1, do_rm_user },
    };

    可以看到 dexopt 对应的是 do_dexopt 函数

  6. do_dexopt 会调用 dexopt 函数

    int dexopt(const char *apk_path, uid_t uid, int is_public)
    {
        struct utimbuf ut;
        struct stat apk_stat, dex_stat;
        char dex_path[PKG_PATH_MAX];
        char dexopt_flags[PROPERTY_VALUE_MAX];
        char *end;
        int res, zip_fd=-1, odex_fd=-1;
    
            /* Before anything else: is there a .odex file?  If so, we have
             * pre-optimized the apk and there is nothing to do here.
             */
        if (strlen(apk_path) >= (PKG_PATH_MAX - 8)) {
            return -1;
        }
    
        /* platform-specific flags affecting optimization and verification */
        property_get("dalvik.vm.dexopt-flags", dexopt_flags, "");
    
        strcpy(dex_path, apk_path);
        end = strrchr(dex_path, '.');
        if (end != NULL) {
            strcpy(end, ".odex");
            if (stat(dex_path, &dex_stat) == 0) {
                return 0;
            }
        }
    
        if (create_cache_path(dex_path, apk_path)) {
            return -1;
        }
    
        memset(&apk_stat, 0, sizeof(apk_stat));
        stat(apk_path, &apk_stat);
    
        zip_fd = open(apk_path, O_RDONLY, 0);
        if (zip_fd < 0) {
            LOGE("dexopt cannot open '%s' for input\n", apk_path);
            return -1;
        }
    
        unlink(dex_path);
        odex_fd = open(dex_path, O_RDWR | O_CREAT | O_EXCL, 0644);
        if (odex_fd < 0) {
            LOGE("dexopt cannot open '%s' for output\n", dex_path);
            goto fail;
        }
        if (fchown(odex_fd, AID_SYSTEM, uid) < 0) {
            LOGE("dexopt cannot chown '%s'\n", dex_path);
            goto fail;
        }
        if (fchmod(odex_fd,
                   S_IRUSR|S_IWUSR|S_IRGRP |
                   (is_public ? S_IROTH : 0)) < 0) {
            LOGE("dexopt cannot chmod '%s'\n", dex_path);
            goto fail;
        }
    
        LOGV("DexInv: --- BEGIN '%s' ---\n", apk_path);
    
        pid_t pid;
        pid = fork();
        if (pid == 0) {
            /* child -- drop privileges before continuing */
            if (setgid(uid) != 0) {
                LOGE("setgid(%d) failed during dexopt\n", uid);
                exit(64);
            }
            if (setuid(uid) != 0) {
                LOGE("setuid(%d) during dexopt\n", uid);
                exit(65);
            }
            if (flock(odex_fd, LOCK_EX | LOCK_NB) != 0) {
                LOGE("flock(%s) failed: %s\n", dex_path, strerror(errno));
                exit(66);
            }
    
            run_dexopt(zip_fd, odex_fd, apk_path, dexopt_flags);
            exit(67);   /* only get here on exec failure */
        } else {
            res = wait_dexopt(pid, apk_path);
            if (res != 0) {
                LOGE("dexopt failed on '%s' res = %d\n", dex_path, res);
                goto fail;
            }
        }
    
        ut.actime = apk_stat.st_atime;
        ut.modtime = apk_stat.st_mtime;
        utime(dex_path, &ut);
    
        close(odex_fd);
        close(zip_fd);
        return 0;
    
    fail:
        if (odex_fd >= 0) {
            close(odex_fd);
            unlink(dex_path);
        }
        if (zip_fd >= 0) {
            close(zip_fd);
        }
        return -1;
    }

    这里 dexopt 会启动一个新进程去执行 run_dexopt 函数,并等待那个进程结束

  7. run_dexopt 会调用 /system/bin/dexopt命令

    static void run_dexopt(int zip_fd, int odex_fd, const char* input_file_name,
        const char* dexopt_flags)
    {
        static const char* DEX_OPT_BIN = "/system/bin/dexopt";
        static const int MAX_INT_LEN = 12;      // '-'+10dig+'\0' -OR- 0x+8dig
        char zip_num[MAX_INT_LEN];
        char odex_num[MAX_INT_LEN];
    
        sprintf(zip_num, "%d", zip_fd);
        sprintf(odex_num, "%d", odex_fd);
    
        execl(DEX_OPT_BIN, DEX_OPT_BIN, "--zip", zip_num, odex_num, input_file_name,
            dexopt_flags, (char*) NULL);
        LOGE("execl(%s) failed: %s\n", DEX_OPT_BIN, strerror(errno));
    }

位置2 在调用DexClassLoader时调用(未完待续)

你可能感兴趣的:(android 调用 dexopt 的位置)