adb: Another attempted workaround for the adb disconnect problem.

For adb shell commands (as well as shell based commands like logcat and bugreport) we now
batch USB writes into 4K chunks instead of sending small packets of data as they come in.
A timeout prevents us from blocking for more than 100ms when our 4K buffer is partially full.

Signed-off-by: Mike Lockwood <lockwood@android.com>
diff --git a/adb/services.c b/adb/services.c
index 0a5edcf..78d092b 100644
--- a/adb/services.c
+++ b/adb/services.c
@@ -31,6 +31,8 @@
 #    include <netinet/in.h>
 #    include <netdb.h>
 #  endif
+#else
+#include <sys/poll.h>
 #endif
 
 typedef struct stinfo stinfo;
@@ -196,12 +198,9 @@
     return s[0];
 }
 
+#if !ADB_HOST
 static int create_subprocess(const char *cmd, const char *arg0, const char *arg1)
 {
-#ifdef HAVE_WIN32_PROC
-	fprintf(stderr, "error: create_subprocess not implemented on Win32 (%s %s %s)\n", cmd, arg0, arg1);
-	return -1;
-#else /* !HAVE_WIN32_PROC */
     char *devname;
     int ptm;
     pid_t pid;
@@ -244,7 +243,6 @@
                 cmd, strerror(errno), errno);
         exit(-1);
     } else {
-#if !ADB_HOST
         // set child's OOM adjustment to zero
         char text[64];
         snprintf(text, sizeof text, "/proc/%d/oom_adj", pid);
@@ -255,11 +253,11 @@
         } else {
            D("adb: unable to open %s\n", text);
         }
-#endif
+
         return ptm;
     }
-#endif /* !HAVE_WIN32_PROC */
 }
+#endif /* !ADB_HOST */
 
 #if ADB_HOST
 #define SHELL_COMMAND "/bin/sh"
@@ -267,6 +265,76 @@
 #define SHELL_COMMAND "/system/bin/sh"
 #endif
 
+#if !ADB_HOST
+static void shell_service(int s, void *command)
+{
+    char    buffer[MAX_PAYLOAD];
+    char    buffer2[MAX_PAYLOAD];
+    struct pollfd ufds[2];
+    int     fd, ret = 0;
+    unsigned count = 0;
+    char** args = (char **)command;
+    fd = create_subprocess(SHELL_COMMAND, args[0], args[1]);
+
+    while (1) {
+        while (count < sizeof(buffer)) {
+            ufds[0].fd = fd;
+            ufds[0].events = POLLIN | POLLHUP;
+            ufds[0].revents = 0;
+            ufds[1].fd = s;
+            ufds[1].events = POLLIN | POLLHUP;
+            ufds[1].revents = 0;
+            // use a 100ms timeout so we don't block indefinitely with our
+            // buffer partially filled.
+            ret = poll(ufds, 2, 100);
+            if (ret <= 0) {
+                D("poll returned %d\n", ret);
+                // file has closed or we timed out
+                // set ret to 1 so we don't exit the outer loop
+                ret = 1;
+                break;
+            }
+
+            if (ufds[0].revents & POLLIN) {
+                ret = adb_read(fd, buffer + count, sizeof(buffer) - count);
+                D("read fd ret: %d, count: %d\n", ret, count);
+                if (ret > 0)
+                    count += ret;
+                else
+                    break;
+            }
+            if (ufds[1].revents & POLLIN) {
+                ret = adb_read(s, buffer2, sizeof(buffer2));
+                D("read s ret: %d\n", ret);
+                if (ret > 0)
+                    adb_write(fd, buffer2, ret);
+                else
+                    break;
+            }
+
+            if ((ufds[0].revents & POLLHUP) || (ufds[1].revents & POLLHUP)) {
+                // set flag to exit after flushing the buffer
+                ret = -1;
+                break;
+            }
+        }
+
+        D("writing: %d\n", count);
+        if (count > 0) {
+            adb_write(s, buffer, count);
+            count = 0;
+        }
+        if (ret <= 0)
+            break;
+    }
+
+    D("shell_service done\n");
+
+    adb_close(fd);
+    adb_close(s);
+}
+#endif // !ADB_HOST
+
 int service_to_fd(const char *name)
 {
     int ret = -1;
@@ -317,14 +385,16 @@
         ret = create_jdwp_connection_fd(atoi(name+5));
     } else if (!strncmp(name, "log:", 4)) {
         ret = create_service_thread(log_service, get_log_file_path(name + 4));
-#endif
     } else if(!HOST && !strncmp(name, "shell:", 6)) {
+        const char* args[2];
         if(name[6]) {
-            ret = create_subprocess(SHELL_COMMAND, "-c", name + 6);
+            args[0] = "-c";
+            args[1] = name + 6;
         } else {
-            ret = create_subprocess(SHELL_COMMAND, "-", 0);
+            args[0] = "-";
+            args[1] = 0;
         }
-#if !ADB_HOST
+        ret = create_service_thread(shell_service, (void *)args);
     } else if(!strncmp(name, "sync:", 5)) {
         ret = create_service_thread(file_sync_service, NULL);
     } else if(!strncmp(name, "remount:", 8)) {