1. xapk介绍

xapkapk相似,是android的一种打包运用程序的办法。运用这种办法打成的包以.xapk结束.能够将.xapk后缀直接改为zip后缀,然后运用解压缩东西解压xapk包。xapk有两种组织办法:

  1. xapk=中心apk+obb文件夹
  2. xapk=中心apk+一个或多个其他apk

两种办法都包括一个中心apk,该apk的姓名一般便是包名.apk,是运用必须的骨架包。在中心apk+obb文件夹的组合中,只包括一个apk,即中心apk,运用需求的音视频,图片等资源则抽离成若干个obb文件,存放在obb文件夹下。在中心apk+一个或多个其他apk的组合中,会将不同语言装备,so库等数据抽离成若干个单独的apk。在Google play中下载的运用,大部分都是这两种形式。

2. xapk包的装置

2.1 中心apk+obb文件夹的xapk装置

这种办法的xapk包装置比较简单,首要装置中心apk,然后手动将obb文件复制到/sdcard/Android/obb目录即可。

2.2 中心apk+一个或多个其他apk

这种办法的apk包,一般需求借助第三方的xapk装置东西,常见的有XapkInstaller,能够直接在Google play下载,,此外,也能够运用adb办法装置,adb装置指令如下

adb install-multiple au.com.metrotrains.dwtd4.apk config.arm64_v8a.apk

其间adb install-multiple指令是用于装置由多个apk组成的运用,后边跟的参数便是该运用的所有apk。
留意,需求将该指令和install-multi-package指令区别开,该指令用于批量装置多个运用。

本系列以adb install-multiple指令为线索,简单剖析一下xapk的装置进程,同时也学习了解Android装置运用的进程,涉及到的Android 源码为Android 10源码

3.adb install-multiple装置流程

adbAndroid系统供给的调试东西,分为Host端和daemon端,Host端即PC装置的运用程序,daemon即运转于android真机上的adb后台服务。其间Host端又分为adb client 和adb service,其间adb client担任接纳adb指令或处理不需求daemon处理的指令,若adb指令需求daemon处理,则将该指令发给运转于Host端的后台服务adb service,再由adb service发给daemon,本文不区别adb clientadb service,统一由adb host代替。adb源码坐落/system/core/adb目录,下面先看Host端的履行进程

3.1 adb Host端

adb指令在Host的履行进口为/system/core/adb/client/main.cpp#main()

int main(int argc, char* argv[], char* envp[]) {
    __adb_argv = const_cast<const char**>(argv);
    __adb_envp = const_cast<const char**>(envp);
    adb_trace_init(argv);
    return adb_commandline(argc - 1, const_cast<const char**>(argv + 1));
}

在main办法中,调用了adb_commandline()办法,该办法坐落 /system/core/adb/client/commandline.cpp 中,该办法用于解析各种adb指令,和install-multiple有关的代码如下

