adb: Add "adb disconnect" command for disconnecting TCP/IP devices.

Also check that device is not already connected in "adb connect"

Change-Id: I5f84b56b63d8c6932f23791cac319fd6bc39d36c
Signed-off-by: Mike Lockwood <lockwood@android.com>
diff --git a/adb/adb.c b/adb/adb.c
index 283ebce..c1646b8 100644
--- a/adb/adb.c
+++ b/adb/adb.c
@@ -1020,19 +1020,24 @@
         char* portstr = strchr(host, ':');
 
         if (!portstr) {
-            snprintf(buffer, sizeof(buffer), "unable to parse %s as <host>:<port>\n", host);
+            snprintf(buffer, sizeof(buffer), "unable to parse %s as <host>:<port>", host);
             goto done;
         }
+        if (find_transport(host)) {
+            snprintf(buffer, sizeof(buffer), "Already connected to %s", host);
+            goto done;
+        }
+
         // zero terminate host by overwriting the ':'
         *portstr++ = 0;
         if (sscanf(portstr, "%d", &port) == 0) {
-            snprintf(buffer, sizeof(buffer), "bad port number %s\n", portstr);
+            snprintf(buffer, sizeof(buffer), "bad port number %s", portstr);
             goto done;
         }
 
         fd = socket_network_client(host, port, SOCK_STREAM);
         if (fd < 0) {
-            snprintf(buffer, sizeof(buffer), "unable to connect to %s:%d\n", host, port);
+            snprintf(buffer, sizeof(buffer), "unable to connect to %s:%d", host, port);
             goto done;
         }
 
@@ -1041,10 +1046,28 @@
         disable_tcp_nagle(fd);
         snprintf(buf, sizeof buf, "%s:%d", host, port);
         register_socket_transport(fd, buf, port, 0);
-        snprintf(buffer, sizeof(buffer), "connected to %s:%d\n", host, port);
+        snprintf(buffer, sizeof(buffer), "connected to %s:%d", host, port);
 
 done:
-        snprintf(buf, sizeof(buf), "OKAY%04x%s",(unsigned)strlen(buffer),buffer);
+        snprintf(buf, sizeof(buf), "OKAY%04x%s",(unsigned)strlen(buffer), buffer);
+        writex(reply_fd, buf, strlen(buf));
+        return 0;
+    }
+
+    // remove TCP transport
+    if (!strncmp(service, "disconnect:", 11)) {
+        char buffer[4096];
+        memset(buffer, 0, sizeof(buffer));
+        char* serial = service + 11;
+        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);
         writex(reply_fd, buf, strlen(buf));
         return 0;
     }
diff --git a/adb/adb.h b/adb/adb.h
index 713666f..b958682 100644
--- a/adb/adb.h
+++ b/adb/adb.h
@@ -270,11 +270,17 @@
 
 /* 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 */
+void unregister_transport(atransport *t);
+
 void register_usb_transport(usb_handle *h, const char *serial, unsigned writeable);
 
 /* this should only be used for transports with connection_state == CS_NOPERM */
 void unregister_usb_transport(usb_handle *usb);
 
+atransport *find_transport(const char *serial);
+
 int service_to_fd(const char *name);
 #if ADB_HOST
 asocket *host_service_to_socket(const char*  name, const char *serial);
diff --git a/adb/commandline.c b/adb/commandline.c
index 411bb82..055aa10 100644
--- a/adb/commandline.c
+++ b/adb/commandline.c
@@ -106,6 +106,7 @@
         "                                 be an absolute path.\n"
         " devices                       - list all connected devices\n"
         " connect <host>:<port>         - connect to a device via TCP/IP"
+        " disconnect <host>:<port>      - disconnect from a TCP/IP device"
         "\n"
         "device commands:\n"
         "  adb push <local> <remote>    - copy file/dir to device\n"
@@ -853,10 +854,10 @@
         }
     }
 
