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>
This commit is contained in:
Stefan Hilzinger 2010-04-26 10:17:43 +01:00 committed by Mike Lockwood
parent 5762ec2905
commit d9d1ca4780
3 changed files with 179 additions and 54 deletions

132
adb/adb.c
View File

@ -966,6 +966,99 @@ int adb_main(int is_daemon, int server_port)
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 @@ int handle_host_request(char *service, transport_type ttype, char* serial, int r
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;

View File

@ -183,6 +183,7 @@ struct atransport
/* used to identify transports for clients */
char *serial;
char *product;
int adb_port; // Use for emulators (local transport)
/* a list of adisconnect callbacks called when the transport is kicked */
int kicked;
@ -262,6 +263,9 @@ void run_transport_disconnects( atransport* t );
void kick_transport( atransport* t );
/* initialize a transport object's func pointers and state */
#if ADB_HOST
int get_available_local_transport_index();
#endif
int init_socket_transport(atransport *t, int s, int port, int local);
void init_usb_transport(atransport *t, usb_handle *usb, int state);
@ -280,6 +284,9 @@ void register_usb_transport(usb_handle *h, const char *serial, unsigned writeabl
void unregister_usb_transport(usb_handle *usb);
atransport *find_transport(const char *serial);
#if ADB_HOST
atransport* find_emulator_transport_by_adb_port(int adb_port);
#endif
int service_to_fd(const char *name);
#if ADB_HOST
@ -368,6 +375,7 @@ typedef enum {
void local_init(int port);
int local_connect(int port);
int local_connect_arbitrary_ports(int console_port, int adb_port);
/* usb host/client interface */
void usb_init();

View File

@ -41,9 +41,9 @@ static inline void fix_endians(apacket *p)
#endif
#if ADB_HOST
/* we keep a list of opened transports, transport 0 is bound to 5555,
* transport 1 to 5557, .. transport n to 5555 + n*2. the list is used
* to detect when we're trying to connect twice to a given local transport
/* we keep a list of opened transports. The atransport struct knows to which
* local transport it is connected. The list is used to detect when we're
* trying to connect twice to a given local transport.
*/
#define ADB_LOCAL_TRANSPORT_MAX 16
@ -102,7 +102,11 @@ static int remote_write(apacket *p, atransport *t)
}
int local_connect(int port)
int local_connect(int port) {
return local_connect_arbitrary_ports(port-1, port);
}
int local_connect_arbitrary_ports(int console_port, int adb_port)
{
char buf[64];
int fd = -1;
@ -110,19 +114,19 @@ int local_connect(int port)
#if ADB_HOST
const char *host = getenv("ADBHOST");
if (host) {
fd = socket_network_client(host, port, SOCK_STREAM);
fd = socket_network_client(host, adb_port, SOCK_STREAM);
}
#endif
if (fd < 0) {
fd = socket_loopback_client(port, SOCK_STREAM);
fd = socket_loopback_client(adb_port, SOCK_STREAM);
}
if (fd >= 0) {
D("client: connected on remote on fd %d\n", fd);
close_on_exec(fd);
disable_tcp_nagle(fd);
snprintf(buf, sizeof buf, "%s%d", LOCAL_CLIENT_PREFIX, port - 1);
register_socket_transport(fd, buf, port, 1);
snprintf(buf, sizeof buf, "%s%d", LOCAL_CLIENT_PREFIX, console_port);
register_socket_transport(fd, buf, adb_port, 1);
return 0;
}
return -1;
@ -227,7 +231,50 @@ static void remote_close(atransport *t)
adb_close(t->fd);
}
int init_socket_transport(atransport *t, int s, int port, int local)
#if ADB_HOST
/* Only call this function if you already hold local_transports_lock. */
atransport* find_emulator_transport_by_adb_port_locked(int adb_port)
{
int i;
for (i = 0; i < ADB_LOCAL_TRANSPORT_MAX; i++) {
if (local_transports[i] && local_transports[i]->adb_port == adb_port) {
return local_transports[i];
}
}
return NULL;
}
atransport* find_emulator_transport_by_adb_port(int adb_port)
{
adb_mutex_lock( &local_transports_lock );
atransport* result = find_emulator_transport_by_adb_port_locked(adb_port);
adb_mutex_unlock( &local_transports_lock );
return result;
}
/* Only call this function if you already hold local_transports_lock. */
int get_available_local_transport_index_locked()
{
int i;
for (i = 0; i < ADB_LOCAL_TRANSPORT_MAX; i++) {
if (local_transports[i] == NULL) {
return i;
}
}
return -1;
}
int get_available_local_transport_index()
{
adb_mutex_lock( &local_transports_lock );
int result = get_available_local_transport_index_locked();
adb_mutex_unlock( &local_transports_lock );
return result;
}
#endif
int init_socket_transport(atransport *t, int s, int adb_port, int local)
{
int fail = 0;
@ -239,26 +286,30 @@ int init_socket_transport(atransport *t, int s, int port, int local)
t->sync_token = 1;
t->connection_state = CS_OFFLINE;
t->type = kTransportLocal;
t->adb_port = 0;
#if ADB_HOST
if (HOST && local) {
adb_mutex_lock( &local_transports_lock );
{
int index = (port - DEFAULT_ADB_LOCAL_TRANSPORT_PORT)/2;
if (!(port & 1) || index < 0 || index >= ADB_LOCAL_TRANSPORT_MAX) {
D("bad local transport port number: %d\n", port);
fail = -1;
}
else if (local_transports[index] != NULL) {
t->adb_port = adb_port;
atransport* existing_transport =
find_emulator_transport_by_adb_port_locked(adb_port);
int index = get_available_local_transport_index_locked();
if (existing_transport != NULL) {
D("local transport for port %d already registered (%p)?\n",
port, local_transports[index]);
adb_port, existing_transport);
fail = -1;
}
else
} else if (index < 0) {
// Too many emulators.
D("cannot register more emulators. Maximum is %d\n",
ADB_LOCAL_TRANSPORT_MAX);
fail = -1;
} else {
local_transports[index] = t;
}
adb_mutex_unlock( &local_transports_lock );
}
}
adb_mutex_unlock( &local_transports_lock );
}
#endif
return fail;