...
else if (!strcmp(argv[0], "install-multiple")) {
        if (argc < 2) error_exit("install-multiple requires an argument");
        return install_multiple_app(argc, argv);
...

在该办法中,若adb的指令是install-multiple,就调用install_multiple_app办法进行后续处理。该办法源码坐落/system/core/adb/client/adb_install.cpp中,是运用adb 装置xapk的中心办法。该办法的履行首要分为如下几步

(1) 遍历需求装置的apk参数,计算出需求装置的apk文件总巨细
int first_apk = -1;
uint64_t total_size = 0;
for (int i = argc - 1; i >= 0; i--) {
     const char* file = argv[i];
     if (android::base::EndsWithIgnoreCase(argv[i], ".apex")) {
     error_exit("APEX packages are not compatible with install-multiple");
     }
     if (android::base::EndsWithIgnoreCase(file, ".apk") ||
         android::base::EndsWithIgnoreCase(file, ".dm") ||
         android::base::EndsWithIgnoreCase(file, ".fsv_sig")) {
         struct stat sb;
         if (stat(file, &sb) != -1) total_size += sb.st_size;
         first_apk = i;
     } else {
         break;
     }
}
if (first_apk == -1) error_exit("need APK file on command line");

从上述代码中能够看到,install-multiple指令不能装置以apex结束的文件,只能装置.apk,.dmfsv_sig结束的文件,一般运用的都是以.apk结束的文件。对每一个apk文件,运用stat获取其文件巨细,并终究计算出运用程序包总的巨细。

(2)向服务端恳求履行install-create指令
    std::string install_cmd;
    if (use_legacy_install()) {
        install_cmd = "exec:pm";
    } else {
        install_cmd = "exec:cmd package";
    }
    std::string cmd = android::base::StringPrintf("%s install-create -S %" PRIu64,
                                                  install_cmd.c_str(), total_size);
    for (int i = 1; i < first_apk; i++) {
        cmd += " " + escape_arg(argv[i]);
    }
    // Create install session
    std::string error;
    char buf[BUFSIZ];
    {
        unique_fd fd(adb_connect(cmd, &error));
        if (fd < 0) {
            fprintf(stderr, "adb: connect error for create: %s\n", error.c_str());
            return EXIT_FAILURE;
        }
        read_status_line(fd.get(), buf, sizeof(buf));
    }
    int session_id = -1;
    if (!strncmp("Success", buf, 7)) {
        char* start = strrchr(buf, '[');
        char* end = strrchr(buf, ']');
        if (start && end) {
            *end = '\0';
            session_id = strtol(start + 1, nullptr, 10);
        }
    }
    if (session_id < 0) {
        fprintf(stderr, "adb: failed to create session\n");
        fputs(buf, stderr);
        return EXIT_FAILURE;
    }

在这一步,结构一个install-create指令保存到变量install_cmd中,然后将该指令传给adb daemon端,让服务端履行该指令。use_legacy_install()用来判别是否运用传统办法装置apk,所谓传统办法,指的是将apk文件复制到特定目录下,然后告诉系统进行装置的办法。在较早android版本中较常见,现在一般不会运用,所以install_cmd初始值一般是exec:cmd package。在确定了install_cmd的初始值后,运用cmd拼接install_cmd的参数,其间-S用来表示需求装置的apk的巨细,后边再跟上每个apk文件的途径,经过上述操作,终究得到的install_cmd相似:

exec:cmd package install-create -S size au.com.metrotrains.dwtd4.apk config.arm64_v8a.apk

在结构好install_cmd指令后,调用adb_connect办法将该指令发往daemon端,并等候daemon端返回成果,然后将生成的sessionId保存到变量session_id中。adb_connect办法放在下面剖析,现在持续向下看install_multiple_app办法

(3)履行install-write指令写入apk

在过程(2)生成sessionId后,就遍历apk文件,每一个apk文件履行install-write指令写入apk文件

 int success = 1;
    for (int i = first_apk; i < argc; i++) {
        const char* file = argv[i];
        struct stat sb;
        if (stat(file, &sb) == -1) {
            fprintf(stderr, "adb: failed to stat %s: %s\n", file, strerror(errno));
            success = 0;
            goto finalize_session;
        }
        std::string cmd =
                android::base::StringPrintf("%s install-write -S %" PRIu64 " %d %s -",
                                            install_cmd.c_str(), static_cast<uint64_t>(sb.st_size),
                                            session_id, android::base::Basename(file).c_str());
        unique_fd local_fd(adb_open(file, O_RDONLY | O_CLOEXEC));
        if (local_fd < 0) {
            fprintf(stderr, "adb: failed to open %s: %s\n", file, strerror(errno));
            success = 0;
            goto finalize_session;
        }
        std::string error;
        unique_fd remote_fd(adb_connect(cmd, &error));
        if (remote_fd < 0) {
            fprintf(stderr, "adb: connect error for write: %s\n", error.c_str());
            success = 0;
            goto finalize_session;
        }
        copy_to_file(local_fd.get(), remote_fd.get());
        read_status_line(remote_fd.get(), buf, sizeof(buf));
        if (strncmp("Success", buf, 7)) {
            fprintf(stderr, "adb: failed to write %s\n", file);
            fputs(buf, stderr);
            success = 0;
            goto finalize_session;
        }
    }

在上面代码中,生成的cmd形式为:

exec:cmd package install-write -S apkSize sessionId  au.com.metrotrains.dwtd4.apk -

在生成cmd后,先调用adb_open办法打开apk文件,然后调用adb_connect办法连接daemon端履行cmd,daemon端返回成果后读取其间的内容判别操作是否成功,不管操作成功与否,终究都会跳转到finalize_session代码块

(4) install-commit提交本次装置

在过程(3)中,不管install-write的成果如何,终究都会跳转到finalize_session代码块:

finalize_session:
    // Commit session if we streamed everything okay; otherwise abandon
    std::string service = android::base::StringPrintf("%s install-%s %d", install_cmd.c_str(),
                                                      success ? "commit" : "abandon", session_id);
    {
        unique_fd fd(adb_connect(service, &error));
        if (fd < 0) {
            fprintf(stderr, "adb: connect error for finalize: %s\n", error.c_str());
            return EXIT_FAILURE;
        }
        read_status_line(fd.get(), buf, sizeof(buf));
    }
    if (!strncmp("Success", buf, 7)) {
        fputs(buf, stdout);
        return 0;
    }
    fprintf(stderr, "adb: failed to finalize session\n");
    fputs(buf, stderr);
    return EXIT_FAILURE;
}

