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
后台服务。其间Hos
t端又分为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
中的履行进程