diff options
Diffstat (limited to 'adb/adb_client.cpp')
-rw-r--r-- | adb/adb_client.cpp | 363 |
1 files changed, 363 insertions, 0 deletions
diff --git a/adb/adb_client.cpp b/adb/adb_client.cpp new file mode 100644 index 0000000..d677db8 --- /dev/null +++ b/adb/adb_client.cpp @@ -0,0 +1,363 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <errno.h> +#include <limits.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/types.h> + +#include "sysdeps.h" + +#define TRACE_TAG TRACE_ADB +#include "adb_client.h" +#include "adb_io.h" +#include "zipfile/zipfile.h" + +static transport_type __adb_transport = kTransportAny; +static const char* __adb_serial = NULL; + +static int __adb_server_port = DEFAULT_ADB_PORT; +static const char* __adb_server_name = NULL; + +void adb_set_transport(transport_type type, const char* serial) +{ + __adb_transport = type; + __adb_serial = serial; +} + +void adb_set_tcp_specifics(int server_port) +{ + __adb_server_port = server_port; +} + +void adb_set_tcp_name(const char* hostname) +{ + __adb_server_name = hostname; +} + +int adb_get_emulator_console_port(void) +{ + const char* serial = __adb_serial; + int port; + + if (serial == NULL) { + /* if no specific device was specified, we need to look at */ + /* the list of connected devices, and extract an emulator */ + /* name from it. two emulators is an error */ + char* tmp = adb_query("host:devices"); + char* p = tmp; + if(!tmp) { + printf("no emulator connected\n"); + return -1; + } + while (*p) { + char* q = strchr(p, '\n'); + if (q != NULL) + *q++ = 0; + else + q = p + strlen(p); + + if (!memcmp(p, LOCAL_CLIENT_PREFIX, sizeof(LOCAL_CLIENT_PREFIX)-1)) { + if (serial != NULL) { /* more than one emulator listed */ + free(tmp); + return -2; + } + serial = p; + } + + p = q; + } + free(tmp); + + if (serial == NULL) + return -1; /* no emulator found */ + } + else { + if (memcmp(serial, LOCAL_CLIENT_PREFIX, sizeof(LOCAL_CLIENT_PREFIX)-1) != 0) + return -1; /* not an emulator */ + } + + serial += sizeof(LOCAL_CLIENT_PREFIX)-1; + port = strtol(serial, NULL, 10); + return port; +} + +static char __adb_error[256] = { 0 }; + +const char *adb_error(void) +{ + return __adb_error; +} + +static int switch_socket_transport(int fd) +{ + char service[64]; + char tmp[5]; + int len; + + if (__adb_serial) + snprintf(service, sizeof service, "host:transport:%s", __adb_serial); + else { + const char* transport_type = "???"; + + switch (__adb_transport) { + case kTransportUsb: + transport_type = "transport-usb"; + break; + case kTransportLocal: + transport_type = "transport-local"; + break; + case kTransportAny: + transport_type = "transport-any"; + break; + case kTransportHost: + // no switch necessary + return 0; + break; + } + + snprintf(service, sizeof service, "host:%s", transport_type); + } + len = strlen(service); + snprintf(tmp, sizeof tmp, "%04x", len); + + if(!WriteFdExactly(fd, tmp, 4) || !WriteFdExactly(fd, service, len)) { + strcpy(__adb_error, "write failure during connection"); + adb_close(fd); + return -1; + } + D("Switch transport in progress\n"); + + if(adb_status(fd)) { + adb_close(fd); + D("Switch transport failed\n"); + return -1; + } + D("Switch transport success\n"); + return 0; +} + +int adb_status(int fd) +{ + unsigned char buf[5]; + unsigned len; + + if(!ReadFdExactly(fd, buf, 4)) { + strcpy(__adb_error, "protocol fault (no status)"); + return -1; + } + + if(!memcmp(buf, "OKAY", 4)) { + return 0; + } + + if(memcmp(buf, "FAIL", 4)) { + sprintf(__adb_error, + "protocol fault (status %02x %02x %02x %02x?!)", + buf[0], buf[1], buf[2], buf[3]); + return -1; + } + + if(!ReadFdExactly(fd, buf, 4)) { + strcpy(__adb_error, "protocol fault (status len)"); + return -1; + } + buf[4] = 0; + len = strtoul((char*)buf, 0, 16); + if(len > 255) len = 255; + if(!ReadFdExactly(fd, __adb_error, len)) { + strcpy(__adb_error, "protocol fault (status read)"); + return -1; + } + __adb_error[len] = 0; + return -1; +} + +int _adb_connect(const char *service) +{ + char tmp[5]; + int len; + int fd; + + D("_adb_connect: %s\n", service); + len = strlen(service); + if((len < 1) || (len > 1024)) { + strcpy(__adb_error, "service name too long"); + return -1; + } + snprintf(tmp, sizeof tmp, "%04x", len); + + if (__adb_server_name) + fd = socket_network_client(__adb_server_name, __adb_server_port, SOCK_STREAM); + else + fd = socket_loopback_client(__adb_server_port, SOCK_STREAM); + + if(fd < 0) { + strcpy(__adb_error, "cannot connect to daemon"); + return -2; + } + + if (memcmp(service,"host",4) != 0 && switch_socket_transport(fd)) { + return -1; + } + + if(!WriteFdExactly(fd, tmp, 4) || !WriteFdExactly(fd, service, len)) { + strcpy(__adb_error, "write failure during connection"); + adb_close(fd); + return -1; + } + + if(adb_status(fd)) { + adb_close(fd); + return -1; + } + + D("_adb_connect: return fd %d\n", fd); + return fd; +} + +int adb_connect(const char *service) +{ + // first query the adb server's version + int fd = _adb_connect("host:version"); + + D("adb_connect: service %s\n", service); + if(fd == -2 && __adb_server_name) { + fprintf(stderr,"** Cannot start server on remote host\n"); + return fd; + } else if(fd == -2) { + fprintf(stdout,"* daemon not running. starting it now on port %d *\n", + __adb_server_port); + start_server: + if(launch_server(__adb_server_port)) { + fprintf(stderr,"* failed to start daemon *\n"); + return -1; + } else { + fprintf(stdout,"* daemon started successfully *\n"); + } + /* give the server some time to start properly and detect devices */ + adb_sleep_ms(3000); + // fall through to _adb_connect + } else { + // if server was running, check its version to make sure it is not out of date + char buf[100]; + size_t n; + int version = ADB_SERVER_VERSION - 1; + + // if we have a file descriptor, then parse version result + if(fd >= 0) { + if(!ReadFdExactly(fd, buf, 4)) goto error; + + buf[4] = 0; + n = strtoul(buf, 0, 16); + if(n > sizeof(buf)) goto error; + if(!ReadFdExactly(fd, buf, n)) goto error; + adb_close(fd); + + if (sscanf(buf, "%04x", &version) != 1) goto error; + } else { + // if fd is -1, then check for "unknown host service", + // which would indicate a version of adb that does not support the version command + if (strcmp(__adb_error, "unknown host service") != 0) + return fd; + } + + if(version != ADB_SERVER_VERSION) { + printf("adb server is out of date. killing...\n"); + fd = _adb_connect("host:kill"); + adb_close(fd); + + /* XXX can we better detect its death? */ + adb_sleep_ms(2000); + goto start_server; + } + } + + // if the command is start-server, we are done. + if (!strcmp(service, "host:start-server")) + return 0; + + fd = _adb_connect(service); + if(fd == -1) { + D("_adb_connect error: %s", __adb_error); + } else if(fd == -2) { + fprintf(stderr,"** daemon still not running\n"); + } + D("adb_connect: return fd %d\n", fd); + + return fd; +error: + adb_close(fd); + return -1; +} + + +int adb_command(const char *service) +{ + int fd = adb_connect(service); + if(fd < 0) { + fprintf(stderr, "error: %s\n", adb_error()); + return -1; + } + + if(adb_status(fd)) { + adb_close(fd); + return -1; + } + + return 0; +} + +char *adb_query(const char *service) +{ + char buf[5]; + unsigned long n; + char* tmp; + + D("adb_query: %s\n", service); + int fd = adb_connect(service); + if(fd < 0) { + fprintf(stderr,"error: %s\n", __adb_error); + return 0; + } + + if(!ReadFdExactly(fd, buf, 4)) goto oops; + + buf[4] = 0; + n = strtoul(buf, 0, 16); + if(n >= 0xffff) { + strcpy(__adb_error, "reply is too long (>= 64kB)"); + goto oops; + } + + tmp = reinterpret_cast<char*>(malloc(n + 1)); + if(tmp == 0) goto oops; + + if(!ReadFdExactly(fd, tmp, n) == 0) { + tmp[n] = 0; + adb_close(fd); + return tmp; + } + free(tmp); + +oops: + adb_close(fd); + return 0; +} |