-    if(!strcmp(argv[0], "connect")) {
+    if(!strcmp(argv[0], "connect") || !strcmp(argv[0], "disconnect")) {
         char *tmp;
         if (argc != 2) {
-            fprintf(stderr, "Usage: adb connect <host>:<port>\n");
+            fprintf(stderr, "Usage: adb %s <host>:<port>\n", argv[0]);
             return 1;
         }
         snprintf(buf, sizeof buf, "host:%s:%s", argv[0], argv[1]);
diff --git a/adb/jdwp_service.c b/adb/jdwp_service.c
index ae7f12d..0c26f7b 100644
--- a/adb/jdwp_service.c
+++ b/adb/jdwp_service.c
@@ -164,7 +164,7 @@
         proc->next->prev = proc->prev;
 
         if (proc->socket >= 0) {
-            shutdown(proc->socket, SHUT_RDWR);
+            adb_shutdown(proc->socket);
             adb_close(proc->socket);
             proc->socket = -1;
         }
diff --git a/adb/sysdeps.h b/adb/sysdeps.h
index 389fbd2..6372649 100644
--- a/adb/sysdeps.h
+++ b/adb/sysdeps.h
@@ -113,6 +113,7 @@
 extern int  adb_read(int  fd, void* buf, int len);
 extern int  adb_write(int  fd, const void*  buf, int  len);
 extern int  adb_lseek(int  fd, int  pos, int  where);
+extern int  adb_shutdown(int  fd);
 extern int  adb_close(int  fd);
 
 static __inline__ int  unix_close(int fd)
@@ -327,6 +328,13 @@
 #undef   open
 #define  open    ___xxx_open
 
+static __inline__ int  adb_shutdown(int fd)
+{
+    return shutdown(fd, SHUT_RDWR);
+}
+#undef   shutdown
+#define  shutdown   ____xxx_shutdown
+
 static __inline__ int  adb_close(int fd)
 {
     return close(fd);
diff --git a/adb/sysdeps_win32.c b/adb/sysdeps_win32.c
index a8e3bb9..ced91e8 100644
--- a/adb/sysdeps_win32.c
+++ b/adb/sysdeps_win32.c
@@ -435,6 +435,20 @@
 }
 
 
+int  adb_shutdown(int  fd)
+{
+    FH   f = _fh_from_int(fd);
+
+    if (!f) {
+        return -1;
+    }
+
+    D( "adb_shutdown: %s\n", f->name);
+    shutdown( f->fh_socket, SD_BOTH );
+    return 0;
+}
+
+
 int  adb_close(int  fd)
 {
     FH   f = _fh_from_int(fd);
diff --git a/adb/transport.c b/adb/transport.c
index 617dabf..c2877d2 100644
--- a/adb/transport.c
+++ b/adb/transport.c
@@ -864,6 +864,38 @@
     register_transport(t);
 }
 
+#if ADB_HOST
+atransport *find_transport(const char *serial)
+{
+    atransport *t;
+
+    adb_mutex_lock(&transport_lock);
+    for(t = transport_list.next; t != &transport_list; t = t->next) {
+        if (t->serial && !strcmp(serial, t->serial)) {
+            break;
+        }
+     }
+    adb_mutex_unlock(&transport_lock);
+
+    if (t != &transport_list)
+        return t;
+    else
+        return 0;
+}
+
+void unregister_transport(atransport *t)
+{
+    adb_mutex_lock(&transport_lock);
+    t->next->prev = t->prev;
+    t->prev->next = t->next;
+    adb_mutex_unlock(&transport_lock);
+
+    kick_transport(t);
+    transport_unref(t);
+}
+
+#endif
+
 void register_usb_transport(usb_handle *usb, const char *serial, unsigned writeable)
 {
     atransport *t = calloc(1, sizeof(atransport));
diff --git a/adb/transport_local.c b/adb/transport_local.c
index c528d1f..81d120e 100644
--- a/adb/transport_local.c
+++ b/adb/transport_local.c
@@ -204,6 +204,7 @@
 {
     int fd = t->sfd;
     t->sfd = -1;
+    adb_shutdown(fd);
     adb_close(fd);
 
 #if ADB_HOST