diff options
Diffstat (limited to 'adb/commandline.c')
-rw-r--r-- | adb/commandline.c | 489 |
1 files changed, 349 insertions, 140 deletions
diff --git a/adb/commandline.c b/adb/commandline.c index 1ba6049..e1ff856 100644 --- a/adb/commandline.c +++ b/adb/commandline.c @@ -41,8 +41,9 @@ static int do_cmd(transport_type ttype, char* serial, char *cmd, ...); 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 **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,12 +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] [-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" - " ('-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" @@ -195,7 +199,7 @@ void help() " adb get-serialno - prints: <serial-number>\n" " adb get-devpath - prints: <device-path>\n" " adb status-window - continuously print device status for a specified device\n" - " adb remount - remounts the /system partition on the device read-write\n" + " adb remount - remounts the /system and /vendor (if present) partitions on the device read-write\n" " adb reboot [bootloader|recovery] - reboots the device, optionally into the bootloader or recovery program\n" " adb reboot-bootloader - reboots the device into the bootloader\n" " adb root - restarts the adbd daemon with root permissions\n" @@ -211,9 +215,9 @@ void help() "adb sync notes: adb sync [ <directory> ]\n" " <localdir> can be interpreted in several ways:\n" "\n" - " - If <directory> is not specified, both /system and /data partitions will be updated.\n" + " - If <directory> is not specified, /system, /vendor (if present), and /data partitions will be updated.\n" "\n" - " - If it is \"system\" or \"data\", only the corresponding partition\n" + " - If it is \"system\", \"vendor\" or \"data\", only the corresponding partition\n" " is updated.\n" "\n" "environmental variables:\n" @@ -279,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); @@ -500,6 +522,115 @@ int adb_download(const char *service, const char *fn, unsigned progress) return status; } +#define SIDELOAD_HOST_BLOCK_SIZE (CHUNK_SIZE) + +/* + * The sideload-host protocol serves the data in a file (given on the + * command line) to the client, using a simple protocol: + * + * - The connect message includes the total number of bytes in the + * file and a block size chosen by us. + * + * - The other side sends the desired block number as eight decimal + * digits (eg "00000023" for block 23). Blocks are numbered from + * zero. + * + * - We send back the data of the requested block. The last block is + * likely to be partial; when the last block is requested we only + * send the part of the block that exists, it's not padded up to the + * block size. + * + * - When the other side sends "DONEDONE" instead of a block number, + * we hang up. + */ +int adb_sideload_host(const char* fn) { + uint8_t* data; + unsigned sz; + size_t xfer = 0; + int status; + + printf("loading: '%s'", fn); + fflush(stdout); + data = load_file(fn, &sz); + if (data == 0) { + printf("\n"); + fprintf(stderr, "* cannot read '%s' *\n", fn); + return -1; + } + + char buf[100]; + sprintf(buf, "sideload-host:%d:%d", sz, SIDELOAD_HOST_BLOCK_SIZE); + int fd = adb_connect(buf); + if (fd < 0) { + // Try falling back to the older sideload method. Maybe this + // is an older device that doesn't support sideload-host. + printf("\n"); + status = adb_download_buffer("sideload", fn, data, sz, 1); + goto done; + } + + int opt = SIDELOAD_HOST_BLOCK_SIZE; + opt = setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (const void *) &opt, sizeof(opt)); + + int last_percent = -1; + for (;;) { + if (readx(fd, buf, 8)) { + fprintf(stderr, "* failed to read command: %s\n", adb_error()); + status = -1; + goto done; + } + + if (strncmp("DONEDONE", buf, 8) == 0) { + status = 0; + break; + } + + buf[8] = '\0'; + int block = strtol(buf, NULL, 10); + + size_t offset = block * SIDELOAD_HOST_BLOCK_SIZE; + if (offset >= sz) { + fprintf(stderr, "* attempt to read past end: %s\n", adb_error()); + status = -1; + goto done; + } + uint8_t* start = data + offset; + size_t offset_end = offset + SIDELOAD_HOST_BLOCK_SIZE; + size_t to_write = SIDELOAD_HOST_BLOCK_SIZE; + if (offset_end > sz) { + to_write = sz - offset; + } + + if(writex(fd, start, to_write)) { + adb_status(fd); + fprintf(stderr,"* failed to write data '%s' *\n", adb_error()); + status = -1; + goto done; + } + xfer += to_write; + + // For normal OTA packages, we expect to transfer every byte + // twice, plus a bit of overhead (one read during + // verification, one read of each byte for installation, plus + // extra access to things like the zip central directory). + // This estimate of the completion becomes 100% when we've + // transferred ~2.13 (=100/47) times the package size. + int percent = (int)(xfer * 47LL / (sz ? sz : 1)); + if (percent != last_percent) { + printf("\rserving: '%s' (~%d%%) ", fn, percent); + fflush(stdout); + last_percent = percent; + } + } + + printf("\rTotal xfer: %.2fx%*s\n", (double)xfer / (sz ? sz : 1), (int)strlen(fn)+10, ""); + + done: + if (fd >= 0) adb_close(fd); + free(data); + return status; +} + static void status_window(transport_type ttype, const char* serial) { char command[4096]; @@ -544,39 +675,40 @@ static void status_window(transport_type ttype, const char* serial) } } -/** duplicate string and quote all \ " ( ) chars + space character. */ -static char * -dupAndQuote(const char *s) +/** Duplicate and escape given argument. */ +static char *escape_arg(const char *s) { const char *ts; size_t alloc_len; char *ret; char *dest; - ts = s; - alloc_len = 0; - - for( ;*ts != '\0'; ts++) { + for (ts = s; *ts != '\0'; ts++) { alloc_len++; if (*ts == ' ' || *ts == '"' || *ts == '\\' || *ts == '(' || *ts == ')') { alloc_len++; } } - ret = (char *)malloc(alloc_len + 1); + if (alloc_len == 0) { + // Preserve empty arguments + ret = (char *) malloc(3); + ret[0] = '\"'; + ret[1] = '\"'; + ret[2] = '\0'; + return ret; + } - ts = s; + ret = (char *) malloc(alloc_len + 1); dest = ret; - for ( ;*ts != '\0'; ts++) { + for (ts = s; *ts != '\0'; ts++) { if (*ts == ' ' || *ts == '"' || *ts == '\\' || *ts == '(' || *ts == ')') { *dest++ = '\\'; } - *dest++ = *ts; } - *dest++ = '\0'; return ret; @@ -683,30 +815,24 @@ static int logcat(transport_type transport, char* serial, int argc, char **argv) char buf[4096]; char *log_tags; - char *quoted_log_tags; + char *quoted; log_tags = getenv("ANDROID_LOG_TAGS"); - quoted_log_tags = dupAndQuote(log_tags == NULL ? "" : log_tags); - + quoted = escape_arg(log_tags == NULL ? "" : log_tags); snprintf(buf, sizeof(buf), - "shell:export ANDROID_LOG_TAGS=\"\%s\" ; exec logcat", - quoted_log_tags); - - free(quoted_log_tags); + "shell:export ANDROID_LOG_TAGS=\"%s\"; exec logcat", quoted); + free(quoted); - if (!strcmp(argv[0],"longcat")) { - strncat(buf, " -v long", sizeof(buf)-1); + if (!strcmp(argv[0], "longcat")) { + strncat(buf, " -v long", sizeof(buf) - 1); } argc -= 1; argv += 1; while(argc-- > 0) { - char *quoted; - - quoted = dupAndQuote (*argv++); - - strncat(buf, " ", sizeof(buf)-1); - strncat(buf, quoted, sizeof(buf)-1); + quoted = escape_arg(*argv++); + strncat(buf, " ", sizeof(buf) - 1); + strncat(buf, quoted, sizeof(buf) - 1); free(quoted); } @@ -1218,7 +1344,7 @@ top: argc -= 2; argv += 2; while (argc-- > 0) { - char *quoted = dupAndQuote(*argv++); + char *quoted = escape_arg(*argv++); strncat(buf, " ", sizeof(buf) - 1); strncat(buf, quoted, sizeof(buf) - 1); free(quoted); @@ -1261,7 +1387,7 @@ top: argc -= 2; argv += 2; while (argc-- > 0) { - char *quoted = dupAndQuote(*argv++); + char *quoted = escape_arg(*argv++); strncat(buf, " ", sizeof(buf) - 1); strncat(buf, quoted, sizeof(buf) - 1); free(quoted); @@ -1295,7 +1421,7 @@ top: if(!strcmp(argv[0], "sideload")) { if(argc != 2) return usage(); - if(adb_download("sideload", argv[1], 1)) { + if (adb_sideload_host(argv[1])) { return 1; } else { return 0; @@ -1471,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(); @@ -1491,18 +1617,23 @@ 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); } if(!strcmp(argv[0], "sync")) { - char *srcarg, *android_srcpath, *data_srcpath; + char *srcarg, *android_srcpath, *data_srcpath, *vendor_srcpath; int listonly = 0; int ret; @@ -1522,15 +1653,18 @@ top: } else { return usage(); } - ret = find_sync_dirs(srcarg, &android_srcpath, &data_srcpath); + ret = find_sync_dirs(srcarg, &android_srcpath, &data_srcpath, &vendor_srcpath); if(ret != 0) return usage(); if(android_srcpath != NULL) ret = do_sync_sync(android_srcpath, "/system", listonly); + if(ret == 0 && vendor_srcpath != NULL) + ret = do_sync_sync(vendor_srcpath, "/vendor", listonly); if(ret == 0 && data_srcpath != NULL) ret = do_sync_sync(data_srcpath, "/data", listonly); free(android_srcpath); + free(vendor_srcpath); free(data_srcpath); return ret; } @@ -1641,25 +1775,30 @@ static int do_cmd(transport_type ttype, char* serial, char *cmd, ...) } int find_sync_dirs(const char *srcarg, - char **android_srcdir_out, char **data_srcdir_out) + char **android_srcdir_out, char **data_srcdir_out, char **vendor_srcdir_out) { - char *android_srcdir, *data_srcdir; + char *android_srcdir = NULL, *data_srcdir = NULL, *vendor_srcdir = NULL; + struct stat st; if(srcarg == NULL) { android_srcdir = product_file("system"); data_srcdir = product_file("data"); + vendor_srcdir = product_file("vendor"); + /* Check if vendor partition exists */ + if (lstat(vendor_srcdir, &st) || !S_ISDIR(st.st_mode)) + vendor_srcdir = NULL; } else { /* srcarg may be "data", "system" or NULL. * if srcarg is NULL, then both data and system are synced */ if(strcmp(srcarg, "system") == 0) { android_srcdir = product_file("system"); - data_srcdir = NULL; } else if(strcmp(srcarg, "data") == 0) { - android_srcdir = NULL; data_srcdir = product_file("data"); + } else if(strcmp(srcarg, "vendor") == 0) { + vendor_srcdir = product_file("vendor"); } else { - /* It's not "system" or "data". + /* It's not "system", "vendor", or "data". */ return 1; } @@ -1670,11 +1809,15 @@ int find_sync_dirs(const char *srcarg, else free(android_srcdir); - if(data_srcdir_out != NULL) - *data_srcdir_out = data_srcdir; + if(vendor_srcdir_out != NULL) + *vendor_srcdir_out = vendor_srcdir; else - free(data_srcdir); + free(vendor_srcdir); + if(data_srcdir_out != NULL) + *data_srcdir_out = data_srcdir; + else + free(data_srcdir); return 0; } @@ -1686,12 +1829,9 @@ static int pm_command(transport_type transport, char* serial, snprintf(buf, sizeof(buf), "shell:pm"); while(argc-- > 0) { - char *quoted; - - quoted = dupAndQuote(*argv++); - - strncat(buf, " ", sizeof(buf)-1); - strncat(buf, quoted, sizeof(buf)-1); + char *quoted = escape_arg(*argv++); + strncat(buf, " ", sizeof(buf) - 1); + strncat(buf, quoted, sizeof(buf) - 1); free(quoted); } @@ -1723,7 +1863,7 @@ static int delete_file(transport_type transport, char* serial, char* filename) char* quoted; snprintf(buf, sizeof(buf), "shell:rm "); - quoted = dupAndQuote(filename); + quoted = escape_arg(filename); strncat(buf, quoted, sizeof(buf)-1); free(quoted); @@ -1742,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"); + fputs(buf, stderr); + 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 -", + (long long int) 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); + fputs(buf, stderr); + 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 abandon + if (success) { + snprintf(buf, sizeof(buf), "exec:pm install-commit %d", session_id); + } else { + snprintf(buf, sizeof(buf), "exec:pm install-abandon %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)) { + fputs(buf, stderr); + return 0; + } else { + fprintf(stderr, "Failed to finalize session\n"); + fputs(buf, stderr); + return -1; + } } |