1. xapk介绍
xapk和apk相似,是android的一种打包运用程序的办法。运用这种办法打成的包以.xapk结束.能够将.xapk后缀直接改为zip后缀,然后运用解压缩东西解压xapk包。xapk有两种组织办法:
- xapk=中心apk+obb文件夹
- 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装置流程
adb是Android系统供给的调试东西,分为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 client和adb 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,.dm和fsv_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步:
- 获取到
cmd后边的第一个参数,本文即package - 根据获取到参数在
SystemServer中查找对应的service,package对应的便是PackageManagerService,所以实践上便是获取PackageManagerService - 在获取到对应的
service后,调用IBinder::shellcommand办法履行后续操作。
IBinder::shellcommand办法实践上调用的是对应的service的onShellCommand办法在本文实践便是调用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中的履行进程
