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
This commit is contained in:
Jeff Sharkey 2014-06-09 17:30:57 -07:00
parent e60eb62384
commit 960df97c23
4 changed files with 197 additions and 158 deletions

View File

@ -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)
{
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;
}
}

View File

@ -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();

View File

@ -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);

View File

@ -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);