Let "adb connect" connect to emulators too
- adb can now connect to an emulator configured with an arbitrary
pair of <console port, adb port>. These two ports do not have to be
adjacent.
This can be done from the commandline at any time using
adb connect emu:<console_port>,<adb_port>
- Emulators running on ports outside the normal range
(5554/5555-5584/5585) register themselves on startup if they follow
the convention "console port+1==abd port".
- Emulators outside the normal port range will not be auto-detected on
adb startup as these ports are not probed.
- The index into local_transports[] array in transport_local.c does no
longer indicate the port number of the local transport. Use the altered
atransport struct to get the port number.
- I have chosen not to document the adb connect emu:console_port,adb_port
syntax on adb's help screen as this might be confusing to most readers
and useful to very few.
- I don't expect this to introduce any (backwards) compatibility issues.
Change-Id: Iad3eccb2dcdde174b24ef0644d705ecfbff6e59d
Signed-off-by: Mike Lockwood <lockwood@android.com>
diff --git a/adb/adb.c b/adb/adb.c
index 4655d7c..311498b 100644
--- a/adb/adb.c
+++ b/adb/adb.c
@@ -966,6 +966,99 @@
return 0;
}
+#if ADB_HOST
+void connect_device(char* host, char* buffer, int buffer_size)
+{
+ int port, fd;
+ char* portstr = strchr(host, ':');
+ char buf[4096];
+
+ if (!portstr) {
+ snprintf(buffer, buffer_size, "unable to parse %s as <host>:<port>", host);
+ return;
+ }
+ if (find_transport(host)) {
+ snprintf(buffer, buffer_size, "already connected to %s", host);
+ 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);
+ if (fd < 0) {
+ snprintf(buffer, buffer_size, "unable to connect to %s:%d", host, port);
+ return;
+ }
+
+ 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);
+}
+
+void connect_emulator(char* port_spec, char* buffer, int buffer_size)
+{
+ char* port_separator = strchr(port_spec, ',');
+ if (!port_separator) {
+ snprintf(buffer, buffer_size,
+ "unable to parse '%s' as <console port>,<adb port>",
+ port_spec);
+ return;
+ }
+
+ // Zero-terminate console port and make port_separator point to 2nd port.
+ *port_separator++ = 0;
+ int console_port = strtol(port_spec, NULL, 0);
+ int adb_port = strtol(port_separator, NULL, 0);
+ if (!(console_port > 0 && adb_port > 0)) {
+ *(port_separator - 1) = ',';
+ snprintf(buffer, buffer_size,
+ "Invalid port numbers: Expected positive numbers, got '%s'",
+ port_spec);
+ return;
+ }
+
+ /* Check if the emulator is already known.
+ * Note: There's a small but harmless race condition here: An emulator not
+ * present just yet could be registered by another invocation right
+ * after doing this check here. However, local_connect protects
+ * against double-registration too. From here, a better error message
+ * can be produced. In the case of the race condition, the very specific
+ * error message won't be shown, but the data doesn't get corrupted. */
+ atransport* known_emulator = find_emulator_transport_by_adb_port(adb_port);
+ if (known_emulator != NULL) {
+ snprintf(buffer, buffer_size,
+ "Emulator on port %d already registered.", adb_port);
+ return;
+ }
+
+ /* Check if more emulators can be registered. Similar unproblematic
+ * race condition as above. */
+ int candidate_slot = get_available_local_transport_index();
+ if (candidate_slot < 0) {
+ snprintf(buffer, buffer_size, "Cannot accept more emulators.");
+ return;
+ }
+
+ /* Preconditions met, try to connect to the emulator. */
+ if (!local_connect_arbitrary_ports(console_port, adb_port)) {
+ snprintf(buffer, buffer_size,
+ "Connected to emulator on ports %d,%d", console_port, adb_port);
+ } else {
+ snprintf(buffer, buffer_size,
+ "Could not connect to emulator on ports %d,%d",
+ console_port, adb_port);
+ }
+}
+#endif
+
int handle_host_request(char *service, transport_type ttype, char* serial, int reply_fd, asocket *s)
{
atransport *transport = NULL;
@@ -1023,43 +1116,16 @@
return 0;
}
- // add a new TCP transport
+ // add a new TCP transport, device or emulator
if (!strncmp(service, "connect:", 8)) {
char buffer[4096];
- int port, fd;
char* host = service + 8;
- char* portstr = strchr(host, ':');
-
- if (!portstr) {
- snprintf(buffer, sizeof(buffer), "unable to parse %s as <host>:<port>", host);
- goto done;
+ if (!strncmp(host, "emu:", 4)) {
+ connect_emulator(host + 4, buffer, sizeof(buffer));
+ } else {
+ connect_device(host, buffer, sizeof(buffer));
}
- 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", portstr);
- goto done;
- }
-
- fd = socket_network_client(host, port, SOCK_STREAM);
- if (fd < 0) {
- snprintf(buffer, sizeof(buffer), "unable to connect to %s:%d", host, port);
- goto done;
- }
-
- 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, sizeof(buffer), "connected to %s:%d", host, port);
-
-done:
+ // Send response for emulator and device
snprintf(buf, sizeof(buf), "OKAY%04x%s",(unsigned)strlen(buffer), buffer);
writex(reply_fd, buf, strlen(buf));
return 0;