diff --git a/adb/commandline.c b/adb/commandline.c index 23722b6d3..cf02545b1 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 --key --iv ] \n" + " adb install [-lrtsd] \n" + " adb install-multiple [-lrtsdp] \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] - 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, ©_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) -{ - struct stat st; - - if (filename == NULL) { - return 0; - } - - if (stat(filename, &st) != 0) { - fprintf(stderr, "can't find '%s' to install\n", filename); - return 1; - } - - if (!S_ISREG(st.st_mode)) { - fprintf(stderr, "can't install '%s' because it's not a file\n", filename); - return 1; - } - - return 0; -} - int install_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; int i; - int verify_apk = 1; + struct stat sb; for (i = 1; i < argc; i++) { - if (*argv[i] != '-') { - file_arg = i; - break; - } else if (!strcmp(argv[i], "-i")) { - // Skip the installer package name. - i++; - } else if (!strcmp(argv[i], "-s")) { + 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"); - 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; + } } - apk_file = argv[file_arg]; - - if (file_arg != argc - 1) { - verification_file = argv[file_arg + 1]; - } - - if (check_file(apk_file) || check_file(verification_file)) { - return 1; + if (last_apk == -1) { + fprintf(stderr, "Missing APK file\n"); + return -1; } + char* apk_file = argv[last_apk]; + char apk_dest[PATH_MAX]; 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)); - - if (!strcmp(apk_dest, verification_dest)) { - fprintf(stderr, "APK and verification file can't have the same name\n"); - return 1; - } - } - - err = do_sync_push(apk_file, apk_dest, verify_apk, 0 /* no show progress */); + int err = do_sync_push(apk_file, apk_dest, 0 /* no show progress */); if (err) { goto cleanup_apk; } else { - argv[file_arg] = apk_dest; /* destination name, not source location */ - } - - 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 */ - } + argv[last_apk] = apk_dest; /* destination name, not source location */ } pm_command(transport, serial, argc, argv); cleanup_apk: - if (verification_file != NULL) { - delete_file(transport, serial, verification_dest); - } - delete_file(transport, serial, apk_dest); - return err; } + +int install_multiple_app(transport_type transport, char* serial, int argc, char** argv) +{ + char buf[1024]; + int i; + 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; + } + + total_size += sb.st_size; + first_apk = i; + } else { + break; + } + } + + if (first_apk == -1) { + fprintf(stderr, "Missing APK file\n"); + return 1; + } + + 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); + } + + // 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); + + 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; + } + + // 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; + } + + 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; + } + + 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; + } + + 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; + } + } + +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); + } + + 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); + + 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 c1ab80855..ad59e817d 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 8ea239ec0..5dd2e8041 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 e48e4603e..bff935cc5 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);