diff options
author | Doug Zongker <dougz@google.com> | 2014-06-26 15:35:36 -0700 |
---|---|---|
committer | Kenny Root <kroot@google.com> | 2014-09-16 10:27:38 -0700 |
commit | 8e49ae4bb8c7a58d03b9907e0b14a7305a4af526 (patch) | |
tree | d735bb753abb08dc03affd2df9a2e0f87ae804a3 /adb | |
parent | 2c805883408339823d3f3ca23aceb08a1783ebbd (diff) | |
download | system_core-8e49ae4bb8c7a58d03b9907e0b14a7305a4af526.zip system_core-8e49ae4bb8c7a58d03b9907e0b14a7305a4af526.tar.gz system_core-8e49ae4bb8c7a58d03b9907e0b14a7305a4af526.tar.bz2 |
add sideload-host mode to adb
The sideload-host mode turns the host into a server capable of sending
the device various pieces of the file on request, rather than
downloading it all in one transfer. It's used to support sideloading
OTA packages to devices without the need for them to hold the whole
package in RAM.
If the connected device doesn't support sideload-host mode, we fall
back to the older sideload connection.
(cherry-picked from commit 71fe584a1a7256c057267ae46a35f7c74d1d549e)
Change-Id: I5adaedd8243dc3b76414bba0149879ca2bbf35fa
Diffstat (limited to 'adb')
-rw-r--r-- | adb/adb.h | 2 | ||||
-rw-r--r-- | adb/adb_client.c | 3 | ||||
-rw-r--r-- | adb/commandline.c | 111 |
3 files changed, 113 insertions, 3 deletions
@@ -36,7 +36,7 @@ #define ADB_VERSION_MAJOR 1 // Used for help/version information #define ADB_VERSION_MINOR 0 // Used for help/version information -#define ADB_SERVER_VERSION 31 // Increment this when we want to force users to start a new adb server +#define ADB_SERVER_VERSION 32 // Increment this when we want to force users to start a new adb server typedef struct amessage amessage; typedef struct apacket apacket; diff --git a/adb/adb_client.c b/adb/adb_client.c index 1e47486..eb1720d 100644 --- a/adb/adb_client.c +++ b/adb/adb_client.c @@ -279,7 +279,7 @@ int adb_connect(const char *service) fd = _adb_connect(service); if(fd == -1) { - fprintf(stderr,"error: %s\n", __adb_error); + D("_adb_connect error: %s\n", __adb_error); } else if(fd == -2) { fprintf(stderr,"** daemon still not running\n"); } @@ -296,6 +296,7 @@ int adb_command(const char *service) { int fd = adb_connect(service); if(fd < 0) { + fprintf(stderr, "error: %s\n", adb_error()); return -1; } diff --git a/adb/commandline.c b/adb/commandline.c index 356c0db..cee6c8c 100644 --- a/adb/commandline.c +++ b/adb/commandline.c @@ -500,6 +500,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; + while (true) { + 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), 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]; @@ -1290,7 +1399,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; |