From dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0 Mon Sep 17 00:00:00 2001 From: The Android Open Source Project Date: Tue, 3 Mar 2009 19:32:55 -0800 Subject: auto import from //depot/cupcake/@135843 --- adb/Android.mk | 140 ++++ adb/OVERVIEW.TXT | 139 ++++ adb/SERVICES.TXT | 236 ++++++ adb/adb.c | 1083 +++++++++++++++++++++++++ adb/adb.h | 407 ++++++++++ adb/adb_client.c | 318 ++++++++ adb/adb_client.h | 49 ++ adb/commandline.c | 1249 +++++++++++++++++++++++++++++ adb/console.c | 45 ++ adb/file_sync_client.c | 1022 ++++++++++++++++++++++++ adb/file_sync_service.c | 412 ++++++++++ adb/file_sync_service.h | 87 ++ adb/framebuffer_service.c | 70 ++ adb/get_my_path_darwin.c | 31 + adb/get_my_path_linux.c | 33 + adb/get_my_path_windows.c | 31 + adb/history.h | 13 + adb/jdwp_service.c | 709 ++++++++++++++++ adb/log_service.c | 92 +++ adb/mutex_list.h | 14 + adb/protocol.txt | 252 ++++++ adb/remount_service.c | 103 +++ adb/services.c | 367 +++++++++ adb/shlist.c | 185 +++++ adb/shlist.h | 34 + adb/sockets.c | 787 ++++++++++++++++++ adb/sockets.dia | Bin 0 -> 2333 bytes adb/sysdeps.h | 473 +++++++++++ adb/sysdeps_win32.c | 1953 +++++++++++++++++++++++++++++++++++++++++++++ adb/test_track_devices.c | 97 +++ adb/test_track_jdwp.c | 97 +++ adb/transport.c | 958 ++++++++++++++++++++++ adb/transport_local.c | 263 ++++++ adb/transport_usb.c | 147 ++++ adb/usb_linux.c | 654 +++++++++++++++ adb/usb_linux_client.c | 156 ++++ adb/usb_osx.c | 536 +++++++++++++ adb/usb_windows.c | 513 ++++++++++++ adb/utils.c | 106 +++ adb/utils.h | 68 ++ 40 files changed, 13929 insertions(+) create mode 100644 adb/Android.mk create mode 100644 adb/OVERVIEW.TXT create mode 100644 adb/SERVICES.TXT create mode 100644 adb/adb.c create mode 100644 adb/adb.h create mode 100644 adb/adb_client.c create mode 100644 adb/adb_client.h create mode 100644 adb/commandline.c create mode 100644 adb/console.c create mode 100644 adb/file_sync_client.c create mode 100644 adb/file_sync_service.c create mode 100644 adb/file_sync_service.h create mode 100644 adb/framebuffer_service.c create mode 100644 adb/get_my_path_darwin.c create mode 100644 adb/get_my_path_linux.c create mode 100644 adb/get_my_path_windows.c create mode 100755 adb/history.h create mode 100644 adb/jdwp_service.c create mode 100644 adb/log_service.c create mode 100644 adb/mutex_list.h create mode 100644 adb/protocol.txt create mode 100644 adb/remount_service.c create mode 100644 adb/services.c create mode 100755 adb/shlist.c create mode 100755 adb/shlist.h create mode 100644 adb/sockets.c create mode 100644 adb/sockets.dia create mode 100644 adb/sysdeps.h create mode 100644 adb/sysdeps_win32.c create mode 100644 adb/test_track_devices.c create mode 100644 adb/test_track_jdwp.c create mode 100644 adb/transport.c create mode 100644 adb/transport_local.c create mode 100644 adb/transport_usb.c create mode 100644 adb/usb_linux.c create mode 100644 adb/usb_linux_client.c create mode 100644 adb/usb_osx.c create mode 100644 adb/usb_windows.c create mode 100644 adb/utils.c create mode 100644 adb/utils.h (limited to 'adb') diff --git a/adb/Android.mk b/adb/Android.mk new file mode 100644 index 0000000..2296610 --- /dev/null +++ b/adb/Android.mk @@ -0,0 +1,140 @@ +# Copyright 2005 The Android Open Source Project +# +# Android.mk for adb +# + +LOCAL_PATH:= $(call my-dir) + +# adb host tool +# ========================================================= +ifneq ($(TARGET_SIMULATOR),true) # not 64 bit clean (also unused with the sim) +include $(CLEAR_VARS) + +# Default to a virtual (sockets) usb interface +USB_SRCS := +EXTRA_SRCS := + +ifeq ($(HOST_OS),linux) + USB_SRCS := usb_linux.c + EXTRA_SRCS := get_my_path_linux.c + LOCAL_LDLIBS += -lrt -lncurses -lpthread +endif + +ifeq ($(HOST_OS),darwin) + USB_SRCS := usb_osx.c + EXTRA_SRCS := get_my_path_darwin.c + LOCAL_LDLIBS += -lpthread -framework CoreFoundation -framework IOKit -framework Carbon +endif + +ifeq ($(HOST_OS),windows) + USB_SRCS := usb_windows.c + EXTRA_SRCS := get_my_path_windows.c + EXTRA_STATIC_LIBS := AdbWinApi + LOCAL_C_INCLUDES += /usr/include/w32api/ddk development/host/windows/usb/api/ + ifneq ($(strip $(USE_CYGWIN)),) + LOCAL_LDLIBS += -lpthread + else + LOCAL_LDLIBS += -lws2_32 + USE_SYSDEPS_WIN32 := 1 + endif +endif + +LOCAL_SRC_FILES := \ + adb.c \ + console.c \ + transport.c \ + transport_local.c \ + transport_usb.c \ + commandline.c \ + adb_client.c \ + sockets.c \ + services.c \ + file_sync_client.c \ + $(EXTRA_SRCS) \ + $(USB_SRCS) \ + shlist.c \ + utils.c \ + + +ifneq ($(USE_SYSDEPS_WIN32),) + LOCAL_SRC_FILES += sysdeps_win32.c +endif + +LOCAL_CFLAGS += -O2 -g -DADB_HOST=1 -Wall -Wno-unused-parameter +LOCAL_CFLAGS += -D_XOPEN_SOURCE -D_GNU_SOURCE -DSH_HISTORY +LOCAL_MODULE := adb + +LOCAL_STATIC_LIBRARIES := libzipfile libunz $(EXTRA_STATIC_LIBS) +ifeq ($(USE_SYSDEPS_WIN32),) + LOCAL_STATIC_LIBRARIES += libcutils +endif + +include $(BUILD_HOST_EXECUTABLE) + +$(call dist-for-goals,droid,$(LOCAL_BUILT_MODULE)) + +ifeq ($(HOST_OS),windows) +$(LOCAL_INSTALLED_MODULE): $(HOST_OUT_EXECUTABLES)/AdbWinApi.dll +endif + +endif + +# adbd device daemon +# ========================================================= + +# build adbd in all non-simulator builds +BUILD_ADBD := false +ifneq ($(TARGET_SIMULATOR),true) + BUILD_ADBD := true +endif + +# build adbd for the Linux simulator build +# so we can use it to test the adb USB gadget driver on x86 +ifeq ($(HOST_OS),linux) + BUILD_ADBD := true +endif + + +ifeq ($(BUILD_ADBD),true) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + adb.c \ + transport.c \ + transport_local.c \ + transport_usb.c \ + sockets.c \ + services.c \ + file_sync_service.c \ + jdwp_service.c \ + framebuffer_service.c \ + remount_service.c \ + usb_linux_client.c \ + log_service.c \ + utils.c \ + +LOCAL_CFLAGS := -O2 -g -DADB_HOST=0 -Wall -Wno-unused-parameter +LOCAL_CFLAGS += -D_XOPEN_SOURCE -D_GNU_SOURCE + +# TODO: This should probably be board specific, whether or not the kernel has +# the gadget driver; rather than relying on the architecture type. +ifeq ($(TARGET_ARCH),arm) +LOCAL_CFLAGS += -DANDROID_GADGET=1 +endif + +LOCAL_MODULE := adbd + +LOCAL_FORCE_STATIC_EXECUTABLE := true +LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT_SBIN) +LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_SBIN_UNSTRIPPED) + +ifeq ($(TARGET_SIMULATOR),true) + LOCAL_STATIC_LIBRARIES := libcutils + LOCAL_LDLIBS += -lpthread + include $(BUILD_HOST_EXECUTABLE) +else + LOCAL_STATIC_LIBRARIES := libcutils libc + include $(BUILD_EXECUTABLE) +endif + +endif diff --git a/adb/OVERVIEW.TXT b/adb/OVERVIEW.TXT new file mode 100644 index 0000000..6a5191a --- /dev/null +++ b/adb/OVERVIEW.TXT @@ -0,0 +1,139 @@ +Implementation notes regarding ADB. + +I. General Overview: + +The Android Debug Bridge (ADB) is used to: + +- keep track of all Android devices and emulators instances + connected to or running on a given host developer machine + +- implement various control commands (e.g. "adb shell", "adb pull", etc..) + for the benefit of clients (command-line users, or helper programs like + DDMS). These commands are what is called a 'service' in ADB. + +As a whole, everything works through the following components: + + 1. The ADB server + + This is a background process that runs on the host machine. Its purpose + if to sense the USB ports to know when devices are attached/removed, + as well as when emulator instances start/stop. + + It thus maintains a list of "connected devices" and assigns a 'state' + to each one of them: OFFLINE, BOOTLOADER, RECOVERY or ONLINE (more on + this below). + + The ADB server is really one giant multiplexing loop whose purpose is + to orchestrate the exchange of data (packets, really) between clients, + services and devices. + + + 2. The ADB daemon (adbd) + + The 'adbd' program runs as a background process within an Android device + or emulated system. Its purpose is to connect to the ADB server + (through USB for devices, through TCP for emulators) and provide a + few services for clients that run on the host. + + The ADB server considers that a device is ONLINE when it has succesfully + connected to the adbd program within it. Otherwise, the device is OFFLINE, + meaning that the ADB server detected a new device/emulator, but could not + connect to the adbd daemon. + + the BOOTLOADER and RECOVERY states correspond to alternate states of + devices when they are in the bootloader or recovery mode. + + 3. The ADB command-line client + + The 'adb' command-line program is used to run adb commands from a shell + or a script. It first tries to locate the ADB server on the host machine, + and will start one automatically if none is found. + + then, the client sends its service requests to the ADB server. It doesn't + need to know. + + Currently, a single 'adb' binary is used for both the server and client. + this makes distribution and starting the server easier. + + + 4. Services + + There are essentially two kinds of services that a client can talk to. + + Host Services: + these services run within the ADB Server and thus do not need to + communicate with a device at all. A typical example is "adb devices" + which is used to return the list of currently known devices and their + state. They are a few couple other services though. + + Local Services: + these services either run within the adbd daemon, or are started by + it on the device. The ADB server is used to multiplex streams + between the client and the service running in adbd. In this case + its role is to initiate the connection, then of being a pass-through + for the data. + + +II. Protocol details: + + 1. Client <-> Server protocol: + + This details the protocol used between ADB clients and the ADB + server itself. The ADB server listens on TCP:localhost:5037. + + A client sends a request using the following format: + + 1. A 4-byte hexadecimal string giving the length of the payload + 2. Followed by the payload itself. + + For example, to query the ADB server for its internal version number, + the client will do the following: + + 1. Connect to tcp:localhost:5037 + 2. Send the string "000Chost:version" to the corresponding socket + + The 'host:' prefix is used to indicate that the request is addressed + to the server itself (we will talk about other kinds of requests later). + The content length is encoded in ASCII for easier debugging. + + The server should answer a request with one of the following: + + 1. For success, the 4-byte "OKAY" string + + 2. For failure, the 4-byte "FAIL" string, followed by a + 4-byte hex length, followed by a string giving the reason + for failure. + + 3. As a special exception, for 'host:version', a 4-byte + hex string corresponding to the server's internal version number + + Note that the connection is still alive after an OKAY, which allows the + client to make other requests. But in certain cases, an OKAY will even + change the state of the connection. + + For example, the case of the 'host:transport:' request, + where '' is used to identify a given device/emulator; after + the "OKAY" answer, all further requests made by the client will go + directly to the corresponding adbd daemon. + + The file SERVICES.TXT lists all services currently implemented by ADB. + + + 2. Transports: + + An ADB transport models a connection between the ADB server and one device + or emulator. There are currently two kinds of transports: + + - USB transports, for physical devices through USB + + - Local transports, for emulators running on the host, connected to + the server through TCP + + In theory, it should be possible to write a local transport that proxies + a connection between an ADB server and a device/emulator connected to/ + running on another machine. This hasn't been done yet though. + + Each transport can carry one or more multiplexed streams between clients + and the device/emulator they point to. The ADB server must handle + unexpected transport disconnections (e.g. when a device is physically + unplugged) properly. diff --git a/adb/SERVICES.TXT b/adb/SERVICES.TXT new file mode 100644 index 0000000..b0124a4 --- /dev/null +++ b/adb/SERVICES.TXT @@ -0,0 +1,236 @@ +This file tries to document all requests a client can make +to the ADB server of an adbd daemon. See the OVERVIEW.TXT document +to understand what's going on here. + +HOST SERVICES: + +host:version + Ask the ADB server for its internal version number. + + As a special exception, the server will respond with a 4-byte + hex string corresponding to its internal version number, without + any OKAY or FAIL. + +host:kill + Ask the ADB server to quit immediately. This is used when the + ADB client detects that an obsolete server is running after an + upgrade. + +host:devices + Ask to return the list of available Android devices and their + state. After the OKAY, this is followed by a 4-byte hex len, + and a string that will be dumped as-is by the client, then + the connection is closed + +host:track-devices + This is a variant of host:devices which doesn't close the + connection. Instead, a new device list description is sent + each time a device is added/removed or the state of a given + device changes (hex4 + content). This allows tools like DDMS + to track the state of connected devices in real-time without + polling the server repeatedly. + +host:emulator: + This is a special query that is sent to the ADB server when a + new emulator starts up. is a decimal number corresponding + to the emulator's ADB control port, i.e. the TCP port that the + emulator will forward automatically to the adbd daemon running + in the emulator system. + + This mechanism allows the ADB server to know when new emulator + instances start. + +host:transport: + Ask to switch the connection to the device/emulator identified by + . After the OKAY response, every client request will + be sent directly to the adbd daemon running on the device. + (Used to implement the -s option) + +host:transport-usb + Ask to switch the connection to one device connected through USB + to the host machine. This will fail if there are more than one such + devices. (Used to implement the -d convenience option) + +host:transport-local + Ask to switch the connection to one emulator connected through TCP. + This will fail if there is more than one such emulator instance + running. (Used to implement the -e convenience option) + +host:transport-any + Another host:transport variant. Ask to switch the connection to + either the device or emulator connect to/running on the host. + Will fail if there is more than one such device/emulator available. + (Used when neither -s, -d or -e are provided) + +host-serial:: + This is a special form of query, where the 'host-serial::' + prefix can be used to indicate that the client is asking the ADB server + for information related to a specific device. can be in one + of the format described below. + +host-usb: + A variant of host-serial used to target the single USB device connected + to the host. This will fail if there is none or more than one. + +host-local: + A variant of host-serial used to target the single emulator instance + running on the host. This will fail if therre is none or more than one. + +host: + When asking for information related to a device, 'host:' can also be + interpreted as 'any single device or emulator connected to/running on + the host'. + +:get-product + XXX + +:get-serialno + Returns the serial number of the corresponding device/emulator. + Note that emulator serial numbers are of the form "emulator-5554" + +:get-state + Returns the state of a given device as a string. + +:forward:; + Asks the ADB server to forward local connections from + to the address on a given device. + + There, can be one of the + host-serial/host-usb/host-local/host prefixes as described previously + and indicates which device/emulator to target. + + the format of is one of: + + tcp: -> TCP connection on localhost: + local: -> Unix local domain socket on + + the format of is one of: + + tcp: -> TCP localhost: on device + local: -> Unix local domain socket on device + jdwp: -> JDWP thread on VM process + + or even any one of the local services described below. + + + +LOCAL SERVICES: + +All the queries below assumed that you already switched the transport +to a real device, or that you have used a query prefix as described +above. + +shell:command arg1 arg2 ... + Run 'command arg1 arg2 ...' in a shell on the device, and return + its output and error streams. Note that arguments must be separated + by spaces. If an argument contains a space, it must be quoted with + double-quotes. Arguments cannot contain double quotes or things + will go very wrong. + + Note that this is the non-interactive version of "adb shell" + +shell: + Start an interactive shell session on the device. Redirect + stdin/stdout/stderr as appropriate. Note that the ADB server uses + this to implement "adb shell", but will also cook the input before + sending it to the device (see interactive_shell() in commandline.c) + +remount: + Ask adbd to remount the device's filesystem in read-write mode, + instead of read-only. This is usually necessary before performing + an "adb sync" or "adb push" request. + + This request may not succeed on certain builds which do not allow + that. + +dev: + Opens a device file and connects the client directly to it for + read/write purposes. Useful for debugging, but may require special + priviledges and thus may not run on all devices. is a full + path from the root of the filesystem. + +tcp: + Tries to connect to tcp port on localhost. + +tcp:: + Tries to connect to tcp port on machine from + the device. This can be useful to debug some networking/proxy + issues that can only be revealed on the device itself. + +local: + Tries to connect to a Unix domain socket on the device + +localreserved: +localabstract: +localfilesystem: + Variants of local: that are used to access other Android + socket namespaces. + +log: + Opens one of the system logs (/dev/log/) and allows the client + to read them directly. Used to implement 'adb logcat'. The stream + will be read-only for the client. + +framebuffer: + This service is used to send snapshots of the framebuffer to a client. + It requires sufficient priviledges but works as follow: + + After the OKAY, the service sends 16-byte binary structure + containing the following fields (little-endian format): + + depth: uint32_t: framebuffer depth + size: uint32_t: framebuffer size in bytes + width: uint32_t: framebuffer width in pixels + height: uint32_t: framebuffer height in pixels + + With the current implementation, depth is always 16, and + size is always width*height*2 + + Then, each time the client wants a snapshot, it should send + one byte through the channel, which will trigger the service + to send it 'size' bytes of framebuffer data. + + If the adbd daemon doesn't have sufficient priviledges to open + the framebuffer device, the connection is simply closed immediately. + +dns: + This service is an exception because it only runs within the ADB server. + It is used to implement USB networking, i.e. to provide a network connection + to the device through the host machine (note: this is the exact opposite of + network thetering). + + It is used to perform a gethostbyname(
) on the host and return + the corresponding IP address as a 4-byte string. + +recover: + This service is used to upload a recovery image to the device. + must be a number corresponding to the size of the file. The service works + by: + + - creating a file named /tmp/update + - reading 'size' bytes from the client and writing them to /tmp/update + - when everything is read succesfully, create a file named /tmp/update.start + + This service can only work when the device is in recovery mode. Otherwise, + the /tmp directory doesn't exist and the connection will be closed immediately. + +jdwp: + Connects to the JDWP thread running in the VM of process . + +track-jdwp + This is used to send the list of JDWP pids periodically to the client. + The format of the returned data is the following: + + : the length of all content as a 4-char hexadecimal string + : a series of ASCII lines of the following format: + "\n" + + This service is used by DDMS to know which debuggable processes are running + on the device/emulator. + + Note that there is no single-shot service to retrieve the list only once. + +sync: + This starts the file synchronisation service, used to implement "adb push" + and "adb pull". Since this service is pretty complex, it will be detailed + in a companion document named SYNC.TXT diff --git a/adb/adb.c b/adb/adb.c new file mode 100644 index 0000000..fa5269f --- /dev/null +++ b/adb/adb.c @@ -0,0 +1,1083 @@ +/* + * Copyright (C) 2007 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. + */ + +#define TRACE_TAG TRACE_ADB + +#include +#include +#include +#include +#include +#include +#include + +#include "sysdeps.h" +#include "adb.h" + +#if !ADB_HOST +#include +#endif + + +int HOST = 0; + +static const char *adb_device_banner = "device"; + +void fatal(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + fprintf(stderr, "error: "); + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); + va_end(ap); + exit(-1); +} + +void fatal_errno(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + fprintf(stderr, "error: %s: ", strerror(errno)); + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); + va_end(ap); + exit(-1); +} + +int adb_trace_mask; + +/* read a comma/space/colum/semi-column separated list of tags + * from the ADB_TRACE environment variable and build the trace + * mask from it. note that '1' and 'all' are special cases to + * enable all tracing + */ +void adb_trace_init(void) +{ + const char* p = getenv("ADB_TRACE"); + const char* q; + + static const struct { + const char* tag; + int flag; + } tags[] = { + { "1", 0 }, + { "all", 0 }, + { "adb", TRACE_ADB }, + { "sockets", TRACE_SOCKETS }, + { "packets", TRACE_PACKETS }, + { "rwx", TRACE_RWX }, + { "usb", TRACE_USB }, + { "sync", TRACE_SYNC }, + { "sysdeps", TRACE_SYSDEPS }, + { "transport", TRACE_TRANSPORT }, + { "jdwp", TRACE_JDWP }, + { NULL, 0 } + }; + + if (p == NULL) + return; + + /* use a comma/column/semi-colum/space separated list */ + while (*p) { + int len, tagn; + + q = strpbrk(p, " ,:;"); + if (q == NULL) { + q = p + strlen(p); + } + len = q - p; + + for (tagn = 0; tags[tagn].tag != NULL; tagn++) + { + int taglen = strlen(tags[tagn].tag); + + if (len == taglen && !memcmp(tags[tagn].tag, p, len) ) + { + int flag = tags[tagn].flag; + if (flag == 0) { + adb_trace_mask = ~0; + return; + } + adb_trace_mask |= (1 << flag); + break; + } + } + p = q; + if (*p) + p++; + } +} + + +apacket *get_apacket(void) +{ + apacket *p = malloc(sizeof(apacket)); + if(p == 0) fatal("failed to allocate an apacket"); + memset(p, 0, sizeof(apacket) - MAX_PAYLOAD); + return p; +} + +void put_apacket(apacket *p) +{ + free(p); +} + +void handle_online(void) +{ + D("adb: online\n"); +#if !ADB_HOST + property_set("adb.connected","1"); +#endif +} + +void handle_offline(atransport *t) +{ + D("adb: offline\n"); + //Close the associated usb + run_transport_disconnects(t); +#if !ADB_HOST + property_set("adb.connected",""); +#endif +} + +#if TRACE_PACKETS +#define DUMPMAX 32 +void print_packet(const char *label, apacket *p) +{ + char *tag; + char *x; + unsigned count; + + switch(p->msg.command){ + case A_SYNC: tag = "SYNC"; break; + case A_CNXN: tag = "CNXN" ; break; + case A_OPEN: tag = "OPEN"; break; + case A_OKAY: tag = "OKAY"; break; + case A_CLSE: tag = "CLSE"; break; + case A_WRTE: tag = "WRTE"; break; + default: tag = "????"; break; + } + + fprintf(stderr, "%s: %s %08x %08x %04x \"", + label, tag, p->msg.arg0, p->msg.arg1, p->msg.data_length); + count = p->msg.data_length; + x = (char*) p->data; + if(count > DUMPMAX) { + count = DUMPMAX; + tag = "\n"; + } else { + tag = "\"\n"; + } + while(count-- > 0){ + if((*x >= ' ') && (*x < 127)) { + fputc(*x, stderr); + } else { + fputc('.', stderr); + } + x++; + } + fprintf(stderr, tag); +} +#endif + +static void send_ready(unsigned local, unsigned remote, atransport *t) +{ + D("Calling send_ready \n"); + apacket *p = get_apacket(); + p->msg.command = A_OKAY; + p->msg.arg0 = local; + p->msg.arg1 = remote; + send_packet(p, t); +} + +static void send_close(unsigned local, unsigned remote, atransport *t) +{ + D("Calling send_close \n"); + apacket *p = get_apacket(); + p->msg.command = A_CLSE; + p->msg.arg0 = local; + p->msg.arg1 = remote; + send_packet(p, t); +} + +static void send_connect(atransport *t) +{ + D("Calling send_connect \n"); + apacket *cp = get_apacket(); + cp->msg.command = A_CNXN; + cp->msg.arg0 = A_VERSION; + cp->msg.arg1 = MAX_PAYLOAD; + snprintf((char*) cp->data, sizeof cp->data, "%s::", + HOST ? "host" : adb_device_banner); + cp->msg.data_length = strlen((char*) cp->data) + 1; + send_packet(cp, t); +#if ADB_HOST + /* XXX why sleep here? */ + // allow the device some time to respond to the connect message + adb_sleep_ms(1000); +#endif +} + +static char *connection_state_name(atransport *t) +{ + if (t == NULL) { + return "unknown"; + } + + switch(t->connection_state) { + case CS_BOOTLOADER: + return "bootloader"; + case CS_DEVICE: + return "device"; + case CS_OFFLINE: + return "offline"; + default: + return "unknown"; + } +} + +void parse_banner(char *banner, atransport *t) +{ + char *type, *product, *end; + + D("parse_banner: %s\n", banner); + type = banner; + product = strchr(type, ':'); + if(product) { + *product++ = 0; + } else { + product = ""; + } + + /* remove trailing ':' */ + end = strchr(product, ':'); + if(end) *end = 0; + + /* save product name in device structure */ + if (t->product == NULL) { + t->product = strdup(product); + } else if (strcmp(product, t->product) != 0) { + free(t->product); + t->product = strdup(product); + } + + if(!strcmp(type, "bootloader")){ + D("setting connection_state to CS_BOOTLOADER\n"); + t->connection_state = CS_BOOTLOADER; + update_transports(); + return; + } + + if(!strcmp(type, "device")) { + D("setting connection_state to CS_DEVICE\n"); + t->connection_state = CS_DEVICE; + update_transports(); + return; + } + + if(!strcmp(type, "recovery")) { + D("setting connection_state to CS_RECOVERY\n"); + t->connection_state = CS_RECOVERY; + update_transports(); + return; + } + + t->connection_state = CS_HOST; +} + +void handle_packet(apacket *p, atransport *t) +{ + asocket *s; + + D("handle_packet() %d\n", p->msg.command); + + print_packet("recv", p); + + switch(p->msg.command){ + case A_SYNC: + if(p->msg.arg0){ + send_packet(p, t); + if(HOST) send_connect(t); + } else { + t->connection_state = CS_OFFLINE; + handle_offline(t); + send_packet(p, t); + } + return; + + case A_CNXN: /* CONNECT(version, maxdata, "system-id-string") */ + /* XXX verify version, etc */ + if(t->connection_state != CS_OFFLINE) { + t->connection_state = CS_OFFLINE; + handle_offline(t); + } + parse_banner((char*) p->data, t); + handle_online(); + if(!HOST) send_connect(t); + break; + + case A_OPEN: /* OPEN(local-id, 0, "destination") */ + if(t->connection_state != CS_OFFLINE) { + char *name = (char*) p->data; + name[p->msg.data_length > 0 ? p->msg.data_length - 1 : 0] = 0; + s = create_local_service_socket(name); + if(s == 0) { + send_close(0, p->msg.arg0, t); + } else { + s->peer = create_remote_socket(p->msg.arg0, t); + s->peer->peer = s; + send_ready(s->id, s->peer->id, t); + s->ready(s); + } + } + break; + + case A_OKAY: /* READY(local-id, remote-id, "") */ + if(t->connection_state != CS_OFFLINE) { + if((s = find_local_socket(p->msg.arg1))) { + if(s->peer == 0) { + s->peer = create_remote_socket(p->msg.arg0, t); + s->peer->peer = s; + } + s->ready(s); + } + } + break; + + case A_CLSE: /* CLOSE(local-id, remote-id, "") */ + if(t->connection_state != CS_OFFLINE) { + if((s = find_local_socket(p->msg.arg1))) { + s->close(s); + } + } + break; + + case A_WRTE: + if(t->connection_state != CS_OFFLINE) { + if((s = find_local_socket(p->msg.arg1))) { + unsigned rid = p->msg.arg0; + p->len = p->msg.data_length; + + if(s->enqueue(s, p) == 0) { + D("Enqueue the socket\n"); + send_ready(s->id, rid, t); + } + return; + } + } + break; + + default: + printf("handle_packet: what is %08x?!\n", p->msg.command); + } + + put_apacket(p); +} + +alistener listener_list = { + .next = &listener_list, + .prev = &listener_list, +}; + +static void ss_listener_event_func(int _fd, unsigned ev, void *_l) +{ + asocket *s; + + if(ev & FDE_READ) { + struct sockaddr addr; + socklen_t alen; + int fd; + + alen = sizeof(addr); + fd = adb_socket_accept(_fd, &addr, &alen); + if(fd < 0) return; + + adb_socket_setbufsize(fd, CHUNK_SIZE); + + s = create_local_socket(fd); + if(s) { + connect_to_smartsocket(s); + return; + } + + adb_close(fd); + } +} + +static void listener_event_func(int _fd, unsigned ev, void *_l) +{ + alistener *l = _l; + asocket *s; + + if(ev & FDE_READ) { + struct sockaddr addr; + socklen_t alen; + int fd; + + alen = sizeof(addr); + fd = adb_socket_accept(_fd, &addr, &alen); + if(fd < 0) return; + + s = create_local_socket(fd); + if(s) { + s->transport = l->transport; + connect_to_remote(s, l->connect_to); + return; + } + + adb_close(fd); + } +} + +static void free_listener(alistener* l) +{ + if (l->next) { + l->next->prev = l->prev; + l->prev->next = l->next; + l->next = l->prev = l; + } + + // closes the corresponding fd + fdevent_remove(&l->fde); + + if (l->local_name) + free((char*)l->local_name); + + if (l->connect_to) + free((char*)l->connect_to); + + if (l->transport) { + remove_transport_disconnect(l->transport, &l->disconnect); + } + free(l); +} + +static void listener_disconnect(void* _l, atransport* t) +{ + alistener* l = _l; + + free_listener(l); +} + +int local_name_to_fd(const char *name) +{ + int port; + + if(!strncmp("tcp:", name, 4)){ + int ret; + port = atoi(name + 4); + ret = socket_loopback_server(port, SOCK_STREAM); + return ret; + } +#ifndef HAVE_WIN32_IPC /* no Unix-domain sockets on Win32 */ + // It's non-sensical to support the "reserved" space on the adb host side + if(!strncmp(name, "local:", 6)) { + return socket_local_server(name + 6, + ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM); + } else if(!strncmp(name, "localabstract:", 14)) { + return socket_local_server(name + 14, + ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM); + } else if(!strncmp(name, "localfilesystem:", 16)) { + return socket_local_server(name + 16, + ANDROID_SOCKET_NAMESPACE_FILESYSTEM, SOCK_STREAM); + } + +#endif + printf("unknown local portname '%s'\n", name); + return -1; +} + +static int remove_listener(const char *local_name, const char *connect_to, atransport* transport) +{ + alistener *l; + + for (l = listener_list.next; l != &listener_list; l = l->next) { + if (!strcmp(local_name, l->local_name) && + !strcmp(connect_to, l->connect_to) && + l->transport && l->transport == transport) { + + listener_disconnect(l, transport); + return 0; + } + } + + return -1; +} + +static int install_listener(const char *local_name, const char *connect_to, atransport* transport) +{ + alistener *l; + + //printf("install_listener('%s','%s')\n", local_name, connect_to); + + for(l = listener_list.next; l != &listener_list; l = l->next){ + if(strcmp(local_name, l->local_name) == 0) { + char *cto; + + /* can't repurpose a smartsocket */ + if(l->connect_to[0] == '*') { + return -1; + } + + cto = strdup(connect_to); + if(cto == 0) { + return -1; + } + + //printf("rebinding '%s' to '%s'\n", local_name, connect_to); + free((void*) l->connect_to); + l->connect_to = cto; + if (l->transport != transport) { + remove_transport_disconnect(l->transport, &l->disconnect); + l->transport = transport; + add_transport_disconnect(l->transport, &l->disconnect); + } + return 0; + } + } + + if((l = calloc(1, sizeof(alistener))) == 0) goto nomem; + if((l->local_name = strdup(local_name)) == 0) goto nomem; + if((l->connect_to = strdup(connect_to)) == 0) goto nomem; + + + l->fd = local_name_to_fd(local_name); + if(l->fd < 0) { + free((void*) l->local_name); + free((void*) l->connect_to); + free(l); + printf("cannot bind '%s'\n", local_name); + return -2; + } + + close_on_exec(l->fd); + if(!strcmp(l->connect_to, "*smartsocket*")) { + fdevent_install(&l->fde, l->fd, ss_listener_event_func, l); + } else { + fdevent_install(&l->fde, l->fd, listener_event_func, l); + } + fdevent_set(&l->fde, FDE_READ); + + l->next = &listener_list; + l->prev = listener_list.prev; + l->next->prev = l; + l->prev->next = l; + l->transport = transport; + + if (transport) { + l->disconnect.opaque = l; + l->disconnect.func = listener_disconnect; + add_transport_disconnect(transport, &l->disconnect); + } + return 0; + +nomem: + fatal("cannot allocate listener"); + return 0; +} + +#ifdef HAVE_FORKEXEC +static void sigchld_handler(int n) +{ + int status; + while(waitpid(-1, &status, WNOHANG) > 0) ; +} +#endif + +#ifdef HAVE_WIN32_PROC +static BOOL WINAPI ctrlc_handler(DWORD type) +{ + exit(STATUS_CONTROL_C_EXIT); + return TRUE; +} +#endif + +static void adb_cleanup(void) +{ + usb_cleanup(); +} + +void start_logging(void) +{ +#ifdef HAVE_WIN32_PROC + char temp[ MAX_PATH ]; + FILE* fnul; + FILE* flog; + + GetTempPath( sizeof(temp) - 8, temp ); + strcat( temp, "adb.log" ); + + /* Win32 specific redirections */ + fnul = fopen( "NUL", "rt" ); + if (fnul != NULL) + stdin[0] = fnul[0]; + + flog = fopen( temp, "at" ); + if (flog == NULL) + flog = fnul; + + setvbuf( flog, NULL, _IONBF, 0 ); + + stdout[0] = flog[0]; + stderr[0] = flog[0]; + fprintf(stderr,"--- adb starting (pid %d) ---\n", getpid()); +#else + int fd; + + fd = unix_open("/dev/null", O_RDONLY); + dup2(fd, 0); + + fd = unix_open("/tmp/adb.log", O_WRONLY | O_CREAT | O_APPEND, 0640); + if(fd < 0) { + fd = unix_open("/dev/null", O_WRONLY); + } + dup2(fd, 1); + dup2(fd, 2); + fprintf(stderr,"--- adb starting (pid %d) ---\n", getpid()); +#endif +} + +#if !ADB_HOST +void start_device_log(void) +{ + int fd; + char path[100]; + + snprintf(path, sizeof path, "/data/adb_%ld.txt", (long)time(NULL)); + fd = unix_open(path, O_WRONLY | O_CREAT | O_APPEND, 0640); + if (fd < 0) + return; + + // redirect stdout and stderr to the log file + dup2(fd, 1); + dup2(fd, 2); + fprintf(stderr,"--- adb starting (pid %d) ---\n", getpid()); + + fd = unix_open("/dev/null", O_RDONLY); + dup2(fd, 0); + + // log everything + adb_trace_mask = ~0; + // except TRACE_RWX is a bit too verbose + adb_trace_mask &= ~TRACE_RWX; +} +#endif + +#if ADB_HOST +int launch_server() +{ +#ifdef HAVE_WIN32_PROC + /* we need to start the server in the background */ + /* we create a PIPE that will be used to wait for the server's "OK" */ + /* message since the pipe handles must be inheritable, we use a */ + /* security attribute */ + HANDLE pipe_read, pipe_write; + SECURITY_ATTRIBUTES sa; + STARTUPINFO startup; + PROCESS_INFORMATION pinfo; + char program_path[ MAX_PATH ]; + int ret; + + sa.nLength = sizeof(sa); + sa.lpSecurityDescriptor = NULL; + sa.bInheritHandle = TRUE; + + /* create pipe, and ensure its read handle isn't inheritable */ + ret = CreatePipe( &pipe_read, &pipe_write, &sa, 0 ); + if (!ret) { + fprintf(stderr, "CreatePipe() failure, error %ld\n", GetLastError() ); + return -1; + } + + SetHandleInformation( pipe_read, HANDLE_FLAG_INHERIT, 0 ); + + ZeroMemory( &startup, sizeof(startup) ); + startup.cb = sizeof(startup); + startup.hStdInput = GetStdHandle( STD_INPUT_HANDLE ); + startup.hStdOutput = pipe_write; + startup.hStdError = GetStdHandle( STD_ERROR_HANDLE ); + startup.dwFlags = STARTF_USESTDHANDLES; + + ZeroMemory( &pinfo, sizeof(pinfo) ); + + /* get path of current program */ + GetModuleFileName( NULL, program_path, sizeof(program_path) ); + + ret = CreateProcess( + program_path, /* program path */ + "adb fork-server server", + /* the fork-server argument will set the + debug = 2 in the child */ + NULL, /* process handle is not inheritable */ + NULL, /* thread handle is not inheritable */ + TRUE, /* yes, inherit some handles */ + DETACHED_PROCESS, /* the new process doesn't have a console */ + NULL, /* use parent's environment block */ + NULL, /* use parent's starting directory */ + &startup, /* startup info, i.e. std handles */ + &pinfo ); + + CloseHandle( pipe_write ); + + if (!ret) { + fprintf(stderr, "CreateProcess failure, error %ld\n", GetLastError() ); + CloseHandle( pipe_read ); + return -1; + } + + CloseHandle( pinfo.hProcess ); + CloseHandle( pinfo.hThread ); + + /* wait for the "OK\n" message */ + { + char temp[3]; + DWORD count; + + ret = ReadFile( pipe_read, temp, 3, &count, NULL ); + CloseHandle( pipe_read ); + if ( !ret ) { + fprintf(stderr, "could not read ok from ADB Server, error = %ld\n", GetLastError() ); + return -1; + } + if (count != 3 || temp[0] != 'O' || temp[1] != 'K' || temp[2] != '\n') { + fprintf(stderr, "ADB server didn't ACK\n" ); + return -1; + } + } +#elif defined(HAVE_FORKEXEC) + char path[PATH_MAX]; + int fd[2]; + + // set up a pipe so the child can tell us when it is ready. + // fd[0] will be parent's end, and fd[1] will get mapped to stderr in the child. + if (pipe(fd)) { + fprintf(stderr, "pipe failed in launch_server, errno: %d\n", errno); + return -1; + } + get_my_path(path); + pid_t pid = fork(); + if(pid < 0) return -1; + + if (pid == 0) { + // child side of the fork + + // redirect stderr to the pipe + // we use stderr instead of stdout due to stdout's buffering behavior. + adb_close(fd[0]); + dup2(fd[1], STDERR_FILENO); + adb_close(fd[1]); + + // child process + int result = execl(path, "adb", "fork-server", "server", NULL); + // this should not return + fprintf(stderr, "OOPS! execl returned %d, errno: %d\n", result, errno); + } else { + // parent side of the fork + + char temp[3]; + + temp[0] = 'A'; temp[1] = 'B'; temp[2] = 'C'; + // wait for the "OK\n" message + adb_close(fd[1]); + int ret = adb_read(fd[0], temp, 3); + adb_close(fd[0]); + if (ret < 0) { + fprintf(stderr, "could not read ok from ADB Server, errno = %d\n", errno); + return -1; + } + if (ret != 3 || temp[0] != 'O' || temp[1] != 'K' || temp[2] != '\n') { + fprintf(stderr, "ADB server didn't ACK\n" ); + return -1; + } + + setsid(); + } +#else +#error "cannot implement background server start on this platform" +#endif + return 0; +} +#endif + +int adb_main(int is_daemon) +{ +#if !ADB_HOST + int secure = 0; + char value[PROPERTY_VALUE_MAX]; + + // prevent the OOM killer from killing us + char text[64]; + snprintf(text, sizeof text, "/proc/%d/oom_adj", (int)getpid()); + int fd = adb_open(text, O_WRONLY); + if (fd >= 0) { + // -17 should make us immune to OOM + snprintf(text, sizeof text, "%d", -17); + adb_write(fd, text, strlen(text)); + adb_close(fd); + } else { + D("adb: unable to open %s\n", text); + } +#endif + + atexit(adb_cleanup); +#ifdef HAVE_WIN32_PROC + SetConsoleCtrlHandler( ctrlc_handler, TRUE ); +#elif defined(HAVE_FORKEXEC) + signal(SIGCHLD, sigchld_handler); + signal(SIGPIPE, SIG_IGN); +#endif + + init_transport_registration(); + + +#if ADB_HOST + HOST = 1; + usb_init(); + local_init(); + + if(install_listener("tcp:5037", "*smartsocket*", NULL)) { + exit(1); + } +#else + /* run adbd in secure mode if ro.secure is set and + ** we are not in the emulator + */ + property_get("ro.kernel.qemu", value, ""); + if (strcmp(value, "1") != 0) { + property_get("ro.secure", value, ""); + if (strcmp(value, "1") == 0) + secure = 1; + } + + /* don't listen on port 5037 if we are running in secure mode */ + /* don't run as root if we are running in secure mode */ + if (secure) { + /* add extra groups: + ** AID_ADB to access the USB driver + ** AID_LOG to read system logs (adb logcat) + ** AID_INPUT to diagnose input issues (getevent) + ** AID_INET to diagnose network issues (netcfg, ping) + ** AID_GRAPHICS to access the frame buffer + */ + gid_t groups[] = { AID_ADB, AID_LOG, AID_INPUT, AID_INET, AID_GRAPHICS }; + setgroups(sizeof(groups)/sizeof(groups[0]), groups); + + /* then switch user and group to "shell" */ + setgid(AID_SHELL); + setuid(AID_SHELL); + + D("Local port 5037 disabled\n"); + } else { + if(install_listener("tcp:5037", "*smartsocket*", NULL)) { + exit(1); + } + } + + /* for the device, start the usb transport if the + ** android usb device exists, otherwise start the + ** network transport. + */ + if(access("/dev/android_adb", F_OK) == 0 || + access("/dev/android", F_OK) == 0) { + usb_init(); + } else { + local_init(); + } + init_jdwp(); +#endif + + if (is_daemon) + { + // inform our parent that we are up and running. +#ifdef HAVE_WIN32_PROC + DWORD count; + WriteFile( GetStdHandle( STD_OUTPUT_HANDLE ), "OK\n", 3, &count, NULL ); +#elif defined(HAVE_FORKEXEC) + fprintf(stderr, "OK\n"); +#endif + start_logging(); + } + + fdevent_loop(); + + usb_cleanup(); + + return 0; +} + +int handle_host_request(char *service, transport_type ttype, char* serial, int reply_fd, asocket *s) +{ + atransport *transport = NULL; + char buf[4096]; + + if(!strcmp(service, "kill")) { + fprintf(stderr,"adb server killed by remote request\n"); + fflush(stdout); + adb_write(reply_fd, "OKAY", 4); + usb_cleanup(); + exit(0); + } + +#if ADB_HOST + // "transport:" is used for switching transport with a specified serial number + // "transport-usb:" is used for switching transport to the only USB transport + // "transport-local:" is used for switching transport to the only local transport + // "transport-any:" is used for switching transport to the only transport + if (!strncmp(service, "transport", strlen("transport"))) { + char* error_string = "unknown failure"; + transport_type type = kTransportAny; + + if (!strncmp(service, "transport-usb", strlen("transport-usb"))) { + type = kTransportUsb; + } else if (!strncmp(service, "transport-local", strlen("transport-local"))) { + type = kTransportLocal; + } else if (!strncmp(service, "transport-any", strlen("transport-any"))) { + type = kTransportAny; + } else if (!strncmp(service, "transport:", strlen("transport:"))) { + service += strlen("transport:"); + serial = strdup(service); + } + + transport = acquire_one_transport(CS_ANY, type, serial, &error_string); + + if (transport) { + s->transport = transport; + adb_write(reply_fd, "OKAY", 4); + } else { + sendfailmsg(reply_fd, error_string); + } + return 1; + } + + // return a list of all connected devices + if (!strcmp(service, "devices")) { + char buffer[4096]; + memset(buf, 0, sizeof(buf)); + memset(buffer, 0, sizeof(buffer)); + D("Getting device list \n"); + list_transports(buffer, sizeof(buffer)); + snprintf(buf, sizeof(buf), "OKAY%04x%s",(unsigned)strlen(buffer),buffer); + D("Wrote device list \n"); + writex(reply_fd, buf, strlen(buf)); + return 0; + } + + // returns our value for ADB_SERVER_VERSION + if (!strcmp(service, "version")) { + char version[12]; + snprintf(version, sizeof version, "%04x", ADB_SERVER_VERSION); + snprintf(buf, sizeof buf, "OKAY%04x%s", (unsigned)strlen(version), version); + writex(reply_fd, buf, strlen(buf)); + return 0; + } + + if(!strncmp(service,"get-serialno",strlen("get-serialno"))) { + char *out = "unknown"; + transport = acquire_one_transport(CS_ANY, ttype, serial, NULL); + if (transport && transport->serial) { + out = transport->serial; + } + snprintf(buf, sizeof buf, "OKAY%04x%s",(unsigned)strlen(out),out); + writex(reply_fd, buf, strlen(buf)); + return 0; + } + // indicates a new emulator instance has started + if (!strncmp(service,"emulator:",9)) { + int port = atoi(service+9); + local_connect(port); + /* we don't even need to send a reply */ + return 0; + } +#endif // ADB_HOST + + if(!strncmp(service,"forward:",8) || !strncmp(service,"killforward:",12)) { + char *local, *remote, *err; + int r; + atransport *transport; + + int createForward = strncmp(service,"kill",4); + + local = service + (createForward ? 8 : 12); + remote = strchr(local,';'); + if(remote == 0) { + sendfailmsg(reply_fd, "malformed forward spec"); + return 0; + } + + *remote++ = 0; + if((local[0] == 0) || (remote[0] == 0) || (remote[0] == '*')){ + sendfailmsg(reply_fd, "malformed forward spec"); + return 0; + } + + transport = acquire_one_transport(CS_ANY, ttype, serial, &err); + if (!transport) { + sendfailmsg(reply_fd, err); + return 0; + } + + if (createForward) { + r = install_listener(local, remote, transport); + } else { + r = remove_listener(local, remote, transport); + } + if(r == 0) { + /* 1st OKAY is connect, 2nd OKAY is status */ + writex(reply_fd, "OKAYOKAY", 8); + return 0; + } + + if (createForward) { + sendfailmsg(reply_fd, (r == -1) ? "cannot rebind smartsocket" : "cannot bind socket"); + } else { + sendfailmsg(reply_fd, "cannot remove listener"); + } + return 0; + } + + if(!strncmp(service,"get-state",strlen("get-state"))) { + transport = acquire_one_transport(CS_ANY, ttype, serial, NULL); + char *state = connection_state_name(transport); + snprintf(buf, sizeof buf, "OKAY%04x%s",(unsigned)strlen(state),state); + writex(reply_fd, buf, strlen(buf)); + return 0; + } + return -1; +} + +#if !ADB_HOST +int recovery_mode = 0; +#endif + +int main(int argc, char **argv) +{ + adb_trace_init(); +#if ADB_HOST + adb_sysdeps_init(); + return adb_commandline(argc - 1, argv + 1); +#else + if((argc > 1) && (!strcmp(argv[1],"recovery"))) { + adb_device_banner = "recovery"; + recovery_mode = 1; + } +#if ADB_DEVICE_LOG + start_device_log(); +#endif + return adb_main(0); +#endif +} + diff --git a/adb/adb.h b/adb/adb.h new file mode 100644 index 0000000..a17c8dd --- /dev/null +++ b/adb/adb.h @@ -0,0 +1,407 @@ +/* + * Copyright (C) 2007 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. + */ + +#ifndef __ADB_H +#define __ADB_H + +#include + +#define MAX_PAYLOAD 4096 + +#define A_SYNC 0x434e5953 +#define A_CNXN 0x4e584e43 +#define A_OPEN 0x4e45504f +#define A_OKAY 0x59414b4f +#define A_CLSE 0x45534c43 +#define A_WRTE 0x45545257 + +#define A_VERSION 0x01000000 // ADB protocol version + +#define ADB_VERSION_MAJOR 1 // Used for help/version information +#define ADB_VERSION_MINOR 0 // Used for help/version information + +#define ADB_SERVER_VERSION 20 // Increment this when we want to force users to start a new adb server + +typedef struct amessage amessage; +typedef struct apacket apacket; +typedef struct asocket asocket; +typedef struct alistener alistener; +typedef struct aservice aservice; +typedef struct atransport atransport; +typedef struct adisconnect adisconnect; +typedef struct usb_handle usb_handle; + +struct amessage { + unsigned command; /* command identifier constant */ + unsigned arg0; /* first argument */ + unsigned arg1; /* second argument */ + unsigned data_length; /* length of payload (0 is allowed) */ + unsigned data_check; /* checksum of data payload */ + unsigned magic; /* command ^ 0xffffffff */ +}; + +struct apacket +{ + apacket *next; + + unsigned len; + unsigned char *ptr; + + amessage msg; + unsigned char data[MAX_PAYLOAD]; +}; + +/* An asocket represents one half of a connection between a local and +** remote entity. A local asocket is bound to a file descriptor. A +** remote asocket is bound to the protocol engine. +*/ +struct asocket { + /* chain pointers for the local/remote list of + ** asockets that this asocket lives in + */ + asocket *next; + asocket *prev; + + /* the unique identifier for this asocket + */ + unsigned id; + + /* flag: set when the socket's peer has closed + ** but packets are still queued for delivery + */ + int closing; + + /* the asocket we are connected to + */ + + asocket *peer; + + /* For local asockets, the fde is used to bind + ** us to our fd event system. For remote asockets + ** these fields are not used. + */ + fdevent fde; + int fd; + + /* queue of apackets waiting to be written + */ + apacket *pkt_first; + apacket *pkt_last; + + /* enqueue is called by our peer when it has data + ** for us. It should return 0 if we can accept more + ** data or 1 if not. If we return 1, we must call + ** peer->ready() when we once again are ready to + ** receive data. + */ + int (*enqueue)(asocket *s, apacket *pkt); + + /* ready is called by the peer when it is ready for + ** us to send data via enqueue again + */ + void (*ready)(asocket *s); + + /* close is called by the peer when it has gone away. + ** we are not allowed to make any further calls on the + ** peer once our close method is called. + */ + void (*close)(asocket *s); + + /* socket-type-specific extradata */ + void *extra; + + /* A socket is bound to atransport */ + atransport *transport; +}; + + +/* the adisconnect structure is used to record a callback that +** will be called whenever a transport is disconnected (e.g. by the user) +** this should be used to cleanup objects that depend on the +** transport (e.g. remote sockets, listeners, etc...) +*/ +struct adisconnect +{ + void (*func)(void* opaque, atransport* t); + void* opaque; + adisconnect* next; + adisconnect* prev; +}; + + +/* a transport object models the connection to a remote device or emulator +** there is one transport per connected device/emulator. a "local transport" +** connects through TCP (for the emulator), while a "usb transport" through +** USB (for real devices) +** +** note that kTransportHost doesn't really correspond to a real transport +** object, it's a special value used to indicate that a client wants to +** connect to a service implemented within the ADB server itself. +*/ +typedef enum transport_type { + kTransportUsb, + kTransportLocal, + kTransportAny, + kTransportHost, +} transport_type; + +struct atransport +{ + atransport *next; + atransport *prev; + + int (*read_from_remote)(apacket *p, atransport *t); + int (*write_to_remote)(apacket *p, atransport *t); + void (*close)(atransport *t); + void (*kick)(atransport *t); + + int fd; + int transport_socket; + fdevent transport_fde; + int ref_count; + unsigned sync_token; + int connection_state; + transport_type type; + + /* usb handle or socket fd as needed */ + usb_handle *usb; + int sfd; + + /* used to identify transports for clients */ + char *serial; + char *product; + + /* a list of adisconnect callbacks called when the transport is kicked */ + int kicked; + adisconnect disconnects; +}; + + +/* A listener is an entity which binds to a local port +** and, upon receiving a connection on that port, creates +** an asocket to connect the new local connection to a +** specific remote service. +** +** TODO: some listeners read from the new connection to +** determine what exact service to connect to on the far +** side. +*/ +struct alistener +{ + alistener *next; + alistener *prev; + + fdevent fde; + int fd; + + const char *local_name; + const char *connect_to; + atransport *transport; + adisconnect disconnect; +}; + + +void print_packet(const char *label, apacket *p); + +asocket *find_local_socket(unsigned id); +void install_local_socket(asocket *s); +void remove_socket(asocket *s); +void close_all_sockets(atransport *t); + +#define LOCAL_CLIENT_PREFIX "emulator-" + +asocket *create_local_socket(int fd); +asocket *create_local_service_socket(const char *destination); + +asocket *create_remote_socket(unsigned id, atransport *t); +void connect_to_remote(asocket *s, const char *destination); +void connect_to_smartsocket(asocket *s); + +void fatal(const char *fmt, ...); +void fatal_errno(const char *fmt, ...); + +void handle_packet(apacket *p, atransport *t); +void send_packet(apacket *p, atransport *t); + +void get_my_path(char s[PATH_MAX]); +int launch_server(); +int adb_main(int is_daemon); + + +/* transports are ref-counted +** get_device_transport does an acquire on your behalf before returning +*/ +void init_transport_registration(void); +int list_transports(char *buf, size_t bufsize); +void update_transports(void); + +asocket* create_device_tracker(void); + +/* Obtain a transport from the available transports. +** If state is != CS_ANY, only transports in that state are considered. +** If serial is non-NULL then only the device with that serial will be chosen. +** If no suitable transport is found, error is set. +*/ +atransport *acquire_one_transport(int state, transport_type ttype, const char* serial, char **error_out); +void add_transport_disconnect( atransport* t, adisconnect* dis ); +void remove_transport_disconnect( atransport* t, adisconnect* dis ); +void run_transport_disconnects( atransport* t ); +void kick_transport( atransport* t ); + +/* initialize a transport object's func pointers and state */ +int init_socket_transport(atransport *t, int s, int port); +void init_usb_transport(atransport *t, usb_handle *usb); + +/* for MacOS X cleanup */ +void close_usb_devices(); + +/* cause new transports to be init'd and added to the list */ +void register_socket_transport(int s, const char *serial, int port); +void register_usb_transport(usb_handle *h, const char *serial); + +int service_to_fd(const char *name); +#if ADB_HOST +asocket *host_service_to_socket(const char* name, const char *serial); +#endif + +#if !ADB_HOST +int init_jdwp(void); +asocket* create_jdwp_service_socket(); +asocket* create_jdwp_tracker_service_socket(); +int create_jdwp_connection_fd(int jdwp_pid); +#endif + +#if !ADB_HOST +void framebuffer_service(int fd, void *cookie); +void log_service(int fd, void *cookie); +void remount_service(int fd, void *cookie); +char * get_log_file_path(const char * log_name); +#endif + +/* packet allocator */ +apacket *get_apacket(void); +void put_apacket(apacket *p); + +int check_header(apacket *p); +int check_data(apacket *p); + +/* convenience wrappers around read/write that will retry on +** EINTR and/or short read/write. Returns 0 on success, -1 +** on error or EOF. +*/ +int readx(int fd, void *ptr, size_t len); +int writex(int fd, const void *ptr, size_t len); + +/* define ADB_TRACE to 1 to enable tracing support, or 0 to disable it */ + +#define ADB_TRACE 1 + +/* IMPORTANT: if you change the following list, don't + * forget to update the corresponding 'tags' table in + * the adb_trace_init() function implemented in adb.c + */ +typedef enum { + TRACE_ADB = 0, + TRACE_SOCKETS, + TRACE_PACKETS, + TRACE_TRANSPORT, + TRACE_RWX, + TRACE_USB, + TRACE_SYNC, + TRACE_SYSDEPS, + TRACE_JDWP, +} AdbTrace; + +#if ADB_TRACE + + int adb_trace_mask; + + void adb_trace_init(void); + +# define ADB_TRACING ((adb_trace_mask & (1 << TRACE_TAG)) != 0) + + /* you must define TRACE_TAG before using this macro */ + #define D(...) \ + do { \ + if (ADB_TRACING) \ + fprintf(stderr, __VA_ARGS__ ); \ + } while (0) +#else +# define D(...) ((void)0) +# define ADB_TRACING 0 +#endif + + +/* set this to log to /data/adb/adb_