Adds atomic install support to adb

This change adds an install-atomic command to adb that is shorthand for
creating an atomic install session and an individual session for each
APK supplied to the command.

Bug: 109941548
Test: run command with multiple APKs, observe atomic install
Change-Id: I2817a1ed2d312925d9c7bd621e6c82670a6275fd
This commit is contained in:
Patrick Baumann 2018-10-09 10:45:03 -07:00 committed by Todd Kennedy
parent d75913a371
commit dc58b0a388
3 changed files with 199 additions and 1 deletions

View file

@ -501,6 +501,197 @@ finalize_session:
return EXIT_FAILURE;
}
int install_multi_package(int argc, const char** argv) {
// Find all APK arguments starting at end.
// All other arguments passed through verbatim.
int first_apk = -1;
for (int i = argc - 1; i >= 0; i--) {
const char* file = argv[i];
if (android::base::EndsWithIgnoreCase(file, ".apk")) {
first_apk = i;
} else {
break;
}
}
if (first_apk == -1) error_exit("need APK file on command line");
if (use_legacy_install()) {
fprintf(stderr, "adb: multi-package install is not supported on this device\n");
return EXIT_FAILURE;
}
std::string install_cmd = "exec:cmd package";
std::string multi_package_cmd =
android::base::StringPrintf("%s install-create --multi-package", install_cmd.c_str());
// Create multi-package install session
std::string error;
int fd = adb_connect(multi_package_cmd, &error);
if (fd < 0) {
fprintf(stderr, "adb: connect error for create multi-package: %s\n", error.c_str());
return EXIT_FAILURE;
}
char buf[BUFSIZ];
read_status_line(fd, buf, sizeof(buf));
adb_close(fd);
int parent_session_id = -1;
if (!strncmp("Success", buf, 7)) {
char* start = strrchr(buf, '[');
char* end = strrchr(buf, ']');
if (start && end) {
*end = '\0';
parent_session_id = strtol(start + 1, nullptr, 10);
}
}
if (parent_session_id < 0) {
fprintf(stderr, "adb: failed to create multi-package session\n");
fputs(buf, stderr);
return EXIT_FAILURE;
}
fprintf(stdout, "Created parent session ID %d.\n", parent_session_id);
std::vector<int> session_ids;
// Valid session, now create the individual sessions and stream the APKs
int success = EXIT_FAILURE;
std::string individual_cmd =
android::base::StringPrintf("%s install-create", install_cmd.c_str());
std::string all_session_ids = "";
for (int i = 1; i < first_apk; i++) {
individual_cmd += " " + escape_arg(argv[i]);
}
std::string cmd = "";
for (int i = first_apk; i < argc; i++) {
// Create individual install session
fd = adb_connect(individual_cmd, &error);
if (fd < 0) {
fprintf(stderr, "adb: connect error for create: %s\n", error.c_str());
goto finalize_multi_package_session;
}
char buf[BUFSIZ];
read_status_line(fd, buf, sizeof(buf));
adb_close(fd);
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 multi-package session\n");
fputs(buf, stderr);
goto finalize_multi_package_session;
}
fprintf(stdout, "Created child session ID %d.\n", session_id);
session_ids.push_back(session_id);
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));
goto finalize_multi_package_session;
}
std::string cmd =
android::base::StringPrintf("%s install-write -S %" PRIu64 " %d %d_%s -",
install_cmd.c_str(), static_cast<uint64_t>(sb.st_size),
session_id, i, android::base::Basename(file).c_str());
int localFd = adb_open(file, O_RDONLY);
if (localFd < 0) {
fprintf(stderr, "adb: failed to open %s: %s\n", file, strerror(errno));
goto finalize_multi_package_session;
}
std::string error;
int remoteFd = adb_connect(cmd, &error);
if (remoteFd < 0) {
fprintf(stderr, "adb: connect error for write: %s\n", error.c_str());
adb_close(localFd);
goto finalize_multi_package_session;
}
copy_to_file(localFd, remoteFd);
read_status_line(remoteFd, buf, sizeof(buf));
adb_close(localFd);
adb_close(remoteFd);
if (strncmp("Success", buf, 7)) {
fprintf(stderr, "adb: failed to write %s\n", file);
fputs(buf, stderr);
goto finalize_multi_package_session;
}
all_session_ids += android::base::StringPrintf(" %d", session_id);
}
cmd = android::base::StringPrintf("%s install-add-session %d%s", install_cmd.c_str(),
parent_session_id, all_session_ids.c_str());
fd = adb_connect(cmd, &error);
if (fd < 0) {
fprintf(stderr, "adb: connect error for create: %s\n", error.c_str());
goto finalize_multi_package_session;
}
read_status_line(fd, buf, sizeof(buf));
adb_close(fd);
if (strncmp("Success", buf, 7)) {
fprintf(stderr, "adb: failed to link sessions (%s)\n", cmd.c_str());
fputs(buf, stderr);
goto finalize_multi_package_session;
}
// no failures means we can proceed with the assumption of success
success = 0;
finalize_multi_package_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 == 0 ? "commit" : "abandon", parent_session_id);
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, buf, sizeof(buf));
adb_close(fd);
if (!strncmp("Success", buf, 7)) {
fputs(buf, stdout);
if (success == 0) {
return 0;
}
} else {
fprintf(stderr, "adb: failed to finalize session\n");
fputs(buf, stderr);
}
// try to abandon all remaining sessions
for (std::size_t i = 0; i < session_ids.size(); i++) {
service = android::base::StringPrintf("%s install-abandon %d", install_cmd.c_str(),
session_ids[i]);
fprintf(stderr, "Attempting to abandon session ID %d\n", session_ids[i]);
fd = adb_connect(service, &error);
if (fd < 0) {
fprintf(stderr, "adb: connect error for finalize: %s\n", error.c_str());
continue;
}
read_status_line(fd, buf, sizeof(buf));
adb_close(fd);
}
return EXIT_FAILURE;
}
int delete_device_file(const std::string& filename) {
std::string cmd = "rm -f " + escape_arg(filename);
return send_shell_command(cmd);

View file

@ -20,6 +20,7 @@
int install_app(int argc, const char** argv);
int install_multiple_app(int argc, const char** argv);
int install_multi_package(int argc, const char** argv);
int uninstall_app(int argc, const char** argv);
int delete_device_file(const std::string& filename);

View file

@ -144,8 +144,11 @@ static void help() {
"\n"
"app installation:\n"
" install [-lrtsdg] [--instant] PACKAGE\n"
" push a single package to the device and install it\n"
" install-multiple [-lrtsdpg] [--instant] PACKAGE...\n"
" push package(s) to the device and install them\n"
" push multiple APKs to the device for a single package and install them\n"
" install-multi-package [-lrtsdpg] [--instant] PACKAGE...\n"
" push one or more packages to the device and install them atomically\n"
" -r: replace existing application\n"
" -t: allow test packages\n"
" -d: allow version code downgrade (debuggable packages only)\n"
@ -1734,6 +1737,9 @@ int adb_commandline(int argc, const char** argv) {
} else if (!strcmp(argv[0], "install-multiple")) {
if (argc < 2) error_exit("install-multiple requires an argument");
return install_multiple_app(argc, argv);
} else if (!strcmp(argv[0], "install-multi-package")) {
if (argc < 3) error_exit("install-multi-package requires an argument");
return install_multi_package(argc, argv);
} else if (!strcmp(argv[0], "uninstall")) {
if (argc < 2) error_exit("uninstall requires an argument");
return uninstall_app(argc, argv);