adb: add reconnect command.

Add reconnect command for debugging. `reconnect` kicks a transport
from the host side, `reconnect device` kicks a transport from
the device side. They can be used to produce transport errors.

Bug: 25935458

Change-Id: I47daa338796b561941e7aba44a51a6dd117d1e98
diff --git a/adb/adb.cpp b/adb/adb.cpp
index cb54d04..e0c0503 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -1134,6 +1134,13 @@
         /* we don't even need to send a reply */
         return 0;
     }
+
+    if (!strcmp(service, "reconnect")) {
+        if (s->transport != nullptr) {
+            kick_transport(s->transport);
+        }
+        return SendOkay(reply_fd, "done");
+    }
 #endif // ADB_HOST
 
     int ret = handle_forward_request(service, type, serial, reply_fd);
diff --git a/adb/adb_client.cpp b/adb/adb_client.cpp
index d29c08e..a27dd47 100644
--- a/adb/adb_client.cpp
+++ b/adb/adb_client.cpp
@@ -158,7 +158,8 @@
         }
     }
 
-    if (memcmp(&service[0],"host",4) != 0 && switch_socket_transport(fd, error)) {
+    if ((memcmp(&service[0],"host",4) != 0 || service == "host:reconnect") &&
+        switch_socket_transport(fd, error)) {
         return -1;
     }
 
@@ -168,9 +169,11 @@
         return -1;
     }
 
-    if (!adb_status(fd, error)) {
-        adb_close(fd);
-        return -1;
+    if (service != "reconnect") {
+        if (!adb_status(fd, error)) {
+            adb_close(fd);
+            return -1;
+        }
     }
 
     D("_adb_connect: return fd %d", fd);
diff --git a/adb/commandline.cpp b/adb/commandline.cpp
index a856672..323b9d3 100644
--- a/adb/commandline.cpp
+++ b/adb/commandline.cpp
@@ -239,6 +239,9 @@
         "  - If it is \"system\", \"vendor\", \"oem\" or \"data\", only the corresponding partition\n"
         "    is updated.\n"
         "\n"
+        "internal debugging:\n"
+        "  adb reconnect                  Kick current connection from host side and make it reconnect.\n"
+        "  adb reconnect device           Kick current connection from device side and make it reconnect.\n"
         "environment variables:\n"
         "  ADB_TRACE                    - Print debug information. A comma separated list of the following values\n"
         "                                 1 or all, adb, sockets, packets, rwx, usb, sync, sysdeps, transport, jdwp\n"
@@ -1905,6 +1908,14 @@
             }
         }
         return 0;
+    } else if (!strcmp(argv[0], "reconnect")) {
+        if (argc == 1) {
+            return adb_query_command("host:reconnect");
+        } else if (argc == 2 && !strcmp(argv[1], "device")) {
+            std::string err;
+            adb_connect("reconnect", &err);
+            return 0;
+        }
     }
 
     usage();
diff --git a/adb/services.cpp b/adb/services.cpp
index d5e963b..3c1aab8 100644
--- a/adb/services.cpp
+++ b/adb/services.cpp
@@ -184,6 +184,13 @@
     adb_close(fd);
 }
 
+static void reconnect_service(int fd, void* arg) {
+    WriteFdExactly(fd, "done");
+    adb_close(fd);
+    atransport* t = static_cast<atransport*>(arg);
+    kick_transport(t);
+}
+
 int reverse_service(const char* command) {
     int s[2];
     if (adb_socketpair(s)) {
@@ -345,6 +352,8 @@
         ret = create_service_thread(set_verity_enabled_state_service, (void*)0);
     } else if(!strncmp(name, "enable-verity:", 15)) {
         ret = create_service_thread(set_verity_enabled_state_service, (void*)1);
+    } else if (!strcmp(name, "reconnect")) {
+        ret = create_service_thread(reconnect_service, const_cast<atransport*>(transport));
 #endif
     }
     if (ret >= 0) {
diff --git a/adb/transport.cpp b/adb/transport.cpp
index e3340af..aaab21d 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -305,7 +305,11 @@
 
 void kick_transport(atransport* t) {
     adb_mutex_lock(&transport_lock);
-    kick_transport_locked(t);
+    // As kick_transport() can be called from threads without guarantee that t is valid,
+    // check if the transport is in transport_list first.
+    if (std::find(transport_list.begin(), transport_list.end(), t) != transport_list.end()) {
+        kick_transport_locked(t);
+    }
     adb_mutex_unlock(&transport_lock);
 }