finalize_session代码块中,如果install-write操作写入成功,就履行install-commit操作,commit本次装置,若写入失败,就履行install abandon指令抛弃本次装置,这两个指令如下

exec:cmd package install-commit sessionId
exec:cmd package intall-abandon sessionId

本系统只考虑commit的情形。从代码能够看到,也是经过adb_connect办法将该指令发送给daemon端,由服务端处理,daemon处理完结后,将成果打印到终端上,结束本次装置操作。

总结:经过剖析install_multiple_app办法可知,adb install-multiple指令实践上是经过adb_connect办法向daemon端发送cmd:package install-create,cmd:packgae:install-write,cmd:packgae:install-commit三个指令完结装置操作。在剖析adb daemon端相关代码前,先看看adb_connect的代码

3.2 adb_connect

adb_connect办法是adb Host端和daemon端通信的中心办法,源码途径为/system/core/adb/client/adb_client.cpp

int adb_connect(std::string_view service, std::string* error) {
    return adb_connect(nullptr, service, error);
}
int adb_connect(TransportId* transport, std::string_view service, std::string* error) {
    LOG(DEBUG) << "adb_connect: service: " << service;
    // Query the adb server's version.
    if (!adb_check_server_version(error)) {
        return -1;
    }
    // if the command is start-server, we are done.
    if (service == "host:start-server") {
        return 0;
    }
    unique_fd fd(_adb_connect(service, transport, error));
    if (fd == -1) {
        D("_adb_connect error: %s", error->c_str());
    } else if(fd == -2) {
        fprintf(stderr, "* daemon still not running\n");
    }
    D("adb_connect: return fd %d", fd.get());
    return fd.release();
}

adb_connect办法中,首要调用adb_check_server_version办法检查adb server的版本,在该办法中也会调用_adb_connect办法。检查完版本后,调用_adb_connect办法将指令发给adb daemon端:

static int _adb_connect(std::string_view service, TransportId* transport, std::string* error) {
    LOG(DEBUG) << "_adb_connect: " << service;
    if (service.empty() || service.size() > MAX_PAYLOAD) {
        *error = android::base::StringPrintf("bad service name length (%zd)", service.size());
        return -1;
    }
    std::string reason;
    unique_fd fd;
    if (!socket_spec_connect(&fd, __adb_server_socket_spec, nullptr, nullptr, &reason)) {
        *error = android::base::StringPrintf("cannot connect to daemon at %s: %s",
                                             __adb_server_socket_spec, reason.c_str());
        return -2;
    }
    if (!service.starts_with("host")) {
        std::optional<TransportId> transport_result = switch_socket_transport(fd.get(), error);
        if (!transport_result) {
            return -1;
        }
        if (transport) {
            *transport = *transport_result;
        }
    }
    if (!SendProtocolString(fd.get(), service)) {
        *error = perror_str("write failure during connection");
        return -1;
    }
    if (!adb_status(fd.get(), error)) {
        return -1;
    }
    D("_adb_connect: return fd %d", fd.get());
    return fd.release();
}

_adb_connect办法中,先建立和daemon端的tcp连接,然后经过SendProtocolString办法将数据传输给daemon

3.3 adb daemon端

