adb connect and disconnect improvements:

Port number is now optional.  Will use default port 5555 if not specified.
"adb disconnect" with no additional arguments will disconnect all TCP devices.

Change-Id: I7fc26528ed85e66a73b8f6254cea7bf83d98109f
Signed-off-by: Mike Lockwood <lockwood@android.com>
diff --git a/adb/adb.c b/adb/adb.c
index 311498b..b1a0e62 100644
--- a/adb/adb.c
+++ b/adb/adb.c
@@ -971,25 +971,32 @@
 {
     int port, fd;
     char* portstr = strchr(host, ':');
-    char buf[4096];
+    char hostbuf[100];
+    char serial[100];
 
-    if (!portstr) {
-        snprintf(buffer, buffer_size, "unable to parse %s as <host>:<port>", host);
-        return;
+    strncpy(hostbuf, host, sizeof(hostbuf) - 1);
+    if (portstr) {
+        if (portstr - host >= sizeof(hostbuf)) {
+            snprintf(buffer, buffer_size, "bad host name %s", host);
+            return;
+        }
+        // zero terminate the host at the point we found the colon
+        hostbuf[portstr - host] = 0;
+        if (sscanf(portstr + 1, "%d", &port) == 0) {
+            snprintf(buffer, buffer_size, "bad port number %s", portstr);
+            return;
+        }
+    } else {
+        port = DEFAULT_ADB_LOCAL_TRANSPORT_PORT;
     }
-    if (find_transport(host)) {
-        snprintf(buffer, buffer_size, "already connected to %s", host);
+
+    snprintf(serial, sizeof(serial), "%s:%d", hostbuf, port);
+    if (find_transport(serial)) {
+        snprintf(buffer, buffer_size, "already connected to %s", serial);
         return;
     }
 
-    // zero terminate host by overwriting the ':'
-    *portstr++ = 0;
-    if (sscanf(portstr, "%d", &port) == 0) {
-        snprintf(buffer, buffer_size, "bad port number %s", portstr);
-        return;
-    }
-
-    fd = socket_network_client(host, port, SOCK_STREAM);
+    fd = socket_network_client(hostbuf, port, SOCK_STREAM);
     if (fd < 0) {
         snprintf(buffer, buffer_size, "unable to connect to %s:%d", host, port);
         return;
@@ -998,9 +1005,8 @@
     D("client: connected on remote on fd %d\n", fd);
     close_on_exec(fd);
     disable_tcp_nagle(fd);
-    snprintf(buf, sizeof buf, "%s:%d", host, port);
-    register_socket_transport(fd, buf, port, 0);
-    snprintf(buffer, buffer_size, "connected to %s:%d", host, port);
+    register_socket_transport(fd, serial, port, 0);
+    snprintf(buffer, buffer_size, "connected to %s", serial);
 }
 
 void connect_emulator(char* port_spec, char* buffer, int buffer_size)
@@ -1136,12 +1142,23 @@
         char buffer[4096];
         memset(buffer, 0, sizeof(buffer));
         char* serial = service + 11;
-        atransport *t = find_transport(serial);
-
-        if (t) {
-            unregister_transport(t);
+        if (serial[0] == 0) {
+            // disconnect from all TCP devices
+            unregister_all_tcp_transports();
         } else {
-            snprintf(buffer, sizeof(buffer), "No such device %s", serial);
+            char hostbuf[100];
+            // assume port 5555 if no port is specified
+            if (!strchr(serial, ':')) {
+                snprintf(hostbuf, sizeof(hostbuf) - 1, "%s:5555", serial);
+                serial = hostbuf;
+            }
+            atransport *t = find_transport(serial);
+
+            if (t) {
+                unregister_transport(t);
+            } else {
+                snprintf(buffer, sizeof(buffer), "No such device %s", serial);
+            }
         }
 
         snprintf(buf, sizeof(buf), "OKAY%04x%s",(unsigned)strlen(buffer), buffer);
diff --git a/adb/adb.h b/adb/adb.h
index 292e415..3d2a77b 100644
--- a/adb/adb.h
+++ b/adb/adb.h
@@ -275,8 +275,9 @@
 /* cause new transports to be init'd and added to the list */
 void register_socket_transport(int s, const char *serial, int port, int local);
 
-/* this should only be used for the "adb disconnect" command */
+/* these should only be used for the "adb disconnect" command */
 void unregister_transport(atransport *t);
+void unregister_all_tcp_transports();
 
 void register_usb_transport(usb_handle *h, const char *serial, unsigned writeable);
 
diff --git a/adb/commandline.c b/adb/commandline.c
index d1eba5a..02e4658 100644
--- a/adb/commandline.c
+++ b/adb/commandline.c
@@ -100,8 +100,12 @@
         "                                 environment variable is used, which must\n"
         "                                 be an absolute path.\n"
         " devices                       - list all connected devices\n"
-        " connect <host>:<port>         - connect to a device via TCP/IP\n"
-        " disconnect <host>:<port>      - disconnect from a TCP/IP device\n"
+        " connect <host>[:<port>]       - connect to a device via TCP/IP\n"
+        "                                 Port 5555 is used by default if no port number is specified.\n"
+        " disconnect [<host>[:<port>]]  - disconnect from a TCP/IP device.\n"
+        "                                 Port 5555 is used by default if no port number is specified.\n"
+        "                                 Using this ocmmand with no additional arguments\n"
+        "                                 will disconnect from all connected TCP/IP devices.\n"
         "\n"
         "device commands:\n"
         "  adb push <local> <remote>    - copy file/dir to device\n"
@@ -793,13 +797,33 @@
         }
     }
 
-    if(!strcmp(argv[0], "connect") || !strcmp(argv[0], "disconnect")) {
+    if(!strcmp(argv[0], "connect")) {
         char *tmp;
         if (argc != 2) {
-            fprintf(stderr, "Usage: adb %s <host>:<port>\n", argv[0]);
+            fprintf(stderr, "Usage: adb connect <host>[:<port>]\n");
             return 1;
         }
-        snprintf(buf, sizeof buf, "host:%s:%s", argv[0], argv[1]);
+        snprintf(buf, sizeof buf, "host:connect:%s", argv[1]);
+        tmp = adb_query(buf);
+        if(tmp) {
+            printf("%s\n", tmp);
+            return 0;
+        } else {
+            return 1;
+        }
+    }
+
+    if(!strcmp(argv[0], "disconnect")) {
+        char *tmp;
+        if (argc > 2) {
+            fprintf(stderr, "Usage: adb disconnect [<host>[:<port>]]\n");
+            return 1;
+        }
+        if (argc == 2) {
+            snprintf(buf, sizeof buf, "host:disconnect:%s", argv[1]);
+        } else {
+            snprintf(buf, sizeof buf, "host:disconnect:");
+        }
         tmp = adb_query(buf);
         if(tmp) {
             printf("%s\n", tmp);
diff --git a/adb/transport.c b/adb/transport.c
index c2877d2..62bdfdb 100644
--- a/adb/transport.c
+++ b/adb/transport.c
@@ -671,21 +671,26 @@
 }
 
 
+static void transport_unref_locked(atransport *t)
+{
+    t->ref_count--;
+    D("transport: %p R- (ref=%d)\n", t, t->ref_count);
+    if (t->ref_count == 0) {
+        D("transport: %p kicking and closing\n", t);
+        if (!t->kicked) {
+            t->kicked = 1;
+            t->kick(t);
+        }
+        t->close(t);
+        remove_transport(t);
+    }
+}
+
 static void transport_unref(atransport *t)
 {
     if (t) {
         adb_mutex_lock(&transport_lock);
-        t->ref_count--;
-        D("transport: %p R- (ref=%d)\n", t, t->ref_count);
-        if (t->ref_count == 0) {
-            D("transport: %p kicking and closing\n", t);
-            if (!t->kicked) {
-                t->kicked = 1;
-                t->kick(t);
-            }
-            t->close(t);
-            remove_transport(t);
-        }
+        transport_unref_locked(t);
         adb_mutex_unlock(&transport_lock);
     }
 }
@@ -894,6 +899,29 @@
     transport_unref(t);
 }
 
+// unregisters all non-emulator TCP transports
+void unregister_all_tcp_transports()
+{
+    atransport *t, *next;
+    adb_mutex_lock(&transport_lock);
+    for (t = transport_list.next; t != &transport_list; t = next) {
+        next = t->next;
+        if (t->type == kTransportLocal && t->adb_port == 0) {
+            t->next->prev = t->prev;
+            t->prev->next = next;
+            // we cannot call kick_transport when holding transport_lock
+            if (!t->kicked)
+            {
+                t->kicked = 1;
+                t->kick(t);
+            }
+            transport_unref_locked(t);
+        }
+     }
+
+    adb_mutex_unlock(&transport_lock);
+}
+
 #endif
 
 void register_usb_transport(usb_handle *usb, const char *serial, unsigned writeable)