add "adb sideload" and sideload connection state

Recovery will soon support a minimal implementation of adbd which will
do nothing but accept downloads from the "adb sideload" command and
install them.  This is the client side command (mostly resurrected out
of the old circa-2007 "adb recover" command) and the new connection
state.

Change-Id: I4f67b63f1b3b38d28c285d1278d46782679762a2
diff --git a/adb/adb.c b/adb/adb.c
index e35c334..6dbd562 100644
--- a/adb/adb.c
+++ b/adb/adb.c
@@ -299,6 +299,13 @@
         return;
     }
 
+    if(!strcmp(type, "sideload")) {
+        D("setting connection_state to CS_SIDELOAD\n");
+        t->connection_state = CS_SIDELOAD;
+        update_transports();
+        return;
+    }
+
     t->connection_state = CS_HOST;
 }
 
diff --git a/adb/adb.h b/adb/adb.h
index ac31f11..f7667c2 100644
--- a/adb/adb.h
+++ b/adb/adb.h
@@ -434,6 +434,7 @@
 #define CS_HOST       3
 #define CS_RECOVERY   4
 #define CS_NOPERM     5 /* Insufficient permissions to communicate with the device */
+#define CS_SIDELOAD   6
 
 extern int HOST;
 extern int SHELL_EXIT_NOTIFY_FD;
diff --git a/adb/commandline.c b/adb/commandline.c
index ffce883..5b2aa88 100644
--- a/adb/commandline.c
+++ b/adb/commandline.c
@@ -367,6 +367,83 @@
     }
 }
 
+int adb_download_buffer(const char *service, const void* data, int sz,
+                        unsigned progress)
+{
+    char buf[4096];
+    unsigned total;
+    int fd;
+    const unsigned char *ptr;
+
+    sprintf(buf,"%s:%d", service, sz);
+    fd = adb_connect(buf);
+    if(fd < 0) {
+        fprintf(stderr,"error: %s\n", adb_error());
+        return -1;
+    }
+
+    int opt = CHUNK_SIZE;
+    opt = setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &opt, sizeof(opt));
+
+    total = sz;
+    ptr = data;
+
+    if(progress) {
+        char *x = strrchr(service, ':');
+        if(x) service = x + 1;
+    }
+
+    while(sz > 0) {
+        unsigned xfer = (sz > CHUNK_SIZE) ? CHUNK_SIZE : sz;
+        if(writex(fd, ptr, xfer)) {
+            adb_status(fd);
+            fprintf(stderr,"* failed to write data '%s' *\n", adb_error());
+            return -1;
+        }
+        sz -= xfer;
+        ptr += xfer;
+        if(progress) {
+            printf("sending: '%s' %4d%%    \r", service, (int)(100LL - ((100LL * sz) / (total))));
+            fflush(stdout);
+        }
+    }
+    if(progress) {
+        printf("\n");
+    }
+
+    if(readx(fd, buf, 4)){
+        fprintf(stderr,"* error reading response *\n");
+        adb_close(fd);
+        return -1;
+    }
+    if(memcmp(buf, "OKAY", 4)) {
+        buf[4] = 0;
+        fprintf(stderr,"* error response '%s' *\n", buf);
+        adb_close(fd);
+        return -1;
+    }
+
+    adb_close(fd);
+    return 0;
+}
+
+
+int adb_download(const char *service, const char *fn, unsigned progress)
+{
+    void *data;
+    unsigned sz;
+
+    data = load_file(fn, &sz);
+    if(data == 0) {
+        fprintf(stderr,"* cannot read '%s' *\n", service);
+        return -1;
+    }
+
+    int status = adb_download_buffer(service, data, sz, progress);
+    free(data);
+    return status;
+}
+
 static void status_window(transport_type ttype, const char* serial)
 {
     char command[4096];
@@ -1063,6 +1140,15 @@
         return 0;
     }
 
+    if(!strcmp(argv[0], "sideload")) {
+        if(argc != 2) return usage();
+        if(adb_download("sideload", argv[1], 1)) {
+            return 1;
+        } else {
+            return 0;
+        }
+    }
+
     if(!strcmp(argv[0], "remount") || !strcmp(argv[0], "reboot")
             || !strcmp(argv[0], "reboot-bootloader")
             || !strcmp(argv[0], "tcpip") || !strcmp(argv[0], "usb")
diff --git a/adb/transport.c b/adb/transport.c
index 83a349a..2f7bd27 100644
--- a/adb/transport.c
+++ b/adb/transport.c
@@ -831,6 +831,7 @@
     case CS_DEVICE: return "device";
     case CS_HOST: return "host";
     case CS_RECOVERY: return "recovery";
+    case CS_SIDELOAD: return "sideload";
     case CS_NOPERM: return "no permissions";
     default: return "unknown";
     }