daemon端处理Host端发送过来的adb 指令的进口函数为service_to_fd办法,坐落/system/core/adb/service.cpp

unique_fd service_to_fd(std::string_view name, atransport* transport) {
    unique_fd ret;
    if (is_socket_spec(name)) {
        std::string error;
        if (!socket_spec_connect(&ret, name, nullptr, nullptr, &error)) {
            LOG(ERROR) << "failed to connect to socket '" << name << "': " << error;
        }
    } else {
#if !ADB_HOST
        ret = daemon_service_to_fd(name, transport);
#endif
    }
    if (ret >= 0) {
        close_on_exec(ret.get());
    }
    return ret;
}

其间ADB_HOST宏用于标识是否是HOST端,在adb daemon端,该值为0,HOST端该值为1,所以在daemon端是调用daemon_service_to_fd办法处理adb指令,该办法坐落/system/core/adb/daemon/service.cpp

unique_fd daemon_service_to_fd(std::string_view name, atransport* transport) {
#if defined(__ANDROID__) && !defined(__ANDROID_RECOVERY__)
    if (name.starts_with("abb:") || name.starts_with("abb_exec:")) {
        return execute_abb_command(name);
    }
#endif
#if defined(__ANDROID__)
    if (name.starts_with("framebuffer:")) {
        return create_service_thread("fb", framebuffer_service);
    } else if (ConsumePrefix(&name, "remount:")) {
        std::string arg(name);
        return create_service_thread("remount",
                                     std::bind(remount_service, std::placeholders::_1, arg));
    } else if (ConsumePrefix(&name, "reboot:")) {
        std::string arg(name);
        return create_service_thread("reboot",
                                     std::bind(reboot_service, std::placeholders::_1, arg));
    } else if (name.starts_with("root:")) {
        return create_service_thread("root", restart_root_service);
    } else if (name.starts_with("unroot:")) {
        return create_service_thread("unroot", restart_unroot_service);
    } else if (ConsumePrefix(&name, "backup:")) {
        std::string cmd = "/system/bin/bu backup ";
        cmd += name;
        return StartSubprocess(cmd, nullptr, SubprocessType::kRaw, SubprocessProtocol::kNone);
    } else if (name.starts_with("restore:")) {
        return StartSubprocess("/system/bin/bu restore", nullptr, SubprocessType::kRaw,
                               SubprocessProtocol::kNone);
    } else if (name.starts_with("disable-verity:")) {
        return create_service_thread("verity-on", std::bind(set_verity_enabled_state_service,
                                                            std::placeholders::_1, false));
    } else if (name.starts_with("enable-verity:")) {
        return create_service_thread("verity-off", std::bind(set_verity_enabled_state_service,
                                                             std::placeholders::_1, true));
    } else if (ConsumePrefix(&name, "tcpip:")) {
        std::string str(name);
        int port;
        if (sscanf(str.c_str(), "%d", &port) != 1) {
            return unique_fd{};
        }
        return create_service_thread("tcp",
                                     std::bind(restart_tcp_service, std::placeholders::_1, port));
    } else if (name.starts_with("usb:")) {
        return create_service_thread("usb", restart_usb_service);
    }
#endif
    if (ConsumePrefix(&name, "dev:")) {
        return unique_fd{unix_open(name, O_RDWR | O_CLOEXEC)};
    } else if (ConsumePrefix(&name, "jdwp:")) {
        pid_t pid;
        if (!ParseUint(&pid, name)) {
            return unique_fd{};
        }
        return create_jdwp_connection_fd(pid);
    } else if (ConsumePrefix(&name, "shell")) {
        return ShellService(name, transport);
    } else if (ConsumePrefix(&name, "exec:")) {
        return StartSubprocess(std::string(name), nullptr, SubprocessType::kRaw,
                               SubprocessProtocol::kNone);
    } else if (name.starts_with("sync:")) {
        return create_service_thread("sync", file_sync_service);
    } else if (ConsumePrefix(&name, "reverse:")) {
        return reverse_service(name, transport);
    } else if (name == "reconnect") {
        return create_service_thread(
                "reconnect", std::bind(reconnect_service, std::placeholders::_1, transport));
    } else if (name == "spin") {
        return create_service_thread("spin", spin_service);
    }
    return unique_fd{};
}

