summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJeff Sharkey <jsharkey@android.com>2014-06-09 17:30:57 -0700
committerJeff Sharkey <jsharkey@android.com>2014-07-14 10:26:21 -0700
commit960df97c2356f5a804d3ef87fe49f788d7ecdfaf (patch)
tree1c0bb5d596f7bc747b5ee7d76796ddf9c3db8e08
parente60eb623845b6809657b18ea068a0b93f052fa0c (diff)
downloadsystem_core-960df97c2356f5a804d3ef87fe49f788d7ecdfaf.zip
system_core-960df97c2356f5a804d3ef87fe49f788d7ecdfaf.tar.gz
system_core-960df97c2356f5a804d3ef87fe49f788d7ecdfaf.tar.bz2
Add install-multiple to adb.
The new install-multiple command automates creating an install session, streaming multiple files into place, and then committing or destroying the session. This uses the recent "exec" feature to stream APK contents over stdin directly into their final resting place, requiring no extra copies. Blindly pass through command line arguments to "pm" to make adding new flags easier in future. Remove support for verifying APK before sending across wire, since it was reading the entire APK into memory (!) before sending. Also remove encrypted APKs, since they are no longer supported. Drop support for undocumented verification files. Bug: 14975160 Change-Id: I0c538471873061798160e2e47cec4c0424c27361
-rw-r--r--adb/commandline.c275
-rw-r--r--adb/file_sync_client.c65
-rw-r--r--adb/file_sync_service.h2
-rw-r--r--adb/services.c3
4 files changed, 192 insertions, 153 deletions
diff --git a/adb/commandline.c b/adb/commandline.c
index 23722b6..cf02545 100644
--- a/adb/commandline.c
+++ b/adb/commandline.c
@@ -43,6 +43,7 @@ void get_my_path(char *s, size_t maxLen);
int find_sync_dirs(const char *srcarg,
char **android_srcdir_out, char **data_srcdir_out, char **vendor_srcdir_out);
int install_app(transport_type transport, char* serial, int argc, char** argv);
+int install_multiple_app(transport_type transport, char* serial, int argc, char** argv);
int uninstall_app(transport_type transport, char* serial, int argc, char** argv);
static const char *gProductOutPath = NULL;
@@ -151,13 +152,15 @@ void help()
" - remove a specific reversed socket connection\n"
" adb reverse --remove-all - remove all reversed socket connections from device\n"
" adb jdwp - list PIDs of processes hosting a JDWP transport\n"
- " adb install [-l] [-r] [-d] [-s] [--algo <algorithm name> --key <hex-encoded key> --iv <hex-encoded iv>] <file>\n"
+ " adb install [-lrtsd] <file>\n"
+ " adb install-multiple [-lrtsdp] <file...>\n"
" - push this package file to the device and install it\n"
- " ('-l' means forward-lock the app)\n"
- " ('-r' means reinstall the app, keeping its data)\n"
- " ('-d' means allow version code downgrade)\n"
- " ('-s' means install on SD card instead of internal storage)\n"
- " ('--algo', '--key', and '--iv' mean the file is encrypted already)\n"
+ " (-l: forward lock application)\n"
+ " (-r: replace existing application)\n"
+ " (-t: allow test packages)\n"
+ " (-s: install application on sdcard)\n"
+ " (-d: allow version code downgrade)\n"
+ " (-p: partial application install)\n"
" adb uninstall [-k] <package> - remove this app package from the device\n"
" ('-k' means keep the data and cache directories)\n"
" adb bugreport - return all information from the device\n"
@@ -280,6 +283,24 @@ static void read_and_dump(int fd)
}
}
+static void read_status_line(int fd, char* buf, size_t count)
+{
+ count--;
+ while (count > 0) {
+ int len = adb_read(fd, buf, count);
+ if (len == 0) {
+ break;
+ } else if (len < 0) {
+ if (errno == EINTR) continue;
+ break;
+ }
+
+ buf += len;
+ count -= len;
+ }
+ *buf = '\0';
+}
+
static void copy_to_file(int inFd, int outFd) {
const size_t BUFSIZE = 32 * 1024;
char* buf = (char*) malloc(BUFSIZE);
@@ -1576,7 +1597,7 @@ top:
parse_push_pull_args(&argv[1], argc - 1, &lpath, &rpath, &show_progress, &copy_attrs);
if ((lpath != NULL) && (rpath != NULL)) {
- return do_sync_push(lpath, rpath, 0 /* no verify APK */, show_progress);
+ return do_sync_push(lpath, rpath, show_progress);
}
return usage();
@@ -1596,12 +1617,17 @@ top:
return usage();
}
- if(!strcmp(argv[0], "install")) {
+ if (!strcmp(argv[0], "install")) {
if (argc < 2) return usage();
return install_app(ttype, serial, argc, argv);
}
- if(!strcmp(argv[0], "uninstall")) {
+ if (!strcmp(argv[0], "install-multiple")) {
+ if (argc < 2) return usage();
+ return install_multiple_app(ttype, serial, argc, argv);
+ }
+
+ if (!strcmp(argv[0], "uninstall")) {
if (argc < 2) return usage();
return uninstall_app(ttype, serial, argc, argv);
}
@@ -1856,117 +1882,186 @@ static const char* get_basename(const char* filename)
}
}
-static int check_file(const char* filename)
+int install_app(transport_type transport, char* serial, int argc, char** argv)
{
- struct stat st;
+ static const char *const DATA_DEST = "/data/local/tmp/%s";
+ static const char *const SD_DEST = "/sdcard/tmp/%s";
+ const char* where = DATA_DEST;
+ int i;
+ struct stat sb;
- if (filename == NULL) {
- return 0;
+ for (i = 1; i < argc; i++) {
+ if (!strcmp(argv[i], "-s")) {
+ where = SD_DEST;
+ }
}
- if (stat(filename, &st) != 0) {
- fprintf(stderr, "can't find '%s' to install\n", filename);
- return 1;
+ // Find last APK argument.
+ // All other arguments passed through verbatim.
+ int last_apk = -1;
+ for (i = argc - 1; i >= 0; i--) {
+ char* file = argv[i];
+ char* dot = strrchr(file, '.');
+ if (dot && !strcasecmp(dot, ".apk")) {
+ if (stat(file, &sb) == -1 || !S_ISREG(sb.st_mode)) {
+ fprintf(stderr, "Invalid APK file: %s\n", file);
+ return -1;
+ }
+
+ last_apk = i;
+ break;
+ }
}
- if (!S_ISREG(st.st_mode)) {
- fprintf(stderr, "can't install '%s' because it's not a file\n", filename);
- return 1;
+ if (last_apk == -1) {
+ fprintf(stderr, "Missing APK file\n");
+ return -1;
}
- return 0;
+ char* apk_file = argv[last_apk];
+ char apk_dest[PATH_MAX];
+ snprintf(apk_dest, sizeof apk_dest, where, get_basename(apk_file));
+ int err = do_sync_push(apk_file, apk_dest, 0 /* no show progress */);
+ if (err) {
+ goto cleanup_apk;
+ } else {
+ argv[last_apk] = apk_dest; /* destination name, not source location */
+ }
+
+ pm_command(transport, serial, argc, argv);
+
+cleanup_apk:
+ delete_file(transport, serial, apk_dest);
+ return err;
}
-int install_app(transport_type transport, char* serial, int argc, char** argv)
+int install_multiple_app(transport_type transport, char* serial, int argc, char** argv)
{
- static const char *const DATA_DEST = "/data/local/tmp/%s";
- static const char *const SD_DEST = "/sdcard/tmp/%s";
- const char* where = DATA_DEST;
- char apk_dest[PATH_MAX];
- char verification_dest[PATH_MAX];
- char* apk_file;
- char* verification_file = NULL;
- int file_arg = -1;
- int err;
+ char buf[1024];
int i;
- int verify_apk = 1;
+ struct stat sb;
+ unsigned long long total_size = 0;
+
+ // Find all APK arguments starting at end.
+ // All other arguments passed through verbatim.
+ int first_apk = -1;
+ for (i = argc - 1; i >= 0; i--) {
+ char* file = argv[i];
+ char* dot = strrchr(file, '.');
+ if (dot && !strcasecmp(dot, ".apk")) {
+ if (stat(file, &sb) == -1 || !S_ISREG(sb.st_mode)) {
+ fprintf(stderr, "Invalid APK file: %s\n", file);
+ return -1;
+ }
- for (i = 1; i < argc; i++) {
- if (*argv[i] != '-') {
- file_arg = i;
+ total_size += sb.st_size;
+ first_apk = i;
+ } else {
break;
- } else if (!strcmp(argv[i], "-i")) {
- // Skip the installer package name.
- i++;
- } else if (!strcmp(argv[i], "-s")) {
- where = SD_DEST;
- } else if (!strcmp(argv[i], "--algo")) {
- verify_apk = 0;
- i++;
- } else if (!strcmp(argv[i], "--iv")) {
- verify_apk = 0;
- i++;
- } else if (!strcmp(argv[i], "--key")) {
- verify_apk = 0;
- i++;
- } else if (!strcmp(argv[i], "--abi")) {
- i++;
- }
- }
-
- if (file_arg < 0) {
- fprintf(stderr, "can't find filename in arguments\n");
- return 1;
- } else if (file_arg + 2 < argc) {
- fprintf(stderr, "too many files specified; only takes APK file and verifier file\n");
+ }
+ }
+
+ if (first_apk == -1) {
+ fprintf(stderr, "Missing APK file\n");
return 1;
}
- apk_file = argv[file_arg];
+ snprintf(buf, sizeof(buf), "exec:pm install-create -S %lld", total_size);
+ for (i = 1; i < first_apk; i++) {
+ char *quoted = escape_arg(argv[i]);
+ strncat(buf, " ", sizeof(buf) - 1);
+ strncat(buf, quoted, sizeof(buf) - 1);
+ free(quoted);
+ }
- if (file_arg != argc - 1) {
- verification_file = argv[file_arg + 1];
+ // Create install session
+ int fd = adb_connect(buf);
+ if (fd < 0) {
+ fprintf(stderr, "Connect error for create: %s\n", adb_error());
+ return -1;
}
+ read_status_line(fd, buf, sizeof(buf));
+ adb_close(fd);
- if (check_file(apk_file) || check_file(verification_file)) {
- return 1;
+ 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, NULL, 10);
+ }
+ }
+ if (session_id < 0) {
+ fprintf(stderr, "Failed to create session\n");
+ fprintf(stderr, buf);
+ return -1;
}
- snprintf(apk_dest, sizeof apk_dest, where, get_basename(apk_file));
- if (verification_file != NULL) {
- snprintf(verification_dest, sizeof(verification_dest), where, get_basename(verification_file));
+ // Valid session, now stream the APKs
+ int success = 1;
+ for (i = first_apk; i < argc; i++) {
+ char* file = argv[i];
+ if (stat(file, &sb) == -1) {
+ fprintf(stderr, "Failed to stat %s\n", file);
+ success = 0;
+ goto finalize_session;
+ }
- if (!strcmp(apk_dest, verification_dest)) {
- fprintf(stderr, "APK and verification file can't have the same name\n");
- return 1;
+ snprintf(buf, sizeof(buf), "exec:pm install-write -S %lld %d %d_%s -",
+ sb.st_size, session_id, i, get_basename(file));
+
+ int localFd = adb_open(file, O_RDONLY);
+ if (localFd < 0) {
+ fprintf(stderr, "Failed to open %s: %s\n", file, adb_error());
+ success = 0;
+ goto finalize_session;
}
- }
- err = do_sync_push(apk_file, apk_dest, verify_apk, 0 /* no show progress */);
- if (err) {
- goto cleanup_apk;
- } else {
- argv[file_arg] = apk_dest; /* destination name, not source location */
- }
+ int remoteFd = adb_connect(buf);
+ if (remoteFd < 0) {
+ fprintf(stderr, "Connect error for write: %s\n", adb_error());
+ adb_close(localFd);
+ success = 0;
+ goto finalize_session;
+ }
- if (verification_file != NULL) {
- err = do_sync_push(verification_file, verification_dest, 0 /* no verify APK */,
- 0 /* no show progress */);
- if (err) {
- goto cleanup_apk;
- } else {
- argv[file_arg + 1] = verification_dest; /* destination name, not source location */
+ 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, "Failed to write %s\n", file);
+ fprintf(stderr, buf);
+ success = 0;
+ goto finalize_session;
}
}
- pm_command(transport, serial, argc, argv);
-
-cleanup_apk:
- if (verification_file != NULL) {
- delete_file(transport, serial, verification_dest);
+finalize_session:
+ // Commit session if we streamed everything okay; otherwise destroy
+ if (success) {
+ snprintf(buf, sizeof(buf), "exec:pm install-commit %d", session_id);
+ } else {
+ snprintf(buf, sizeof(buf), "exec:pm install-destroy %d", session_id);
}
- delete_file(transport, serial, apk_dest);
+ fd = adb_connect(buf);
+ if (fd < 0) {
+ fprintf(stderr, "Connect error for finalize: %s\n", adb_error());
+ return -1;
+ }
+ read_status_line(fd, buf, sizeof(buf));
+ adb_close(fd);
- return err;
+ if (!strncmp("Success", buf, 7)) {
+ fprintf(stderr, buf);
+ return 0;
+ } else {
+ fprintf(stderr, "Failed to finalize session\n");
+ fprintf(stderr, buf);
+ return -1;
+ }
}
diff --git a/adb/file_sync_client.c b/adb/file_sync_client.c
index c1ab808..ad59e81 100644
--- a/adb/file_sync_client.c
+++ b/adb/file_sync_client.c
@@ -335,7 +335,7 @@ static int write_data_link(int fd, const char *path, syncsendbuf *sbuf)
#endif
static int sync_send(int fd, const char *lpath, const char *rpath,
- unsigned mtime, mode_t mode, int verifyApk, int show_progress)
+ unsigned mtime, mode_t mode, int show_progress)
{
syncmsg msg;
int len, r;
@@ -350,63 +350,6 @@ static int sync_send(int fd, const char *lpath, const char *rpath,
snprintf(tmp, sizeof(tmp), ",%d", mode);
r = strlen(tmp);
- if (verifyApk) {
- int lfd;
- zipfile_t zip;
- zipentry_t entry;
- int amt;
-
- // if we are transferring an APK file, then sanity check to make sure
- // we have a real zip file that contains an AndroidManifest.xml
- // this requires that we read the entire file into memory.
- lfd = adb_open(lpath, O_RDONLY);
- if(lfd < 0) {
- fprintf(stderr,"cannot open '%s': %s\n", lpath, strerror(errno));
- return -1;
- }
-
- size = adb_lseek(lfd, 0, SEEK_END);
- if (size == -1 || -1 == adb_lseek(lfd, 0, SEEK_SET)) {
- fprintf(stderr, "error seeking in file '%s'\n", lpath);
- adb_close(lfd);
- return 1;
- }
-
- file_buffer = (char *)malloc(size);
- if (file_buffer == NULL) {
- fprintf(stderr, "could not allocate buffer for '%s'\n",
- lpath);
- adb_close(lfd);
- return 1;
- }
- amt = adb_read(lfd, file_buffer, size);
- if (amt != size) {
- fprintf(stderr, "error reading from file: '%s'\n", lpath);
- adb_close(lfd);
- free(file_buffer);
- return 1;
- }
-
- adb_close(lfd);
-
- zip = init_zipfile(file_buffer, size);
- if (zip == NULL) {
- fprintf(stderr, "file '%s' is not a valid zip file\n",
- lpath);
- free(file_buffer);
- return 1;
- }
-
- entry = lookup_zipentry(zip, "AndroidManifest.xml");
- release_zipfile(zip);
- if (entry == NULL) {
- fprintf(stderr, "file '%s' does not contain AndroidManifest.xml\n",
- lpath);
- free(file_buffer);
- return 1;
- }
- }
-
msg.req.id = ID_SEND;
msg.req.namelen = htoll(len + r);
@@ -789,7 +732,7 @@ static int copy_local_dir_remote(int fd, const char *lpath, const char *rpath, i
if(ci->flag == 0) {
fprintf(stderr,"%spush: %s -> %s\n", listonly ? "would " : "", ci->src, ci->dst);
if(!listonly &&
- sync_send(fd, ci->src, ci->dst, ci->time, ci->mode, 0 /* no verify APK */,
+ sync_send(fd, ci->src, ci->dst, ci->time, ci->mode,
0 /* no show progress */)) {
return 1;
}
@@ -808,7 +751,7 @@ static int copy_local_dir_remote(int fd, const char *lpath, const char *rpath, i
}
-int do_sync_push(const char *lpath, const char *rpath, int verifyApk, int show_progress)
+int do_sync_push(const char *lpath, const char *rpath, int show_progress)
{
struct stat st;
unsigned mode;
@@ -855,7 +798,7 @@ int do_sync_push(const char *lpath, const char *rpath, int verifyApk, int show_p
rpath = tmp;
}
BEGIN();
- if(sync_send(fd, lpath, rpath, st.st_mtime, st.st_mode, verifyApk, show_progress)) {
+ if(sync_send(fd, lpath, rpath, st.st_mtime, st.st_mode, show_progress)) {
return 1;
} else {
END();
diff --git a/adb/file_sync_service.h b/adb/file_sync_service.h
index 8ea239e..5dd2e80 100644
--- a/adb/file_sync_service.h
+++ b/adb/file_sync_service.h
@@ -78,7 +78,7 @@ typedef union {
void file_sync_service(int fd, void *cookie);
int do_sync_ls(const char *path);
-int do_sync_push(const char *lpath, const char *rpath, int verifyApk, int show_progress);
+int do_sync_push(const char *lpath, const char *rpath, int show_progress);
int do_sync_sync(const char *lpath, const char *rpath, int listonly);
int do_sync_pull(const char *rpath, const char *lpath, int show_progress, int pullTime);
diff --git a/adb/services.c b/adb/services.c
index e48e460..bff935c 100644
--- a/adb/services.c
+++ b/adb/services.c
@@ -283,9 +283,10 @@ static int create_subproc_raw(const char *cmd, const char *arg0, const char *arg
adb_close(sv[0]);
init_subproc_child();
- // Only hook up stdin/stdout; drop stderr
dup2(sv[1], STDIN_FILENO);
dup2(sv[1], STDOUT_FILENO);
+ dup2(sv[1], STDERR_FILENO);
+
adb_close(sv[1]);
execl(cmd, cmd, arg0, arg1, NULL);