dexopt 是 android dalvik 虚拟机使用的优化程序,它负责把dex文件优化成odex。
在 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
函数
execute 函数会调用 transaction
函数
private int execute(String cmd) {
String res = transaction(cmd);
try {
return Integer.parseInt(res);
} catch (NumberFormatException ex) {
return -1;
}
}
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";
}
}
/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;
}
当它收到消息后它会调用 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
函数
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
函数,并等待那个进程结束
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));
}