在该办法中,首要便是解析各种adb指令,然后运用对应的办法去处理,在本文中,传到daemon端的指令前缀是exec:,从代码61行能够看到,关于exec:指令,是创立一个子线程去履行对应的指令,本文接纳到的指令以exec:cmd开头,对应的便是创立一个子线程去履行cmd指令。cmd指令的代码源码坐落/frameworks/native/cmds/cmd目录,其进口为main.cpp#main()

int main(int argc, char* const argv[]) {
    signal(SIGPIPE, SIG_IGN);
    std::vector<std::string_view> arguments;
    arguments.reserve(argc - 1);
    // 0th argument is a program name, skipping.
    for (int i = 1; i < argc; ++i) {
        arguments.emplace_back(argv[i]);
    }
    return cmdMain(arguments, android::aout, android::aerr, STDIN_FILENO, STDOUT_FILENO,
                   STDERR_FILENO, RunMode::kStandalone);
}

main办法中,先获取到cmd的参数,关于exec:cmd package install-create -S size au.com.metrotrains.dwtd4.apk config.arm64_v8a.apk便是cmd后边的一系列参数。在获取到这些参数后,调用cmdMain办法进行后续处理,该办法坐落/frameworks/native/cmds/cmd/cmd.cpp中,其间心代码如下

...
  const auto cmd = argv[0];
    Vector<String16> args;
    String16 serviceName = String16(cmd.data(), cmd.size());
    for (int i = 1; i < argc; i++) {
        args.add(String16(argv[i].data(), argv[i].size()));
    }
    sp<IBinder> service = sm->checkService(serviceName);
    if (service == nullptr) {
        if (runMode == RunMode::kStandalone) {
            ALOGW("Can't find service %.*s", static_cast<int>(cmd.size()), cmd.data());
        }
        errorLog << "cmd: Can't find service: " << cmd << endl;
        return 20;
    }
    sp<MyShellCallback> cb = new MyShellCallback(errorLog);
    sp<MyResultReceiver> result = new MyResultReceiver();
#if DEBUG
    ALOGD("cmd: Invoking %s in=%d, out=%d, err=%d", cmd, in, out, err);
#endif
    // TODO: block until a result is returned to MyResultReceiver.
    status_t error = IBinder::shellCommand(service, in, out, err, args, cb, result);
    if (error < 0) {
        const char* errstr;
        switch (error) {
            case BAD_TYPE: errstr = "Bad type"; break;
            case FAILED_TRANSACTION: errstr = "Failed transaction"; break;
            case FDS_NOT_ALLOWED: errstr = "File descriptors not allowed"; break;
            case UNEXPECTED_NULL: errstr = "Unexpected null"; break;
            default: errstr = strerror(-error); break;
        }
        if (runMode == RunMode::kStandalone) {
            ALOGW("Failure calling service %.*s: %s (%d)", static_cast<int>(cmd.size()), cmd.data(),
                  errstr, -error);
        }
        outputLog << "cmd: Failure calling service " << cmd << ": " << errstr << " (" << (-error)
                  << ")" << endl;
        return error;
    }
    cb->mActive = false;
    status_t res = result->waitForResult();
#if DEBUG
    ALOGD("result=%d", (int)res);
#endif
    return res;

在该办法中,首要履行的过程有3步:

  1. 获取到cmd后边的第一个参数,本文即package
  2. 根据获取到参数在SystemServer中查找对应的service,package对应的便是PackageManagerService,所以实践上便是获取PackageManagerService
  3. 在获取到对应的service后,调用IBinder::shellcommand办法履行后续操作。

IBinder::shellcommand办法实践上调用的是对应的serviceonShellCommand办法在本文实践便是调用PackageManagerService#onShellCommand办法,该办法源码坐落/frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java。到这里,实践上后续的操作便是在PackageManagerService中完结了。关于在PackageManagerService中的操作,后文持续剖析

4 总结

本文首要简单介绍了xapk包的组成,然后以adb install-multiple为线索,简单剖析了装置xapk的进程,整体来说,便是履行install-create,install-write,install-commit三个指令完结xapk的装置,这三个指令终究都走到了PackageManagerService中。后续的文章中将持续剖析PackageManagerService中的履行进程