Initial Contribution

This commit is contained in:
The Android Open Source Project 2008-10-21 07:00:00 -07:00
commit 4f6e8d7a00
419 changed files with 98588 additions and 0 deletions

28
Android.mk Normal file
View file

@ -0,0 +1,28 @@
#
# Copyright (C) 2008 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.
#
LOCAL_PATH := $(my-dir)
ifneq ($(TARGET_SIMULATOR),true)
include $(call first-makefiles-under,$(LOCAL_PATH))
else
include $(addprefix $(LOCAL_PATH)/,$(addsuffix /Android.mk, \
libcutils \
liblog \
libnetutils \
libpixelflinger \
libzipfile \
))
endif

20
README Normal file
View file

@ -0,0 +1,20 @@
The system/ directory is intended for pieces of the world that are the
core of the embedded linux platform at the heart of Android. These
essential bits are required for basic booting, operation, and debugging.
They should not depend on libraries outside of system/... (some of them
do currently -- they need to be updated or changed) and they should not
be required for the simulator build.
The license for all these pieces should be clean (Apache2, BSD, or MIT).
Currently system/bluetooth/... and system/extra/... have some pieces
with GPL/LGPL licensed code.
Assorted Issues:
- pppd depends on libutils for logging
- pppd depends on libcrypt/libcrypto
- init, linker, debuggerd, toolbox, usbd depend on libcutils
- should probably rename bionic to libc

116
adb/Android.mk Normal file
View file

@ -0,0 +1,116 @@
# Copyright 2005 The Android Open Source Project
#
# Android.mk for adb
#
LOCAL_PATH:= $(call my-dir)
# adb host tool
# =========================================================
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 $(LOCAL_PATH)/../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
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)
ifeq ($(HOST_OS),windows)
$(LOCAL_INSTALLED_MODULE): $(HOST_OUT_EXECUTABLES)/AdbWinApi.dll
endif
ifeq ($(HOST_OS),linux)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := \
kdbg.c
LOCAL_MODULE := kdbg
include $(BUILD_HOST_EXECUTABLE)
endif
# adbd device daemon
# =========================================================
ifeq ($(TARGET_ARCH),arm)
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
LOCAL_CFLAGS := -O2 -g -DADB_HOST=0 -DANDROID_GADGET=1 -Wall -Wno-unused-parameter
LOCAL_CFLAGS += -D_XOPEN_SOURCE -D_GNU_SOURCE
LOCAL_MODULE := adbd
LOCAL_FORCE_STATIC_EXECUTABLE := true
LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT_SBIN)
LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_SBIN_UNSTRIPPED)
LOCAL_STATIC_LIBRARIES := libcutils libc
include $(BUILD_EXECUTABLE)
endif

1093
adb/adb.c Normal file

File diff suppressed because it is too large Load diff

402
adb/adb.h Normal file
View file

@ -0,0 +1,402 @@
/*
* 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 <limits.h>
#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;
/* 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_<time>.txt on the device.
* has no effect if the /data/adb/ directory does not exist.
*/
#define ADB_DEVICE_LOG 0
#if !TRACE_PACKETS
#define print_packet(tag,p) do {} while (0)
#endif
#define ADB_PORT 5037
#define ADB_LOCAL_TRANSPORT_PORT 5555
// Google's USB Vendor ID
#define VENDOR_ID_GOOGLE 0x18d1
// HTC's USB Vendor ID
#define VENDOR_ID_HTC 0x0bb4
// products for VENDOR_ID_GOOGLE
#define PRODUCT_ID_SOONER 0xd00d // Sooner bootloader
#define PRODUCT_ID_SOONER_COMP 0xdeed // Sooner composite device
// products for VENDOR_ID_HTC
#define PRODUCT_ID_DREAM 0x0c01 // Dream bootloader
#define PRODUCT_ID_DREAM_COMP 0x0c02 // Dream composite device
void local_init();
int local_connect(int port);
/* usb host/client interface */
void usb_init();
void usb_cleanup();
int usb_write(usb_handle *h, const void *data, int len);
int usb_read(usb_handle *h, void *data, int len);
int usb_close(usb_handle *h);
void usb_kick(usb_handle *h);
/* used for USB device detection */
int is_adb_interface(int vid, int pid, int usb_class, int usb_subclass, int usb_protocol);
unsigned host_to_le32(unsigned n);
int adb_commandline(int argc, char **argv);
int connection_state(atransport *t);
#define CS_ANY -1
#define CS_OFFLINE 0
#define CS_BOOTLOADER 1
#define CS_DEVICE 2
#define CS_HOST 3
#define CS_RECOVERY 4
#define CS_ERROR 5
extern int HOST;
#define CHUNK_SIZE (64*1024)
int sendfailmsg(int fd, const char *reason);
int handle_host_request(char *service, transport_type ttype, char* serial, int reply_fd, asocket *s);
#endif

318
adb/adb_client.c Normal file
View file

@ -0,0 +1,318 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <limits.h>
#include <stdarg.h>
#include <zipfile/zipfile.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "sysdeps.h"
#define TRACE_TAG TRACE_ADB
#include "adb_client.h"
static transport_type __adb_transport = kTransportAny;
static const char* __adb_serial = NULL;
void adb_set_transport(transport_type type, const char* serial)
{
__adb_transport = type;
__adb_serial = serial;
}
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 {
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(writex(fd, tmp, 4) || writex(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(readx(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(readx(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(readx(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);
fd = socket_loopback_client(ADB_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(writex(fd, tmp, 4) || writex(fd, service, len)) {
strcpy(__adb_error, "write failure during connection");
adb_close(fd);
return -1;
}
if(adb_status(fd)) {
adb_close(fd);
return -1;
}
return fd;
}
int adb_connect(const char *service)
{
// first query the adb server's version
int fd = _adb_connect("host:version");
if(fd == -2) {
fprintf(stdout,"* daemon not running. starting it now *\n");
start_server:
if(launch_server(0)) {
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(2000);
// 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];
int n;
int version = ADB_SERVER_VERSION - 1;
// if we have a file descriptor, then parse version result
if(fd >= 0) {
if(readx(fd, buf, 4)) goto error;
buf[4] = 0;
n = strtoul(buf, 0, 16);
if(n > (int)sizeof(buf)) goto error;
if(readx(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 == -2) {
fprintf(stderr,"** daemon still not running");
}
return fd;
error:
adb_close(fd);
return -1;
}
int adb_command(const char *service)
{
int fd = adb_connect(service);
if(fd < 0) {
return -1;
}
if(adb_status(fd)) {
adb_close(fd);
return -1;
}
return 0;
}
char *adb_query(const char *service)
{
char buf[5];
unsigned 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(readx(fd, buf, 4)) goto oops;
buf[4] = 0;
n = strtoul(buf, 0, 16);
if(n > 1024) goto oops;
tmp = malloc(n + 1);
if(tmp == 0) goto oops;
if(readx(fd, tmp, n) == 0) {
tmp[n] = 0;
adb_close(fd);
return tmp;
}
free(tmp);
oops:
adb_close(fd);
return 0;
}

49
adb/adb_client.h Normal file
View file

@ -0,0 +1,49 @@
#ifndef _ADB_CLIENT_H_
#define _ADB_CLIENT_H_
#include "adb.h"
/* connect to adb, connect to the named service, and return
** a valid fd for interacting with that service upon success
** or a negative number on failure
*/
int adb_connect(const char *service);
int _adb_connect(const char *service);
/* connect to adb, connect to the named service, return 0 if
** the connection succeeded AND the service returned OKAY
*/
int adb_command(const char *service);
/* connect to adb, connect to the named service, return
** a malloc'd string of its response upon success or NULL
** on failure.
*/
char *adb_query(const char *service);
/* Set the preferred transport to connect to.
*/
void adb_set_transport(transport_type type, const char* serial);
/* Return the console port of the currently connected emulator (if any)
* of -1 if there is no emulator, and -2 if there is more than one.
* assumes adb_set_transport() was alled previously...
*/
int adb_get_emulator_console_port(void);
/* send commands to the current emulator instance. will fail if there
* is zero, or more than one emulator connected (or if you use -s <serial>
* with a <serial> that does not designate an emulator)
*/
int adb_send_emulator_command(int argc, char** argv);
/* return verbose error string from last operation */
const char *adb_error(void);
/* read a standard adb status response (OKAY|FAIL) and
** return 0 in the event of OKAY, -1 in the event of FAIL
** or protocol error
*/
int adb_status(int fd);
#endif

1371
adb/commandline.c Normal file

File diff suppressed because it is too large Load diff

45
adb/console.c Normal file
View file

@ -0,0 +1,45 @@
#include "sysdeps.h"
#include "adb.h"
#include "adb_client.h"
#include <stdio.h>
static int connect_to_console(void)
{
int fd, port;
port = adb_get_emulator_console_port();
if (port < 0) {
if (port == -2)
fprintf(stderr, "error: more than one emulator detected. use -s option\n");
else
fprintf(stderr, "error: no emulator detected\n");
return -1;
}
fd = socket_loopback_client( port, SOCK_STREAM );
if (fd < 0) {
fprintf(stderr, "error: could not connect to TCP port %d\n", port);
return -1;
}
return fd;
}
int adb_send_emulator_command(int argc, char** argv)
{
int fd, nn;
fd = connect_to_console();
if (fd < 0)
return 1;
#define QUIT "quit\n"
for (nn = 1; nn < argc; nn++) {
adb_write( fd, argv[nn], strlen(argv[nn]) );
adb_write( fd, (nn == argc-1) ? "\n" : " ", 1 );
}
adb_write( fd, QUIT, sizeof(QUIT)-1 );
adb_close(fd);
return 0;
}

1022
adb/file_sync_client.c Normal file

File diff suppressed because it is too large Load diff

412
adb/file_sync_service.c Normal file
View file

@ -0,0 +1,412 @@
/*
* 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.
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <dirent.h>
#include <utime.h>
#include <errno.h>
#include "sysdeps.h"
#define TRACE_TAG TRACE_SYNC
#include "adb.h"
#include "file_sync_service.h"
static int mkdirs(char *name)
{
int ret;
char *x = name + 1;
if(name[0] != '/') return -1;
for(;;) {
x = adb_dirstart(x);
if(x == 0) return 0;
*x = 0;
ret = adb_mkdir(name, 0775);
if((ret < 0) && (errno != EEXIST)) {
D("mkdir(\"%s\") -> %s\n", name, strerror(errno));
*x = '/';
return ret;
}
*x++ = '/';
}
return 0;
}
static int do_stat(int s, const char *path)
{
syncmsg msg;
struct stat st;
msg.stat.id = ID_STAT;
if(lstat(path, &st)) {
msg.stat.mode = 0;
msg.stat.size = 0;
msg.stat.time = 0;
} else {
msg.stat.mode = htoll(st.st_mode);
msg.stat.size = htoll(st.st_size);
msg.stat.time = htoll(st.st_mtime);
}
return writex(s, &msg.stat, sizeof(msg.stat));
}
static int do_list(int s, const char *path)
{
DIR *d;
struct dirent *de;
struct stat st;
syncmsg msg;
int len;
char tmp[1024 + 256 + 1];
char *fname;
len = strlen(path);
memcpy(tmp, path, len);
tmp[len] = '/';
fname = tmp + len + 1;
msg.dent.id = ID_DENT;
d = opendir(path);
if(d == 0) goto done;
while((de = readdir(d))) {
int len = strlen(de->d_name);
/* not supposed to be possible, but
if it does happen, let's not buffer overrun */
if(len > 256) continue;
strcpy(fname, de->d_name);
if(lstat(tmp, &st) == 0) {
msg.dent.mode = htoll(st.st_mode);
msg.dent.size = htoll(st.st_size);
msg.dent.time = htoll(st.st_mtime);
msg.dent.namelen = htoll(len);
if(writex(s, &msg.dent, sizeof(msg.dent)) ||
writex(s, de->d_name, len)) {
return -1;
}
}
}
closedir(d);
done:
msg.dent.id = ID_DONE;
msg.dent.mode = 0;
msg.dent.size = 0;
msg.dent.time = 0;
msg.dent.namelen = 0;
return writex(s, &msg.dent, sizeof(msg.dent));
}
static int fail_message(int s, const char *reason)
{
syncmsg msg;
int len = strlen(reason);
D("sync: failure: %s\n", reason);
msg.data.id = ID_FAIL;
msg.data.size = htoll(len);
if(writex(s, &msg.data, sizeof(msg.data)) ||
writex(s, reason, len)) {
return -1;
} else {
return 0;
}
}
static int fail_errno(int s)
{
return fail_message(s, strerror(errno));
}
static int handle_send_file(int s, char *path, mode_t mode, char *buffer)
{
syncmsg msg;
unsigned int timestamp = 0;
int fd;
fd = adb_open_mode(path, O_WRONLY | O_CREAT | O_EXCL, mode);
if(fd < 0 && errno == ENOENT) {
mkdirs(path);
fd = adb_open_mode(path, O_WRONLY | O_CREAT | O_EXCL, mode);
}
if(fd < 0 && errno == EEXIST) {
fd = adb_open_mode(path, O_WRONLY, mode);
}
if(fd < 0) {
if(fail_errno(s))
return -1;
fd = -1;
}
for(;;) {
unsigned int len;
if(readx(s, &msg.data, sizeof(msg.data)))
goto fail;
if(msg.data.id != ID_DATA) {
if(msg.data.id == ID_DONE) {
timestamp = ltohl(msg.data.size);
break;
}
fail_message(s, "invalid data message");
goto fail;
}
len = ltohl(msg.data.size);
if(len > SYNC_DATA_MAX) {
fail_message(s, "oversize data message");
goto fail;
}
if(readx(s, buffer, len))
goto fail;
if(fd < 0)
continue;
if(writex(fd, buffer, len)) {
adb_close(fd);
adb_unlink(path);
fd = -1;
if(fail_errno(s)) return -1;
}
}
if(fd >= 0) {
struct utimbuf u;
adb_close(fd);
u.actime = timestamp;
u.modtime = timestamp;
utime(path, &u);
msg.status.id = ID_OKAY;
msg.status.msglen = 0;
if(writex(s, &msg.status, sizeof(msg.status)))
return -1;
}
return 0;
fail:
if(fd >= 0)
adb_close(fd);
adb_unlink(path);
return -1;
}
#ifdef HAVE_SYMLINKS
static int handle_send_link(int s, char *path, char *buffer)
{
syncmsg msg;
unsigned int len;
int ret;
if(readx(s, &msg.data, sizeof(msg.data)))
return -1;
if(msg.data.id != ID_DATA) {
fail_message(s, "invalid data message: expected ID_DATA");
return -1;
}
len = ltohl(msg.data.size);
if(len > SYNC_DATA_MAX) {
fail_message(s, "oversize data message");
return -1;
}
if(readx(s, buffer, len))
return -1;
ret = symlink(buffer, path);
if(ret && errno == ENOENT) {
mkdirs(path);
ret = symlink(buffer, path);
}
if(ret) {
fail_errno(s);
return -1;
}
if(readx(s, &msg.data, sizeof(msg.data)))
return -1;
if(msg.data.id == ID_DONE) {
msg.status.id = ID_OKAY;
msg.status.msglen = 0;
if(writex(s, &msg.status, sizeof(msg.status)))
return -1;
} else {
fail_message(s, "invalid data message: expected ID_DONE");
return -1;
}
return 0;
}
#endif /* HAVE_SYMLINKS */
static int do_send(int s, char *path, char *buffer)
{
char *tmp;
mode_t mode;
int is_link, ret;
tmp = strrchr(path,',');
if(tmp) {
*tmp = 0;
errno = 0;
mode = strtoul(tmp + 1, NULL, 0);
#ifndef HAVE_SYMLINKS
is_link = 0;
#else
is_link = S_ISLNK(mode);
#endif
mode &= 0777;
}
if(!tmp || errno) {
mode = 0644;
is_link = 0;
}
adb_unlink(path);
#ifdef HAVE_SYMLINKS
if(is_link)
ret = handle_send_link(s, path, buffer);
else {
#else
{
#endif
/* copy user permission bits to "group" and "other" permissions */
mode |= ((mode >> 3) & 0070);
mode |= ((mode >> 3) & 0007);
ret = handle_send_file(s, path, mode, buffer);
}
return ret;
}
static int do_recv(int s, const char *path, char *buffer)
{
syncmsg msg;
int fd, r;
fd = adb_open(path, O_RDONLY);
if(fd < 0) {
if(fail_errno(s)) return -1;
return 0;
}
msg.data.id = ID_DATA;
for(;;) {
r = adb_read(fd, buffer, SYNC_DATA_MAX);
if(r <= 0) {
if(r == 0) break;
if(errno == EINTR) continue;
r = fail_errno(s);
adb_close(fd);
return r;
}
msg.data.size = htoll(r);
if(writex(s, &msg.data, sizeof(msg.data)) ||
writex(s, buffer, r)) {
adb_close(fd);
return -1;
}
}
adb_close(fd);
msg.data.id = ID_DONE;
msg.data.size = 0;
if(writex(s, &msg.data, sizeof(msg.data))) {
return -1;
}
return 0;
}
void file_sync_service(int fd, void *cookie)
{
syncmsg msg;
char name[1025];
unsigned namelen;
char *buffer = malloc(SYNC_DATA_MAX);
if(buffer == 0) goto fail;
for(;;) {
D("sync: waiting for command\n");
if(readx(fd, &msg.req, sizeof(msg.req))) {
fail_message(fd, "command read failure");
break;
}
namelen = ltohl(msg.req.namelen);
if(namelen > 1024) {
fail_message(fd, "invalid namelen");
break;
}
if(readx(fd, name, namelen)) {
fail_message(fd, "filename read failure");
break;
}
name[namelen] = 0;
msg.req.namelen = 0;
D("sync: '%s' '%s'\n", (char*) &msg.req, name);
switch(msg.req.id) {
case ID_STAT:
if(do_stat(fd, name)) goto fail;
break;
case ID_LIST:
if(do_list(fd, name)) goto fail;
break;
case ID_SEND:
if(do_send(fd, name, buffer)) goto fail;
break;
case ID_RECV:
if(do_recv(fd, name, buffer)) goto fail;
break;
case ID_QUIT:
goto fail;
default:
fail_message(fd, "unknown command");
goto fail;
}
}
fail:
if(buffer != 0) free(buffer);
D("sync: done\n");
adb_close(fd);
}

87
adb/file_sync_service.h Normal file
View file

@ -0,0 +1,87 @@
/*
* 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 _FILE_SYNC_SERVICE_H_
#define _FILE_SYNC_SERVICE_H_
#ifdef __ppc__
static inline unsigned __swap_uint32(unsigned x)
{
return (((x) & 0xFF000000) >> 24)
| (((x) & 0x00FF0000) >> 8)
| (((x) & 0x0000FF00) << 8)
| (((x) & 0x000000FF) << 24);
}
#define htoll(x) __swap_uint32(x)
#define ltohl(x) __swap_uint32(x)
#define MKID(a,b,c,d) ((d) | ((c) << 8) | ((b) << 16) | ((a) << 24))
#else
#define htoll(x) (x)
#define ltohl(x) (x)
#define MKID(a,b,c,d) ((a) | ((b) << 8) | ((c) << 16) | ((d) << 24))
#endif
#define ID_STAT MKID('S','T','A','T')
#define ID_LIST MKID('L','I','S','T')
#define ID_ULNK MKID('U','L','N','K')
#define ID_SEND MKID('S','E','N','D')
#define ID_RECV MKID('R','E','C','V')
#define ID_DENT MKID('D','E','N','T')
#define ID_DONE MKID('D','O','N','E')
#define ID_DATA MKID('D','A','T','A')
#define ID_OKAY MKID('O','K','A','Y')
#define ID_FAIL MKID('F','A','I','L')
#define ID_QUIT MKID('Q','U','I','T')
typedef union {
unsigned id;
struct {
unsigned id;
unsigned namelen;
} req;
struct {
unsigned id;
unsigned mode;
unsigned size;
unsigned time;
} stat;
struct {
unsigned id;
unsigned mode;
unsigned size;
unsigned time;
unsigned namelen;
} dent;
struct {
unsigned id;
unsigned size;
} data;
struct {
unsigned id;
unsigned msglen;
} status;
} syncmsg;
void file_sync_service(int fd, void *cookie);
int do_sync_ls(const char *path);
int do_sync_push(const char *lpath, const char *rpath, int verifyApk);
int do_sync_sync(const char *lpath, const char *rpath);
int do_sync_pull(const char *rpath, const char *lpath);
#define SYNC_DATA_MAX (64*1024)
#endif

69
adb/framebuffer_service.c Normal file
View file

@ -0,0 +1,69 @@
/*
* 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.
*/
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <cutils/fdevent.h>
#include "adb.h"
#include <linux/fb.h>
#include <sys/mman.h>
/* TODO:
** - grab the current buffer, not the first buffer
** - sync with vsync to avoid tearing
*/
void framebuffer_service(int fd, void *cookie)
{
struct fb_var_screeninfo vinfo;
int fb;
void *ptr = MAP_FAILED;
char x;
unsigned fbinfo[4];
fb = open("/dev/graphics/fb0", O_RDONLY);
if(fb < 0) goto done;
if(ioctl(fb, FBIOGET_VSCREENINFO, &vinfo) < 0) goto done;
fcntl(fb, F_SETFD, FD_CLOEXEC);
fbinfo[0] = 16;
fbinfo[1] = vinfo.xres * vinfo.yres * 2;
fbinfo[2] = vinfo.xres;
fbinfo[3] = vinfo.yres;
ptr = mmap(0, fbinfo[1], PROT_READ, MAP_SHARED, fb, 0);
if(ptr == MAP_FAILED) goto done;
if(writex(fd, fbinfo, sizeof(unsigned) * 4)) goto done;
for(;;) {
if(readx(fd, &x, 1)) goto done;
if(writex(fd, ptr, fbinfo[1])) goto done;
}
done:
if(ptr != MAP_FAILED) munmap(ptr, fbinfo[1]);
if(fb >= 0) close(fb);
close(fd);
}

31
adb/get_my_path_darwin.c Normal file
View file

@ -0,0 +1,31 @@
/*
* 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.
*/
#include <utils/executablepath.h>
#import <Carbon/Carbon.h>
#include <unistd.h>
void get_my_path(char s[PATH_MAX])
{
ProcessSerialNumber psn;
GetCurrentProcess(&psn);
CFDictionaryRef dict;
dict = ProcessInformationCopyDictionary(&psn, 0xffffffff);
CFStringRef value = (CFStringRef)CFDictionaryGetValue(dict,
CFSTR("CFBundleExecutable"));
CFStringGetCString(value, s, PATH_MAX - 1, kCFStringEncodingUTF8);
}

33
adb/get_my_path_linux.c Normal file
View file

@ -0,0 +1,33 @@
/*
* 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.
*/
#include <sys/types.h>
#include <unistd.h>
#include <limits.h>
#include <stdio.h>
void get_my_path(char exe[PATH_MAX])
{
char proc[64];
snprintf(proc, sizeof proc, "/proc/%d/exe", getpid());
int err = readlink(proc, exe, PATH_MAX - 1);
if(err > 0) {
exe[err] = 0;
} else {
exe[0] = 0;
}
}

31
adb/get_my_path_windows.c Normal file
View file

@ -0,0 +1,31 @@
/*
* 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.
*/
#include <limits.h>
#include <assert.h>
#include <windows.h>
void get_my_path(char exe[PATH_MAX])
{
char* r;
GetModuleFileName( NULL, exe, PATH_MAX-1 );
exe[PATH_MAX-1] = 0;
r = strrchr( exe, '\\' );
if (r)
*r = 0;
}

13
adb/history.h Executable file
View file

@ -0,0 +1,13 @@
#ifndef _HISTORY_H_
#define _HISTORY_H_
#define SH_ARROW_ANY "\x1b\x5b"
#define SH_ARROW_UP '\x41'
#define SH_ARROW_DOWN '\x42'
#define SH_ARROW_RIGHT '\x43'
#define SH_ARROW_LEFT '\x44'
#define SH_DEL_CHAR '\x7F'
#define SH_BLANK_CHAR '\x20'
#endif

709
adb/jdwp_service.c Normal file
View file

@ -0,0 +1,709 @@
/* implement the "debug-ports" and "track-debug-ports" device services */
#include "sysdeps.h"
#define TRACE_TAG TRACE_JDWP
#include "adb.h"
#include <errno.h>
#include <stdio.h>
#include <string.h>
/* here's how these things work.
when adbd starts, it creates a unix server socket
named @vm-debug-control (@ is a shortcut for "first byte is zero"
to use the private namespace instead of the file system)
when a new JDWP daemon thread starts in a new VM process, it creates
a connection to @vm-debug-control to announce its availability.
JDWP thread @vm-debug-control
| |
|-------------------------------> |
| hello I'm in process <pid> |
| |
| |
the connection is kept alive. it will be closed automatically if
the JDWP process terminates (this allows adbd to detect dead
processes).
adbd thus maintains a list of "active" JDWP processes. it can send
its content to clients through the "device:debug-ports" service,
or even updates through the "device:track-debug-ports" service.
when a debugger wants to connect, it simply runs the command
equivalent to "adb forward tcp:<hostport> jdwp:<pid>"
"jdwp:<pid>" is a new forward destination format used to target
a given JDWP process on the device. when sutch a request arrives,
adbd does the following:
- first, it calls socketpair() to create a pair of equivalent
sockets.
- it attaches the first socket in the pair to a local socket
which is itself attached to the transport's remote socket:
- it sends the file descriptor of the second socket directly
to the JDWP process with the help of sendmsg()
JDWP thread @vm-debug-control
| |
| <----------------------|
| OK, try this file descriptor |
| |
| |
then, the JDWP thread uses this new socket descriptor as its
pass-through connection to the debugger (and receives the
JDWP-Handshake message, answers to it, etc...)
this gives the following graphics:
____________________________________
| |
| ADB Server (host) |
| |
Debugger <---> LocalSocket <----> RemoteSocket |
| ^^ |
|___________________________||_______|
||
Transport ||
(TCP for emulator - USB for device) ||
||
___________________________||_______
| || |
| ADBD (device) || |
| VV |
JDWP <======> LocalSocket <----> RemoteSocket |
| |
|____________________________________|
due to the way adb works, this doesn't need a special socket
type or fancy handling of socket termination if either the debugger
or the JDWP process closes the connection.
THIS IS THE SIMPLEST IMPLEMENTATION I COULD FIND, IF YOU HAPPEN
TO HAVE A BETTER IDEA, LET ME KNOW - Digit
**********************************************************************/
/** JDWP PID List Support Code
** for each JDWP process, we record its pid and its connected socket
**/
#define MAX_OUT_FDS 4
#if !ADB_HOST
#include <sys/socket.h>
#include <sys/un.h>
typedef struct JdwpProcess JdwpProcess;
struct JdwpProcess {
JdwpProcess* next;
JdwpProcess* prev;
int pid;
int socket;
fdevent* fde;
char in_buff[4]; /* input character to read PID */
int in_len; /* number from JDWP process */
int out_fds[MAX_OUT_FDS]; /* output array of file descriptors */
int out_count; /* to send to the JDWP process */
};
static JdwpProcess _jdwp_list;
static int
jdwp_process_list( char* buffer, int bufferlen )
{
char* end = buffer + bufferlen;
char* p = buffer;
JdwpProcess* proc = _jdwp_list.next;
for ( ; proc != &_jdwp_list; proc = proc->next ) {
int len;
/* skip transient connections */
if (proc->pid < 0)
continue;
len = snprintf(p, end-p, "%d\n", proc->pid);
if (p + len >= end)
break;
p += len;
}
p[0] = 0;
return (p - buffer);
}
static int
jdwp_process_list_msg( char* buffer, int bufferlen )
{
char head[5];
int len = jdwp_process_list( buffer+4, bufferlen-4 );
snprintf(head, sizeof head, "%04x", len);
memcpy(buffer, head, 4);
return len + 4;
}
static void jdwp_process_list_updated(void);
static void
jdwp_process_free( JdwpProcess* proc )
{
if (proc) {
int n;
proc->prev->next = proc->next;
proc->next->prev = proc->prev;
if (proc->socket >= 0) {
shutdown(proc->socket, SHUT_RDWR);
adb_close(proc->socket);
proc->socket = -1;
}
if (proc->fde != NULL) {
fdevent_destroy(proc->fde);
proc->fde = NULL;
}
proc->pid = -1;
for (n = 0; n < proc->out_count; n++) {
adb_close(proc->out_fds[n]);
}
proc->out_count = 0;
free(proc);
jdwp_process_list_updated();
}
}
static void jdwp_process_event(int, unsigned, void*); /* forward */
static JdwpProcess*
jdwp_process_alloc( int socket )
{
JdwpProcess* proc = calloc(1,sizeof(*proc));
if (proc == NULL) {
D("not enough memory to create new JDWP process\n");
return NULL;
}
proc->socket = socket;
proc->pid = -1;
proc->next = proc;
proc->prev = proc;
proc->fde = fdevent_create( socket, jdwp_process_event, proc );
if (proc->fde == NULL) {
D("could not create fdevent for new JDWP process\n" );
free(proc);
return NULL;
}
proc->fde->state |= FDE_DONT_CLOSE;
proc->in_len = 0;
proc->out_count = 0;
/* append to list */
proc->next = &_jdwp_list;
proc->prev = proc->next->prev;
proc->prev->next = proc;
proc->next->prev = proc;
/* start by waiting for the PID */
fdevent_add(proc->fde, FDE_READ);
return proc;
}
static void
jdwp_process_event( int socket, unsigned events, void* _proc )
{
JdwpProcess* proc = _proc;
if (events & FDE_READ) {
if (proc->pid < 0) {
/* read the PID as a 4-hexchar string */
char* p = proc->in_buff + proc->in_len;
int size = 4 - proc->in_len;
char temp[5];
while (size > 0) {
int len = recv( socket, p, size, 0 );
if (len < 0) {
if (errno == EINTR)
continue;
if (errno == EAGAIN)
return;
/* this can fail here if the JDWP process crashes very fast */
D("weird unknown JDWP process failure: %s\n",
strerror(errno));
goto CloseProcess;
}
if (len == 0) { /* end of stream ? */
D("weird end-of-stream from unknown JDWP process\n");
goto CloseProcess;
}
p += len;
proc->in_len += len;
size -= len;
}
/* we have read 4 characters, now decode the pid */
memcpy(temp, proc->in_buff, 4);
temp[4] = 0;
if (sscanf( temp, "%04x", &proc->pid ) != 1) {
D("could not decode JDWP %p PID number: '%s'\n", proc, temp);
goto CloseProcess;
}
/* all is well, keep reading to detect connection closure */
D("Adding pid %d to jdwp process list\n", proc->pid);
jdwp_process_list_updated();
}
else
{
/* the pid was read, if we get there it's probably because the connection
* was closed (e.g. the JDWP process exited or crashed) */
char buf[32];
for (;;) {
int len = recv(socket, buf, sizeof(buf), 0);
if (len <= 0) {
if (len < 0 && errno == EINTR)
continue;
if (len < 0 && errno == EAGAIN)
return;
else {
D("terminating JDWP %d connection: %s\n", proc->pid,
strerror(errno));
break;
}
}
else {
D( "ignoring unexpected JDWP %d control socket activity (%d bytes)\n",
proc->pid, len );
}
}
CloseProcess:
if (proc->pid >= 0)
D( "remove pid %d to jdwp process list\n", proc->pid );
jdwp_process_free(proc);
return;
}
}
if (events & FDE_WRITE) {
D("trying to write to JDWP pid controli (count=%d first=%d) %d\n",
proc->pid, proc->out_count, proc->out_fds[0]);
if (proc->out_count > 0) {
int fd = proc->out_fds[0];
int n, ret;
struct cmsghdr* cmsg;
struct msghdr msg;
struct iovec iov;
char dummy = '!';
char buffer[sizeof(struct cmsghdr) + sizeof(int)];
iov.iov_base = &dummy;
iov.iov_len = 1;
msg.msg_name = NULL;
msg.msg_namelen = 0;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_flags = 0;
msg.msg_control = buffer;
msg.msg_controllen = sizeof(buffer);
cmsg = CMSG_FIRSTHDR(&msg);
cmsg->cmsg_len = msg.msg_controllen;
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
((int*)CMSG_DATA(cmsg))[0] = fd;
for (;;) {
ret = sendmsg(proc->socket, &msg, 0);
if (ret >= 0)
break;
if (errno == EINTR)
continue;
D("sending new file descriptor to JDWP %d failed: %s\n",
proc->pid, strerror(errno));
goto CloseProcess;
}
D("sent file descriptor %d to JDWP process %d\n",
fd, proc->pid);
for (n = 1; n < proc->out_count; n++)
proc->out_fds[n-1] = proc->out_fds[n];
if (--proc->out_count == 0)
fdevent_del( proc->fde, FDE_WRITE );
}
}
}
int
create_jdwp_connection_fd(int pid)
{
JdwpProcess* proc = _jdwp_list.next;
D("looking for pid %d in JDWP process list\n", pid);
for ( ; proc != &_jdwp_list; proc = proc->next ) {
if (proc->pid == pid) {
goto FoundIt;
}
}
D("search failed !!\n");
return -1;
FoundIt:
{
int fds[2];
if (proc->out_count >= MAX_OUT_FDS) {
D("%s: too many pending JDWP connection for pid %d\n",
__FUNCTION__, pid);
return -1;
}
if (adb_socketpair(fds) < 0) {
D("%s: socket pair creation failed: %s\n",
__FUNCTION__, strerror(errno));
return -1;
}
proc->out_fds[ proc->out_count ] = fds[1];
if (++proc->out_count == 1)
fdevent_add( proc->fde, FDE_WRITE );
return fds[0];
}
}
/** VM DEBUG CONTROL SOCKET
**
** we do implement a custom asocket to receive the data
**/
/* name of the debug control Unix socket */
#define JDWP_CONTROL_NAME "\0jdwp-control"
#define JDWP_CONTROL_NAME_LEN (sizeof(JDWP_CONTROL_NAME)-1)
typedef struct {
int listen_socket;
fdevent* fde;
} JdwpControl;
static void
jdwp_control_event(int s, unsigned events, void* user);
static int
jdwp_control_init( JdwpControl* control,
const char* sockname,
int socknamelen )
{
struct sockaddr_un addr;
socklen_t addrlen;
int s;
int maxpath = sizeof(addr.sun_path);
int pathlen = socknamelen;
if (pathlen >= maxpath) {
D( "vm debug control socket name too long (%d extra chars)\n",
pathlen+1-maxpath );
return -1;
}
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
memcpy(addr.sun_path, sockname, socknamelen);
s = socket( AF_UNIX, SOCK_STREAM, 0 );
if (s < 0) {
D( "could not create vm debug control socket. %d: %s\n",
errno, strerror(errno));
return -1;
}
addrlen = (pathlen + sizeof(addr.sun_family));
if (bind(s, (struct sockaddr*)&addr, addrlen) < 0) {
D( "could not bind vm debug control socket: %d: %s\n",
errno, strerror(errno) );
adb_close(s);
return -1;
}
if ( listen(s, 4) < 0 ) {
D("listen failed in jdwp control socket: %d: %s\n",
errno, strerror(errno));
adb_close(s);
return -1;
}
control->listen_socket = s;
control->fde = fdevent_create(s, jdwp_control_event, control);
if (control->fde == NULL) {
D( "could not create fdevent for jdwp control socket\n" );
adb_close(s);
return -1;
}
/* only wait for incoming connections */
fdevent_add(control->fde, FDE_READ);
D("jdwp control socket started (%d)\n", control->listen_socket);
return 0;
}
static void
jdwp_control_event( int s, unsigned events, void* _control )
{
JdwpControl* control = (JdwpControl*) _control;
if (events & FDE_READ) {
struct sockaddr addr;
socklen_t addrlen = sizeof(addr);
int s = -1;
JdwpProcess* proc;
do {
s = adb_socket_accept( control->listen_socket, &addr, &addrlen );
if (s < 0) {
if (errno == EINTR)
continue;
if (errno == ECONNABORTED) {
/* oops, the JDWP process died really quick */
D("oops, the JDWP process died really quick\n");
return;
}
/* the socket is probably closed ? */
D( "weird accept() failed on jdwp control socket: %s\n",
strerror(errno) );
return;
}
}
while (s < 0);
proc = jdwp_process_alloc( s );
if (proc == NULL)
return;
}
}
static JdwpControl _jdwp_control;
/** "jdwp" local service implementation
** this simply returns the list of known JDWP process pids
**/
typedef struct {
asocket socket;
int pass;
} JdwpSocket;
static void
jdwp_socket_close( asocket* s )
{
asocket* peer = s->peer;
remove_socket(s);
if (peer) {
peer->peer = NULL;
peer->close(peer);
}
free(s);
}
static int
jdwp_socket_enqueue( asocket* s, apacket* p )
{
/* you can't write to this asocket */
put_apacket(p);
s->peer->close(s->peer);
return -1;
}
static void
jdwp_socket_ready( asocket* s )
{
JdwpSocket* jdwp = (JdwpSocket*)s;
asocket* peer = jdwp->socket.peer;
/* on the first call, send the list of pids,
* on the second one, close the connection
*/
if (jdwp->pass == 0) {
apacket* p = get_apacket();
p->len = jdwp_process_list((char*)p->data, MAX_PAYLOAD);
peer->enqueue(peer, p);
jdwp->pass = 1;
}
else {
peer->close(peer);
}
}
asocket*
create_jdwp_service_socket( void )
{
JdwpSocket* s = calloc(sizeof(*s),1);
if (s == NULL)
return NULL;
install_local_socket(&s->socket);
s->socket.ready = jdwp_socket_ready;
s->socket.enqueue = jdwp_socket_enqueue;
s->socket.close = jdwp_socket_close;
s->pass = 0;
return &s->socket;
}
/** "track-jdwp" local service implementation
** this periodically sends the list of known JDWP process pids
** to the client...
**/
typedef struct JdwpTracker JdwpTracker;
struct JdwpTracker {
asocket socket;
JdwpTracker* next;
JdwpTracker* prev;
int need_update;
};
static JdwpTracker _jdwp_trackers_list;
static void
jdwp_process_list_updated(void)
{
char buffer[1024];
int len;
JdwpTracker* t = _jdwp_trackers_list.next;
len = jdwp_process_list_msg(buffer, sizeof(buffer));
for ( ; t != &_jdwp_trackers_list; t = t->next ) {
apacket* p = get_apacket();
asocket* peer = t->socket.peer;
memcpy(p->data, buffer, len);
p->len = len;
peer->enqueue( peer, p );
}
}
static void
jdwp_tracker_close( asocket* s )
{
JdwpTracker* tracker = (JdwpTracker*) s;
asocket* peer = s->peer;
if (peer) {
peer->peer = NULL;
peer->close(peer);
}
remove_socket(s);
tracker->prev->next = tracker->next;
tracker->next->prev = tracker->prev;
free(s);
}
static void
jdwp_tracker_ready( asocket* s )
{
JdwpTracker* t = (JdwpTracker*) s;
if (t->need_update) {
apacket* p = get_apacket();
t->need_update = 0;
p->len = jdwp_process_list_msg((char*)p->data, sizeof(p->data));
s->peer->enqueue(s->peer, p);
}
}
static int
jdwp_tracker_enqueue( asocket* s, apacket* p )
{
/* you can't write to this socket */
put_apacket(p);
s->peer->close(s->peer);
return -1;
}
asocket*
create_jdwp_tracker_service_socket( void )
{
JdwpTracker* t = calloc(sizeof(*t),1);
if (t == NULL)
return NULL;
t->next = &_jdwp_trackers_list;
t->prev = t->next->prev;
t->next->prev = t;
t->prev->next = t;
install_local_socket(&t->socket);
t->socket.ready = jdwp_tracker_ready;
t->socket.enqueue = jdwp_tracker_enqueue;
t->socket.close = jdwp_tracker_close;
t->need_update = 1;
return &t->socket;
}
int
init_jdwp(void)
{
_jdwp_list.next = &_jdwp_list;
_jdwp_list.prev = &_jdwp_list;
_jdwp_trackers_list.next = &_jdwp_trackers_list;
_jdwp_trackers_list.prev = &_jdwp_trackers_list;
return jdwp_control_init( &_jdwp_control,
JDWP_CONTROL_NAME,
JDWP_CONTROL_NAME_LEN );
}
#endif /* !ADB_HOST */

474
adb/kdbg.c Normal file
View file

@ -0,0 +1,474 @@
/*
* 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 _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <dirent.h>
#include <fcntl.h>
#include <errno.h>
#include <pthread.h>
#include <ctype.h>
#include <linux/usbdevice_fs.h>
#include <linux/version.h>
#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 20)
#include <linux/usb/ch9.h>
#else
#include <linux/usb_ch9.h>
#endif
#include <asm/byteorder.h>
#include <cutils/fdevent.h>
#include "adb.h"
#define TRACE_USB 0
#if TRACE_USB
#define DBG1(x...) fprintf(stderr, x)
#define DBG(x...) fprintf(stderr, x)
#else
#define DBG(x...)
#define DBG1(x...)
#endif
struct usb_handle
{
struct usb_handle *next;
char fname[32];
int desc;
unsigned char ep_in;
unsigned char ep_out;
unsigned int interface;
};
static struct usb_handle *g_first_usb_device;
static struct usb_handle *g_last_usb_device;
static void new_device(char *dev_name, unsigned char ep_in, unsigned char ep_out, unsigned int interface)
{
struct usb_handle* usb;
DBG("New device being added %s \n", dev_name);
usb = (struct usb_handle *)calloc(1, sizeof(struct usb_handle));
strcpy(usb->fname, dev_name);
usb->ep_in = ep_in;
usb->ep_out = ep_out;
usb->interface = interface;
usb->next = NULL;
if(g_last_usb_device)
g_last_usb_device->next = usb;
else
g_first_usb_device = usb;
g_last_usb_device = usb;
}
static inline int badname(const char *name)
{
if(!isdigit(name[0])) return 1;
if(!isdigit(name[1])) return 1;
if(!isdigit(name[2])) return 1;
if(name[3] != 0) return 1;
return 0;
}
static int find_usb_devices(const char *base, unsigned vendor, unsigned product1, unsigned product2,
unsigned ifclass, unsigned ifsubclass,
unsigned ifprotocol, unsigned numendpoints)
{
char busname[32], devname[32];
unsigned char local_ep_in, local_ep_out;
DIR *busdir , *devdir ;
struct dirent *de;
int fd ;
int ret_val = -1;
int found_device = 0;
busdir = opendir(base);
if(busdir == 0) return 0;
while((de = readdir(busdir)) != 0) {
if(badname(de->d_name)) continue;
snprintf(busname, sizeof busname, "%s/%s", base, de->d_name);
devdir = opendir(busname);
if(devdir == 0) continue;
DBG("[ scanning %s ]\n", busname);
while((de = readdir(devdir))) {
if(badname(de->d_name)) continue;
snprintf(devname, sizeof devname, "%s/%s", busname, de->d_name);
DBG("[ scanning %s ]\n", devname);
fd = open(devname, O_RDWR);
if(fd < 0) {
continue;
} else {
unsigned char devdesc[256];
unsigned char* bufptr = devdesc;
struct usb_device_descriptor* device;
struct usb_config_descriptor* config;
struct usb_interface_descriptor* interface;
struct usb_endpoint_descriptor *ep1, *ep2;
unsigned vid, pid;
int i, interfaces;
size_t desclength = read(fd, devdesc, sizeof(devdesc));
// should have device and configuration descriptors, and atleast two endpoints
if (desclength < USB_DT_DEVICE_SIZE + USB_DT_CONFIG_SIZE) {
DBG("desclength %d is too small\n", desclength);
close(fd);
continue;
}
device = (struct usb_device_descriptor*)bufptr;
bufptr += USB_DT_DEVICE_SIZE;
if(device->bLength == USB_DT_DEVICE_SIZE && device->bDescriptorType == USB_DT_DEVICE) {
vid = __le16_to_cpu(device->idVendor);
pid = __le16_to_cpu(device->idProduct);
pid = devdesc[10] | (devdesc[11] << 8);
DBG("[ %s is V:%04x P:%04x ]\n", devname, vid, pid);
if((vendor == vid) && (product1 == pid || product2 == pid)){
// should have config descriptor next
config = (struct usb_config_descriptor *)bufptr;
bufptr += USB_DT_CONFIG_SIZE;
if (config->bLength != USB_DT_CONFIG_SIZE || config->bDescriptorType != USB_DT_CONFIG) {
DBG("usb_config_descriptor not found\n");
close(fd);
continue;
}
// loop through all the interfaces and look for the ADB interface
interfaces = config->bNumInterfaces;
for (i = 0; i < interfaces; i++) {
if (bufptr + USB_DT_ENDPOINT_SIZE > devdesc + desclength)
break;
interface = (struct usb_interface_descriptor *)bufptr;
bufptr += USB_DT_INTERFACE_SIZE;
if (interface->bLength != USB_DT_INTERFACE_SIZE ||
interface->bDescriptorType != USB_DT_INTERFACE) {
DBG("usb_interface_descriptor not found\n");
break;
}
DBG("bInterfaceClass: %d, bInterfaceSubClass: %d,\
bInterfaceProtocol: %d, bNumEndpoints: %d\n",
interface->bInterfaceClass, interface->bInterfaceSubClass,
interface->bInterfaceProtocol, interface->bNumEndpoints);
// Sooner bootloader has zero for bInterfaceClass, while adb has USB_CLASS_CDC_DATA
if (interface->bInterfaceClass == ifclass &&
interface->bInterfaceSubClass == ifsubclass &&
interface->bInterfaceProtocol == ifprotocol &&
interface->bNumEndpoints == numendpoints) {
DBG("looking for bulk endpoints\n");
// looks like ADB...
ep1 = (struct usb_endpoint_descriptor *)bufptr;
bufptr += USB_DT_ENDPOINT_SIZE;
ep2 = (struct usb_endpoint_descriptor *)bufptr;
bufptr += USB_DT_ENDPOINT_SIZE;
if (bufptr > devdesc + desclength ||
ep1->bLength != USB_DT_ENDPOINT_SIZE ||
ep1->bDescriptorType != USB_DT_ENDPOINT ||
ep2->bLength != USB_DT_ENDPOINT_SIZE ||
ep2->bDescriptorType != USB_DT_ENDPOINT) {
DBG("endpoints not found\n");
break;
}
// both endpoints should be bulk
if (ep1->bmAttributes != USB_ENDPOINT_XFER_BULK ||
ep2->bmAttributes != USB_ENDPOINT_XFER_BULK) {
DBG("bulk endpoints not found\n");
continue;
}
// we have a match. now we just need to figure out which is in and which is out.
if (ep1->bEndpointAddress & USB_ENDPOINT_DIR_MASK) {
local_ep_in = ep1->bEndpointAddress;
local_ep_out = ep2->bEndpointAddress;
} else {
local_ep_in = ep2->bEndpointAddress;
local_ep_out = ep1->bEndpointAddress;
}
new_device(devname, local_ep_in, local_ep_out, i);
found_device = 1;
close(fd);
} else {
// skip to next interface
bufptr += (interface->bNumEndpoints * USB_DT_ENDPOINT_SIZE);
}
} // end of for
} //end of productid if
}
close(fd);
} // end of if
} // end of devdir while
closedir(devdir);
} //end of busdir while
closedir(busdir);
return found_device;
}
static void find_devices(unsigned vendor, unsigned product1, unsigned product2)
{
// don't scan /proc/bus/usb if we find something in /dev/bus/usb, to avoid duplication of devices.
if (!find_usb_devices("/dev/bus/usb", vendor, product1, product2, USB_CLASS_VENDOR_SPEC, 1, 0, 2)) {
find_usb_devices("/proc/bus/usb", vendor, product1, product2, USB_CLASS_VENDOR_SPEC, 1, 0, 2);
}
}
void usb_open_device(struct usb_handle *h)
{
int n = 0;
h->desc = open(h->fname, O_RDWR);
//DBG("[ usb open %s fd = %d]\n", h->fname, h->desc);
n = ioctl(h->desc, USBDEVFS_CLAIMINTERFACE, &h->interface);
if(n != 0) goto fail;
// t->usb_is_open = 1;
return;
fail:
DBG("[ usb open %s error=%d, err_str = %s]\n",
h->fname, errno, strerror(errno));
if(h->desc >= 0) {
close(h->desc);
h->desc = -1;
}
// t->usb_is_open = 0;
}
int usb_write(struct usb_handle *h, const void *_data, int len)
{
unsigned char *data = (unsigned char*) _data;
struct usbdevfs_bulktransfer bulk;
int n;
while(len >= 0) {
int xfer = (len > 4096) ? 4096 : len;
bulk.ep = h->ep_out;
bulk.len = xfer;
bulk.data = data;
bulk.timeout = 500 + xfer * 8;
bulk.timeout *= 10;
n = ioctl(h->desc, USBDEVFS_BULK, &bulk);
if(n != xfer) {
DBG("ERROR: n = %d, errno = %d (%s)\n",
n, errno, strerror(errno));
return -1;
}
if(len == 0)
break;
len -= xfer;
data += xfer;
if(len == 0)
break;
}
return 0;
}
int usb_read(struct usb_handle *h, void *_data, int len)
{
unsigned char *data_start = (unsigned char*) _data;
unsigned char *data = (unsigned char*) _data;
struct usbdevfs_bulktransfer bulk;
int n;
while(len > 0) {
int xfer = (len > 4096) ? 4096 : len;
bulk.ep = h->ep_in;
bulk.len = xfer;
bulk.data = data;
// adjust timeout based on the data we're transferring,
// otherwise the timeout interrupts us partway through
// and we get out of sync...
bulk.timeout = 500 + xfer * 8;
bulk.timeout = 500 + xfer / 128;
// bulk.timeout *= 10;
DBG1("[ usb read %d fd = %d], fname=%s\n", xfer, h->desc, h->fname);
n = ioctl(h->desc, USBDEVFS_BULK, &bulk);
DBG1("[ usb read %d ] = %d, fname=%s\n", xfer, n, h->fname);
if(n < 0) {
if((errno == ETIMEDOUT) && (h->desc != -1)) {
DBG("[ timeout ]\n");
if(n > 0){
data += n;
len -= n;
}
continue;
}
DBG1("ERROR: n = %d, errno = %d (%s)\n",
n, errno, strerror(errno));
return -1;
}
len -= n;
data += n;
if(n != xfer)
break;
}
return data - data_start;
}
void usb_kick(struct usb_handle *h)
{
close(h->desc);
h->desc = -1;
}
int usb_close(struct usb_handle *h)
{
close(h->desc);
h->desc = -1;
return 0;
}
void list_devices()
{
int i = 0;
struct usb_handle *h = g_first_usb_device;
while(h) {
printf("%d: %s\n", i, h->fname);
i++;
h = h->next;
}
}
int main(int argc, char **argv)
{
char buffer[4096/*-64*/];
int len;
int c;
char *arg;
int device_index = 0;
struct usb_handle *h;
int i;
find_devices(VENDOR_ID_GOOGLE, PRODUCT_ID_SOONER, PRODUCT_ID_SOONER_COMP);
while(1) {
c = getopt(argc, argv, "d:l");
if (c == EOF)
break;
switch(c) {
case 'd':
device_index = strtol(optarg, NULL, 0);
break;
case 'l':
list_devices();
return 0;
case '?':
fprintf(stderr, "%s: invalid option -%c\n",
argv[0], optopt);
return 1;
}
}
argc -= optind - 1;
argv += optind - 1;
h = g_first_usb_device;
i = device_index;
while(i-- > 0 && h) {
h = h->next;
}
if(h == NULL) {
fprintf(stderr, "no device %d\n", device_index);
return 1;
}
usb_open_device(h);
if(g_first_usb_device->desc < 0) {
fprintf(stderr, "could not open device (%s), %s\n", h->fname, strerror(errno));
return 1;
}
len = 0;
if(argc == 1) {
char *line = NULL;
size_t line_size = 0;
while((len = getline(&line, &line_size, stdin)) >= 0) {
//if(len > 0 && line[len - 1] == '\n')
// len--;
usb_write(h, line, len);
while(1) {
len = usb_read(h, buffer, sizeof(buffer));
if(len < 0)
break;
write(STDOUT_FILENO, buffer, len);
if(len < (int)sizeof(buffer))
break;
}
}
return 0;
}
while(argc > 1) {
argc--;
argv++;
arg = *argv;
while(arg) {
if(*arg)
buffer[len++] = *arg++;
else {
arg = NULL;
if(argc > 1)
buffer[len++] = ' ';
else
break;
}
if(len == sizeof(buffer)) {
usb_write(h, buffer, len);
len = 0;
}
}
}
usb_write(h, buffer, len);
while(1) {
len = usb_read(h, buffer, sizeof(buffer));
if(len < 0)
break;
write(STDOUT_FILENO, buffer, len);
if(len < (int)sizeof(buffer))
break;
}
return 0;
}

92
adb/log_service.c Normal file
View file

@ -0,0 +1,92 @@
/*
* 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.
*/
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/socket.h>
#include <utils/logger.h>
#include "sysdeps.h"
#include "adb.h"
#define LOG_FILE_DIR "/dev/log/"
void write_log_entry(int fd, struct logger_entry *buf);
void log_service(int fd, void *cookie)
{
/* get the name of the log filepath to read */
char * log_filepath = cookie;
/* open the log file. */
int logfd = unix_open(log_filepath, O_RDONLY);
if (logfd < 0) {
goto done;
}
// temp buffer to read the entries
unsigned char buf[LOGGER_ENTRY_MAX_LEN + 1] __attribute__((aligned(4)));
struct logger_entry *entry = (struct logger_entry *) buf;
while (1) {
int ret;
ret = unix_read(logfd, entry, LOGGER_ENTRY_MAX_LEN);
if (ret < 0) {
if (errno == EINTR || errno == EAGAIN)
continue;
// perror("logcat read");
goto done;
}
else if (!ret) {
// fprintf(stderr, "read: Unexpected EOF!\n");
goto done;
}
/* NOTE: driver guarantees we read exactly one full entry */
entry->msg[entry->len] = '\0';
write_log_entry(fd, entry);
}
done:
unix_close(fd);
free(log_filepath);
}
/* returns the full path to the log file in a newly allocated string */
char * get_log_file_path(const char * log_name) {
char *log_device = malloc(strlen(LOG_FILE_DIR) + strlen(log_name) + 1);
strcpy(log_device, LOG_FILE_DIR);
strcat(log_device, log_name);
return log_device;
}
/* prints one log entry into the file descriptor fd */
void write_log_entry(int fd, struct logger_entry *buf)
{
size_t size = sizeof(struct logger_entry) + buf->len;
writex(fd, buf, size);
}

14
adb/mutex_list.h Normal file
View file

@ -0,0 +1,14 @@
/* the list of mutexes used by addb */
#ifndef ADB_MUTEX
#error ADB_MUTEX not defined when including this file
#endif
ADB_MUTEX(dns_lock)
ADB_MUTEX(socket_list_lock)
ADB_MUTEX(transport_lock)
#if ADB_HOST
ADB_MUTEX(local_transports_lock)
#endif
ADB_MUTEX(usb_lock)
#undef ADB_MUTEX

252
adb/protocol.txt Normal file
View file

@ -0,0 +1,252 @@
--- a replacement for aproto -------------------------------------------
When it comes down to it, aproto's primary purpose is to forward
various streams between the host computer and client device (in either
direction).
This replacement further simplifies the concept, reducing the protocol
to an extremely straightforward model optimized to accomplish the
forwarding of these streams and removing additional state or
complexity.
The host side becomes a simple comms bridge with no "UI", which will
be used by either commandline or interactive tools to communicate with
a device or emulator that is connected to the bridge.
The protocol is designed to be straightforward and well-defined enough
that if it needs to be reimplemented in another environment (Java
perhaps), there should not problems ensuring perfect interoperability.
The protocol discards the layering aproto has and should allow the
implementation to be much more robust.
--- protocol overview and basics ---------------------------------------
The transport layer deals in "messages", which consist of a 24 byte
header followed (optionally) by a payload. The header consists of 6
32 bit words which are sent across the wire in little endian format.
struct message {
unsigned command; /* command identifier constant */
unsigned arg0; /* first argument */
unsigned arg1; /* second argument */
unsigned data_length; /* length of payload (0 is allowed) */
unsigned data_crc32; /* crc32 of data payload */
unsigned magic; /* command ^ 0xffffffff */
};
Receipt of an invalid message header, corrupt message payload, or an
unrecognized command MUST result in the closing of the remote
connection. The protocol depends on shared state and any break in the
message stream will result in state getting out of sync.
The following sections describe the six defined message types in
detail. Their format is COMMAND(arg0, arg1, payload) where the payload
is represented by a quoted string or an empty string if none should be
sent.
The identifiers "local-id" and "remote-id" are always relative to the
*sender* of the message, so for a receiver, the meanings are effectively
reversed.
--- CONNECT(version, maxdata, "system-identity-string") ----------------
The CONNECT message establishes the presence of a remote system.
The version is used to ensure protocol compatibility and maxdata
declares the maximum message body size that the remote system
is willing to accept.
Currently, version=0x01000000 and maxdata=4096
Both sides send a CONNECT message when the connection between them is
established. Until a CONNECT message is received no other messages may
be sent. Any messages received before a CONNECT message MUST be ignored.
If a CONNECT message is received with an unknown version or insufficiently
large maxdata value, the connection with the other side must be closed.
The system identity string should be "<systemtype>:<serialno>:<banner>"
where systemtype is "bootloader", "device", or "host", serialno is some
kind of unique ID (or empty), and banner is a human-readable version
or identifier string (informational only).
--- OPEN(local-id, 0, "destination") -----------------------------------
The OPEN message informs the recipient that the sender has a stream
identified by local-id that it wishes to connect to the named
destination in the message payload. The local-id may not be zero.
The OPEN message MUST result in either a READY message indicating that
the connection has been established (and identifying the other end) or
a CLOSE message, indicating failure. An OPEN message also implies
a READY message sent at the same time.
Common destination naming conventions include:
* "tcp:<host>:<port>" - host may be omitted to indicate localhost
* "udp:<host>:<port>" - host may be omitted to indicate localhost
* "local-dgram:<identifier>"
* "local-stream:<identifier>"
* "shell" - local shell service
* "upload" - service for pushing files across (like aproto's /sync)
* "fs-bridge" - FUSE protocol filesystem bridge
--- READY(local-id, remote-id, "") -------------------------------------
The READY message informs the recipient that the sender's stream
identified by local-id is ready for write messages and that it is
connected to the recipient's stream identified by remote-id.
Neither the local-id nor the remote-id may be zero.
A READY message containing a remote-id which does not map to an open
stream on the recipient's side is ignored. The stream may have been
closed while this message was in-flight.
The local-id is ignored on all but the first READY message (where it
is used to establish the connection). Nonetheless, the local-id MUST
not change on later READY messages sent to the same stream.
--- WRITE(0, remote-id, "data") ----------------------------------------
The WRITE message sends data to the recipient's stream identified by
remote-id. The payload MUST be <= maxdata in length.
A WRITE message containing a remote-id which does not map to an open
stream on the recipient's side is ignored. The stream may have been
closed while this message was in-flight.
A WRITE message may not be sent until a READY message is received.
Once a WRITE message is sent, an additional WRITE message may not be
sent until another READY message has been received. Recipients of
a WRITE message that is in violation of this requirement will CLOSE
the connection.
--- CLOSE(local-id, remote-id, "") -------------------------------------
The CLOSE message informs recipient that the connection between the
sender's stream (local-id) and the recipient's stream (remote-id) is
broken. The remote-id MUST not be zero, but the local-id MAY be zero
if this CLOSE indicates a failed OPEN.
A CLOSE message containing a remote-id which does not map to an open
stream on the recipient's side is ignored. The stream may have
already been closed by the recipient while this message was in-flight.
The recipient should not respond to a CLOSE message in any way. The
recipient should cancel pending WRITEs or CLOSEs, but this is not a
requirement, since they will be ignored.
--- SYNC(online, sequence, "") -----------------------------------------
The SYNC message is used by the io pump to make sure that stale
outbound messages are discarded when the connection to the remote side
is broken. It is only used internally to the bridge and never valid
to send across the wire.
* when the connection to the remote side goes offline, the io pump
sends a SYNC(0, 0) and starts discarding all messages
* when the connection to the remote side is established, the io pump
sends a SYNC(1, token) and continues to discard messages
* when the io pump receives a matching SYNC(1, token), it once again
starts accepting messages to forward to the remote side
--- message command constants ------------------------------------------
#define A_SYNC 0x434e5953
#define A_CNXN 0x4e584e43
#define A_OPEN 0x4e45504f
#define A_OKAY 0x59414b4f
#define A_CLSE 0x45534c43
#define A_WRTE 0x45545257
--- implementation details ---------------------------------------------
The core of the bridge program will use three threads. One thread
will be a select/epoll loop to handle io between various inbound and
outbound connections and the connection to the remote side.
The remote side connection will be implemented as two threads (one for
reading, one for writing) and a datagram socketpair to provide the
channel between the main select/epoll thread and the remote connection
threadpair. The reason for this is that for usb connections, the
kernel interface on linux and osx does not allow you to do meaningful
nonblocking IO.
The endian swapping for the message headers will happen (as needed) in
the remote connection threadpair and that the rest of the program will
always treat message header values as native-endian.
The bridge program will be able to have a number of mini-servers
compiled in. They will be published under known names (examples
"shell", "fs-bridge", etc) and upon receiving an OPEN() to such a
service, the bridge program will create a stream socketpair and spawn
a thread or subprocess to handle the io.
--- simplified / embedded implementation -------------------------------
For limited environments, like the bootloader, it is allowable to
support a smaller, fixed number of channels using pre-assigned channel
ID numbers such that only one stream may be connected to a bootloader
endpoint at any given time. The protocol remains unchanged, but the
"embedded" version of it is less dynamic.
The bootloader will support two streams. A "bootloader:debug" stream,
which may be opened to get debug messages from the bootloader and a
"bootloader:control", stream which will support the set of basic
bootloader commands.
Example command stream dialogues:
"flash_kernel,2515049,........\n" "okay\n"
"flash_ramdisk,5038,........\n" "fail,flash write error\n"
"bogus_command......" <CLOSE>
--- future expansion ---------------------------------------------------
I plan on providing either a message or a special control stream so that
the client device could ask the host computer to setup inbound socket
translations on the fly on behalf of the client device.
The initial design does handshaking to provide flow control, with a
message flow that looks like:
>OPEN <READY >WRITE <READY >WRITE <READY >WRITE <CLOSE
The far side may choose to issue the READY message as soon as it receives
a WRITE or it may defer the READY until the write to the local stream
succeeds. A future version may want to do some level of windowing where
multiple WRITEs may be sent without requiring individual READY acks.
------------------------------------------------------------------------
--- smartsockets -------------------------------------------------------
Port 5037 is used for smart sockets which allow a client on the host
side to request access to a service in the host adb daemon or in the
remote (device) daemon. The service is requested by ascii name,
preceeded by a 4 digit hex length. Upon successful connection an
"OKAY" response is sent, otherwise a "FAIL" message is returned. Once
connected the client is talking to that (remote or local) service.
client: <hex4> <service-name>
server: "OKAY"
client: <hex4> <service-name>
server: "FAIL" <hex4> <reason>

103
adb/remount_service.c Normal file
View file

@ -0,0 +1,103 @@
/*
* Copyright (C) 2008 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 <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <sys/mount.h>
#include <errno.h>
#include "sysdeps.h"
#define TRACE_TAG TRACE_ADB
#include "adb.h"
static int system_ro = 1;
/* Returns the mount number of the requested partition from /proc/mtd */
static int find_mount(const char *findme)
{
int fd;
int res;
int size;
char *token = NULL;
const char delims[] = "\n";
char buf[1024];
fd = unix_open("/proc/mtd", O_RDONLY);
if (fd < 0)
return -errno;
buf[sizeof(buf) - 1] = '\0';
size = adb_read(fd, buf, sizeof(buf) - 1);
adb_close(fd);
token = strtok(buf, delims);
while (token) {
char mtdname[16];
int mtdnum, mtdsize, mtderasesize;
res = sscanf(token, "mtd%d: %x %x %15s",
&mtdnum, &mtdsize, &mtderasesize, mtdname);
if (res == 4 && !strcmp(mtdname, findme))
return mtdnum;
token = strtok(NULL, delims);
}
return -1;
}
/* Init mounts /system as read only, remount to enable writes. */
static int remount_system()
{
int num;
char source[64];
if (system_ro == 0) {
return 0;
}
if ((num = find_mount("\"system\"")) < 0)
return -1;
snprintf(source, sizeof source, "/dev/block/mtdblock%d", num);
system_ro = mount(source, "/system", "yaffs2", MS_REMOUNT, NULL);
return system_ro;
}
static void write_string(int fd, const char* str)
{
writex(fd, str, strlen(str));
}
void remount_service(int fd, void *cookie)
{
int ret = remount_system();
if (!ret)
write_string(fd, "remount succeeded\n");
else {
char buffer[200];
snprintf(buffer, sizeof(buffer), "remount failed: %s\n", strerror(errno));
write_string(fd, buffer);
}
adb_close(fd);
}

370
adb/services.c Normal file
View file

@ -0,0 +1,370 @@
/*
* 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.
*/
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include "sysdeps.h"
#define TRACE_TAG TRACE_ADB
#include "adb.h"
#include "file_sync_service.h"
#if ADB_HOST
# ifndef HAVE_WINSOCK
# include <netinet/in.h>
# include <netdb.h>
# endif
#endif
typedef struct stinfo stinfo;
struct stinfo {
void (*func)(int fd, void *cookie);
int fd;
void *cookie;
};
void *service_bootstrap_func(void *x)
{
stinfo *sti = x;
sti->func(sti->fd, sti->cookie);
free(sti);
return 0;
}
#if ADB_HOST
ADB_MUTEX_DEFINE( dns_lock );
static void dns_service(int fd, void *cookie)
{
char *hostname = cookie;
struct hostent *hp;
unsigned zero = 0;
adb_mutex_lock(&dns_lock);
hp = gethostbyname(hostname);
if(hp == 0) {
writex(fd, &zero, 4);
} else {
writex(fd, hp->h_addr, 4);
}
adb_mutex_unlock(&dns_lock);
adb_close(fd);
}
#else
extern int recovery_mode;
static void recover_service(int s, void *cookie)
{
unsigned char buf[4096];
unsigned count = (unsigned) cookie;
int fd;
fd = adb_creat("/tmp/update", 0644);
if(fd < 0) {
adb_close(s);
return;
}
while(count > 0) {
unsigned xfer = (count > 4096) ? 4096 : count;
if(readx(s, buf, xfer)) break;
if(writex(fd, buf, xfer)) break;
count -= xfer;
}
if(count == 0) {
writex(s, "OKAY", 4);
} else {
writex(s, "FAIL", 4);
}
adb_close(fd);
adb_close(s);
fd = adb_creat("/tmp/update.begin", 0644);
adb_close(fd);
}
#endif
#if 0
static void echo_service(int fd, void *cookie)
{
char buf[4096];
int r;
char *p;
int c;
for(;;) {
r = read(fd, buf, 4096);
if(r == 0) goto done;
if(r < 0) {
if(errno == EINTR) continue;
else goto done;
}
c = r;
p = buf;
while(c > 0) {
r = write(fd, p, c);
if(r > 0) {
c -= r;
p += r;
continue;
}
if((r < 0) && (errno == EINTR)) continue;
goto done;
}
}
done:
close(fd);
}
#endif
static int create_service_thread(void (*func)(int, void *), void *cookie)
{
stinfo *sti;
adb_thread_t t;
int s[2];
if(adb_socketpair(s)) {
printf("cannot create service socket pair\n");
return -1;
}
sti = malloc(sizeof(stinfo));
if(sti == 0) fatal("cannot allocate stinfo");
sti->func = func;
sti->cookie = cookie;
sti->fd = s[1];
if(adb_thread_create( &t, service_bootstrap_func, sti)){
free(sti);
adb_close(s[0]);
adb_close(s[1]);
printf("cannot create service thread\n");
return -1;
}
D("service thread started, %d:%d\n",s[0], s[1]);
return s[0];
}
static int create_subprocess(const char *cmd, const char *arg0, const char *arg1)
{
#ifdef HAVE_WIN32_PROC
fprintf(stderr, "error: create_subprocess not implemented on Win32 (%s %s %s)\n", cmd, arg0, arg1);
return -1;
#else /* !HAVE_WIN32_PROC */
char *devname;
int ptm;
pid_t pid;
ptm = unix_open("/dev/ptmx", O_RDWR); // | O_NOCTTY);
if(ptm < 0){
printf("[ cannot open /dev/ptmx - %s ]\n",strerror(errno));
return -1;
}
fcntl(ptm, F_SETFD, FD_CLOEXEC);
if(grantpt(ptm) || unlockpt(ptm) ||
((devname = (char*) ptsname(ptm)) == 0)){
printf("[ trouble with /dev/ptmx - %s ]\n", strerror(errno));
return -1;
}
pid = fork();
if(pid < 0) {
printf("- fork failed: %s -\n", strerror(errno));
return -1;
}
if(pid == 0){
int pts;
setsid();
pts = unix_open(devname, O_RDWR);
if(pts < 0) exit(-1);
dup2(pts, 0);
dup2(pts, 1);
dup2(pts, 2);
adb_close(ptm);
execl(cmd, cmd, arg0, arg1, NULL);
fprintf(stderr, "- exec '%s' failed: %s (%d) -\n",
cmd, strerror(errno), errno);
exit(-1);
} else {
return ptm;
}
#endif /* !HAVE_WIN32_PROC */
}
#if ADB_HOST
#define SHELL_COMMAND "/bin/sh"
#else
#define SHELL_COMMAND "/system/bin/sh"
#endif
int service_to_fd(const char *name)
{
int ret = -1;
if(!strncmp(name, "tcp:", 4)) {
int port = atoi(name + 4);
name = strchr(name + 4, ':');
if(name == 0) {
ret = socket_loopback_client(port, SOCK_STREAM);
if (ret >= 0)
disable_tcp_nagle(ret);
} else {
#if ADB_HOST
adb_mutex_lock(&dns_lock);
ret = socket_network_client(name + 1, port, SOCK_STREAM);
adb_mutex_unlock(&dns_lock);
#else
return -1;
#endif
}
#ifndef HAVE_WINSOCK /* winsock doesn't implement unix domain sockets */
} else if(!strncmp(name, "local:", 6)) {
ret = socket_local_client(name + 6,
ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM);
} else if(!strncmp(name, "localreserved:", 14)) {
ret = socket_local_client(name + 14,
ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM);
} else if(!strncmp(name, "localabstract:", 14)) {
ret = socket_local_client(name + 14,
ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM);
} else if(!strncmp(name, "localfilesystem:", 16)) {
ret = socket_local_client(name + 16,
ANDROID_SOCKET_NAMESPACE_FILESYSTEM, SOCK_STREAM);
#endif
#if ADB_HOST
} else if(!strncmp("dns:", name, 4)){
char *n = strdup(name + 4);
if(n == 0) return -1;
ret = create_service_thread(dns_service, n);
#else /* !ADB_HOST */
} else if(!strncmp("dev:", name, 4)) {
ret = unix_open(name + 4, O_RDWR);
} else if(!strncmp(name, "framebuffer:", 12)) {
ret = create_service_thread(framebuffer_service, 0);
} else if(recovery_mode && !strncmp(name, "recover:", 8)) {
ret = create_service_thread(recover_service, (void*) atoi(name + 8));
} else if (!strncmp(name, "jdwp:", 5)) {
ret = create_jdwp_connection_fd(atoi(name+5));
} else if (!strncmp(name, "log:", 4)) {
ret = create_service_thread(log_service, get_log_file_path(name + 4));
#endif
} else if(!HOST && !strncmp(name, "shell:", 6)) {
if(name[6]) {
ret = create_subprocess(SHELL_COMMAND, "-c", name + 6);
} else {
ret = create_subprocess(SHELL_COMMAND, "-", 0);
}
#if !ADB_HOST
} else if(!strncmp(name, "sync:", 5)) {
ret = create_service_thread(file_sync_service, NULL);
} else if(!strncmp(name, "remount:", 8)) {
ret = create_service_thread(remount_service, NULL);
#endif
#if 0
} else if(!strncmp(name, "echo:", 5)){
ret = create_service_thread(echo_service, 0);
#endif
}
if (ret >= 0) {
close_on_exec(ret);
}
return ret;
}
#if ADB_HOST
struct state_info {
transport_type transport;
char* serial;
int state;
};
static void wait_for_state(int fd, void* cookie)
{
struct state_info* sinfo = cookie;
char* err = "unknown error";
D("wait_for_state %d\n", sinfo->state);
atransport *t = acquire_one_transport(sinfo->state, sinfo->transport, sinfo->serial, &err);
if(t != 0) {
writex(fd, "OKAY", 4);
} else {
sendfailmsg(fd, err);
}
if (sinfo->serial)
free(sinfo->serial);
free(sinfo);
adb_close(fd);
D("wait_for_state is done\n");
}
#endif
#if ADB_HOST
asocket* host_service_to_socket(const char* name, const char *serial)
{
if (!strcmp(name,"track-devices")) {
return create_device_tracker();
} else if (!strncmp(name, "wait-for-", strlen("wait-for-"))) {
struct state_info* sinfo = malloc(sizeof(struct state_info));
if (serial)
sinfo->serial = strdup(serial);
else
sinfo->serial = NULL;
name += strlen("wait-for-");
if (!strncmp(name, "bootloader", strlen("bootloader"))) {
sinfo->transport = kTransportUsb;
sinfo->state = CS_BOOTLOADER;
} else if (!strncmp(name, "local", strlen("local"))) {
sinfo->transport = kTransportLocal;
sinfo->state = CS_DEVICE;
} else if (!strncmp(name, "usb", strlen("usb"))) {
sinfo->transport = kTransportUsb;
sinfo->state = CS_DEVICE;
} else if (!strncmp(name, "any", strlen("any"))) {
sinfo->transport = kTransportAny;
sinfo->state = CS_DEVICE;
} else {
free(sinfo);
return NULL;
}
int fd = create_service_thread(wait_for_state, sinfo);
return create_local_socket(fd);
}
return NULL;
}
#endif /* ADB_HOST */

185
adb/shlist.c Executable file
View file

@ -0,0 +1,185 @@
/*-------------------------------------------------------------------*/
/* List Functionality */
/*-------------------------------------------------------------------*/
/* #define SH_LIST_DEBUG */
/*-------------------------------------------------------------------*/
#include <stdio.h>
#include <stdlib.h>
#include "shlist.h"
/*-------------------------------------------------------------------*/
void shListInitList( SHLIST *listPtr )
{
listPtr->data = (void *)0L;
listPtr->next = listPtr;
listPtr->prev = listPtr;
}
SHLIST *shListFindItem( SHLIST *head, void *val, shListEqual func )
{
SHLIST *item;
for(item=head->next;( item != head );item=item->next)
if( func ) {
if( func( val, item->data ) ) {
return( item );
}
}
else {
if( item->data == val ) {
return( item );
}
}
return( NULL );
}
SHLIST *shListGetLastItem( SHLIST *head )
{
if( head->prev != head )
return( head->prev );
return( NULL );
}
SHLIST *shListGetFirstItem( SHLIST *head )
{
if( head->next != head )
return( head->next );
return( NULL );
}
SHLIST *shListGetNItem( SHLIST *head, unsigned long num )
{
SHLIST *item;
unsigned long i;
for(i=0,item=head->next;( (i < num) && (item != head) );i++,item=item->next);
if( item != head )
return( item );
return( NULL );
}
SHLIST *shListGetNextItem( SHLIST *head, SHLIST *item )
{
if( item == NULL )
return( NULL );
if( item->next != head )
return( item->next );
return( NULL );
}
SHLIST *shListGetPrevItem( SHLIST *head, SHLIST *item )
{
if( item == NULL )
return( NULL );
if( item->prev != head )
return( item->prev );
return( NULL );
}
void shListDelItem( SHLIST *head, SHLIST *item, shListFree func )
{
if( item == NULL )
return;
#ifdef SH_LIST_DEBUG
fprintf(stderr, "Del %lx\n", (unsigned long)(item->data));
#endif
(item->prev)->next = item->next;
(item->next)->prev = item->prev;
if( func && item->data ) {
func( (void *)(item->data) );
}
free( item );
head->data = (void *)((unsigned long)(head->data) - 1);
}
void shListInsFirstItem( SHLIST *head, void *val )
{ /* Insert to the beginning of the list */
SHLIST *item;
item = (SHLIST *)malloc( sizeof(SHLIST) );
if( item == NULL )
return;
item->data = val;
item->next = head->next;
item->prev = head;
(head->next)->prev = item;
head->next = item;
#ifdef SH_LIST_DEBUG
fprintf(stderr, "Ins First %lx\n", (unsigned long)(item->data));
#endif
head->data = (void *)((unsigned long)(head->data) + 1);
}
void shListInsLastItem( SHLIST *head, void *val )
{ /* Insert to the end of the list */
SHLIST *item;
item = (SHLIST *)malloc( sizeof(SHLIST) );
if( item == NULL )
return;
item->data = val;
item->next = head;
item->prev = head->prev;
(head->prev)->next = item;
head->prev = item;
#ifdef SH_LIST_DEBUG
fprintf(stderr, "Ins Last %lx\n", (unsigned long)(item->data));
#endif
head->data = (void *)((unsigned long)(head->data) + 1);
}
void shListInsBeforeItem( SHLIST *head, void *val, void *etal,
shListCmp func )
{
SHLIST *item, *iptr;
if( func == NULL )
shListInsFirstItem( head, val );
else {
item = (SHLIST *)malloc( sizeof(SHLIST) );
if( item == NULL )
return;
item->data = val;
for(iptr=head->next;( iptr != head );iptr=iptr->next)
if( func( val, iptr->data, etal ) )
break;
item->next = iptr;
item->prev = iptr->prev;
(iptr->prev)->next = item;
iptr->prev = item;
#ifdef SH_LIST_DEBUG
fprintf(stderr, "Ins Before %lx\n", (unsigned long)(item->data));
#endif
head->data = (void *)((unsigned long)(head->data) + 1);
}
}
void shListDelAllItems( SHLIST *head, shListFree func )
{
SHLIST *item;
for(item=head->next;( item != head );) {
shListDelItem( head, item, func );
item = head->next;
}
head->data = (void *)0L;
}
void shListPrintAllItems( SHLIST *head, shListPrint func )
{
#ifdef SH_LIST_DEBUG
SHLIST *item;
for(item=head->next;( item != head );item=item->next)
if( func ) {
func(item->data);
}
else {
fprintf(stderr, "Item: %lx\n",(unsigned long)(item->data));
}
#endif
}
unsigned long shListGetCount( SHLIST *head )
{
return( (unsigned long)(head->data) );
}

34
adb/shlist.h Executable file
View file

@ -0,0 +1,34 @@
/*-------------------------------------------------------------------*/
/* List Functionality */
/*-------------------------------------------------------------------*/
#ifndef _SHLIST_H_
#define _SHLIST_H_
typedef struct SHLIST_STRUC {
void *data;
struct SHLIST_STRUC *next;
struct SHLIST_STRUC *prev;
} SHLIST;
typedef int (*shListCmp)( void *valo, void *valn, void *etalon );
typedef int (*shListPrint)( void *val );
typedef void (*shListFree)( void *val );
typedef int (*shListEqual)( void *val, void *idata );
void shListInitList( SHLIST *listPtr );
SHLIST *shListFindItem( SHLIST *head, void *val, shListEqual func );
SHLIST *shListGetFirstItem( SHLIST *head );
SHLIST *shListGetNItem( SHLIST *head, unsigned long num );
SHLIST *shListGetLastItem( SHLIST *head );
SHLIST *shListGetNextItem( SHLIST *head, SHLIST *item );
SHLIST *shListGetPrevItem( SHLIST *head, SHLIST *item );
void shListDelItem( SHLIST *head, SHLIST *item, shListFree func );
void shListInsFirstItem( SHLIST *head, void *val );
void shListInsBeforeItem( SHLIST *head, void *val, void *etalon,
shListCmp func );
void shListInsLastItem( SHLIST *head, void *val );
void shListDelAllItems( SHLIST *head, shListFree func );
void shListPrintAllItems( SHLIST *head, shListPrint func );
unsigned long shListGetCount( SHLIST *head );
#endif

733
adb/sockets.c Normal file
View file

@ -0,0 +1,733 @@
/*
* 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.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <ctype.h>
#include "sysdeps.h"
#define TRACE_TAG TRACE_SOCKETS
#include "adb.h"
ADB_MUTEX_DEFINE( socket_list_lock );
static void local_socket_close_locked(asocket *s);
int sendfailmsg(int fd, const char *reason)
{
char buf[9];
int len;
len = strlen(reason);
if(len > 0xffff) len = 0xffff;
snprintf(buf, sizeof buf, "FAIL%04x", len);
if(writex(fd, buf, 8)) return -1;
return writex(fd, reason, len);
}
//extern int online;
static unsigned local_socket_next_id = 1;
static asocket local_socket_list = {
.next = &local_socket_list,
.prev = &local_socket_list,
};
asocket *find_local_socket(unsigned id)
{
asocket *s;
asocket *result = NULL;
adb_mutex_lock(&socket_list_lock);
for(s = local_socket_list.next; s != &local_socket_list && !result; s = s->next) {
if(s->id == id) result = s;
}
adb_mutex_unlock(&socket_list_lock);
return result;
}
void install_local_socket(asocket *s)
{
adb_mutex_lock(&socket_list_lock);
s->id = local_socket_next_id++;
s->next = &local_socket_list;
s->prev = local_socket_list.prev;
s->prev->next = s;
s->next->prev = s;
adb_mutex_unlock(&socket_list_lock);
}
void remove_socket(asocket *s)
{
// socket_list_lock should already be held
if (s->prev && s->next)
{
s->prev->next = s->next;
s->next->prev = s->prev;
s->next = 0;
s->prev = 0;
s->id = 0;
}
}
void close_all_sockets(atransport *t)
{
asocket *s;
/* this is a little gross, but since s->close() *will* modify
** the list out from under you, your options are limited.
*/
adb_mutex_lock(&socket_list_lock);
restart:
for(s = local_socket_list.next; s != &local_socket_list; s = s->next){
if(s->transport == t || (s->peer && s->peer->transport == t)) {
local_socket_close_locked(s);
goto restart;
}
}
adb_mutex_unlock(&socket_list_lock);
}
static int local_socket_enqueue(asocket *s, apacket *p)
{
D("LS(%d): enqueue %d\n", s->id, p->len);
p->ptr = p->data;
/* if there is already data queue'd, we will receive
** events when it's time to write. just add this to
** the tail
*/
if(s->pkt_first) {
goto enqueue;
}
/* write as much as we can, until we
** would block or there is an error/eof
*/
while(p->len > 0) {
int r = adb_write(s->fd, p->ptr, p->len);
if(r > 0) {
p->len -= r;
p->ptr += r;
continue;
}
if((r == 0) || (errno != EAGAIN)) {
D( "LS(%d): not ready, errno=%d: %s\n", s->id, errno, strerror(errno) );
s->close(s);
return 1; /* not ready (error) */
} else {
break;
}
}
if(p->len == 0) {
put_apacket(p);
return 0; /* ready for more data */
}
enqueue:
p->next = 0;
if(s->pkt_first) {
s->pkt_last->next = p;
} else {
s->pkt_first = p;
}
s->pkt_last = p;
/* make sure we are notified when we can drain the queue */
fdevent_add(&s->fde, FDE_WRITE);
return 1; /* not ready (backlog) */
}
static void local_socket_ready(asocket *s)
{
/* far side is ready for data, pay attention to
readable events */
fdevent_add(&s->fde, FDE_READ);
// D("LS(%d): ready()\n", s->id);
}
static void local_socket_close(asocket *s)
{
adb_mutex_lock(&socket_list_lock);
local_socket_close_locked(s);
adb_mutex_unlock(&socket_list_lock);
}
static void local_socket_close_locked(asocket *s)
{
apacket *p, *n;
if(s->peer) {
s->peer->peer = 0;
// tweak to avoid deadlock
if (s->peer->close == local_socket_close)
local_socket_close_locked(s->peer);
else
s->peer->close(s->peer);
}
/* IMPORTANT: the remove closes the fd
** that belongs to this socket
*/
fdevent_remove(&s->fde);
/* dispose of any unwritten data */
for(p = s->pkt_first; p; p = n) {
D("LS(%d): discarding %d bytes\n", s->id, p->len);
n = p->next;
put_apacket(p);
}
D("LS(%d): closed\n", s->id);
remove_socket(s);
free(s);
}
static void local_socket_event_func(int fd, unsigned ev, void *_s)
{
asocket *s = _s;
if(ev & FDE_READ){
apacket *p = get_apacket();
unsigned char *x = p->data;
size_t avail = MAX_PAYLOAD;
int r;
int is_eof = 0;
while(avail > 0) {
r = adb_read(fd, x, avail);
if(r > 0) {
avail -= r;
x += r;
continue;
}
if(r < 0) {
if(errno == EAGAIN) break;
if(errno == EINTR) continue;
}
/* r = 0 or unhandled error */
is_eof = 1;
break;
}
if((avail == MAX_PAYLOAD) || (s->peer == 0)) {
put_apacket(p);
} else {
p->len = MAX_PAYLOAD - avail;
r = s->peer->enqueue(s->peer, p);
if(r < 0) {
/* error return means they closed us as a side-effect
** and we must retutn immediately
*/
return;
}
if(r > 0) {
/* if the remote cannot accept further events,
** we disable notification of READs. They'll
** be enabled again when we get a call to ready()
*/
fdevent_del(&s->fde, FDE_READ);
}
}
if(is_eof) {
s->close(s);
}
return;
}
if(ev & FDE_WRITE){
apacket *p;
while((p = s->pkt_first) != 0) {
while(p->len > 0) {
int r = adb_write(fd, p->ptr, p->len);
if(r > 0) {
p->ptr += r;
p->len -= r;
continue;
}
if(r < 0) {
if(errno == EAGAIN) return;
if(errno == EINTR) continue;
}
s->close(s);
return;
}
if(p->len == 0) {
s->pkt_first = p->next;
if(s->pkt_first == 0) s->pkt_last = 0;
put_apacket(p);
}
}
/* no more packets queued, so we can ignore
** writable events again and tell our peer
** to resume writing
*/
fdevent_del(&s->fde, FDE_WRITE);
s->peer->ready(s->peer);
return;
}
if(ev & FDE_ERROR){
/* this should be caught be the next read or write
** catching it here means we may skip the last few
** bytes of readable data.
*/
// s->close(s);
return;
}
}
asocket *create_local_socket(int fd)
{
asocket *s = calloc(1, sizeof(asocket));
if(s == 0) fatal("cannot allocate socket");
install_local_socket(s);
s->fd = fd;
s->enqueue = local_socket_enqueue;
s->ready = local_socket_ready;
s->close = local_socket_close;
fdevent_install(&s->fde, fd, local_socket_event_func, s);
/* fdevent_add(&s->fde, FDE_ERROR); */
//fprintf(stderr, "Created local socket in create_local_socket \n");
D("LS(%d): created (fd=%d)\n", s->id, s->fd);
return s;
}
asocket *create_local_service_socket(const char *name)
{
asocket *s;
int fd;
#if !ADB_HOST
if (!strcmp(name,"jdwp")) {
return create_jdwp_service_socket();
}
if (!strcmp(name,"track-jdwp")) {
return create_jdwp_tracker_service_socket();
}
#endif
fd = service_to_fd(name);
if(fd < 0) return 0;
s = create_local_socket(fd);
D("LS(%d): bound to '%s'\n", s->id, name);
return s;
}
#if ADB_HOST
static asocket *create_host_service_socket(const char *name, const char* serial)
{
asocket *s;
s = host_service_to_socket(name, serial);
if (s != NULL) {
D("LS(%d) bound to '%s'\n", s->id, name);
return s;
}
return s;
}
#endif /* ADB_HOST */
/* a Remote socket is used to send/receive data to/from a given transport object
** it needs to be closed when the transport is forcibly destroyed by the user
*/
typedef struct aremotesocket {
asocket socket;
adisconnect disconnect;
} aremotesocket;
static int remote_socket_enqueue(asocket *s, apacket *p)
{
D("Calling remote_socket_enqueue\n");
p->msg.command = A_WRTE;
p->msg.arg0 = s->peer->id;
p->msg.arg1 = s->id;
p->msg.data_length = p->len;
send_packet(p, s->transport);
return 1;
}
static void remote_socket_ready(asocket *s)
{
D("Calling remote_socket_ready\n");
apacket *p = get_apacket();
p->msg.command = A_OKAY;
p->msg.arg0 = s->peer->id;
p->msg.arg1 = s->id;
send_packet(p, s->transport);
}
static void remote_socket_close(asocket *s)
{
D("Calling remote_socket_close\n");
apacket *p = get_apacket();
p->msg.command = A_CLSE;
if(s->peer) {
p->msg.arg0 = s->peer->id;
s->peer->peer = 0;
s->peer->close(s->peer);
}
p->msg.arg1 = s->id;
send_packet(p, s->transport);
D("RS(%d): closed\n", s->id);
remove_transport_disconnect( s->transport, &((aremotesocket*)s)->disconnect );
free(s);
}
static void remote_socket_disconnect(void* _s, atransport* t)
{
asocket* s = _s;
asocket* peer = s->peer;
D("remote_socket_disconnect RS(%d)\n", s->id);
if (peer) {
peer->peer = NULL;
peer->close(peer);
}
remove_transport_disconnect( s->transport, &((aremotesocket*)s)->disconnect );
free(s);
}
asocket *create_remote_socket(unsigned id, atransport *t)
{
asocket *s = calloc(1, sizeof(aremotesocket));
adisconnect* dis = &((aremotesocket*)s)->disconnect;
if(s == 0) fatal("cannot allocate socket");
s->id = id;
s->enqueue = remote_socket_enqueue;
s->ready = remote_socket_ready;
s->close = remote_socket_close;
s->transport = t;
dis->func = remote_socket_disconnect;
dis->opaque = s;
add_transport_disconnect( t, dis );
D("RS(%d): created\n", s->id);
return s;
}
void connect_to_remote(asocket *s, const char *destination)
{
D("Connect_to_remote call \n");
apacket *p = get_apacket();
int len = strlen(destination) + 1;
if(len > (MAX_PAYLOAD-1)) {
fatal("destination oversized");
}
D("LS(%d): connect('%s')\n", s->id, destination);
p->msg.command = A_OPEN;
p->msg.arg0 = s->id;
p->msg.data_length = len;
strcpy((char*) p->data, destination);
send_packet(p, s->transport);
}
/* this is used by magic sockets to rig local sockets to
send the go-ahead message when they connect */
static void local_socket_ready_notify(asocket *s)
{
s->ready = local_socket_ready;
s->close = local_socket_close;
adb_write(s->fd, "OKAY", 4);
s->ready(s);
}
/* this is used by magic sockets to rig local sockets to
send the failure message if they are closed before
connected (to avoid closing them without a status message) */
static void local_socket_close_notify(asocket *s)
{
s->ready = local_socket_ready;
s->close = local_socket_close;
sendfailmsg(s->fd, "closed");
s->close(s);
}
unsigned unhex(unsigned char *s, int len)
{
unsigned n = 0, c;
while(len-- > 0) {
switch((c = *s++)) {
case '0': case '1': case '2':
case '3': case '4': case '5':
case '6': case '7': case '8':
case '9':
c -= '0';
break;
case 'a': case 'b': case 'c':
case 'd': case 'e': case 'f':
c = c - 'a' + 10;
break;
case 'A': case 'B': case 'C':
case 'D': case 'E': case 'F':
c = c - 'A' + 10;
break;
default:
return 0xffffffff;
}
n = (n << 4) | c;
}
return n;
}
static int smart_socket_enqueue(asocket *s, apacket *p)
{
unsigned len;
#if ADB_HOST
char *service = NULL;
char* serial = NULL;
transport_type ttype = kTransportAny;
#endif
D("SS(%d): enqueue %d\n", s->id, p->len);
if(s->pkt_first == 0) {
s->pkt_first = p;
s->pkt_last = p;
} else {
if((s->pkt_first->len + p->len) > MAX_PAYLOAD) {
D("SS(%d): overflow\n", s->id);
put_apacket(p);
goto fail;
}
memcpy(s->pkt_first->data + s->pkt_first->len,
p->data, p->len);
s->pkt_first->len += p->len;
put_apacket(p);
p = s->pkt_first;
}
/* don't bother if we can't decode the length */
if(p->len < 4) return 0;
len = unhex(p->data, 4);
if((len < 1) || (len > 1024)) {
D("SS(%d): bad size (%d)\n", s->id, len);
goto fail;
}
D("SS(%d): len is %d\n", s->id, len );
/* can't do anything until we have the full header */
if((len + 4) > p->len) {
D("SS(%d): waiting for %d more bytes\n", s->id, len+4 - p->len);
return 0;
}
p->data[len + 4] = 0;
D("SS(%d): '%s'\n", s->id, (char*) (p->data + 4));
#if ADB_HOST
service = (char *)p->data + 4;
if(!strncmp(service, "host-serial:", strlen("host-serial:"))) {
char* serial_end;
service += strlen("host-serial:");
// serial number should follow "host:"
serial_end = strchr(service, ':');
if (serial_end) {
*serial_end = 0; // terminate string
serial = service;
service = serial_end + 1;
}
} else if (!strncmp(service, "host-usb:", strlen("host-usb:"))) {
ttype = kTransportUsb;
service += strlen("host-usb:");
} else if (!strncmp(service, "host-local:", strlen("host-local:"))) {
ttype = kTransportLocal;
service += strlen("host-local:");
} else if (!strncmp(service, "host:", strlen("host:"))) {
ttype = kTransportAny;
service += strlen("host:");
} else {
service = NULL;
}
if (service) {
asocket *s2;
/* some requests are handled immediately -- in that
** case the handle_host_request() routine has sent
** the OKAY or FAIL message and all we have to do
** is clean up.
*/
if(handle_host_request(service, ttype, serial, s->peer->fd, s) == 0) {
/* XXX fail message? */
D( "SS(%d): handled host service '%s'\n", s->id, service );
goto fail;
}
if (!strncmp(service, "transport", strlen("transport"))) {
D( "SS(%d): okay transport\n", s->id );
p->len = 0;
return 0;
}
/* try to find a local service with this name.
** if no such service exists, we'll fail out
** and tear down here.
*/
s2 = create_host_service_socket(service, serial);
if(s2 == 0) {
D( "SS(%d): couldn't create host service '%s'\n", s->id, service );
sendfailmsg(s->peer->fd, "unknown host service");
goto fail;
}
/* we've connected to a local host service,
** so we make our peer back into a regular
** local socket and bind it to the new local
** service socket, acknowledge the successful
** connection, and close this smart socket now
** that its work is done.
*/
adb_write(s->peer->fd, "OKAY", 4);
s->peer->ready = local_socket_ready;
s->peer->close = local_socket_close;
s->peer->peer = s2;
s2->peer = s->peer;
s->peer = 0;
D( "SS(%d): okay\n", s->id );
s->close(s);
/* initial state is "ready" */
s2->ready(s2);
return 0;
}
#else /* !ADB_HOST */
if (s->transport == NULL) {
char* error_string = "unknown failure";
s->transport = acquire_one_transport (CS_ANY,
kTransportAny, NULL, &error_string);
if (s->transport == NULL) {
sendfailmsg(s->peer->fd, error_string);
goto fail;
}
}
#endif
if(!(s->transport) || (s->transport->connection_state == CS_OFFLINE)) {
/* if there's no remote we fail the connection
** right here and terminate it
*/
sendfailmsg(s->peer->fd, "device offline (x)");
goto fail;
}
/* instrument our peer to pass the success or fail
** message back once it connects or closes, then
** detach from it, request the connection, and
** tear down
*/
s->peer->ready = local_socket_ready_notify;
s->peer->close = local_socket_close_notify;
s->peer->peer = 0;
/* give him our transport and upref it */
s->peer->transport = s->transport;
connect_to_remote(s->peer, (char*) (p->data + 4));
s->peer = 0;
s->close(s);
return 1;
fail:
/* we're going to close our peer as a side-effect, so
** return -1 to signal that state to the local socket
** who is enqueueing against us
*/
s->close(s);
return -1;
}
static void smart_socket_ready(asocket *s)
{
D("SS(%d): ready\n", s->id);
}
static void smart_socket_close(asocket *s)
{
D("SS(%d): closed\n", s->id);
if(s->pkt_first){
put_apacket(s->pkt_first);
}
if(s->peer) {
s->peer->peer = 0;
s->peer->close(s->peer);
}
free(s);
}
asocket *create_smart_socket(void (*action_cb)(asocket *s, const char *act))
{
D("Creating smart socket \n");
asocket *s = calloc(1, sizeof(asocket));
if(s == 0) fatal("cannot allocate socket");
s->id = 0;
s->enqueue = smart_socket_enqueue;
s->ready = smart_socket_ready;
s->close = smart_socket_close;
s->extra = action_cb;
D("SS(%d): created %p\n", s->id, action_cb);
return s;
}
void smart_socket_action(asocket *s, const char *act)
{
}
void connect_to_smartsocket(asocket *s)
{
D("Connecting to smart socket \n");
asocket *ss = create_smart_socket(smart_socket_action);
s->peer = ss;
ss->peer = s;
s->ready(s);
}

BIN
adb/sockets.dia Normal file

Binary file not shown.

473
adb/sysdeps.h Normal file
View file

@ -0,0 +1,473 @@
/*
* 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.
*/
/* this file contains system-dependent definitions used by ADB
* they're related to threads, sockets and file descriptors
*/
#ifndef _ADB_SYSDEPS_H
#define _ADB_SYSDEPS_H
#ifdef __CYGWIN__
# undef _WIN32
#endif
#ifdef _WIN32
#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <process.h>
#include <fcntl.h>
#include <io.h>
#include <sys/stat.h>
#include <errno.h>
#include <ctype.h>
#define OS_PATH_SEPARATOR '\\'
#define OS_PATH_SEPARATOR_STR "\\"
typedef CRITICAL_SECTION adb_mutex_t;
#define ADB_MUTEX_DEFINE(x) adb_mutex_t x
/* declare all mutexes */
#define ADB_MUTEX(x) extern adb_mutex_t x;
#include "mutex_list.h"
extern void adb_sysdeps_init(void);
static __inline__ void adb_mutex_lock( adb_mutex_t* lock )
{
EnterCriticalSection( lock );
}
static __inline__ void adb_mutex_unlock( adb_mutex_t* lock )
{
LeaveCriticalSection( lock );
}
typedef struct { unsigned tid; } adb_thread_t;
typedef void* (*adb_thread_func_t)(void* arg);
typedef void (*win_thread_func_t)(void* arg);
static __inline__ int adb_thread_create( adb_thread_t *thread, adb_thread_func_t func, void* arg)
{
thread->tid = _beginthread( (win_thread_func_t)func, 0, arg );
if (thread->tid == (unsigned)-1L) {
return -1;
}
return 0;
}
static __inline__ void close_on_exec(int fd)
{
/* nothing really */
}
extern void disable_tcp_nagle(int fd);
#define lstat stat /* no symlinks on Win32 */
#define S_ISLNK(m) 0 /* no symlinks on Win32 */
static __inline__ int adb_unlink(const char* path)
{
int rc = unlink(path);
if (rc == -1 && errno == EACCES) {
/* unlink returns EACCES when the file is read-only, so we first */
/* try to make it writable, then unlink again... */
rc = chmod(path, _S_IREAD|_S_IWRITE );
if (rc == 0)
rc = unlink(path);
}
return rc;
}
#undef unlink
#define unlink ___xxx_unlink
static __inline__ int adb_mkdir(const char* path, int mode)
{
return _mkdir(path);
}
#undef mkdir
#define mkdir ___xxx_mkdir
extern int adb_open(const char* path, int options);
extern int adb_creat(const char* path, int mode);
extern int adb_read(int fd, void* buf, int len);
extern int adb_write(int fd, const void* buf, int len);
extern int adb_lseek(int fd, int pos, int where);
extern int adb_close(int fd);
static __inline__ int unix_close(int fd)
{
return close(fd);
}
#undef close
#define close ____xxx_close
static __inline__ int unix_read(int fd, void* buf, size_t len)
{
return read(fd, buf, len);
}
#undef read
#define read ___xxx_read
static __inline__ int unix_write(int fd, const void* buf, size_t len)
{
return write(fd, buf, len);
}
#undef write
#define write ___xxx_write
static __inline__ int adb_open_mode(const char* path, int options, int mode)
{
return adb_open(path, options);
}
static __inline__ int unix_open(const char* path, int options,...)
{
if ((options & O_CREAT) == 0)
{
return open(path, options);
}
else
{
int mode;
va_list args;
va_start( args, options );
mode = va_arg( args, int );
va_end( args );
return open(path, options, mode);
}
}
#define open ___xxx_unix_open
/* normally provided by <cutils/misc.h> */
extern void* load_file(const char* pathname, unsigned* psize);
/* normally provided by <cutils/sockets.h> */
extern int socket_loopback_client(int port, int type);
extern int socket_network_client(const char *host, int port, int type);
extern int socket_loopback_server(int port, int type);
extern int socket_inaddr_any_server(int port, int type);
/* normally provided by <cutils/fdevent.h> */
#define FDE_READ 0x0001
#define FDE_WRITE 0x0002
#define FDE_ERROR 0x0004
#define FDE_DONT_CLOSE 0x0080
typedef struct fdevent fdevent;
typedef void (*fd_func)(int fd, unsigned events, void *userdata);
fdevent *fdevent_create(int fd, fd_func func, void *arg);
void fdevent_destroy(fdevent *fde);
void fdevent_install(fdevent *fde, int fd, fd_func func, void *arg);
void fdevent_remove(fdevent *item);
void fdevent_set(fdevent *fde, unsigned events);
void fdevent_add(fdevent *fde, unsigned events);
void fdevent_del(fdevent *fde, unsigned events);
void fdevent_loop();
struct fdevent {
fdevent *next;
fdevent *prev;
int fd;
unsigned short state;
unsigned short events;
fd_func func;
void *arg;
};
static __inline__ void adb_sleep_ms( int mseconds )
{
Sleep( mseconds );
}
extern int adb_socket_accept(int serverfd, struct sockaddr* addr, socklen_t *addrlen);
#undef accept
#define accept ___xxx_accept
static __inline__ int adb_socket_setbufsize( int fd, int bufsize )
{
int opt = bufsize;
return setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (const char*)&opt, sizeof(opt));
}
extern int adb_socketpair( int sv[2] );
static __inline__ char* adb_dirstart( const char* path )
{
char* p = strchr(path, '/');
char* p2 = strchr(path, '\\');
if ( !p )
p = p2;
else if ( p2 && p2 > p )
p = p2;
return p;
}
static __inline__ char* adb_dirstop( const char* path )
{
char* p = strrchr(path, '/');
char* p2 = strrchr(path, '\\');
if ( !p )
p = p2;
else if ( p2 && p2 > p )
p = p2;
return p;
}
static __inline__ int adb_is_absolute_host_path( const char* path )
{
return isalpha(path[0]) && path[1] == ':' && path[2] == '\\';
}
#else /* !_WIN32 a.k.a. Unix */
#include <cutils/fdevent.h>
#include <cutils/sockets.h>
#include <cutils/properties.h>
#include <cutils/misc.h>
#include <signal.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdarg.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <string.h>
#define OS_PATH_SEPARATOR '/'
#define OS_PATH_SEPARATOR_STR "/"
typedef pthread_mutex_t adb_mutex_t;
#define ADB_MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER
#define adb_mutex_init pthread_mutex_init
#define adb_mutex_lock pthread_mutex_lock
#define adb_mutex_unlock pthread_mutex_unlock
#define adb_mutex_destroy pthread_mutex_destroy
#define ADB_MUTEX_DEFINE(m) static adb_mutex_t m = PTHREAD_MUTEX_INITIALIZER
#define adb_cond_t pthread_cond_t
#define adb_cond_init pthread_cond_init
#define adb_cond_wait pthread_cond_wait
#define adb_cond_broadcast pthread_cond_broadcast
#define adb_cond_signal pthread_cond_signal
#define adb_cond_destroy pthread_cond_destroy
static __inline__ void close_on_exec(int fd)
{
fcntl( fd, F_SETFD, FD_CLOEXEC );
}
static __inline__ int unix_open(const char* path, int options,...)
{
if ((options & O_CREAT) == 0)
{
return open(path, options);
}
else
{
int mode;
va_list args;
va_start( args, options );
mode = va_arg( args, int );
va_end( args );
return open(path, options, mode);
}
}
static __inline__ int adb_open_mode( const char* pathname, int options, int mode )
{
return open( pathname, options, mode );
}
static __inline__ int adb_open( const char* pathname, int options )
{
int fd = open( pathname, options );
if (fd < 0)
return -1;
close_on_exec( fd );
return fd;
}
#undef open
#define open ___xxx_open
static __inline__ int adb_close(int fd)
{
return close(fd);
}
#undef close
#define close ____xxx_close
static __inline__ int adb_read(int fd, void* buf, size_t len)
{
return read(fd, buf, len);
}
#undef read
#define read ___xxx_read
static __inline__ int adb_write(int fd, const void* buf, size_t len)
{
return write(fd, buf, len);
}
#undef write
#define write ___xxx_write
static __inline__ int adb_lseek(int fd, int pos, int where)
{
return lseek(fd, pos, where);
}
#undef lseek
#define lseek ___xxx_lseek
static __inline__ int adb_unlink(const char* path)
{
return unlink(path);
}
#undef unlink
#define unlink ___xxx_unlink
static __inline__ int adb_creat(const char* path, int mode)
{
int fd = creat(path, mode);
if ( fd < 0 )
return -1;
close_on_exec(fd);
return fd;
}
#undef creat
#define creat ___xxx_creat
static __inline__ int adb_socket_accept(int serverfd, struct sockaddr* addr, socklen_t *addrlen)
{
return accept( serverfd, addr, addrlen );
}
#undef accept
#define accept ___xxx_accept
#define unix_read adb_read
#define unix_write adb_write
#define unix_close adb_close
typedef pthread_t adb_thread_t;
typedef void* (*adb_thread_func_t)( void* arg );
static __inline__ int adb_thread_create( adb_thread_t *pthread, adb_thread_func_t start, void* arg )
{
pthread_attr_t attr;
pthread_attr_init (&attr);
pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
return pthread_create( pthread, &attr, start, arg );
}
static __inline__ int adb_socket_setbufsize( int fd, int bufsize )
{
int opt = bufsize;
return setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt));
}
static __inline__ void disable_tcp_nagle(int fd)
{
int on = 1;
setsockopt( fd, IPPROTO_TCP, TCP_NODELAY, (void*)&on, sizeof(on) );
}
static __inline__ int unix_socketpair( int d, int type, int protocol, int sv[2] )
{
return socketpair( d, type, protocol, sv );
}
static __inline__ int adb_socketpair( int sv[2] )
{
int rc;
rc = unix_socketpair( AF_UNIX, SOCK_STREAM, 0, sv );
if (rc < 0)
return -1;
close_on_exec( sv[0] );
close_on_exec( sv[1] );
return 0;
}
#undef socketpair
#define socketpair ___xxx_socketpair
static __inline__ void adb_sleep_ms( int mseconds )
{
usleep( mseconds*1000 );
}
static __inline__ int adb_mkdir(const char* path, int mode)
{
return mkdir(path, mode);
}
#undef mkdir
#define mkdir ___xxx_mkdir
static __inline__ void adb_sysdeps_init(void)
{
}
static __inline__ char* adb_dirstart(const char* path)
{
return strchr(path, '/');
}
static __inline__ char* adb_dirstop(const char* path)
{
return strrchr(path, '/');
}
static __inline__ int adb_is_absolute_host_path( const char* path )
{
return path[0] == '/';
}
#endif /* !_WIN32 */
#endif /* _ADB_SYSDEPS_H */

1953
adb/sysdeps_win32.c Normal file

File diff suppressed because it is too large Load diff

97
adb/test_track_devices.c Normal file
View file

@ -0,0 +1,97 @@
/* a simple test program, connects to ADB server, and opens a track-devices session */
#include <netdb.h>
#include <sys/socket.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <memory.h>
static void
panic( const char* msg )
{
fprintf(stderr, "PANIC: %s: %s\n", msg, strerror(errno));
exit(1);
}
static int
unix_write( int fd, const char* buf, int len )
{
int result = 0;
while (len > 0) {
int len2 = write(fd, buf, len);
if (len2 < 0) {
if (errno == EINTR || errno == EAGAIN)
continue;
return -1;
}
result += len2;
len -= len2;
buf += len2;
}
return result;
}
static int
unix_read( int fd, char* buf, int len )
{
int result = 0;
while (len > 0) {
int len2 = read(fd, buf, len);
if (len2 < 0) {
if (errno == EINTR || errno == EAGAIN)
continue;
return -1;
}
result += len2;
len -= len2;
buf += len2;
}
return result;
}
int main( void )
{
int ret, s;
struct sockaddr_in server;
char buffer[1024];
const char* request = "host:track-devices";
int len;
memset( &server, 0, sizeof(server) );
server.sin_family = AF_INET;
server.sin_port = htons(5037);
server.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
s = socket( PF_INET, SOCK_STREAM, 0 );
ret = connect( s, (struct sockaddr*) &server, sizeof(server) );
if (ret < 0) panic( "could not connect to server" );
/* send the request */
len = snprintf( buffer, sizeof buffer, "%04x%s", strlen(request), request );
if (unix_write(s, buffer, len) < 0)
panic( "could not send request" );
/* read the OKAY answer */
if (unix_read(s, buffer, 4) != 4)
panic( "could not read request" );
printf( "server answer: %.*s\n", 4, buffer );
/* now loop */
for (;;) {
char head[5] = "0000";
if (unix_read(s, head, 4) < 0)
panic("could not read length");
if ( sscanf( head, "%04x", &len ) != 1 )
panic("could not decode length");
if (unix_read(s, buffer, len) != len)
panic("could not read data");
printf( "received header %.*s (%d bytes):\n%.*s", 4, head, len, len, buffer );
}
close(s);
}

97
adb/test_track_jdwp.c Normal file
View file

@ -0,0 +1,97 @@
/* a simple test program, connects to ADB server, and opens a track-devices session */
#include <netdb.h>
#include <sys/socket.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <memory.h>
static void
panic( const char* msg )
{
fprintf(stderr, "PANIC: %s: %s\n", msg, strerror(errno));
exit(1);
}
static int
unix_write( int fd, const char* buf, int len )
{
int result = 0;
while (len > 0) {
int len2 = write(fd, buf, len);
if (len2 < 0) {
if (errno == EINTR || errno == EAGAIN)
continue;
return -1;
}
result += len2;
len -= len2;
buf += len2;
}
return result;
}
static int
unix_read( int fd, char* buf, int len )
{
int result = 0;
while (len > 0) {
int len2 = read(fd, buf, len);
if (len2 < 0) {
if (errno == EINTR || errno == EAGAIN)
continue;
return -1;
}
result += len2;
len -= len2;
buf += len2;
}
return result;
}
int main( void )
{
int ret, s;
struct sockaddr_in server;
char buffer[1024];
const char* request = "track-jdwp";
int len;
memset( &server, 0, sizeof(server) );
server.sin_family = AF_INET;
server.sin_port = htons(5037);
server.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
s = socket( PF_INET, SOCK_STREAM, 0 );
ret = connect( s, (struct sockaddr*) &server, sizeof(server) );
if (ret < 0) panic( "could not connect to server" );
/* send the request */
len = snprintf( buffer, sizeof buffer, "%04x%s", strlen(request), request );
if (unix_write(s, buffer, len) < 0)
panic( "could not send request" );
/* read the OKAY answer */
if (unix_read(s, buffer, 4) != 4)
panic( "could not read request" );
printf( "server answer: %.*s\n", 4, buffer );
/* now loop */
for (;;) {
char head[5] = "0000";
if (unix_read(s, head, 4) < 0)
panic("could not read length");
if ( sscanf( head, "%04x", &len ) != 1 )
panic("could not decode length");
if (unix_read(s, buffer, len) != len)
panic("could not read data");
printf( "received header %.*s (%d bytes):\n%.*s", 4, head, len, len, buffer );
}
close(s);
}

958
adb/transport.c Normal file
View file

@ -0,0 +1,958 @@
/*
* 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.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include "sysdeps.h"
#define TRACE_TAG TRACE_TRANSPORT
#include "adb.h"
static void transport_unref(atransport *t);
static atransport transport_list = {
.next = &transport_list,
.prev = &transport_list,
};
ADB_MUTEX_DEFINE( transport_lock );
#if ADB_TRACE
static void dump_hex( const unsigned char* ptr, size_t len )
{
int nn, len2 = len;
if (len2 > 16) len2 = 16;
for (nn = 0; nn < len2; nn++)
D("%02x", ptr[nn]);
D(" ");
for (nn = 0; nn < len2; nn++) {
int c = ptr[nn];
if (c < 32 || c > 127)
c = '.';
D("%c", c);
}
D("\n");
fflush(stdout);
}
#endif
void
kick_transport(atransport* t)
{
if (t && !t->kicked)
{
int kicked;
adb_mutex_lock(&transport_lock);
kicked = t->kicked;
if (!kicked)
t->kicked = 1;
adb_mutex_unlock(&transport_lock);
if (!kicked)
t->kick(t);
}
}
void
run_transport_disconnects(atransport* t)
{
adisconnect* dis = t->disconnects.next;
D("run_transport_disconnects: %p (%s)\n", t, t->serial ? t->serial : "unknown" );
while (dis != &t->disconnects) {
adisconnect* next = dis->next;
dis->func( dis->opaque, t );
dis = next;
}
}
static int
read_packet(int fd, apacket** ppacket)
{
char *p = (char*)ppacket; /* really read a packet address */
int r;
int len = sizeof(*ppacket);
while(len > 0) {
r = adb_read(fd, p, len);
if(r > 0) {
len -= r;
p += r;
} else {
D("read_packet: %d error %d %d\n", fd, r, errno);
if((r < 0) && (errno == EINTR)) continue;
return -1;
}
}
#if ADB_TRACE
if (ADB_TRACING)
{
unsigned command = (*ppacket)->msg.command;
int len = (*ppacket)->msg.data_length;
char cmd[5];
int n;
for (n = 0; n < 4; n++) {
int b = (command >> (n*8)) & 255;
if (b >= 32 && b < 127)
cmd[n] = (char)b;
else
cmd[n] = '.';
}
cmd[4] = 0;
D("read_packet: %d ok: [%08x %s] %08x %08x (%d) ",
fd, command, cmd, (*ppacket)->msg.arg0, (*ppacket)->msg.arg1, len);
dump_hex((*ppacket)->data, len);
}
#endif
return 0;
}
static int
write_packet(int fd, apacket** ppacket)
{
char *p = (char*) ppacket; /* we really write the packet address */
int r, len = sizeof(ppacket);
#if ADB_TRACE
if (ADB_TRACING)
{
unsigned command = (*ppacket)->msg.command;
int len = (*ppacket)->msg.data_length;
char cmd[5];
int n;
for (n = 0; n < 4; n++) {
int b = (command >> (n*8)) & 255;
if (b >= 32 && b < 127)
cmd[n] = (char)b;
else
cmd[n] = '.';
}
cmd[4] = 0;
D("write_packet: %d [%08x %s] %08x %08x (%d) ",
fd, command, cmd, (*ppacket)->msg.arg0, (*ppacket)->msg.arg1, len);
dump_hex((*ppacket)->data, len);
}
#endif
len = sizeof(ppacket);
while(len > 0) {
r = adb_write(fd, p, len);
if(r > 0) {
len -= r;
p += r;
} else {
D("write_packet: %d error %d %d\n", fd, r, errno);
if((r < 0) && (errno == EINTR)) continue;
return -1;
}
}
return 0;
}
static void transport_socket_events(int fd, unsigned events, void *_t)
{
if(events & FDE_READ){
apacket *p = 0;
if(read_packet(fd, &p)){
D("failed to read packet from transport socket on fd %d\n", fd);
} else {
handle_packet(p, (atransport *) _t);
}
}
}
void send_packet(apacket *p, atransport *t)
{
unsigned char *x;
unsigned sum;
unsigned count;
p->msg.magic = p->msg.command ^ 0xffffffff;
count = p->msg.data_length;
x = (unsigned char *) p->data;
sum = 0;
while(count-- > 0){
sum += *x++;
}
p->msg.data_check = sum;
print_packet("send", p);
if (t == NULL) {
fatal_errno("Transport is null");
D("Transport is null \n");
}
if(write_packet(t->transport_socket, &p)){
fatal_errno("cannot enqueue packet on transport socket");
}
}
/* The transport is opened by transport_register_func before
** the input and output threads are started.
**
** The output thread issues a SYNC(1, token) message to let
** the input thread know to start things up. In the event
** of transport IO failure, the output thread will post a
** SYNC(0,0) message to ensure shutdown.
**
** The transport will not actually be closed until both
** threads exit, but the input thread will kick the transport
** on its way out to disconnect the underlying device.
*/
static void *output_thread(void *_t)
{
atransport *t = _t;
apacket *p;
D("from_remote: starting thread for transport %p, on fd %d\n", t, t->fd );
D("from_remote: transport %p SYNC online (%d)\n", t, t->sync_token + 1);
p = get_apacket();
p->msg.command = A_SYNC;
p->msg.arg0 = 1;
p->msg.arg1 = ++(t->sync_token);
p->msg.magic = A_SYNC ^ 0xffffffff;
if(write_packet(t->fd, &p)) {
put_apacket(p);
D("from_remote: failed to write SYNC apacket to transport %p", t);
goto oops;
}
D("from_remote: data pump for transport %p\n", t);
for(;;) {
p = get_apacket();
if(t->read_from_remote(p, t) == 0){
D("from_remote: received remote packet, sending to transport %p\n",
t);
if(write_packet(t->fd, &p)){
put_apacket(p);
D("from_remote: failed to write apacket to transport %p", t);
goto oops;
}
} else {
D("from_remote: remote read failed for transport %p\n", p);
put_apacket(p);
break;
}
}
D("from_remote: SYNC offline for transport %p\n", t);
p = get_apacket();
p->msg.command = A_SYNC;
p->msg.arg0 = 0;
p->msg.arg1 = 0;
p->msg.magic = A_SYNC ^ 0xffffffff;
if(write_packet(t->fd, &p)) {
put_apacket(p);
D("from_remote: failed to write SYNC apacket to transport %p", t);
}
oops:
D("from_remote: thread is exiting for transport %p\n", t);
kick_transport(t);
transport_unref(t);
return 0;
}
static void *input_thread(void *_t)
{
atransport *t = _t;
apacket *p;
int active = 0;
D("to_remote: starting input_thread for %p, reading from fd %d\n",
t, t->fd);
for(;;){
if(read_packet(t->fd, &p)) {
D("to_remote: failed to read apacket from transport %p on fd %d\n",
t, t->fd );
break;
}
if(p->msg.command == A_SYNC){
if(p->msg.arg0 == 0) {
D("to_remote: transport %p SYNC offline\n", t);
put_apacket(p);
break;
} else {
if(p->msg.arg1 == t->sync_token) {
D("to_remote: transport %p SYNC online\n", t);
active = 1;
} else {
D("to_remote: trandport %p ignoring SYNC %d != %d\n",
t, p->msg.arg1, t->sync_token);
}
}
} else {
if(active) {
D("to_remote: transport %p got packet, sending to remote\n", t);
t->write_to_remote(p, t);
} else {
D("to_remote: transport %p ignoring packet while offline\n", t);
}
}
put_apacket(p);
}
// this is necessary to avoid a race condition that occured when a transport closes
// while a client socket is still active.
close_all_sockets(t);
D("to_remote: thread is exiting for transport %p, fd %d\n", t, t->fd);
kick_transport(t);
transport_unref(t);
return 0;
}
static int transport_registration_send = -1;
static int transport_registration_recv = -1;
static fdevent transport_registration_fde;
#if ADB_HOST
static int list_transports_msg(char* buffer, size_t bufferlen)
{
char head[5];
int len;
len = list_transports(buffer+4, bufferlen-4);
snprintf(head, sizeof(head), "%04x", len);
memcpy(buffer, head, 4);
len += 4;
return len;
}
/* this adds support required by the 'track-devices' service.
* this is used to send the content of "list_transport" to any
* number of client connections that want it through a single
* live TCP connection
*/
typedef struct device_tracker device_tracker;
struct device_tracker {
asocket socket;
int update_needed;
device_tracker* next;
};
/* linked list of all device trackers */
static device_tracker* device_tracker_list;
static void
device_tracker_remove( device_tracker* tracker )
{
device_tracker** pnode = &device_tracker_list;
device_tracker* node = *pnode;
adb_mutex_lock( &transport_lock );
while (node) {
if (node == tracker) {
*pnode = node->next;
break;
}
pnode = &node->next;
node = *pnode;
}
adb_mutex_unlock( &transport_lock );
}
static void
device_tracker_close( asocket* socket )
{
device_tracker* tracker = (device_tracker*) socket;
asocket* peer = socket->peer;
D( "device tracker %p removed\n", tracker);
if (peer) {
peer->peer = NULL;
peer->close(peer);
}
device_tracker_remove(tracker);
free(tracker);
}
static int
device_tracker_enqueue( asocket* socket, apacket* p )
{
/* you can't read from a device tracker, close immediately */
put_apacket(p);
device_tracker_close(socket);
return -1;
}
static int
device_tracker_send( device_tracker* tracker,
const char* buffer,
int len )
{
apacket* p = get_apacket();
asocket* peer = tracker->socket.peer;
memcpy(p->data, buffer, len);
p->len = len;
return peer->enqueue( peer, p );
}
static void
device_tracker_ready( asocket* socket )
{
device_tracker* tracker = (device_tracker*) socket;
/* we want to send the device list when the tracker connects
* for the first time, even if no update occured */
if (tracker->update_needed > 0) {
char buffer[1024];
int len;
tracker->update_needed = 0;
len = list_transports_msg(buffer, sizeof(buffer));
device_tracker_send(tracker, buffer, len);
}
}
asocket*
create_device_tracker(void)
{
device_tracker* tracker = calloc(1,sizeof(*tracker));
if(tracker == 0) fatal("cannot allocate device tracker");
D( "device tracker %p created\n", tracker);
tracker->socket.enqueue = device_tracker_enqueue;
tracker->socket.ready = device_tracker_ready;
tracker->socket.close = device_tracker_close;
tracker->update_needed = 1;
tracker->next = device_tracker_list;
device_tracker_list = tracker;
return &tracker->socket;
}
/* call this function each time the transport list has changed */
void update_transports(void)
{
char buffer[1024];
int len;
device_tracker* tracker;
len = list_transports_msg(buffer, sizeof(buffer));
tracker = device_tracker_list;
while (tracker != NULL) {
device_tracker* next = tracker->next;
/* note: this may destroy the tracker if the connection is closed */
device_tracker_send(tracker, buffer, len);
tracker = next;
}
}
#else
void update_transports(void)
{
// nothing to do on the device side
}
#endif // ADB_HOST
typedef struct tmsg tmsg;
struct tmsg
{
atransport *transport;
int action;
};
static int
transport_read_action(int fd, struct tmsg* m)
{
char *p = (char*)m;
int len = sizeof(*m);
int r;
while(len > 0) {
r = adb_read(fd, p, len);
if(r > 0) {
len -= r;
p += r;
} else {
if((r < 0) && (errno == EINTR)) continue;
D("transport_read_action: on fd %d, error %d: %s\n",
fd, errno, strerror(errno));
return -1;
}
}
return 0;
}
static int
transport_write_action(int fd, struct tmsg* m)
{
char *p = (char*)m;
int len = sizeof(*m);
int r;
while(len > 0) {
r = adb_write(fd, p, len);
if(r > 0) {
len -= r;
p += r;
} else {
if((r < 0) && (errno == EINTR)) continue;
D("transport_write_action: on fd %d, error %d: %s\n",
fd, errno, strerror(errno));
return -1;
}
}
return 0;
}
static void transport_registration_func(int _fd, unsigned ev, void *data)
{
tmsg m;
adb_thread_t output_thread_ptr;
adb_thread_t input_thread_ptr;
int s[2];
atransport *t;
if(!(ev & FDE_READ)) {
return;
}
if(transport_read_action(_fd, &m)) {
fatal_errno("cannot read transport registration socket");
}
t = m.transport;
if(m.action == 0){
D("transport: %p removing and free'ing %d\n", t, t->transport_socket);
/* IMPORTANT: the remove closes one half of the
** socket pair. The close closes the other half.
*/
fdevent_remove(&(t->transport_fde));
adb_close(t->fd);
adb_mutex_lock(&transport_lock);
t->next->prev = t->prev;
t->prev->next = t->next;
adb_mutex_unlock(&transport_lock);
run_transport_disconnects(t);
if (t->product)
free(t->product);
if (t->serial)
free(t->serial);
memset(t,0xee,sizeof(atransport));
free(t);
update_transports();
return;
}
/* initial references are the two threads */
t->ref_count = 2;
if(adb_socketpair(s)) {
fatal_errno("cannot open transport socketpair");
}
D("transport: %p (%d,%d) starting\n", t, s[0], s[1]);
t->transport_socket = s[0];
t->fd = s[1];
/* put us on the master device list */
adb_mutex_lock(&transport_lock);
t->next = &transport_list;
t->prev = transport_list.prev;
t->next->prev = t;
t->prev->next = t;
adb_mutex_unlock(&transport_lock);
D("transport: %p install %d\n", t, t->transport_socket );
fdevent_install(&(t->transport_fde),
t->transport_socket,
transport_socket_events,
t);
fdevent_set(&(t->transport_fde), FDE_READ);
if(adb_thread_create(&input_thread_ptr, input_thread, t)){
fatal_errno("cannot create input thread");
}
if(adb_thread_create(&output_thread_ptr, output_thread, t)){
fatal_errno("cannot create output thread");
}
t->disconnects.next = t->disconnects.prev = &t->disconnects;
update_transports();
}
void init_transport_registration(void)
{
int s[2];
if(adb_socketpair(s)){
fatal_errno("cannot open transport registration socketpair");
}
transport_registration_send = s[0];
transport_registration_recv = s[1];
fdevent_install(&transport_registration_fde,
transport_registration_recv,
transport_registration_func,
0);
fdevent_set(&transport_registration_fde, FDE_READ);
}
/* the fdevent select pump is single threaded */
static void register_transport(atransport *transport)
{
tmsg m;
m.transport = transport;
m.action = 1;
D("transport: %p registered\n", transport);
if(transport_write_action(transport_registration_send, &m)) {
fatal_errno("cannot write transport registration socket\n");
}
}
static void remove_transport(atransport *transport)
{
tmsg m;
m.transport = transport;
m.action = 0;
D("transport: %p removed\n", transport);
if(transport_write_action(transport_registration_send, &m)) {
fatal_errno("cannot write transport registration socket\n");
}
}
static void transport_unref(atransport *t)
{
if (t) {
adb_mutex_lock(&transport_lock);
t->ref_count--;
D("transport: %p R- (ref=%d)\n", t, t->ref_count);
if (t->ref_count == 0) {
D("transport: %p kicking and closing\n", t);
if (!t->kicked) {
t->kicked = 1;
t->kick(t);
}
t->close(t);
remove_transport(t);
}
adb_mutex_unlock(&transport_lock);
}
}
void add_transport_disconnect(atransport* t, adisconnect* dis)
{
adb_mutex_lock(&transport_lock);
dis->next = &t->disconnects;
dis->prev = dis->next->prev;
dis->prev->next = dis;
dis->next->prev = dis;
adb_mutex_unlock(&transport_lock);
}
void remove_transport_disconnect(atransport* t, adisconnect* dis)
{
dis->prev->next = dis->next;
dis->next->prev = dis->prev;
dis->next = dis->prev = dis;
}
atransport *acquire_one_transport(int state, transport_type ttype, const char* serial, char** error_out)
{
atransport *t;
atransport *result = NULL;
int ambiguous = 0;
retry:
if (error_out)
*error_out = "device not found";
adb_mutex_lock(&transport_lock);
for (t = transport_list.next; t != &transport_list; t = t->next) {
/* check for matching serial number */
if (serial) {
if (t->serial && !strcmp(serial, t->serial)) {
result = t;
break;
}
} else {
if (ttype == kTransportUsb && t->type == kTransportUsb) {
if (result) {
if (error_out)
*error_out = "more than one device";
ambiguous = 1;
result = NULL;
break;
}
result = t;
} else if (ttype == kTransportLocal && t->type == kTransportLocal) {
if (result) {
if (error_out)
*error_out = "more than one emulator";
ambiguous = 1;
result = NULL;
break;
}
result = t;
} else if (ttype == kTransportAny) {
if (result) {
if (error_out)
*error_out = "more than one device and emulator";
ambiguous = 1;
result = NULL;
break;
}
result = t;
}
}
}
adb_mutex_unlock(&transport_lock);
if (result) {
/* offline devices are ignored -- they are either being born or dying */
if (result && result->connection_state == CS_OFFLINE) {
if (error_out)
*error_out = "device offline";
result = NULL;
}
/* check for required connection state */
if (result && state != CS_ANY && result->connection_state != state) {
if (error_out)
*error_out = "invalid device state";
result = NULL;
}
}
if (result) {
/* found one that we can take */
if (error_out)
*error_out = NULL;
} else if (state != CS_ANY && (serial || !ambiguous)) {
adb_sleep_ms(1000);
goto retry;
}
return result;
}
#if ADB_HOST
static const char *statename(atransport *t)
{
switch(t->connection_state){
case CS_OFFLINE: return "offline";
case CS_BOOTLOADER: return "bootloader";
case CS_DEVICE: return "device";
case CS_HOST: return "host";
case CS_RECOVERY: return "recovery";
default: return "unknown";
}
}
int list_transports(char *buf, size_t bufsize)
{
char* p = buf;
char* end = buf + bufsize;
int len;
atransport *t;
/* XXX OVERRUN PROBLEMS XXX */
adb_mutex_lock(&transport_lock);
for(t = transport_list.next; t != &transport_list; t = t->next) {
len = snprintf(p, end - p, "%s\t%s\n",
t->serial ? t->serial : "",
statename(t));
if (p + len >= end) {
/* discard last line if buffer is too short */
break;
}
p += len;
}
p[0] = 0;
adb_mutex_unlock(&transport_lock);
return p - buf;
}
/* hack for osx */
void close_usb_devices()
{
atransport *t;
adb_mutex_lock(&transport_lock);
for(t = transport_list.next; t != &transport_list; t = t->next) {
if ( !t->kicked ) {
t->kicked = 1;
t->kick(t);
}
}
adb_mutex_unlock(&transport_lock);
}
#endif // ADB_HOST
void register_socket_transport(int s, const char *serial, int port)
{
atransport *t = calloc(1, sizeof(atransport));
D("transport: %p init'ing for socket %d, on port %d\n", t, s, port);
if ( init_socket_transport(t, s, port) < 0 ) {
adb_close(s);
free(t);
return;
}
if(serial) {
t->serial = strdup(serial);
}
register_transport(t);
}
void register_usb_transport(usb_handle *usb, const char *serial)
{
atransport *t = calloc(1, sizeof(atransport));
D("transport: %p init'ing for usb_handle %p (sn='%s')\n", t, usb,
serial ? serial : "");
init_usb_transport(t, usb);
if(serial) {
t->serial = strdup(serial);
}
register_transport(t);
}
#undef TRACE_TAG
#define TRACE_TAG TRACE_RWX
int readx(int fd, void *ptr, size_t len)
{
char *p = ptr;
int r;
#if ADB_TRACE
int len0 = len;
#endif
D("readx: %d %p %d\n", fd, ptr, (int)len);
while(len > 0) {
r = adb_read(fd, p, len);
if(r > 0) {
len -= r;
p += r;
} else {
D("readx: %d %d %s\n", fd, r, strerror(errno));
if((r < 0) && (errno == EINTR)) continue;
return -1;
}
}
#if ADB_TRACE
D("readx: %d ok: ", fd);
dump_hex( ptr, len0 );
#endif
return 0;
}
int writex(int fd, const void *ptr, size_t len)
{
char *p = (char*) ptr;
int r;
#if ADB_TRACE
D("writex: %d %p %d: ", fd, ptr, (int)len);
dump_hex( ptr, len );
#endif
while(len > 0) {
r = adb_write(fd, p, len);
if(r > 0) {
len -= r;
p += r;
} else {
D("writex: %d %d %s\n", fd, r, strerror(errno));
if((r < 0) && (errno == EINTR)) continue;
return -1;
}
}
D("writex: %d ok\n", fd);
return 0;
}
int check_header(apacket *p)
{
if(p->msg.magic != (p->msg.command ^ 0xffffffff)) {
D("check_header(): invalid magic\n");
return -1;
}
if(p->msg.data_length > MAX_PAYLOAD) {
D("check_header(): %d > MAX_PAYLOAD\n", p->msg.data_length);
return -1;
}
return 0;
}
int check_data(apacket *p)
{
unsigned count, sum;
unsigned char *x;
count = p->msg.data_length;
x = p->data;
sum = 0;
while(count-- > 0) {
sum += *x++;
}
if(sum != p->msg.data_check) {
return -1;
} else {
return 0;
}
}

262
adb/transport_local.c Normal file
View file

@ -0,0 +1,262 @@
/*
* 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.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "sysdeps.h"
#include <sys/types.h>
#define TRACE_TAG TRACE_TRANSPORT
#include "adb.h"
#ifdef __ppc__
#define H4(x) (((x) & 0xFF000000) >> 24) | (((x) & 0x00FF0000) >> 8) | (((x) & 0x0000FF00) << 8) | (((x) & 0x000000FF) << 24)
static inline void fix_endians(apacket *p)
{
p->msg.command = H4(p->msg.command);
p->msg.arg0 = H4(p->msg.arg0);
p->msg.arg1 = H4(p->msg.arg1);
p->msg.data_length = H4(p->msg.data_length);
p->msg.data_check = H4(p->msg.data_check);
p->msg.magic = H4(p->msg.magic);
}
#else
#define fix_endians(p) do {} while (0)
#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
*/
#define ADB_LOCAL_TRANSPORT_MAX 16
ADB_MUTEX_DEFINE( local_transports_lock );
static atransport* local_transports[ ADB_LOCAL_TRANSPORT_MAX ];
#endif /* ADB_HOST */
static int remote_read(apacket *p, atransport *t)
{
if(readx(t->sfd, &p->msg, sizeof(amessage))){
D("remote local: read terminated (message)\n");
return -1;
}
fix_endians(p);
#if 0 && defined __ppc__
D("read remote packet: %04x arg0=%0x arg1=%0x data_length=%0x data_check=%0x magic=%0x\n",
p->msg.command, p->msg.arg0, p->msg.arg1, p->msg.data_length, p->msg.data_check, p->msg.magic);
#endif
if(check_header(p)) {
D("bad header: terminated (data)\n");
return -1;
}
if(readx(t->sfd, p->data, p->msg.data_length)){
D("remote local: terminated (data)\n");
return -1;
}
if(check_data(p)) {
D("bad data: terminated (data)\n");
return -1;
}
return 0;
}
static int remote_write(apacket *p, atransport *t)
{
int length = p->msg.data_length;
fix_endians(p);
#if 0 && defined __ppc__
D("write remote packet: %04x arg0=%0x arg1=%0x data_length=%0x data_check=%0x magic=%0x\n",
p->msg.command, p->msg.arg0, p->msg.arg1, p->msg.data_length, p->msg.data_check, p->msg.magic);
#endif
if(writex(t->sfd, &p->msg, sizeof(amessage) + length)) {
D("remote local: write terminated\n");
return -1;
}
return 0;
}
int local_connect(int port)
{
char buf[64];
int fd = -1;
fd = socket_loopback_client(port, SOCK_STREAM);
#if ADB_HOST
if(fd < 0) {
const char *host = getenv("ADBHOST");
if(host) {
fd = socket_network_client(host, port, SOCK_STREAM);
}
}
#endif
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);
return 0;
}
return -1;
}
static void *client_socket_thread(void *x)
{
#if ADB_HOST
int port = ADB_LOCAL_TRANSPORT_PORT;
int count = ADB_LOCAL_TRANSPORT_MAX;
D("transport: client_socket_thread() starting\n");
/* try to connect to any number of running emulator instances */
/* this is only done when ADB starts up. later, each new emulator */
/* will send a message to ADB to indicate that is is starting up */
for ( ; count > 0; count--, port += 2 ) {
(void) local_connect(port);
}
#endif
return 0;
}
static void *server_socket_thread(void *x)
{
int serverfd, fd;
struct sockaddr addr;
socklen_t alen;
D("transport: server_socket_thread() starting\n");
serverfd = -1;
for(;;) {
if(serverfd == -1) {
serverfd = socket_inaddr_any_server(ADB_LOCAL_TRANSPORT_PORT, SOCK_STREAM);
if(serverfd < 0) {
D("server: cannot bind socket yet\n");
adb_sleep_ms(1000);
continue;
}
close_on_exec(serverfd);
}
alen = sizeof(addr);
D("server: trying to get new connection from %d\n", ADB_LOCAL_TRANSPORT_PORT);
fd = adb_socket_accept(serverfd, &addr, &alen);
if(fd >= 0) {
D("server: new connection on fd %d\n", fd);
close_on_exec(fd);
disable_tcp_nagle(fd);
register_socket_transport(fd,"host",ADB_LOCAL_TRANSPORT_PORT);
}
}
D("transport: server_socket_thread() exiting\n");
return 0;
}
void local_init(void)
{
adb_thread_t thr;
void* (*func)(void *);
if(HOST) {
func = client_socket_thread;
} else {
func = server_socket_thread;
}
D("transport: local %s init\n", HOST ? "client" : "server");
if(adb_thread_create(&thr, func, 0)) {
fatal_errno("cannot create local socket %s thread",
HOST ? "client" : "server");
}
}
static void remote_kick(atransport *t)
{
int fd = t->sfd;
t->sfd = -1;
adb_close(fd);
#if ADB_HOST
if(HOST) {
int nn;
adb_mutex_lock( &local_transports_lock );
for (nn = 0; nn < ADB_LOCAL_TRANSPORT_MAX; nn++) {
if (local_transports[nn] == t) {
local_transports[nn] = NULL;
break;
}
}
adb_mutex_unlock( &local_transports_lock );
}
#endif
}
static void remote_close(atransport *t)
{
adb_close(t->fd);
}
int init_socket_transport(atransport *t, int s, int port)
{
int fail = 0;
t->kick = remote_kick;
t->close = remote_close;
t->read_from_remote = remote_read;
t->write_to_remote = remote_write;
t->sfd = s;
t->sync_token = 1;
t->connection_state = CS_OFFLINE;
t->type = kTransportLocal;
#if ADB_HOST
if (HOST) {
adb_mutex_lock( &local_transports_lock );
{
int index = (port - 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) {
D("local transport for port %d already registered (%p)?\n",
port, local_transports[index]);
fail = -1;
}
else
local_transports[index] = t;
}
adb_mutex_unlock( &local_transports_lock );
}
#endif
return fail;
}

147
adb/transport_usb.c Normal file
View file

@ -0,0 +1,147 @@
/*
* 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.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sysdeps.h>
#define TRACE_TAG TRACE_TRANSPORT
#include "adb.h"
/* XXX better define? */
#ifdef __ppc__
#define H4(x) (((x) & 0xFF000000) >> 24) | (((x) & 0x00FF0000) >> 8) | (((x) & 0x0000FF00) << 8) | (((x) & 0x000000FF) << 24)
static inline void fix_endians(apacket *p)
{
p->msg.command = H4(p->msg.command);
p->msg.arg0 = H4(p->msg.arg0);
p->msg.arg1 = H4(p->msg.arg1);
p->msg.data_length = H4(p->msg.data_length);
p->msg.data_check = H4(p->msg.data_check);
p->msg.magic = H4(p->msg.magic);
}
unsigned host_to_le32(unsigned n)
{
return H4(n);
}
#else
#define fix_endians(p) do {} while (0)
unsigned host_to_le32(unsigned n)
{
return n;
}
#endif
static int remote_read(apacket *p, atransport *t)
{
if(usb_read(t->usb, &p->msg, sizeof(amessage))){
D("remote usb: read terminated (message)\n");
return -1;
}
fix_endians(p);
if(check_header(p)) {
D("remote usb: check_header failed\n");
return -1;
}
if(p->msg.data_length) {
if(usb_read(t->usb, p->data, p->msg.data_length)){
D("remote usb: terminated (data)\n");
return -1;
}
}
if(check_data(p)) {
D("remote usb: check_data failed\n");
return -1;
}
return 0;
}
static int remote_write(apacket *p, atransport *t)
{
unsigned size = p->msg.data_length;
fix_endians(p);
if(usb_write(t->usb, &p->msg, sizeof(amessage))) {
D("remote usb: 1 - write terminated\n");
return -1;
}
if(p->msg.data_length == 0) return 0;
if(usb_write(t->usb, &p->data, size)) {
D("remote usb: 2 - write terminated\n");
return -1;
}
return 0;
}
static void remote_close(atransport *t)
{
usb_close(t->usb);
t->usb = 0;
}
static void remote_kick(atransport *t)
{
usb_kick(t->usb);
}
void init_usb_transport(atransport *t, usb_handle *h)
{
D("transport: usb\n");
t->close = remote_close;
t->kick = remote_kick;
t->read_from_remote = remote_read;
t->write_to_remote = remote_write;
t->sync_token = 1;
t->connection_state = CS_OFFLINE;
t->type = kTransportUsb;
t->usb = h;
#if ADB_HOST
HOST = 1;
#else
HOST = 0;
#endif
}
int is_adb_interface(int vid, int pid, int usb_class, int usb_subclass, int usb_protocol)
{
if (vid == VENDOR_ID_GOOGLE) {
/* might support adb */
} else if (vid == VENDOR_ID_HTC) {
/* might support adb */
} else {
/* not supported */
return 0;
}
/* class:vendor (0xff) subclass:android (0x42) proto:adb (0x01) */
if(usb_class == 0xff) {
if((usb_subclass == 0x42) && (usb_protocol == 0x01)) {
return 1;
}
}
return 0;
}

653
adb/usb_linux.c Normal file
View file

@ -0,0 +1,653 @@
/*
* 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.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <dirent.h>
#include <fcntl.h>
#include <errno.h>
#include <ctype.h>
#include <linux/usbdevice_fs.h>
#include <linux/version.h>
#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 20)
#include <linux/usb/ch9.h>
#else
#include <linux/usb_ch9.h>
#endif
#include <asm/byteorder.h>
#include "sysdeps.h"
#define TRACE_TAG TRACE_USB
#include "adb.h"
/* usb scan debugging is waaaay too verbose */
#define DBGX(x...)
static adb_mutex_t usb_lock = ADB_MUTEX_INITIALIZER;
struct usb_handle
{
usb_handle *prev;
usb_handle *next;
char fname[64];
int desc;
unsigned char ep_in;
unsigned char ep_out;
unsigned zero_mask;
struct usbdevfs_urb urb_in;
struct usbdevfs_urb urb_out;
int urb_in_busy;
int urb_out_busy;
int dead;
adb_cond_t notify;
adb_mutex_t lock;
// for garbage collecting disconnected devices
int mark;
// ID of thread currently in REAPURB
pthread_t reaper_thread;
};
static usb_handle handle_list = {
.prev = &handle_list,
.next = &handle_list,
};
static int known_device(const char *dev_name)
{
usb_handle *usb;
adb_mutex_lock(&usb_lock);
for(usb = handle_list.next; usb != &handle_list; usb = usb->next){
if(!strcmp(usb->fname, dev_name)) {
// set mark flag to indicate this device is still alive
usb->mark = 1;
adb_mutex_unlock(&usb_lock);
return 1;
}
}
adb_mutex_unlock(&usb_lock);
return 0;
}
static void kick_disconnected_devices()
{
usb_handle *usb;
adb_mutex_lock(&usb_lock);
// kick any devices in the device list that were not found in the device scan
for(usb = handle_list.next; usb != &handle_list; usb = usb->next){
if (usb->mark == 0) {
usb_kick(usb);
} else {
usb->mark = 0;
}
}
adb_mutex_unlock(&usb_lock);
}
static void register_device(const char *dev_name, unsigned char ep_in, unsigned char ep_out,
int ifc, const char *serial, unsigned zero_mask);
static inline int badname(const char *name)
{
while(*name) {
if(!isdigit(*name++)) return 1;
}
return 0;
}
static int find_usb_device(const char *base,
void (*register_device_callback) (const char *, unsigned char, unsigned char, int, const char *, unsigned))
{
char busname[32], devname[32];
unsigned char local_ep_in, local_ep_out;
DIR *busdir , *devdir ;
struct dirent *de;
int fd ;
int found_device = 0;
char serial[256];
busdir = opendir(base);
if(busdir == 0) return 0;
while((de = readdir(busdir)) != 0) {
if(badname(de->d_name)) continue;
snprintf(busname, sizeof busname, "%s/%s", base, de->d_name);
devdir = opendir(busname);
if(devdir == 0) continue;
// DBGX("[ scanning %s ]\n", busname);
while((de = readdir(devdir))) {
unsigned char devdesc[256];
unsigned char* bufptr = devdesc;
struct usb_device_descriptor* device;
struct usb_config_descriptor* config;
struct usb_interface_descriptor* interface;
struct usb_endpoint_descriptor *ep1, *ep2;
unsigned zero_mask = 0;
unsigned vid, pid;
int i, interfaces;
size_t desclength;
if(badname(de->d_name)) continue;
snprintf(devname, sizeof devname, "%s/%s", busname, de->d_name);
if(known_device(devname)) {
DBGX("skipping %s\n", devname);
continue;
}
// DBGX("[ scanning %s ]\n", devname);
if((fd = unix_open(devname, O_RDWR)) < 0) {
continue;
}
desclength = adb_read(fd, devdesc, sizeof(devdesc));
// should have device and configuration descriptors, and atleast two endpoints
if (desclength < USB_DT_DEVICE_SIZE + USB_DT_CONFIG_SIZE) {
D("desclength %d is too small\n", desclength);
adb_close(fd);
continue;
}
device = (struct usb_device_descriptor*)bufptr;
bufptr += USB_DT_DEVICE_SIZE;
if((device->bLength != USB_DT_DEVICE_SIZE) || (device->bDescriptorType != USB_DT_DEVICE)) {
adb_close(fd);
continue;
}
vid = __le16_to_cpu(device->idVendor);
pid = __le16_to_cpu(device->idProduct);
pid = devdesc[10] | (devdesc[11] << 8);
DBGX("[ %s is V:%04x P:%04x ]\n", devname, vid, pid);
// should have config descriptor next
config = (struct usb_config_descriptor *)bufptr;
bufptr += USB_DT_CONFIG_SIZE;
if (config->bLength != USB_DT_CONFIG_SIZE || config->bDescriptorType != USB_DT_CONFIG) {
D("usb_config_descriptor not found\n");
adb_close(fd);
continue;
}
// loop through all the interfaces and look for the ADB interface
interfaces = config->bNumInterfaces;
for (i = 0; i < interfaces; i++) {
if (bufptr + USB_DT_ENDPOINT_SIZE > devdesc + desclength)
break;
interface = (struct usb_interface_descriptor *)bufptr;
bufptr += USB_DT_INTERFACE_SIZE;
if (interface->bLength != USB_DT_INTERFACE_SIZE ||
interface->bDescriptorType != USB_DT_INTERFACE) {
D("usb_interface_descriptor not found\n");
break;
}
DBGX("bInterfaceClass: %d, bInterfaceSubClass: %d,"
"bInterfaceProtocol: %d, bNumEndpoints: %d\n",
interface->bInterfaceClass, interface->bInterfaceSubClass,
interface->bInterfaceProtocol, interface->bNumEndpoints);
if (interface->bNumEndpoints == 2 &&
is_adb_interface(vid, pid, interface->bInterfaceClass,
interface->bInterfaceSubClass, interface->bInterfaceProtocol)) {
DBGX("looking for bulk endpoints\n");
// looks like ADB...
ep1 = (struct usb_endpoint_descriptor *)bufptr;
bufptr += USB_DT_ENDPOINT_SIZE;
ep2 = (struct usb_endpoint_descriptor *)bufptr;
bufptr += USB_DT_ENDPOINT_SIZE;
if (bufptr > devdesc + desclength ||
ep1->bLength != USB_DT_ENDPOINT_SIZE ||
ep1->bDescriptorType != USB_DT_ENDPOINT ||
ep2->bLength != USB_DT_ENDPOINT_SIZE ||
ep2->bDescriptorType != USB_DT_ENDPOINT) {
D("endpoints not found\n");
break;
}
// both endpoints should be bulk
if (ep1->bmAttributes != USB_ENDPOINT_XFER_BULK ||
ep2->bmAttributes != USB_ENDPOINT_XFER_BULK) {
D("bulk endpoints not found\n");
continue;
}
/* aproto 01 needs 0 termination */
if(interface->bInterfaceProtocol == 0x01) {
zero_mask = ep1->wMaxPacketSize - 1;
}
// we have a match. now we just need to figure out which is in and which is out.
if (ep1->bEndpointAddress & USB_ENDPOINT_DIR_MASK) {
local_ep_in = ep1->bEndpointAddress;
local_ep_out = ep2->bEndpointAddress;
} else {
local_ep_in = ep2->bEndpointAddress;
local_ep_out = ep1->bEndpointAddress;
}
// read the device's serial number
serial[0] = 0;
memset(serial, 0, sizeof(serial));
if (device->iSerialNumber) {
struct usbdevfs_ctrltransfer ctrl;
__u16 buffer[128];
int result;
memset(buffer, 0, sizeof(buffer));
memset(&ctrl, 0, sizeof(ctrl));
ctrl.bRequestType = USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_DEVICE;
ctrl.bRequest = USB_REQ_GET_DESCRIPTOR;
ctrl.wValue = (USB_DT_STRING << 8) | device->iSerialNumber;
ctrl.wIndex = 0;
ctrl.wLength = sizeof(buffer);
ctrl.data = buffer;
result = ioctl(fd, USBDEVFS_CONTROL, &ctrl);
if (result > 0) {
int i;
// skip first word, and copy the rest to the serial string, changing shorts to bytes.
result /= 2;
for (i = 1; i < result; i++)
serial[i - 1] = buffer[i];
serial[i - 1] = 0;
}
}
register_device_callback(devname, local_ep_in, local_ep_out, i, serial, zero_mask);
found_device = 1;
break;
} else {
// skip to next interface
bufptr += (interface->bNumEndpoints * USB_DT_ENDPOINT_SIZE);
}
} // end of for
adb_close(fd);
} // end of devdir while
closedir(devdir);
} //end of busdir while
closedir(busdir);
return found_device;
}
void usb_cleanup()
{
}
static int usb_bulk_write(usb_handle *h, const void *data, int len)
{
struct usbdevfs_urb *urb = &h->urb_out;
int res;
memset(urb, 0, sizeof(*urb));
urb->type = USBDEVFS_URB_TYPE_BULK;
urb->endpoint = h->ep_out;
urb->status = -1;
urb->buffer = (void*) data;
urb->buffer_length = len;
D("++ write ++\n");
adb_mutex_lock(&h->lock);
if(h->dead) {
res = -1;
goto fail;
}
do {
res = ioctl(h->desc, USBDEVFS_SUBMITURB, urb);
} while((res < 0) && (errno == EINTR));
if(res < 0) {
goto fail;
}
res = -1;
h->urb_out_busy = 1;
for(;;) {
adb_cond_wait(&h->notify, &h->lock);
if(h->dead) {
break;
}
if(h->urb_out_busy == 0) {
if(urb->status == 0) {
res = urb->actual_length;
}
break;
}
}
fail:
adb_mutex_unlock(&h->lock);
D("-- write --\n");
return res;
}
static int usb_bulk_read(usb_handle *h, void *data, int len)
{
struct usbdevfs_urb *urb = &h->urb_in;
struct usbdevfs_urb *out = NULL;
int res;
memset(urb, 0, sizeof(*urb));
urb->type = USBDEVFS_URB_TYPE_BULK;
urb->endpoint = h->ep_in;
urb->status = -1;
urb->buffer = data;
urb->buffer_length = len;
adb_mutex_lock(&h->lock);
if(h->dead) {
res = -1;
goto fail;
}
do {
res = ioctl(h->desc, USBDEVFS_SUBMITURB, urb);
} while((res < 0) && (errno == EINTR));
if(res < 0) {
goto fail;
}
h->urb_in_busy = 1;
for(;;) {
D("[ reap urb - wait ]\n");
h->reaper_thread = pthread_self();
adb_mutex_unlock(&h->lock);
res = ioctl(h->desc, USBDEVFS_REAPURB, &out);
adb_mutex_lock(&h->lock);
h->reaper_thread = 0;
if(h->dead) {
res = -1;
break;
}
if(res < 0) {
if(errno == EINTR) {
continue;
}
D("[ reap urb - error ]\n");
break;
}
D("[ urb @%p status = %d, actual = %d ]\n",
out, out->status, out->actual_length);
if(out == &h->urb_in) {
D("[ reap urb - IN complete ]\n");
h->urb_in_busy = 0;
if(urb->status == 0) {
res = urb->actual_length;
} else {
res = -1;
}
break;
}
if(out == &h->urb_out) {
D("[ reap urb - OUT compelete ]\n");
h->urb_out_busy = 0;
adb_cond_broadcast(&h->notify);
}
}
fail:
adb_mutex_unlock(&h->lock);
return res;
}
int usb_write(usb_handle *h, const void *_data, int len)
{
unsigned char *data = (unsigned char*) _data;
int n;
int need_zero = 0;
if(h->zero_mask) {
/* if we need 0-markers and our transfer
** is an even multiple of the packet size,
** we make note of it
*/
if(!(len & h->zero_mask)) {
need_zero = 1;
}
}
while(len > 0) {
int xfer = (len > 4096) ? 4096 : len;
n = usb_bulk_write(h, data, xfer);
if(n != xfer) {
D("ERROR: n = %d, errno = %d (%s)\n",
n, errno, strerror(errno));
return -1;
}
len -= xfer;
data += xfer;
}
if(need_zero){
n = usb_bulk_write(h, _data, 0);
return n;
}
return 0;
}
int usb_read(usb_handle *h, void *_data, int len)
{
unsigned char *data = (unsigned char*) _data;
int n;
D("++ usb_read ++\n");
while(len > 0) {
int xfer = (len > 4096) ? 4096 : len;
D("[ usb read %d fd = %d], fname=%s\n", xfer, h->desc, h->fname);
n = usb_bulk_read(h, data, xfer);
D("[ usb read %d ] = %d, fname=%s\n", xfer, n, h->fname);
if(n != xfer) {
if((errno == ETIMEDOUT) && (h->desc != -1)) {
D("[ timeout ]\n");
if(n > 0){
data += n;
len -= n;
}
continue;
}
D("ERROR: n = %d, errno = %d (%s)\n",
n, errno, strerror(errno));
return -1;
}
len -= xfer;
data += xfer;
}
D("-- usb_read --\n");
return 0;
}
void usb_kick(usb_handle *h)
{
D("[ kicking %p (fd = %d) ]\n", h, h->desc);
adb_mutex_lock(&h->lock);
if(h->dead == 0) {
h->dead = 1;
/* HACK ALERT!
** Sometimes we get stuck in ioctl(USBDEVFS_REAPURB).
** This is a workaround for that problem.
*/
if (h->reaper_thread) {
pthread_kill(h->reaper_thread, SIGALRM);
}
/* cancel any pending transactions
** these will quietly fail if the txns are not active,
** but this ensures that a reader blocked on REAPURB
** will get unblocked
*/
ioctl(h->desc, USBDEVFS_DISCARDURB, &h->urb_in);
ioctl(h->desc, USBDEVFS_DISCARDURB, &h->urb_out);
h->urb_in.status = -ENODEV;
h->urb_out.status = -ENODEV;
h->urb_in_busy = 0;
h->urb_out_busy = 0;
adb_cond_broadcast(&h->notify);
}
adb_mutex_unlock(&h->lock);
}
int usb_close(usb_handle *h)
{
D("[ usb close ... ]\n");
adb_mutex_lock(&usb_lock);
h->next->prev = h->prev;
h->prev->next = h->next;
h->prev = 0;
h->next = 0;
adb_close(h->desc);
D("[ usb closed %p (fd = %d) ]\n", h, h->desc);
adb_mutex_unlock(&usb_lock);
free(h);
return 0;
}
static void register_device(const char *dev_name,
unsigned char ep_in, unsigned char ep_out,
int interface,
const char *serial, unsigned zero_mask)
{
usb_handle* usb = 0;
int n = 0;
/* Since Linux will not reassign the device ID (and dev_name)
** as long as the device is open, we can add to the list here
** once we open it and remove from the list when we're finally
** closed and everything will work out fine.
**
** If we have a usb_handle on the list 'o handles with a matching
** name, we have no further work to do.
*/
adb_mutex_lock(&usb_lock);
for(usb = handle_list.next; usb != &handle_list; usb = usb->next){
if(!strcmp(usb->fname, dev_name)) {
adb_mutex_unlock(&usb_lock);
return;
}
}
adb_mutex_unlock(&usb_lock);
D("[ usb located new device %s (%d/%d/%d) ]\n",
dev_name, ep_in, ep_out, interface);
usb = calloc(1, sizeof(usb_handle));
strcpy(usb->fname, dev_name);
usb->ep_in = ep_in;
usb->ep_out = ep_out;
usb->zero_mask = zero_mask;
adb_cond_init(&usb->notify, 0);
adb_mutex_init(&usb->lock, 0);
/* initialize mark to 1 so we don't get garbage collected after the device scan */
usb->mark = 1;
usb->reaper_thread = 0;
usb->desc = unix_open(usb->fname, O_RDWR);
if(usb->desc < 0) goto fail;
D("[ usb open %s fd = %d]\n", usb->fname, usb->desc);
n = ioctl(usb->desc, USBDEVFS_CLAIMINTERFACE, &interface);
if(n != 0) goto fail;
/* add to the end of the active handles */
adb_mutex_lock(&usb_lock);
usb->next = &handle_list;
usb->prev = handle_list.prev;
usb->prev->next = usb;
usb->next->prev = usb;
adb_mutex_unlock(&usb_lock);
register_usb_transport(usb, serial);
return;
fail:
D("[ usb open %s error=%d, err_str = %s]\n",
usb->fname, errno, strerror(errno));
if(usb->desc >= 0) {
adb_close(usb->desc);
}
free(usb);
}
void* device_poll_thread(void* unused)
{
D("Created device thread\n");
for(;;) {
/* XXX use inotify */
find_usb_device("/dev/bus/usb", register_device);
kick_disconnected_devices();
sleep(1);
}
return NULL;
}
static void sigalrm_handler(int signo)
{
// don't need to do anything here
}
void usb_init()
{
adb_thread_t tid;
struct sigaction actions;
memset(&actions, 0, sizeof(actions));
sigemptyset(&actions.sa_mask);
actions.sa_flags = 0;
actions.sa_handler = sigalrm_handler;
sigaction(SIGALRM,& actions, NULL);
if(adb_thread_create(&tid, device_poll_thread, NULL)){
fatal_errno("cannot create input thread");
}
}

156
adb/usb_linux_client.c Normal file
View file

@ -0,0 +1,156 @@
/*
* 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.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <dirent.h>
#include <errno.h>
#include "sysdeps.h"
#define TRACE_TAG TRACE_USB
#include "adb.h"
struct usb_handle
{
int fd;
adb_cond_t notify;
adb_mutex_t lock;
};
void usb_cleanup()
{
// nothing to do here
}
static void *usb_open_thread(void *x)
{
struct usb_handle *usb = (struct usb_handle *)x;
int fd;
while (1) {
// wait until the USB device needs opening
adb_mutex_lock(&usb->lock);
while (usb->fd != -1)
adb_cond_wait(&usb->notify, &usb->lock);
adb_mutex_unlock(&usb->lock);
D("[ usb_thread - opening device ]\n");
do {
/* XXX use inotify? */
fd = unix_open("/dev/android_adb", O_RDWR);
if (fd < 0) {
// to support older kernels
fd = unix_open("/dev/android", O_RDWR);
}
if (fd < 0) {
adb_sleep_ms(1000);
}
} while (fd < 0);
D("[ opening device succeeded ]\n");
close_on_exec(fd);
usb->fd = fd;
D("[ usb_thread - registering device ]\n");
register_usb_transport(usb, 0);
}
// never gets here
return 0;
}
int usb_write(usb_handle *h, const void *data, int len)
{
int n;
D("[ write %d ]\n", len);
n = adb_write(h->fd, data, len);
if(n != len) {
D("ERROR: n = %d, errno = %d (%s)\n",
n, errno, strerror(errno));
return -1;
}
D("[ done ]\n");
return 0;
}
int usb_read(usb_handle *h, void *data, int len)
{
int n;
D("[ read %d ]\n", len);
n = adb_read(h->fd, data, len);
if(n != len) {
D("ERROR: n = %d, errno = %d (%s)\n",
n, errno, strerror(errno));
return -1;
}
return 0;
}
void usb_init()
{
usb_handle *h;
adb_thread_t tid;
int fd;
h = calloc(1, sizeof(usb_handle));
h->fd = -1;
adb_cond_init(&h->notify, 0);
adb_mutex_init(&h->lock, 0);
// Open the file /dev/android_adb_enable to trigger
// the enabling of the adb USB function in the kernel.
// We never touch this file again - just leave it open
// indefinitely so the kernel will know when we are running
// and when we are not.
fd = unix_open("/dev/android_adb_enable", O_RDWR);
if (fd < 0) {
D("failed to open /dev/android_adb_enable\n");
} else {
close_on_exec(fd);
}
D("[ usb_init - starting thread ]\n");
if(adb_thread_create(&tid, usb_open_thread, h)){
fatal_errno("cannot create usb thread");
}
}
void usb_kick(usb_handle *h)
{
D("usb_kick\n");
adb_mutex_lock(&h->lock);
adb_close(h->fd);
h->fd = -1;
// notify usb_open_thread that we are disconnected
adb_cond_signal(&h->notify);
adb_mutex_unlock(&h->lock);
}
int usb_close(usb_handle *h)
{
// nothing to do here
return 0;
}

536
adb/usb_osx.c Normal file
View file

@ -0,0 +1,536 @@
/*
* 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.
*/
#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/IOKitLib.h>
#include <IOKit/IOCFPlugIn.h>
#include <IOKit/usb/IOUSBLib.h>
#include <IOKit/IOMessage.h>
#include <mach/mach_port.h>
#include "sysdeps.h"
#include <stdio.h>
#define TRACE_TAG TRACE_USB
#include "adb.h"
#define DBG D
typedef struct {
int vid;
int pid;
} VendorProduct;
#define kSupportedDeviceCount 4
VendorProduct kSupportedDevices[kSupportedDeviceCount] = {
{ VENDOR_ID_GOOGLE, PRODUCT_ID_SOONER },
{ VENDOR_ID_GOOGLE, PRODUCT_ID_SOONER_COMP },
{ VENDOR_ID_HTC, PRODUCT_ID_DREAM },
{ VENDOR_ID_HTC, PRODUCT_ID_DREAM_COMP },
};
static IONotificationPortRef notificationPort = 0;
static io_iterator_t notificationIterators[kSupportedDeviceCount];
struct usb_handle
{
UInt8 bulkIn;
UInt8 bulkOut;
IOUSBInterfaceInterface **interface;
io_object_t usbNotification;
unsigned int zero_mask;
};
static CFRunLoopRef currentRunLoop = 0;
static pthread_mutex_t start_lock;
static pthread_cond_t start_cond;
static void AndroidDeviceAdded(void *refCon, io_iterator_t iterator);
static void AndroidDeviceNotify(void *refCon, io_iterator_t iterator, natural_t messageType, void *messageArgument);
static usb_handle* FindDeviceInterface(IOUSBDeviceInterface **dev, UInt16 vendor, UInt16 product);
static int
InitUSB()
{
CFMutableDictionaryRef matchingDict;
CFRunLoopSourceRef runLoopSource;
SInt32 vendor, product;
int i;
//* To set up asynchronous notifications, create a notification port and
//* add its run loop event source to the program's run loop
notificationPort = IONotificationPortCreate(kIOMasterPortDefault);
runLoopSource = IONotificationPortGetRunLoopSource(notificationPort);
CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, kCFRunLoopDefaultMode);
memset(notificationIterators, 0, sizeof(notificationIterators));
//* loop through all supported vendor/product pairs
for (i = 0; i < kSupportedDeviceCount; i++) {
//* Create our matching dictionary to find the Android device
//* IOServiceAddMatchingNotification consumes the reference, so we do not need to release this
matchingDict = IOServiceMatching(kIOUSBDeviceClassName);
if (!matchingDict) {
DBG("ERR: Couldn't create USB matching dictionary.\n");
return -1;
}
//* Set up two matching dictionaries, one for each product ID we support.
//* This will cause the kernel to notify us only if the vendor and product IDs match.
vendor = kSupportedDevices[i].vid;
product = kSupportedDevices[i].pid;
CFDictionarySetValue(matchingDict, CFSTR(kUSBVendorID), CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &vendor));
CFDictionarySetValue(matchingDict, CFSTR(kUSBProductID), CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &product));
//* Now set up two notifications: one to be called when a raw device
//* is first matched by the I/O Kit and another to be called when the
//* device is terminated.
//* we need to do this with each matching dictionary.
IOServiceAddMatchingNotification(
notificationPort,
kIOFirstMatchNotification,
matchingDict,
AndroidDeviceAdded,
NULL,
&notificationIterators[i]);
//* Iterate over set of matching devices to access already-present devices
//* and to arm the notification
AndroidDeviceAdded(NULL, notificationIterators[i]);
}
return 0;
}
static void
AndroidDeviceAdded(void *refCon, io_iterator_t iterator)
{
kern_return_t kr;
io_service_t usbDevice;
IOCFPlugInInterface **plugInInterface = NULL;
IOUSBDeviceInterface182 **dev = NULL;
HRESULT result;
SInt32 score;
UInt16 vendor;
UInt16 product;
UInt8 serialIndex;
char serial[256];
while ((usbDevice = IOIteratorNext(iterator))) {
//* Create an intermediate plugin
kr = IOCreatePlugInInterfaceForService(usbDevice,
kIOUSBDeviceUserClientTypeID,
kIOCFPlugInInterfaceID,
&plugInInterface, &score);
if ((kIOReturnSuccess != kr) || (!plugInInterface)) {
DBG("ERR: Unable to create a plug-in (%08x)\n", kr);
goto continue1;
}
//* Now create the device interface
result = (*plugInInterface)->QueryInterface(plugInInterface,
CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID), (LPVOID) &dev);
if (result || !dev) {
DBG("ERR: Couldn't create a device interface (%08x)\n", (int) result);
goto continue2;
}
//* Check the device to see if it's ours
kr = (*dev)->GetDeviceVendor(dev, &vendor);
kr = (*dev)->GetDeviceProduct(dev, &product);
kr = (*dev)->USBGetSerialNumberStringIndex(dev, &serialIndex);
if (serialIndex > 0) {
IOUSBDevRequest req;
UInt16 buffer[256];
req.bmRequestType = USBmakebmRequestType(kUSBIn, kUSBStandard, kUSBDevice);
req.bRequest = kUSBRqGetDescriptor;
req.wValue = (kUSBStringDesc << 8) | serialIndex;
req.wIndex = 0;
req.pData = buffer;
req.wLength = sizeof(buffer);
kr = (*dev)->DeviceRequest(dev, &req);
if (kr == kIOReturnSuccess && req.wLenDone > 0) {
int i, count;
// skip first word, and copy the rest to the serial string, changing shorts to bytes.
count = (req.wLenDone - 1) / 2;
for (i = 0; i < count; i++)
serial[i] = buffer[i + 1];
serial[i] = 0;
}
}
usb_handle* handle = NULL;
//* Open the device
kr = (*dev)->USBDeviceOpen(dev);
if (kr != kIOReturnSuccess) {
DBG("ERR: Could not open device: %08x\n", kr);
goto continue3;
} else {
//* Find an interface for the device
handle = FindDeviceInterface((IOUSBDeviceInterface**)dev, vendor, product);
}
if (handle == NULL) {
DBG("ERR: Could not find device interface: %08x\n", kr);
(*dev)->USBDeviceClose(dev);
goto continue3;
}
DBG("AndroidDeviceAdded calling register_usb_transport\n");
register_usb_transport(handle, (serial[0] ? serial : NULL));
// Register for an interest notification of this device being removed. Pass the reference to our
// private data as the refCon for the notification.
kr = IOServiceAddInterestNotification(notificationPort,
usbDevice,
kIOGeneralInterest,
AndroidDeviceNotify,
handle,
&handle->usbNotification);
if (kIOReturnSuccess != kr) {
DBG("ERR: Unable to create interest notification (%08x)\n", kr);
}
continue3:
(void)(*dev)->Release(dev);
continue2:
IODestroyPlugInInterface(plugInInterface);
continue1:
IOObjectRelease(usbDevice);
}
}
static void
AndroidDeviceNotify(void *refCon, io_service_t service, natural_t messageType, void *messageArgument)
{
usb_handle *handle = (usb_handle *)refCon;
if (messageType == kIOMessageServiceIsTerminated) {
DBG("AndroidDeviceNotify\n");
IOObjectRelease(handle->usbNotification);
usb_kick(handle);
}
}
static usb_handle*
FindDeviceInterface(IOUSBDeviceInterface **dev, UInt16 vendor, UInt16 product)
{
usb_handle* handle = NULL;
IOReturn kr;
IOUSBFindInterfaceRequest request;
io_iterator_t iterator;
io_service_t usbInterface;
IOCFPlugInInterface **plugInInterface;
IOUSBInterfaceInterface **interface = NULL;
HRESULT result;
SInt32 score;
UInt8 interfaceNumEndpoints, interfaceClass, interfaceSubClass, interfaceProtocol;
UInt8 endpoint, configuration;
//* Placing the constant KIOUSBFindInterfaceDontCare into the following
//* fields of the IOUSBFindInterfaceRequest structure will allow us to
//* find all of the interfaces
request.bInterfaceClass = kIOUSBFindInterfaceDontCare;
request.bInterfaceSubClass = kIOUSBFindInterfaceDontCare;
request.bInterfaceProtocol = kIOUSBFindInterfaceDontCare;
request.bAlternateSetting = kIOUSBFindInterfaceDontCare;
//* SetConfiguration will kill an existing UMS connection, so let's not do this if not necessary.
configuration = 0;
(*dev)->GetConfiguration(dev, &configuration);
if (configuration != 1)
(*dev)->SetConfiguration(dev, 1);
//* Get an iterator for the interfaces on the device
kr = (*dev)->CreateInterfaceIterator(dev, &request, &iterator);
if (kr != kIOReturnSuccess) {
DBG("ERR: Couldn't create a device interface iterator: (%08x)\n", kr);
return NULL;
}
while ((usbInterface = IOIteratorNext(iterator))) {
//* Create an intermediate plugin
kr = IOCreatePlugInInterfaceForService(
usbInterface,
kIOUSBInterfaceUserClientTypeID,
kIOCFPlugInInterfaceID,
&plugInInterface,
&score);
//* No longer need the usbInterface object now that we have the plugin
(void) IOObjectRelease(usbInterface);
if ((kr != kIOReturnSuccess) || (!plugInInterface)) {
DBG("ERR: Unable to create plugin (%08x)\n", kr);
break;
}
//* Now create the interface interface for the interface
result = (*plugInInterface)->QueryInterface(
plugInInterface,
CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID),
(LPVOID) &interface);
//* No longer need the intermediate plugin
(*plugInInterface)->Release(plugInInterface);
if (result || !interface) {
DBG("ERR: Couldn't create interface interface: (%08x)\n",
(unsigned int) result);
break;
}
//* Now open the interface. This will cause the pipes associated with
//* the endpoints in the interface descriptor to be instantiated
kr = (*interface)->USBInterfaceOpen(interface);
if (kr != kIOReturnSuccess)
{
DBG("ERR: Could not open interface: (%08x)\n", kr);
(void) (*interface)->Release(interface);
//* continue so we can try the next interface
continue;
}
//* Get the number of endpoints associated with this interface
kr = (*interface)->GetNumEndpoints(interface, &interfaceNumEndpoints);
if (kr != kIOReturnSuccess) {
DBG("ERR: Unable to get number of endpoints: (%08x)\n", kr);
goto next_interface;
}
//* Get interface class, subclass and protocol
if ((*interface)->GetInterfaceClass(interface, &interfaceClass) != kIOReturnSuccess ||
(*interface)->GetInterfaceSubClass(interface, &interfaceSubClass) != kIOReturnSuccess ||
(*interface)->GetInterfaceProtocol(interface, &interfaceProtocol) != kIOReturnSuccess)
{
DBG("ERR: Unable to get interface class, subclass and protocol\n");
goto next_interface;
}
//* check to make sure interface class, subclass and protocol match ADB
//* avoid opening mass storage endpoints
if (is_adb_interface(vendor, product, interfaceClass, interfaceSubClass, interfaceProtocol)) {
handle = calloc(1, sizeof(usb_handle));
//* Iterate over the endpoints for this interface and find the first
//* bulk in/out pipes available. These will be our read/write pipes.
for (endpoint = 0; endpoint <= interfaceNumEndpoints; endpoint++) {
UInt8 transferType;
UInt16 maxPacketSize;
UInt8 interval;
UInt8 number;
UInt8 direction;
kr = (*interface)->GetPipeProperties(interface, endpoint, &direction,
&number, &transferType, &maxPacketSize, &interval);
if (kIOReturnSuccess == kr) {
if (kUSBBulk != transferType)
continue;
if (kUSBIn == direction)
handle->bulkIn = endpoint;
if (kUSBOut == direction)
handle->bulkOut = endpoint;
if (interfaceProtocol == 0x01) {
handle->zero_mask = maxPacketSize - 1;
}
} else {
DBG("ERR: FindDeviceInterface - could not get pipe properties\n");
}
}
handle->interface = interface;
break;
}
next_interface:
(*interface)->USBInterfaceClose(interface);
(*interface)->Release(interface);
}
return handle;
}
void* RunLoopThread(void* unused)
{
int i;
InitUSB();
currentRunLoop = CFRunLoopGetCurrent();
// Signal the parent that we are running
adb_mutex_lock(&start_lock);
adb_cond_signal(&start_cond);
adb_mutex_unlock(&start_lock);
CFRunLoopRun();
currentRunLoop = 0;
for (i = 0; i < kSupportedDeviceCount; i++) {
IOObjectRelease(notificationIterators[i]);
}
IONotificationPortDestroy(notificationPort);
DBG("RunLoopThread done\n");
return NULL;
}
static int initialized = 0;
void usb_init()
{
if (!initialized)
{
adb_thread_t tid;
adb_mutex_init(&start_lock, NULL);
adb_cond_init(&start_cond, NULL);
if(adb_thread_create(&tid, RunLoopThread, NULL))
fatal_errno("cannot create input thread");
// Wait for initialization to finish
adb_mutex_lock(&start_lock);
adb_cond_wait(&start_cond, &start_lock);
adb_mutex_unlock(&start_lock);
adb_mutex_destroy(&start_lock);
adb_cond_destroy(&start_cond);
initialized = 1;
}
}
void usb_cleanup()
{
DBG("usb_cleanup\n");
close_usb_devices();
if (currentRunLoop)
CFRunLoopStop(currentRunLoop);
}
int usb_write(usb_handle *handle, const void *buf, int len)
{
IOReturn result;
if (!len)
return 0;
if (!handle)
return -1;
if (NULL == handle->interface) {
DBG("ERR: usb_write interface was null\n");
return -1;
}
if (0 == handle->bulkOut) {
DBG("ERR: bulkOut endpoint not assigned\n");
return -1;
}
result =
(*handle->interface)->WritePipe(
handle->interface, handle->bulkOut, (void *)buf, len);
if ((result == 0) && (handle->zero_mask)) {
/* we need 0-markers and our transfer */
if(!(len & handle->zero_mask)) {
result =
(*handle->interface)->WritePipe(
handle->interface, handle->bulkOut, (void *)buf, 0);
}
}
if (0 == result)
return 0;
DBG("ERR: usb_write failed with status %d\n", result);
return -1;
}
int usb_read(usb_handle *handle, void *buf, int len)
{
IOReturn result;
UInt32 numBytes = len;
if (!len) {
return 0;
}
if (!handle) {
return -1;
}
if (NULL == handle->interface) {
DBG("ERR: usb_read interface was null\n");
return -1;
}
if (0 == handle->bulkIn) {
DBG("ERR: bulkIn endpoint not assigned\n");
return -1;
}
result =
(*handle->interface)->ReadPipe(handle->interface,
handle->bulkIn, buf, &numBytes);
if (0 == result)
return 0;
else {
DBG("ERR: usb_read failed with status %d\n", result);
}
return -1;
}
int usb_close(usb_handle *handle)
{
return 0;
}
void usb_kick(usb_handle *handle)
{
/* release the interface */
if (handle->interface)
{
(*handle->interface)->USBInterfaceClose(handle->interface);
(*handle->interface)->Release(handle->interface);
handle->interface = 0;
}
}

513
adb/usb_windows.c Normal file
View file

@ -0,0 +1,513 @@
/*
* 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.
*/
#include <windows.h>
#include <winerror.h>
#include <errno.h>
#include <usb100.h>
#include <adb_api.h>
#include <stdio.h>
#include "sysdeps.h"
#define TRACE_TAG TRACE_USB
#include "adb.h"
/** Structure usb_handle describes our connection to the usb device via
AdbWinApi.dll. This structure is returned from usb_open() routine and
is expected in each subsequent call that is accessing the device.
*/
struct usb_handle {
/// Previous entry in the list of opened usb handles
usb_handle *prev;
/// Next entry in the list of opened usb handles
usb_handle *next;
/// Handle to USB interface
ADBAPIHANDLE adb_interface;
/// Handle to USB read pipe (endpoint)
ADBAPIHANDLE adb_read_pipe;
/// Handle to USB write pipe (endpoint)
ADBAPIHANDLE adb_write_pipe;
/// Interface name
char* interface_name;
/// Mask for determining when to use zero length packets
unsigned zero_mask;
};
/// Class ID assigned to the device by androidusb.sys
static const GUID usb_class_id = ANDROID_USB_CLASS_ID;
/// List of opened usb handles
static usb_handle handle_list = {
.prev = &handle_list,
.next = &handle_list,
};
/// Locker for the list of opened usb handles
ADB_MUTEX_DEFINE( usb_lock );
/// Checks if there is opened usb handle in handle_list for this device.
int known_device(const char* dev_name);
/// Checks if there is opened usb handle in handle_list for this device.
/// usb_lock mutex must be held before calling this routine.
int known_device_locked(const char* dev_name);
/// Registers opened usb handle (adds it to handle_list).
int register_new_device(usb_handle* handle);
/// Checks if interface (device) matches certain criteria
int recognized_device(usb_handle* handle);
/// Enumerates present and available interfaces (devices), opens new ones and
/// registers usb transport for them.
void find_devices();
/// Entry point for thread that polls (every second) for new usb interfaces.
/// This routine calls find_devices in infinite loop.
void* device_poll_thread(void* unused);
/// Initializes this module
void usb_init();
/// Cleans up this module
void usb_cleanup();
/// Opens usb interface (device) by interface (device) name.
usb_handle* do_usb_open(const wchar_t* interface_name);
/// Writes data to the opened usb handle
int usb_write(usb_handle* handle, const void* data, int len);
/// Reads data using the opened usb handle
int usb_read(usb_handle *handle, void* data, int len);
/// Cleans up opened usb handle
void usb_cleanup_handle(usb_handle* handle);
/// Cleans up (but don't close) opened usb handle
void usb_kick(usb_handle* handle);
/// Closes opened usb handle
int usb_close(usb_handle* handle);
/// Gets interface (device) name for an opened usb handle
const char *usb_name(usb_handle* handle);
int known_device_locked(const char* dev_name) {
usb_handle* usb;
if (NULL != dev_name) {
// Iterate through the list looking for the name match.
for(usb = handle_list.next; usb != &handle_list; usb = usb->next) {
// In Windows names are not case sensetive!
if((NULL != usb->interface_name) &&
(0 == stricmp(usb->interface_name, dev_name))) {
return 1;
}
}
}
return 0;
}
int known_device(const char* dev_name) {
int ret = 0;
if (NULL != dev_name) {
adb_mutex_lock(&usb_lock);
ret = known_device_locked(dev_name);
adb_mutex_unlock(&usb_lock);
}
return ret;
}
int register_new_device(usb_handle* handle) {
if (NULL == handle)
return 0;
adb_mutex_lock(&usb_lock);
// Check if device is already in the list
if (known_device_locked(handle->interface_name)) {
adb_mutex_unlock(&usb_lock);
return 0;
}
// Not in the list. Add this handle to the list.
handle->next = &handle_list;
handle->prev = handle_list.prev;
handle->prev->next = handle;
handle->next->prev = handle;
adb_mutex_unlock(&usb_lock);
return 1;
}
void* device_poll_thread(void* unused) {
D("Created device thread\n");
while(1) {
find_devices();
adb_sleep_ms(1000);
}
return NULL;
}
void usb_init() {
adb_thread_t tid;
if(adb_thread_create(&tid, device_poll_thread, NULL)) {
fatal_errno("cannot create input thread");
}
}
void usb_cleanup() {
}
usb_handle* do_usb_open(const wchar_t* interface_name) {
// Allocate our handle
usb_handle* ret = (usb_handle*)malloc(sizeof(usb_handle));
if (NULL == ret)
return NULL;
// Set linkers back to the handle
ret->next = ret;
ret->prev = ret;
// Create interface.
ret->adb_interface = AdbCreateInterfaceByName(interface_name);
if (NULL == ret->adb_interface) {
free(ret);
errno = GetLastError();
return NULL;
}
// Open read pipe (endpoint)
ret->adb_read_pipe =
AdbOpenDefaultBulkReadEndpoint(ret->adb_interface,
AdbOpenAccessTypeReadWrite,
AdbOpenSharingModeReadWrite);
if (NULL != ret->adb_read_pipe) {
// Open write pipe (endpoint)
ret->adb_write_pipe =
AdbOpenDefaultBulkWriteEndpoint(ret->adb_interface,
AdbOpenAccessTypeReadWrite,
AdbOpenSharingModeReadWrite);
if (NULL != ret->adb_write_pipe) {
// Save interface name
unsigned long name_len = 0;
// First get expected name length
AdbGetInterfaceName(ret->adb_interface,
NULL,
&name_len,
true);
if (0 != name_len) {
ret->interface_name = (char*)malloc(name_len);
if (NULL != ret->interface_name) {
// Now save the name
if (AdbGetInterfaceName(ret->adb_interface,
ret->interface_name,
&name_len,
true)) {
// We're done at this point
return ret;
}
} else {
SetLastError(ERROR_OUTOFMEMORY);
}
}
}
}
// Something went wrong.
errno = GetLastError();
usb_cleanup_handle(ret);
free(ret);
SetLastError(errno);
return NULL;
}
int usb_write(usb_handle* handle, const void* data, int len) {
unsigned long time_out = 500 + len * 8;
unsigned long written = 0;
int ret;
D("usb_write %d\n", len);
if (NULL != handle) {
// Perform write
ret = AdbWriteEndpointSync(handle->adb_write_pipe,
(void*)data,
(unsigned long)len,
&written,
time_out);
errno = GetLastError();
if (ret) {
// Make sure that we've written what we were asked to write
D("usb_write got: %ld, expected: %d\n", written, len);
if (written == (unsigned long)len) {
if(handle->zero_mask && (len & handle->zero_mask) == 0) {
// Send a zero length packet
AdbWriteEndpointSync(handle->adb_write_pipe,
(void*)data,
0,
&written,
time_out);
}
return 0;
}
} else {
// assume ERROR_INVALID_HANDLE indicates we are disconnected
if (errno == ERROR_INVALID_HANDLE)
usb_kick(handle);
}
} else {
D("usb_write NULL handle\n");
SetLastError(ERROR_INVALID_HANDLE);
}
D("usb_write failed: %d\n", errno);
return -1;
}
int usb_read(usb_handle *handle, void* data, int len) {
unsigned long time_out = 500 + len * 8;
unsigned long read = 0;
int ret;
D("usb_read %d\n", len);
if (NULL != handle) {
while (len > 0) {
int xfer = (len > 4096) ? 4096 : len;
ret = AdbReadEndpointSync(handle->adb_read_pipe,
(void*)data,
(unsigned long)xfer,
&read,
time_out);
errno = GetLastError();
D("usb_write got: %ld, expected: %d, errno: %d\n", read, xfer, errno);
if (ret) {
data += read;
len -= read;
if (len == 0)
return 0;
} else if (errno != ERROR_SEM_TIMEOUT) {
// assume ERROR_INVALID_HANDLE indicates we are disconnected
if (errno == ERROR_INVALID_HANDLE)
usb_kick(handle);
break;
}
}
} else {
D("usb_read NULL handle\n");
SetLastError(ERROR_INVALID_HANDLE);
}
D("usb_read failed: %d\n", errno);
return -1;
}
void usb_cleanup_handle(usb_handle* handle) {
if (NULL != handle) {
if (NULL != handle->interface_name)
free(handle->interface_name);
if (NULL != handle->adb_write_pipe)
AdbCloseHandle(handle->adb_write_pipe);
if (NULL != handle->adb_read_pipe)
AdbCloseHandle(handle->adb_read_pipe);
if (NULL != handle->adb_interface)
AdbCloseHandle(handle->adb_interface);
handle->interface_name = NULL;
handle->adb_write_pipe = NULL;
handle->adb_read_pipe = NULL;
handle->adb_interface = NULL;
}
}
void usb_kick(usb_handle* handle) {
if (NULL != handle) {
adb_mutex_lock(&usb_lock);
usb_cleanup_handle(handle);
adb_mutex_unlock(&usb_lock);
} else {
SetLastError(ERROR_INVALID_HANDLE);
errno = ERROR_INVALID_HANDLE;
}
}
int usb_close(usb_handle* handle) {
D("usb_close\n");
if (NULL != handle) {
// Remove handle from the list
adb_mutex_lock(&usb_lock);
if ((handle->next != handle) && (handle->prev != handle)) {
handle->next->prev = handle->prev;
handle->prev->next = handle->next;
handle->prev = handle;
handle->next = handle;
}
adb_mutex_unlock(&usb_lock);
// Cleanup handle
usb_cleanup_handle(handle);
free(handle);
}
return 0;
}
const char *usb_name(usb_handle* handle) {
if (NULL == handle) {
SetLastError(ERROR_INVALID_HANDLE);
errno = ERROR_INVALID_HANDLE;
return NULL;
}
return (const char*)handle->interface_name;
}
int recognized_device(usb_handle* handle) {
if (NULL == handle)
return 0;
// Check vendor and product id first
USB_DEVICE_DESCRIPTOR device_desc;
if (!AdbGetUsbDeviceDescriptor(handle->adb_interface,
&device_desc)) {
return 0;
}
// Then check interface properties
USB_INTERFACE_DESCRIPTOR interf_desc;
if (!AdbGetUsbInterfaceDescriptor(handle->adb_interface,
&interf_desc)) {
return 0;
}
// Must have two endpoints
if (2 != interf_desc.bNumEndpoints) {
return 0;
}
if (is_adb_interface(device_desc.idVendor, device_desc.idProduct,
interf_desc.bInterfaceClass, interf_desc.bInterfaceSubClass, interf_desc.bInterfaceProtocol)) {
if(interf_desc.bInterfaceProtocol == 0x01) {
AdbEndpointInformation endpoint_info;
// assuming zero is a valid bulk endpoint ID
if (AdbGetEndpointInformation(handle->adb_interface, 0, &endpoint_info)) {
handle->zero_mask = endpoint_info.max_packet_size - 1;
}
}
return 1;
}
return 0;
}
void find_devices() {
usb_handle* handle = NULL;
char entry_buffer[2048];
char interf_name[2048];
AdbInterfaceInfo* next_interface = (AdbInterfaceInfo*)(&entry_buffer[0]);
unsigned long entry_buffer_size = sizeof(entry_buffer);
char* copy_name;
// Enumerate all present and active interfaces.
ADBAPIHANDLE enum_handle =
AdbEnumInterfaces(usb_class_id, true, true, true);
if (NULL == enum_handle)
return;
while (AdbNextInterface(enum_handle, next_interface, &entry_buffer_size)) {
// TODO: FIXME - temp hack converting wchar_t into char.
// It would be better to change AdbNextInterface so it will return
// interface name as single char string.
const wchar_t* wchar_name = next_interface->device_name;
for(copy_name = interf_name;
L'\0' != *wchar_name;
wchar_name++, copy_name++) {
*copy_name = (char)(*wchar_name);
}
*copy_name = '\0';
// Lets see if we already have this device in the list
if (!known_device(interf_name)) {
// This seems to be a new device. Open it!
handle = do_usb_open(next_interface->device_name);
if (NULL != handle) {
// Lets see if this interface (device) belongs to us
if (recognized_device(handle)) {
D("adding a new device %s\n", interf_name);
char serial_number[512];
unsigned long serial_number_len = sizeof(serial_number);
if (AdbGetSerialNumber(handle->adb_interface,
serial_number,
&serial_number_len,
true)) {
// Lets make sure that we don't duplicate this device
if (register_new_device(handle)) {
register_usb_transport(handle, serial_number);
} else {
D("register_new_device failed for %s\n", interf_name);
usb_cleanup_handle(handle);
free(handle);
}
} else {
D("cannot get serial number\n");
usb_cleanup_handle(handle);
free(handle);
}
} else {
usb_cleanup_handle(handle);
free(handle);
}
}
}
entry_buffer_size = sizeof(entry_buffer);
}
AdbCloseHandle(enum_handle);
}

13
cpio/Android.mk Normal file
View file

@ -0,0 +1,13 @@
# Copyright 2005 The Android Open Source Project
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := \
mkbootfs.c
LOCAL_MODULE := mkbootfs
include $(BUILD_HOST_EXECUTABLE)
$(call dist-for-goals,user userdebug droid,$(LOCAL_BUILT_MODULE))

220
cpio/mkbootfs.c Normal file
View file

@ -0,0 +1,220 @@
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <stdarg.h>
#include <fcntl.h>
#include <private/android_filesystem_config.h>
/* NOTES
**
** - see buffer-format.txt from the linux kernel docs for
** an explanation of this file format
** - dotfiles are ignored
** - directories named 'root' are ignored
** - device notes, pipes, etc are not supported (error)
*/
void die(const char *why, ...)
{
va_list ap;
va_start(ap, why);
fprintf(stderr,"error: ");
vfprintf(stderr, why, ap);
fprintf(stderr,"\n");
va_end(ap);
exit(1);
}
static int verbose = 0;
static int total_size = 0;
static void fix_stat(const char *path, struct stat *s)
{
fs_config(path, S_ISDIR(s->st_mode), &s->st_uid, &s->st_gid, &s->st_mode);
}
static void _eject(struct stat *s, char *out, int olen, char *data, unsigned datasize)
{
while(total_size & 3) {
total_size++;
putchar(0);
}
fix_stat(out, s);
// fprintf(stderr, "_eject %s: mode=0%o\n", out, s->st_mode);
printf("%06x%08x%08x%08x%08x%08x%08x"
"%08x%08x%08x%08x%08x%08x%08x%s%c",
0x070701,
(unsigned) s->st_ino,
s->st_mode,
0, // s.st_uid,
0, // s.st_gid,
1, // s.st_nlink,
(unsigned) s->st_mtime,
datasize,
0, // volmajor
0, // volminor
0, // devmajor
0, // devminor,
olen + 1,
0,
out,
0
);
total_size += 6 + 8*13 + olen + 1;
if(strlen(out) != olen) die("ACK!");
while(total_size & 3) {
total_size++;
putchar(0);
}
if(datasize) {
fwrite(data, datasize, 1, stdout);
total_size += datasize;
}
}
static void _eject_trailer()
{
struct stat s;
memset(&s, 0, sizeof(s));
_eject(&s, "TRAILER!!!", 10, 0, 0);
while(total_size & 0xff) {
total_size++;
putchar(0);
}
}
static void _archive(char *in, char *out, int ilen, int olen);
static void _archive_dir(char *in, char *out, int ilen, int olen)
{
int t;
DIR *d;
struct dirent *de;
if(verbose) {
fprintf(stderr,"_archive_dir('%s','%s',%d,%d)\n",
in, out, ilen, olen);
}
d = opendir(in);
if(d == 0) die("cannot open directory '%s'", in);
while((de = readdir(d)) != 0){
/* xxx: feature? maybe some dotfiles are okay */
if(de->d_name[0] == '.') continue;
/* xxx: hack. use a real exclude list */
if(!strcmp(de->d_name, "root")) continue;
t = strlen(de->d_name);
in[ilen] = '/';
memcpy(in + ilen + 1, de->d_name, t + 1);
if(olen > 0) {
out[olen] = '/';
memcpy(out + olen + 1, de->d_name, t + 1);
_archive(in, out, ilen + t + 1, olen + t + 1);
} else {
memcpy(out, de->d_name, t + 1);
_archive(in, out, ilen + t + 1, t);
}
in[ilen] = 0;
out[olen] = 0;
}
}
static void _archive(char *in, char *out, int ilen, int olen)
{
struct stat s;
if(verbose) {
fprintf(stderr,"_archive('%s','%s',%d,%d)\n",
in, out, ilen, olen);
}
if(lstat(in, &s)) die("could not stat '%s'\n", in);
if(S_ISREG(s.st_mode)){
char *tmp;
int fd;
fd = open(in, O_RDONLY);
if(fd < 0) die("cannot open '%s' for read", in);
tmp = (char*) malloc(s.st_size);
if(tmp == 0) die("cannot allocate %d bytes", s.st_size);
if(read(fd, tmp, s.st_size) != s.st_size) {
die("cannot read %d bytes", s.st_size);
}
_eject(&s, out, olen, tmp, s.st_size);
free(tmp);
close(fd);
} else if(S_ISDIR(s.st_mode)) {
_eject(&s, out, olen, 0, 0);
_archive_dir(in, out, ilen, olen);
} else if(S_ISLNK(s.st_mode)) {
char buf[1024];
int size;
size = readlink(in, buf, 1024);
if(size < 0) die("cannot read symlink '%s'", in);
_eject(&s, out, olen, buf, size);
} else {
die("Unknown '%s' (mode %d)?\n", in, s.st_mode);
}
}
void archive(const char *start, const char *prefix)
{
char in[8192];
char out[8192];
strcpy(in, start);
strcpy(out, prefix);
_archive_dir(in, out, strlen(in), strlen(out));
}
int main(int argc, char *argv[])
{
argc--;
argv++;
if(argc == 0) die("no directories to process?!");
while(argc-- > 0){
char *x = strchr(*argv, '=');
if(x != 0) {
*x++ = 0;
} else {
x = "";
}
archive(*argv, x);
argv++;
}
_eject_trailer();
return 0;
}

22
debuggerd/Android.mk Normal file
View file

@ -0,0 +1,22 @@
# Copyright 2005 The Android Open Source Project
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= debuggerd.c getevent.c unwind-arm.c pr-support.c utility.c
LOCAL_CFLAGS := -Wall
LOCAL_MODULE := debuggerd
LOCAL_STATIC_LIBRARIES := libcutils libc
include $(BUILD_EXECUTABLE)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := crasher.c
LOCAL_SRC_FILES += crashglue.S
LOCAL_MODULE := crasher
LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
LOCAL_MODULE_TAGS := eng
#LOCAL_FORCE_STATIC_EXECUTABLE := true
LOCAL_SHARED_LIBRARIES := libcutils libc
include $(BUILD_EXECUTABLE)

View file

190
debuggerd/NOTICE Normal file
View file

@ -0,0 +1,190 @@
Copyright (c) 2005-2008, 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.
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.
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS

105
debuggerd/crasher.c Normal file
View file

@ -0,0 +1,105 @@
//#include <cutils/misc.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sched.h>
#include <errno.h>
#include <signal.h>
#include <sys/ptrace.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <pthread.h>
#include <cutils/sockets.h>
void crash1(void);
void crashnostack(void);
static void debuggerd_connect()
{
char tmp[1];
int s;
sprintf(tmp, "%d", gettid());
s = socket_local_client("android:debuggerd",
ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM);
if(s >= 0) {
read(s, tmp, 1);
close(s);
}
}
void test_call1()
{
*((int*) 32) = 1;
}
void *test_thread(void *x)
{
printf("crasher: thread pid=%d tid=%d\n", getpid(), gettid());
sleep(1);
test_call1();
printf("goodbye\n");
return 0;
}
void *noisy(void *x)
{
char c = (unsigned) x;
for(;;) {
usleep(250*1000);
write(2, &c, 1);
if(c == 'C') *((unsigned*) 0) = 42;
}
return 0;
}
int ctest()
{
pthread_t thr;
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_create(&thr, &attr, noisy, (void*) 'A');
pthread_create(&thr, &attr, noisy, (void*) 'B');
pthread_create(&thr, &attr, noisy, (void*) 'C');
for(;;) ;
return 0;
}
int main(int argc, char **argv)
{
pthread_t thr;
pthread_attr_t attr;
fprintf(stderr,"crasher: " __TIME__ "!@\n");
fprintf(stderr,"crasher: init pid=%d tid=%d\n", getpid(), gettid());
if(argc > 1) {
if(!strcmp(argv[1],"nostack")) crashnostack();
if(!strcmp(argv[1],"ctest")) return ctest();
if(!strcmp(argv[1],"exit")) exit(1);
if(!strcmp(argv[1],"abort")) maybeabort();
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_create(&thr, &attr, test_thread, 0);
while(1) sleep(1);
} else {
crash1();
// *((int*) 0) = 42;
}
return 0;
}
void maybeabort()
{
if(time(0) != 42) abort();
}

28
debuggerd/crashglue.S Normal file
View file

@ -0,0 +1,28 @@
.globl crash1
.globl crashnostack
crash1:
ldr r0, =0xa5a50000
ldr r1, =0xa5a50001
ldr r2, =0xa5a50002
ldr r3, =0xa5a50003
ldr r4, =0xa5a50004
ldr r5, =0xa5a50005
ldr r6, =0xa5a50006
ldr r7, =0xa5a50007
ldr r8, =0xa5a50008
ldr r9, =0xa5a50009
ldr r10, =0xa5a50010
ldr r11, =0xa5a50011
ldr r12, =0xa5a50012
mov lr, #0
ldr lr, [lr]
b .
crashnostack:
mov sp, #0
mov r0, #0
ldr r0, [r0]
b .

852
debuggerd/debuggerd.c Normal file
View file

@ -0,0 +1,852 @@
/* system/debuggerd/debuggerd.c
**
** Copyright 2006, 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 <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#include <pthread.h>
#include <stdarg.h>
#include <fcntl.h>
#include <sys/types.h>
#include <dirent.h>
#include <sys/ptrace.h>
#include <sys/wait.h>
#include <sys/exec_elf.h>
#include <sys/stat.h>
#include <cutils/sockets.h>
#include <cutils/logd.h>
#include <cutils/sockets.h>
#include <cutils/properties.h>
#include <linux/input.h>
#include <private/android_filesystem_config.h>
#include "utility.h"
/* Main entry point to get the backtrace from the crashing process */
extern int unwind_backtrace_with_ptrace(int tfd, pid_t pid, mapinfo *map,
unsigned int sp_list[],
int *frame0_pc_sane,
bool at_fault);
static char **process_name_ptr;
static int logsocket = -1;
#define ANDROID_LOG_INFO 4
/* Log information onto the tombstone */
void _LOG(int tfd, bool in_tombstone_only, const char *fmt, ...)
{
char buf[128];
va_list ap;
va_start(ap, fmt);
if (tfd >= 0) {
int len;
vsnprintf(buf, sizeof(buf), fmt, ap);
len = strlen(buf);
if(tfd >= 0) write(tfd, buf, len);
}
if (!in_tombstone_only)
__android_log_vprint(ANDROID_LOG_INFO, "DEBUG", fmt, ap);
}
#define LOG(fmt...) _LOG(-1, 0, fmt)
#if 0
#define XLOG(fmt...) _LOG(-1, 0, fmt)
#else
#define XLOG(fmt...) do {} while(0)
#endif
// 6f000000-6f01e000 rwxp 00000000 00:0c 16389419 /system/lib/libcomposer.so
// 012345678901234567890123456789012345678901234567890123456789
// 0 1 2 3 4 5
mapinfo *parse_maps_line(char *line)
{
mapinfo *mi;
int len = strlen(line);
if(len < 1) return 0;
line[--len] = 0;
if(len < 50) return 0;
if(line[20] != 'x') return 0;
mi = malloc(sizeof(mapinfo) + (len - 47));
if(mi == 0) return 0;
mi->start = strtoul(line, 0, 16);
mi->end = strtoul(line + 9, 0, 16);
/* To be filled in parse_exidx_info if the mapped section starts with
* elf_header
*/
mi->exidx_start = mi->exidx_end = 0;
mi->next = 0;
strcpy(mi->name, line + 49);
return mi;
}
void dump_build_info(int tfd)
{
char fingerprint[PROPERTY_VALUE_MAX];
property_get("ro.build.fingerprint", fingerprint, "unknown");
_LOG(tfd, false, "Build fingerprint: '%s'\n", fingerprint);
}
void dump_stack_and_code(int tfd, int pid, mapinfo *map,
int unwind_depth, unsigned int sp_list[],
int frame0_pc_sane, bool at_fault)
{
unsigned int sp, pc, p, end, data;
struct pt_regs r;
int sp_depth;
bool only_in_tombstone = !at_fault;
if(ptrace(PTRACE_GETREGS, pid, 0, &r)) return;
sp = r.ARM_sp;
pc = r.ARM_pc;
/* Died because calling the weeds - dump
* the code around the PC in the next frame instead.
*/
if (frame0_pc_sane == 0) {
pc = r.ARM_lr;
}
_LOG(tfd, true, "code%s:\n", frame0_pc_sane ? "" : " (around frame #01)");
end = p = pc & ~3;
p -= 16;
/* Dump the code as:
* PC contents
* 00008d34 fffffcd0 4c0eb530 b0934a0e 1c05447c
* 00008d44 f7ff18a0 490ced94 68035860 d0012b00
*/
while (p <= end) {
int i;
_LOG(tfd, true, " %08x ", p);
for (i = 0; i < 4; i++) {
data = ptrace(PTRACE_PEEKTEXT, pid, (void*)p, NULL);
_LOG(tfd, true, " %08x", data);
p += 4;
}
_LOG(tfd, true, "\n", p);
}
p = sp - 64;
p &= ~3;
if (unwind_depth != 0) {
if (unwind_depth < STACK_CONTENT_DEPTH) {
end = sp_list[unwind_depth-1];
}
else {
end = sp_list[STACK_CONTENT_DEPTH-1];
}
}
else {
end = sp | 0x000000ff;
end += 0xff;
}
_LOG(tfd, only_in_tombstone, "stack:\n");
/* If the crash is due to PC == 0, there will be two frames that
* have identical SP value.
*/
if (sp_list[0] == sp_list[1]) {
sp_depth = 1;
}
else {
sp_depth = 0;
}
while (p <= end) {
char *prompt;
char level[16];
data = ptrace(PTRACE_PEEKTEXT, pid, (void*)p, NULL);
if (p == sp_list[sp_depth]) {
sprintf(level, "#%02d", sp_depth++);
prompt = level;
}
else {
prompt = " ";
}
/* Print the stack content in the log for the first 3 frames. For the
* rest only print them in the tombstone file.
*/
_LOG(tfd, (sp_depth > 2) || only_in_tombstone,
"%s %08x %08x %s\n", prompt, p, data,
map_to_name(map, data, ""));
p += 4;
}
/* print another 64-byte of stack data after the last frame */
end = p+64;
while (p <= end) {
data = ptrace(PTRACE_PEEKTEXT, pid, (void*)p, NULL);
_LOG(tfd, (sp_depth > 2) || only_in_tombstone,
" %08x %08x %s\n", p, data,
map_to_name(map, data, ""));
p += 4;
}
}
void dump_pc_and_lr(int tfd, int pid, mapinfo *map, int unwound_level,
bool at_fault)
{
struct pt_regs r;
if(ptrace(PTRACE_GETREGS, pid, 0, &r)) {
_LOG(tfd, !at_fault, "tid %d not responding!\n", pid);
return;
}
if (unwound_level == 0) {
_LOG(tfd, !at_fault, " #%02d pc %08x %s\n", 0, r.ARM_pc,
map_to_name(map, r.ARM_pc, "<unknown>"));
}
_LOG(tfd, !at_fault, " #%02d lr %08x %s\n", 1, r.ARM_lr,
map_to_name(map, r.ARM_lr, "<unknown>"));
}
void dump_registers(int tfd, int pid, bool at_fault)
{
struct pt_regs r;
bool only_in_tombstone = !at_fault;
if(ptrace(PTRACE_GETREGS, pid, 0, &r)) {
_LOG(tfd, only_in_tombstone,
"cannot get registers: %s\n", strerror(errno));
return;
}
_LOG(tfd, only_in_tombstone, " r0 %08x r1 %08x r2 %08x r3 %08x\n",
r.ARM_r0, r.ARM_r1, r.ARM_r2, r.ARM_r3);
_LOG(tfd, only_in_tombstone, " r4 %08x r5 %08x r6 %08x r7 %08x\n",
r.ARM_r4, r.ARM_r5, r.ARM_r6, r.ARM_r7);
_LOG(tfd, only_in_tombstone, " r8 %08x r9 %08x 10 %08x fp %08x\n",
r.ARM_r8, r.ARM_r9, r.ARM_r10, r.ARM_fp);
_LOG(tfd, only_in_tombstone,
" ip %08x sp %08x lr %08x pc %08x cpsr %08x\n",
r.ARM_ip, r.ARM_sp, r.ARM_lr, r.ARM_pc, r.ARM_cpsr);
}
const char *get_signame(int sig)
{
switch(sig) {
case SIGILL: return "SIGILL";
case SIGABRT: return "SIGABRT";
case SIGBUS: return "SIGBUS";
case SIGFPE: return "SIGFPE";
case SIGSEGV: return "SIGSEGV";
case SIGSTKFLT: return "SIGSTKFLT";
default: return "?";
}
}
void dump_fault_addr(int tfd, int pid, int sig)
{
siginfo_t si;
memset(&si, 0, sizeof(si));
if(ptrace(PTRACE_GETSIGINFO, pid, 0, &si)){
_LOG(tfd, false, "cannot get siginfo: %s\n", strerror(errno));
} else {
_LOG(tfd, false, "signal %d (%s), fault addr %08x\n",
sig, get_signame(sig), si.si_addr);
}
}
void dump_crash_banner(int tfd, unsigned pid, unsigned tid, int sig)
{
char data[1024];
char *x = 0;
FILE *fp;
sprintf(data, "/proc/%d/cmdline", pid);
fp = fopen(data, "r");
if(fp) {
x = fgets(data, 1024, fp);
fclose(fp);
}
_LOG(tfd, false,
"*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***\n");
dump_build_info(tfd);
_LOG(tfd, false, "pid: %d, tid: %d >>> %s <<<\n",
pid, tid, x ? x : "UNKNOWN");
if(sig) dump_fault_addr(tfd, tid, sig);
}
static void parse_exidx_info(mapinfo *milist, pid_t pid)
{
mapinfo *mi;
for (mi = milist; mi != NULL; mi = mi->next) {
Elf32_Ehdr ehdr;
memset(&ehdr, 0, sizeof(Elf32_Ehdr));
/* Read in sizeof(Elf32_Ehdr) worth of data from the beginning of
* mapped section.
*/
get_remote_struct(pid, (void *) (mi->start), &ehdr,
sizeof(Elf32_Ehdr));
/* Check if it has the matching magic words */
if (IS_ELF(ehdr)) {
Elf32_Phdr phdr;
Elf32_Phdr *ptr;
int i;
ptr = (Elf32_Phdr *) (mi->start + ehdr.e_phoff);
for (i = 0; i < ehdr.e_phnum; i++) {
/* Parse the program header */
get_remote_struct(pid, (void *) ptr+i, &phdr,
sizeof(Elf32_Phdr));
/* Found a EXIDX segment? */
if (phdr.p_type == PT_ARM_EXIDX) {
mi->exidx_start = mi->start + phdr.p_offset;
mi->exidx_end = mi->exidx_start + phdr.p_filesz;
break;
}
}
}
}
}
void dump_crash_report(int tfd, unsigned pid, unsigned tid, bool at_fault)
{
char data[1024];
FILE *fp;
mapinfo *milist = 0;
unsigned int sp_list[STACK_CONTENT_DEPTH];
int stack_depth;
int frame0_pc_sane = 1;
if (!at_fault) {
_LOG(tfd, true,
"--- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---\n");
_LOG(tfd, true, "pid: %d, tid: %d\n", pid, tid);
}
dump_registers(tfd, tid, at_fault);
/* Clear stack pointer records */
memset(sp_list, 0, sizeof(sp_list));
sprintf(data, "/proc/%d/maps", pid);
fp = fopen(data, "r");
if(fp) {
while(fgets(data, 1024, fp)) {
mapinfo *mi = parse_maps_line(data);
if(mi) {
mi->next = milist;
milist = mi;
}
}
fclose(fp);
}
parse_exidx_info(milist, tid);
/* If stack unwinder fails, use the default solution to dump the stack
* content.
*/
stack_depth = unwind_backtrace_with_ptrace(tfd, tid, milist, sp_list,
&frame0_pc_sane, at_fault);
/* The stack unwinder should at least unwind two levels of stack. If less
* level is seen we make sure at lease pc and lr are dumped.
*/
if (stack_depth < 2) {
dump_pc_and_lr(tfd, tid, milist, stack_depth, at_fault);
}
dump_stack_and_code(tfd, tid, milist, stack_depth, sp_list, frame0_pc_sane,
at_fault);
while(milist) {
mapinfo *next = milist->next;
free(milist);
milist = next;
}
}
/* FIXME: unused: use it or lose it*/
#if 0
static
void start_gdbserver_vs(int pid, int port)
{
pid_t p;
char *args[5];
char commspec[16];
char pidspec[16];
p = fork();
if(p < 0) {
LOG("could not fork()\n");
return;
}
if(p == 0) {
sprintf(commspec, ":%d", port);
sprintf(pidspec, "%d", pid);
args[0] = "/system/bin/gdbserver";
args[1] = commspec;
args[2] = "--attach";
args[3] = pidspec;
args[4] = 0;
exit(execv(args[0], args));
} else {
LOG("gdbserver pid=%d port=%d targetpid=%d\n",
p, port, pid);
sleep(5);
}
}
#endif
#define MAX_TOMBSTONES 10
#define typecheck(x,y) { \
typeof(x) __dummy1; \
typeof(y) __dummy2; \
(void)(&__dummy1 == &__dummy2); }
#define TOMBSTONE_DIR "/data/tombstones"
/*
* find_and_open_tombstone - find an available tombstone slot, if any, of the
* form tombstone_XX where XX is 00 to MAX_TOMBSTONES-1, inclusive. If no
* file is available, we reuse the least-recently-modified file.
*/
static int find_and_open_tombstone(void)
{
unsigned long mtime = ULONG_MAX;
struct stat sb;
char path[128];
int fd, i, oldest = 0;
/*
* XXX: Our stat.st_mtime isn't time_t. If it changes, as it probably ought
* to, our logic breaks. This check will generate a warning if that happens.
*/
typecheck(mtime, sb.st_mtime);
/*
* In a single wolf-like pass, find an available slot and, in case none
* exist, find and record the least-recently-modified file.
*/
for (i = 0; i < MAX_TOMBSTONES; i++) {
snprintf(path, sizeof(path), TOMBSTONE_DIR"/tombstone_%02d", i);
if (!stat(path, &sb)) {
if (sb.st_mtime < mtime) {
oldest = i;
mtime = sb.st_mtime;
}
continue;
}
if (errno != ENOENT)
continue;
fd = open(path, O_CREAT | O_EXCL | O_WRONLY, 0600);
if (fd < 0)
continue; /* raced ? */
fchown(fd, AID_SYSTEM, AID_SYSTEM);
return fd;
}
/* we didn't find an available file, so we clobber the oldest one */
snprintf(path, sizeof(path), TOMBSTONE_DIR"/tombstone_%02d", oldest);
fd = open(path, O_CREAT | O_TRUNC | O_WRONLY, 0600);
fchown(fd, AID_SYSTEM, AID_SYSTEM);
return fd;
}
/* Return true if some thread is not detached cleanly */
static bool dump_sibling_thread_report(int tfd, unsigned pid, unsigned tid)
{
char task_path[1024];
sprintf(task_path, "/proc/%d/task", pid);
DIR *d;
struct dirent *de;
int need_cleanup = 0;
d = opendir(task_path);
/* Bail early if cannot open the task directory */
if (d == NULL) {
XLOG("Cannot open /proc/%d/task\n", pid);
return false;
}
while ((de = readdir(d)) != NULL) {
unsigned new_tid;
/* Ignore "." and ".." */
if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
continue;
new_tid = atoi(de->d_name);
/* The main thread at fault has been handled individually */
if (new_tid == tid)
continue;
/* Skip this thread if cannot ptrace it */
if (ptrace(PTRACE_ATTACH, new_tid, 0, 0) < 0)
continue;
dump_crash_report(tfd, pid, new_tid, false);
need_cleanup |= ptrace(PTRACE_DETACH, new_tid, 0, 0);
}
closedir(d);
return need_cleanup != 0;
}
/* Return true if some thread is not detached cleanly */
static bool engrave_tombstone(unsigned pid, unsigned tid, int debug_uid,
int signal)
{
int fd;
bool need_cleanup = false;
mkdir(TOMBSTONE_DIR, 0755);
chown(TOMBSTONE_DIR, AID_SYSTEM, AID_SYSTEM);
fd = find_and_open_tombstone();
if (fd < 0)
return need_cleanup;
dump_crash_banner(fd, pid, tid, signal);
dump_crash_report(fd, pid, tid, true);
/*
* If the user has requested to attach gdb, don't collect the per-thread
* information as it increases the chance to lose track of the process.
*/
if ((signed)pid > debug_uid) {
need_cleanup = dump_sibling_thread_report(fd, pid, tid);
}
close(fd);
return need_cleanup;
}
static int
write_string(const char* file, const char* string)
{
int len;
int fd;
ssize_t amt;
fd = open(file, O_RDWR);
len = strlen(string);
if (fd < 0)
return -errno;
amt = write(fd, string, len);
close(fd);
return amt >= 0 ? 0 : -errno;
}
static
void init_debug_led(void)
{
// trout leds
write_string("/sys/class/leds/red/brightness", "0");
write_string("/sys/class/leds/green/brightness", "0");
write_string("/sys/class/leds/blue/brightness", "0");
write_string("/sys/class/leds/red/device/blink", "0");
// sardine leds
write_string("/sys/class/leds/left/cadence", "0,0");
}
static
void enable_debug_led(void)
{
// trout leds
write_string("/sys/class/leds/red/brightness", "255");
// sardine leds
write_string("/sys/class/leds/left/cadence", "1,0");
}
static
void disable_debug_led(void)
{
// trout leds
write_string("/sys/class/leds/red/brightness", "0");
// sardine leds
write_string("/sys/class/leds/left/cadence", "0,0");
}
extern int init_getevent();
extern void uninit_getevent();
extern int get_event(struct input_event* event, int timeout);
static void wait_for_user_action(unsigned tid, struct ucred* cr)
{
(void)tid;
/* First log a helpful message */
LOG( "********************************************************\n"
"* process %d crashed. debuggerd waiting for gdbserver \n"
"* \n"
"* adb shell gdbserver :port --attach %d & \n"
"* \n"
"* and press the HOME key. \n"
"********************************************************\n",
cr->pid, cr->pid);
/* wait for HOME key */
if (init_getevent() == 0) {
int ms = 1200 / 10;
int dit = 1;
int dah = 3*dit;
int _ = -dit;
int ___ = 3*_;
int _______ = 7*_;
const signed char codes[] = {
dit,_,dit,_,dit,___,dah,_,dah,_,dah,___,dit,_,dit,_,dit,_______
};
size_t s = 0;
struct input_event e;
int home = 0;
init_debug_led();
enable_debug_led();
do {
int timeout = abs((int)(codes[s])) * ms;
int res = get_event(&e, timeout);
if (res == 0) {
if (e.type==EV_KEY && e.code==KEY_HOME && e.value==0)
home = 1;
} else if (res == 1) {
if (++s >= sizeof(codes)/sizeof(*codes))
s = 0;
if (codes[s] > 0) {
enable_debug_led();
} else {
disable_debug_led();
}
}
} while (!home);
uninit_getevent();
}
/* don't forget to turn debug led off */
disable_debug_led();
/* close filedescriptor */
LOG("debuggerd resuming process %d", cr->pid);
}
static void handle_crashing_process(int fd)
{
char buf[64];
struct stat s;
unsigned tid;
struct ucred cr;
int n, len, status;
int tid_attach_status = -1;
unsigned retry = 30;
bool need_cleanup = false;
char value[PROPERTY_VALUE_MAX];
property_get("debug.db.uid", value, "-1");
int debug_uid = atoi(value);
XLOG("handle_crashing_process(%d)\n", fd);
len = sizeof(cr);
n = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cr, &len);
if(n != 0) {
LOG("cannot get credentials\n");
goto done;
}
XLOG("reading tid\n");
fcntl(fd, F_SETFL, O_NONBLOCK);
while((n = read(fd, &tid, sizeof(unsigned))) != sizeof(unsigned)) {
if(errno == EINTR) continue;
if(errno == EWOULDBLOCK) {
if(retry-- > 0) {
usleep(100 * 1000);
continue;
}
LOG("timed out reading tid\n");
goto done;
}
LOG("read failure? %s\n", strerror(errno));
goto done;
}
sprintf(buf,"/proc/%d/task/%d", cr.pid, tid);
if(stat(buf, &s)) {
LOG("tid %d does not exist in pid %d. ignorning debug request\n",
tid, cr.pid);
close(fd);
return;
}
XLOG("BOOM: pid=%d uid=%d gid=%d tid=%d\n", cr.pid, cr.uid, cr.gid, tid);
tid_attach_status = ptrace(PTRACE_ATTACH, tid, 0, 0);
if(tid_attach_status < 0) {
LOG("ptrace attach failed: %s\n", strerror(errno));
goto done;
}
close(fd);
fd = -1;
for(;;) {
n = waitpid(tid, &status, __WALL);
if(n < 0) {
if(errno == EAGAIN) continue;
LOG("waitpid failed: %s\n", strerror(errno));
goto done;
}
XLOG("waitpid: n=%d status=%08x\n", n, status);
if(WIFSTOPPED(status)){
n = WSTOPSIG(status);
switch(n) {
case SIGSTOP:
XLOG("stopped -- continuing\n");
n = ptrace(PTRACE_CONT, tid, 0, 0);
if(n) {
LOG("ptrace failed: %s\n", strerror(errno));
goto done;
}
continue;
case SIGILL:
case SIGABRT:
case SIGBUS:
case SIGFPE:
case SIGSEGV:
case SIGSTKFLT: {
XLOG("stopped -- fatal signal\n");
need_cleanup = engrave_tombstone(cr.pid, tid, debug_uid, n);
kill(tid, SIGSTOP);
goto done;
}
default:
XLOG("stopped -- unexpected signal\n");
goto done;
}
} else {
XLOG("unexpected waitpid response\n");
goto done;
}
}
done:
XLOG("detaching\n");
/* stop the process so we can debug */
kill(cr.pid, SIGSTOP);
/*
* If a thread has been attached by ptrace, make sure it is detached
* successfully otherwise we will get a zombie.
*/
if (tid_attach_status == 0) {
int detach_status;
/* detach so we can attach gdbserver */
detach_status = ptrace(PTRACE_DETACH, tid, 0, 0);
need_cleanup |= (detach_status != 0);
}
/*
* if debug.db.uid is set, its value indicates if we should wait
* for user action for the crashing process.
* in this case, we log a message and turn the debug LED on
* waiting for a gdb connection (for instance)
*/
if ((signed)cr.uid <= debug_uid) {
wait_for_user_action(tid, &cr);
}
/* resume stopped process (so it can crash in peace) */
kill(cr.pid, SIGCONT);
if (need_cleanup) {
LOG("debuggerd committing suicide to free the zombie!\n");
kill(getpid(), SIGKILL);
}
if(fd != -1) close(fd);
}
int main(int argc, char **argv)
{
int s;
struct sigaction act;
process_name_ptr = argv;
logsocket = socket_local_client("logd",
ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_DGRAM);
if(logsocket < 0) {
logsocket = -1;
} else {
fcntl(logsocket, F_SETFD, FD_CLOEXEC);
}
act.sa_handler = SIG_DFL;
sigemptyset(&act.sa_mask);
sigaddset(&act.sa_mask,SIGCHLD);
act.sa_flags = SA_NOCLDWAIT;
sigaction(SIGCHLD, &act, 0);
s = socket_local_server("android:debuggerd",
ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM);
if(s < 0) return -1;
fcntl(s, F_SETFD, FD_CLOEXEC);
LOG("debuggerd: " __DATE__ " " __TIME__ "\n");
for(;;) {
struct sockaddr addr;
socklen_t alen;
int fd;
alen = sizeof(addr);
fd = accept(s, &addr, &alen);
if(fd < 0) continue;
fcntl(fd, F_SETFD, FD_CLOEXEC);
handle_crashing_process(fd);
}
return 0;
}

219
debuggerd/getevent.c Normal file
View file

@ -0,0 +1,219 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <dirent.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/inotify.h>
#include <sys/limits.h>
#include <sys/poll.h>
#include <linux/input.h>
#include <errno.h>
#include <cutils/log.h>
static struct pollfd *ufds;
static char **device_names;
static int nfds;
static int open_device(const char *device)
{
int version;
int fd;
struct pollfd *new_ufds;
char **new_device_names;
char name[80];
char location[80];
char idstr[80];
struct input_id id;
fd = open(device, O_RDWR);
if(fd < 0) {
return -1;
}
if(ioctl(fd, EVIOCGVERSION, &version)) {
return -1;
}
if(ioctl(fd, EVIOCGID, &id)) {
return -1;
}
name[sizeof(name) - 1] = '\0';
location[sizeof(location) - 1] = '\0';
idstr[sizeof(idstr) - 1] = '\0';
if(ioctl(fd, EVIOCGNAME(sizeof(name) - 1), &name) < 1) {
//fprintf(stderr, "could not get device name for %s, %s\n", device, strerror(errno));
name[0] = '\0';
}
if(ioctl(fd, EVIOCGPHYS(sizeof(location) - 1), &location) < 1) {
//fprintf(stderr, "could not get location for %s, %s\n", device, strerror(errno));
location[0] = '\0';
}
if(ioctl(fd, EVIOCGUNIQ(sizeof(idstr) - 1), &idstr) < 1) {
//fprintf(stderr, "could not get idstring for %s, %s\n", device, strerror(errno));
idstr[0] = '\0';
}
new_ufds = realloc(ufds, sizeof(ufds[0]) * (nfds + 1));
if(new_ufds == NULL) {
fprintf(stderr, "out of memory\n");
return -1;
}
ufds = new_ufds;
new_device_names = realloc(device_names, sizeof(device_names[0]) * (nfds + 1));
if(new_device_names == NULL) {
fprintf(stderr, "out of memory\n");
return -1;
}
device_names = new_device_names;
ufds[nfds].fd = fd;
ufds[nfds].events = POLLIN;
device_names[nfds] = strdup(device);
nfds++;
return 0;
}
int close_device(const char *device)
{
int i;
for(i = 1; i < nfds; i++) {
if(strcmp(device_names[i], device) == 0) {
int count = nfds - i - 1;
free(device_names[i]);
memmove(device_names + i, device_names + i + 1, sizeof(device_names[0]) * count);
memmove(ufds + i, ufds + i + 1, sizeof(ufds[0]) * count);
nfds--;
return 0;
}
}
return -1;
}
static int read_notify(const char *dirname, int nfd)
{
int res;
char devname[PATH_MAX];
char *filename;
char event_buf[512];
int event_size;
int event_pos = 0;
struct inotify_event *event;
res = read(nfd, event_buf, sizeof(event_buf));
if(res < (int)sizeof(*event)) {
if(errno == EINTR)
return 0;
fprintf(stderr, "could not get event, %s\n", strerror(errno));
return 1;
}
//printf("got %d bytes of event information\n", res);
strcpy(devname, dirname);
filename = devname + strlen(devname);
*filename++ = '/';
while(res >= (int)sizeof(*event)) {
event = (struct inotify_event *)(event_buf + event_pos);
//printf("%d: %08x \"%s\"\n", event->wd, event->mask, event->len ? event->name : "");
if(event->len) {
strcpy(filename, event->name);
if(event->mask & IN_CREATE) {
open_device(devname);
}
else {
close_device(devname);
}
}
event_size = sizeof(*event) + event->len;
res -= event_size;
event_pos += event_size;
}
return 0;
}
static int scan_dir(const char *dirname)
{
char devname[PATH_MAX];
char *filename;
DIR *dir;
struct dirent *de;
dir = opendir(dirname);
if(dir == NULL)
return -1;
strcpy(devname, dirname);
filename = devname + strlen(devname);
*filename++ = '/';
while((de = readdir(dir))) {
if(de->d_name[0] == '.' &&
(de->d_name[1] == '\0' ||
(de->d_name[1] == '.' && de->d_name[2] == '\0')))
continue;
strcpy(filename, de->d_name);
open_device(devname);
}
closedir(dir);
return 0;
}
int init_getevent()
{
int res;
const char *device_path = "/dev/input";
nfds = 1;
ufds = calloc(1, sizeof(ufds[0]));
ufds[0].fd = inotify_init();
ufds[0].events = POLLIN;
res = inotify_add_watch(ufds[0].fd, device_path, IN_DELETE | IN_CREATE);
if(res < 0) {
return 1;
}
res = scan_dir(device_path);
if(res < 0) {
return 1;
}
return 0;
}
void uninit_getevent()
{
int i;
for(i = 0; i < nfds; i++) {
close(ufds[i].fd);
}
free(ufds);
ufds = 0;
nfds = 0;
}
int get_event(struct input_event* event, int timeout)
{
int res;
int i;
int pollres;
const char *device_path = "/dev/input";
while(1) {
pollres = poll(ufds, nfds, timeout);
if (pollres == 0) {
return 1;
}
if(ufds[0].revents & POLLIN) {
read_notify(device_path, ufds[0].fd);
}
for(i = 1; i < nfds; i++) {
if(ufds[i].revents) {
if(ufds[i].revents & POLLIN) {
res = read(ufds[i].fd, event, sizeof(*event));
if(res < (int)sizeof(event)) {
fprintf(stderr, "could not get event\n");
return -1;
}
return 0;
}
}
}
}
return 0;
}

345
debuggerd/pr-support.c Normal file
View file

@ -0,0 +1,345 @@
/* ARM EABI compliant unwinding routines
Copyright (C) 2004, 2005 Free Software Foundation, Inc.
Contributed by Paul Brook
This file is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation; either version 2, or (at your option) any
later version.
In addition to the permissions in the GNU General Public License, the
Free Software Foundation gives you unlimited permission to link the
compiled version of this file into combinations with other programs,
and to distribute those combinations without any restriction coming
from the use of this file. (The General Public License restrictions
do apply in other respects; for example, they cover modification of
the file, and distribution when not linked into a combine
executable.)
This file is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; see the file COPYING. If not, write to
the Free Software Foundation, 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA. */
/****************************************************************************
* The functions here are derived from gcc/config/arm/pr-support.c from the
* 4.3.x release. The main changes here involve the use of ptrace to retrieve
* memory/processor states from a remote process.
****************************************************************************/
#include <sys/types.h>
#include <unwind.h>
#include "utility.h"
/* We add a prototype for abort here to avoid creating a dependency on
target headers. */
extern void abort (void);
/* Derived from _Unwind_VRS_Pop to use ptrace */
extern _Unwind_VRS_Result
unwind_VRS_Pop_with_ptrace (_Unwind_Context *context,
_Unwind_VRS_RegClass regclass,
_uw discriminator,
_Unwind_VRS_DataRepresentation representation,
pid_t pid);
typedef struct _ZSt9type_info type_info; /* This names C++ type_info type */
/* Misc constants. */
#define R_IP 12
#define R_SP 13
#define R_LR 14
#define R_PC 15
#define uint32_highbit (((_uw) 1) << 31)
void __attribute__((weak)) __cxa_call_unexpected(_Unwind_Control_Block *ucbp);
/* Unwind descriptors. */
typedef struct
{
_uw16 length;
_uw16 offset;
} EHT16;
typedef struct
{
_uw length;
_uw offset;
} EHT32;
/* Personality routine helper functions. */
#define CODE_FINISH (0xb0)
/* Derived from next_unwind_byte to use ptrace */
/* Return the next byte of unwinding information, or CODE_FINISH if there is
no data remaining. */
static inline _uw8
next_unwind_byte_with_ptrace (__gnu_unwind_state * uws, pid_t pid)
{
_uw8 b;
if (uws->bytes_left == 0)
{
/* Load another word */
if (uws->words_left == 0)
return CODE_FINISH; /* Nothing left. */
uws->words_left--;
uws->data = get_remote_word(pid, uws->next);
uws->next++;
uws->bytes_left = 3;
}
else
uws->bytes_left--;
/* Extract the most significant byte. */
b = (uws->data >> 24) & 0xff;
uws->data <<= 8;
return b;
}
/* Execute the unwinding instructions described by UWS. */
_Unwind_Reason_Code
unwind_execute_with_ptrace(_Unwind_Context * context, __gnu_unwind_state * uws,
pid_t pid)
{
_uw op;
int set_pc;
_uw reg;
set_pc = 0;
for (;;)
{
op = next_unwind_byte_with_ptrace (uws, pid);
if (op == CODE_FINISH)
{
/* If we haven't already set pc then copy it from lr. */
if (!set_pc)
{
_Unwind_VRS_Get (context, _UVRSC_CORE, R_LR, _UVRSD_UINT32,
&reg);
_Unwind_VRS_Set (context, _UVRSC_CORE, R_PC, _UVRSD_UINT32,
&reg);
set_pc = 1;
}
/* Drop out of the loop. */
break;
}
if ((op & 0x80) == 0)
{
/* vsp = vsp +- (imm6 << 2 + 4). */
_uw offset;
offset = ((op & 0x3f) << 2) + 4;
_Unwind_VRS_Get (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32, &reg);
if (op & 0x40)
reg -= offset;
else
reg += offset;
_Unwind_VRS_Set (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32, &reg);
continue;
}
if ((op & 0xf0) == 0x80)
{
op = (op << 8) | next_unwind_byte_with_ptrace (uws, pid);
if (op == 0x8000)
{
/* Refuse to unwind. */
return _URC_FAILURE;
}
/* Pop r4-r15 under mask. */
op = (op << 4) & 0xfff0;
if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_CORE, op, _UVRSD_UINT32,
pid)
!= _UVRSR_OK)
return _URC_FAILURE;
if (op & (1 << R_PC))
set_pc = 1;
continue;
}
if ((op & 0xf0) == 0x90)
{
op &= 0xf;
if (op == 13 || op == 15)
/* Reserved. */
return _URC_FAILURE;
/* vsp = r[nnnn]. */
_Unwind_VRS_Get (context, _UVRSC_CORE, op, _UVRSD_UINT32, &reg);
_Unwind_VRS_Set (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32, &reg);
continue;
}
if ((op & 0xf0) == 0xa0)
{
/* Pop r4-r[4+nnn], [lr]. */
_uw mask;
mask = (0xff0 >> (7 - (op & 7))) & 0xff0;
if (op & 8)
mask |= (1 << R_LR);
if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_CORE, mask, _UVRSD_UINT32,
pid)
!= _UVRSR_OK)
return _URC_FAILURE;
continue;
}
if ((op & 0xf0) == 0xb0)
{
/* op == 0xb0 already handled. */
if (op == 0xb1)
{
op = next_unwind_byte_with_ptrace (uws, pid);
if (op == 0 || ((op & 0xf0) != 0))
/* Spare. */
return _URC_FAILURE;
/* Pop r0-r4 under mask. */
if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_CORE, op,
_UVRSD_UINT32, pid)
!= _UVRSR_OK)
return _URC_FAILURE;
continue;
}
if (op == 0xb2)
{
/* vsp = vsp + 0x204 + (uleb128 << 2). */
int shift;
_Unwind_VRS_Get (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32,
&reg);
op = next_unwind_byte_with_ptrace (uws, pid);
shift = 2;
while (op & 0x80)
{
reg += ((op & 0x7f) << shift);
shift += 7;
op = next_unwind_byte_with_ptrace (uws, pid);
}
reg += ((op & 0x7f) << shift) + 0x204;
_Unwind_VRS_Set (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32,
&reg);
continue;
}
if (op == 0xb3)
{
/* Pop VFP registers with fldmx. */
op = next_unwind_byte_with_ptrace (uws, pid);
op = ((op & 0xf0) << 12) | ((op & 0xf) + 1);
if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_VFP, op, _UVRSD_VFPX,
pid)
!= _UVRSR_OK)
return _URC_FAILURE;
continue;
}
if ((op & 0xfc) == 0xb4)
{
/* Pop FPA E[4]-E[4+nn]. */
op = 0x40000 | ((op & 3) + 1);
if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_FPA, op, _UVRSD_FPAX,
pid)
!= _UVRSR_OK)
return _URC_FAILURE;
continue;
}
/* op & 0xf8 == 0xb8. */
/* Pop VFP D[8]-D[8+nnn] with fldmx. */
op = 0x80000 | ((op & 7) + 1);
if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_VFP, op, _UVRSD_VFPX, pid)
!= _UVRSR_OK)
return _URC_FAILURE;
continue;
}
if ((op & 0xf0) == 0xc0)
{
if (op == 0xc6)
{
/* Pop iWMMXt D registers. */
op = next_unwind_byte_with_ptrace (uws, pid);
op = ((op & 0xf0) << 12) | ((op & 0xf) + 1);
if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_WMMXD, op,
_UVRSD_UINT64, pid)
!= _UVRSR_OK)
return _URC_FAILURE;
continue;
}
if (op == 0xc7)
{
op = next_unwind_byte_with_ptrace (uws, pid);
if (op == 0 || (op & 0xf0) != 0)
/* Spare. */
return _URC_FAILURE;
/* Pop iWMMXt wCGR{3,2,1,0} under mask. */
if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_WMMXC, op,
_UVRSD_UINT32, pid)
!= _UVRSR_OK)
return _URC_FAILURE;
continue;
}
if ((op & 0xf8) == 0xc0)
{
/* Pop iWMMXt wR[10]-wR[10+nnn]. */
op = 0xa0000 | ((op & 0xf) + 1);
if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_WMMXD, op,
_UVRSD_UINT64, pid)
!= _UVRSR_OK)
return _URC_FAILURE;
continue;
}
if (op == 0xc8)
{
#ifndef __VFP_FP__
/* Pop FPA registers. */
op = next_unwind_byte_with_ptrace (uws, pid);
op = ((op & 0xf0) << 12) | ((op & 0xf) + 1);
if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_FPA, op, _UVRSD_FPAX,
pid)
!= _UVRSR_OK)
return _URC_FAILURE;
continue;
#else
/* Pop VFPv3 registers D[16+ssss]-D[16+ssss+cccc] with vldm. */
op = next_unwind_byte_with_ptrace (uws, pid);
op = (((op & 0xf0) + 16) << 12) | ((op & 0xf) + 1);
if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_VFP, op,
_UVRSD_DOUBLE, pid)
!= _UVRSR_OK)
return _URC_FAILURE;
continue;
#endif
}
if (op == 0xc9)
{
/* Pop VFP registers with fldmd. */
op = next_unwind_byte_with_ptrace (uws, pid);
op = ((op & 0xf0) << 12) | ((op & 0xf) + 1);
if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_VFP, op,
_UVRSD_DOUBLE, pid)
!= _UVRSR_OK)
return _URC_FAILURE;
continue;
}
/* Spare. */
return _URC_FAILURE;
}
if ((op & 0xf8) == 0xd0)
{
/* Pop VFP D[8]-D[8+nnn] with fldmd. */
op = 0x80000 | ((op & 7) + 1);
if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_VFP, op, _UVRSD_DOUBLE,
pid)
!= _UVRSR_OK)
return _URC_FAILURE;
continue;
}
/* Spare. */
return _URC_FAILURE;
}
return _URC_OK;
}

618
debuggerd/unwind-arm.c Normal file
View file

@ -0,0 +1,618 @@
/* ARM EABI compliant unwinding routines.
Copyright (C) 2004, 2005 Free Software Foundation, Inc.
Contributed by Paul Brook
This file is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation; either version 2, or (at your option) any
later version.
In addition to the permissions in the GNU General Public License, the
Free Software Foundation gives you unlimited permission to link the
compiled version of this file into combinations with other programs,
and to distribute those combinations without any restriction coming
from the use of this file. (The General Public License restrictions
do apply in other respects; for example, they cover modification of
the file, and distribution when not linked into a combine
executable.)
This file is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; see the file COPYING. If not, write to
the Free Software Foundation, 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA. */
/****************************************************************************
* The functions here are derived from gcc/config/arm/unwind-arm.c from the
* 4.3.x release. The main changes here involve the use of ptrace to retrieve
* memory/processor states from a remote process.
****************************************************************************/
#include <cutils/logd.h>
#include <sys/ptrace.h>
#include <unwind.h>
#include "utility.h"
typedef struct _ZSt9type_info type_info; /* This names C++ type_info type */
void __attribute__((weak)) __cxa_call_unexpected(_Unwind_Control_Block *ucbp);
bool __attribute__((weak)) __cxa_begin_cleanup(_Unwind_Control_Block *ucbp);
bool __attribute__((weak)) __cxa_type_match(_Unwind_Control_Block *ucbp,
const type_info *rttip,
bool is_reference,
void **matched_object);
/* Misc constants. */
#define R_IP 12
#define R_SP 13
#define R_LR 14
#define R_PC 15
#define EXIDX_CANTUNWIND 1
#define uint32_highbit (((_uw) 1) << 31)
#define UCB_FORCED_STOP_FN(ucbp) ((ucbp)->unwinder_cache.reserved1)
#define UCB_PR_ADDR(ucbp) ((ucbp)->unwinder_cache.reserved2)
#define UCB_SAVED_CALLSITE_ADDR(ucbp) ((ucbp)->unwinder_cache.reserved3)
#define UCB_FORCED_STOP_ARG(ucbp) ((ucbp)->unwinder_cache.reserved4)
struct core_regs
{
_uw r[16];
};
/* We use normal integer types here to avoid the compiler generating
coprocessor instructions. */
struct vfp_regs
{
_uw64 d[16];
_uw pad;
};
struct vfpv3_regs
{
/* Always populated via VSTM, so no need for the "pad" field from
vfp_regs (which is used to store the format word for FSTMX). */
_uw64 d[16];
};
struct fpa_reg
{
_uw w[3];
};
struct fpa_regs
{
struct fpa_reg f[8];
};
struct wmmxd_regs
{
_uw64 wd[16];
};
struct wmmxc_regs
{
_uw wc[4];
};
/* Unwind descriptors. */
typedef struct
{
_uw16 length;
_uw16 offset;
} EHT16;
typedef struct
{
_uw length;
_uw offset;
} EHT32;
/* The ABI specifies that the unwind routines may only use core registers,
except when actually manipulating coprocessor state. This allows
us to write one implementation that works on all platforms by
demand-saving coprocessor registers.
During unwinding we hold the coprocessor state in the actual hardware
registers and allocate demand-save areas for use during phase1
unwinding. */
typedef struct
{
/* The first fields must be the same as a phase2_vrs. */
_uw demand_save_flags;
struct core_regs core;
_uw prev_sp; /* Only valid during forced unwinding. */
struct vfp_regs vfp;
struct vfpv3_regs vfp_regs_16_to_31;
struct fpa_regs fpa;
struct wmmxd_regs wmmxd;
struct wmmxc_regs wmmxc;
} phase1_vrs;
/* This must match the structure created by the assembly wrappers. */
typedef struct
{
_uw demand_save_flags;
struct core_regs core;
} phase2_vrs;
/* An exception index table entry. */
typedef struct __EIT_entry
{
_uw fnoffset;
_uw content;
} __EIT_entry;
/* Derived version to use ptrace */
typedef _Unwind_Reason_Code (*personality_routine_with_ptrace)
(_Unwind_State,
_Unwind_Control_Block *,
_Unwind_Context *,
pid_t);
/* Derived version to use ptrace */
/* ABI defined personality routines. */
static _Unwind_Reason_Code unwind_cpp_pr0_with_ptrace (_Unwind_State,
_Unwind_Control_Block *, _Unwind_Context *, pid_t);
static _Unwind_Reason_Code unwind_cpp_pr1_with_ptrace (_Unwind_State,
_Unwind_Control_Block *, _Unwind_Context *, pid_t);
static _Unwind_Reason_Code unwind_cpp_pr2_with_ptrace (_Unwind_State,
_Unwind_Control_Block *, _Unwind_Context *, pid_t);
/* Execute the unwinding instructions described by UWS. */
extern _Unwind_Reason_Code
unwind_execute_with_ptrace(_Unwind_Context * context, __gnu_unwind_state * uws,
pid_t pid);
/* Derived version to use ptrace. Only handles core registers. Disregards
* FP and others.
*/
/* ABI defined function to pop registers off the stack. */
_Unwind_VRS_Result unwind_VRS_Pop_with_ptrace (_Unwind_Context *context,
_Unwind_VRS_RegClass regclass,
_uw discriminator,
_Unwind_VRS_DataRepresentation representation,
pid_t pid)
{
phase1_vrs *vrs = (phase1_vrs *) context;
switch (regclass)
{
case _UVRSC_CORE:
{
_uw *ptr;
_uw mask;
int i;
if (representation != _UVRSD_UINT32)
return _UVRSR_FAILED;
mask = discriminator & 0xffff;
ptr = (_uw *) vrs->core.r[R_SP];
/* Pop the requested registers. */
for (i = 0; i < 16; i++)
{
if (mask & (1 << i)) {
vrs->core.r[i] = get_remote_word(pid, ptr);
ptr++;
}
}
/* Writeback the stack pointer value if it wasn't restored. */
if ((mask & (1 << R_SP)) == 0)
vrs->core.r[R_SP] = (_uw) ptr;
}
return _UVRSR_OK;
default:
return _UVRSR_FAILED;
}
}
/* Core unwinding functions. */
/* Calculate the address encoded by a 31-bit self-relative offset at address
P. */
static inline _uw
selfrel_offset31 (const _uw *p, pid_t pid)
{
_uw offset = get_remote_word(pid, (void*)p);
//offset = *p;
/* Sign extend to 32 bits. */
if (offset & (1 << 30))
offset |= 1u << 31;
else
offset &= ~(1u << 31);
return offset + (_uw) p;
}
/* Perform a binary search for RETURN_ADDRESS in TABLE. The table contains
NREC entries. */
static const __EIT_entry *
search_EIT_table (const __EIT_entry * table, int nrec, _uw return_address,
pid_t pid)
{
_uw next_fn;
_uw this_fn;
int n, left, right;
if (nrec == 0)
return (__EIT_entry *) 0;
left = 0;
right = nrec - 1;
while (1)
{
n = (left + right) / 2;
this_fn = selfrel_offset31 (&table[n].fnoffset, pid);
if (n != nrec - 1)
next_fn = selfrel_offset31 (&table[n + 1].fnoffset, pid) - 1;
else
next_fn = (_uw)0 - 1;
if (return_address < this_fn)
{
if (n == left)
return (__EIT_entry *) 0;
right = n - 1;
}
else if (return_address <= next_fn)
return &table[n];
else
left = n + 1;
}
}
/* Find the exception index table eintry for the given address. */
static const __EIT_entry*
get_eitp(_uw return_address, pid_t pid, mapinfo *map, mapinfo **containing_map)
{
const __EIT_entry *eitp = NULL;
int nrec;
mapinfo *mi;
/* The return address is the address of the instruction following the
call instruction (plus one in thumb mode). If this was the last
instruction in the function the address will lie in the following
function. Subtract 2 from the address so that it points within the call
instruction itself. */
if (return_address >= 2)
return_address -= 2;
for (mi = map; mi != NULL; mi = mi->next) {
if (return_address >= mi->start && return_address <= mi->end) break;
}
if (mi) {
if (containing_map) *containing_map = mi;
eitp = (__EIT_entry *) mi->exidx_start;
nrec = (mi->exidx_end - mi->exidx_start)/sizeof(__EIT_entry);
eitp = search_EIT_table (eitp, nrec, return_address, pid);
}
return eitp;
}
/* Find the exception index table eintry for the given address.
Fill in the relevant fields of the UCB.
Returns _URC_FAILURE if an error occurred, _URC_OK on success. */
static _Unwind_Reason_Code
get_eit_entry (_Unwind_Control_Block *ucbp, _uw return_address, pid_t pid,
mapinfo *map, mapinfo **containing_map)
{
const __EIT_entry *eitp;
eitp = get_eitp(return_address, pid, map, containing_map);
if (!eitp)
{
UCB_PR_ADDR (ucbp) = 0;
return _URC_FAILURE;
}
ucbp->pr_cache.fnstart = selfrel_offset31 (&eitp->fnoffset, pid);
_uw eitp_content = get_remote_word(pid, (void *)&eitp->content);
/* Can this frame be unwound at all? */
if (eitp_content == EXIDX_CANTUNWIND)
{
UCB_PR_ADDR (ucbp) = 0;
return _URC_END_OF_STACK;
}
/* Obtain the address of the "real" __EHT_Header word. */
if (eitp_content & uint32_highbit)
{
/* It is immediate data. */
ucbp->pr_cache.ehtp = (_Unwind_EHT_Header *)&eitp->content;
ucbp->pr_cache.additional = 1;
}
else
{
/* The low 31 bits of the content field are a self-relative
offset to an _Unwind_EHT_Entry structure. */
ucbp->pr_cache.ehtp =
(_Unwind_EHT_Header *) selfrel_offset31 (&eitp->content, pid);
ucbp->pr_cache.additional = 0;
}
/* Discover the personality routine address. */
if (get_remote_word(pid, ucbp->pr_cache.ehtp) & (1u << 31))
{
/* One of the predefined standard routines. */
_uw idx = (get_remote_word(pid, ucbp->pr_cache.ehtp) >> 24) & 0xf;
if (idx == 0)
UCB_PR_ADDR (ucbp) = (_uw) &unwind_cpp_pr0_with_ptrace;
else if (idx == 1)
UCB_PR_ADDR (ucbp) = (_uw) &unwind_cpp_pr1_with_ptrace;
else if (idx == 2)
UCB_PR_ADDR (ucbp) = (_uw) &unwind_cpp_pr2_with_ptrace;
else
{ /* Failed */
UCB_PR_ADDR (ucbp) = 0;
return _URC_FAILURE;
}
}
else
{
/* Execute region offset to PR */
UCB_PR_ADDR (ucbp) = selfrel_offset31 (ucbp->pr_cache.ehtp, pid);
/* Since we are unwinding the stack from a different process, it is
* impossible to execute the personality routine in debuggerd. Punt here.
*/
return _URC_FAILURE;
}
return _URC_OK;
}
/* Print out the current call level, pc, and module name in the crash log */
static _Unwind_Reason_Code log_function(_Unwind_Context *context, int tfd,
int stack_level,
mapinfo *map,
unsigned int sp_list[],
bool at_fault)
{
_uw pc;
phase2_vrs *vrs = (phase2_vrs*) context;
const mapinfo *mi;
bool only_in_tombstone = !at_fault;
if (stack_level < STACK_CONTENT_DEPTH) {
sp_list[stack_level] = vrs->core.r[R_SP];
}
pc = vrs->core.r[R_PC];
// Top level frame
if (stack_level == 0) {
pc &= ~1;
}
// For deeper framers, rollback pc by one instruction
else {
pc = vrs->core.r[R_PC];
// Thumb mode
if (pc & 1) {
pc = (pc & ~1) - 2;
}
else {
pc -= 4;
}
}
mi = pc_to_mapinfo(map, pc);
_LOG(tfd, only_in_tombstone,
" #%02d pc %08x %s\n", stack_level, pc,
mi ? mi->name : "");
return _URC_NO_REASON;
}
/* Derived from __gnu_Unwind_Backtrace to use ptrace */
/* Perform stack backtrace through unwind data. Return the level of stack it
* unwinds.
*/
int unwind_backtrace_with_ptrace(int tfd, pid_t pid, mapinfo *map,
unsigned int sp_list[], int *frame0_pc_sane,
bool at_fault)
{
phase1_vrs saved_vrs;
_Unwind_Reason_Code code = _URC_OK;
struct pt_regs r;
int i;
int stack_level = 0;
_Unwind_Control_Block ucb;
_Unwind_Control_Block *ucbp = &ucb;
if(ptrace(PTRACE_GETREGS, pid, 0, &r)) return 0;
for (i = 0; i < 16; i++) {
saved_vrs.core.r[i] = r.uregs[i];
/*
_LOG(tfd, "r[%d] = 0x%x\n", i, saved_vrs.core.r[i]);
*/
}
/* Set demand-save flags. */
saved_vrs.demand_save_flags = ~(_uw) 0;
/*
* If the app crashes because of calling the weeds, we cannot pass the PC
* to the usual unwinding code as the EXIDX mapping will fail.
* Instead, we simply print out the 0 as the top frame, and resume the
* unwinding process with the value stored in LR.
*/
if (get_eitp(saved_vrs.core.r[R_PC], pid, map, NULL) == NULL) {
*frame0_pc_sane = 0;
log_function ((_Unwind_Context *) &saved_vrs, tfd, stack_level,
map, sp_list, at_fault);
saved_vrs.core.r[R_PC] = saved_vrs.core.r[R_LR];
stack_level++;
}
do {
mapinfo *this_map = NULL;
/* Find the entry for this routine. */
if (get_eit_entry(ucbp, saved_vrs.core.r[R_PC], pid, map, &this_map)
!= _URC_OK) {
/* Uncomment the code below to study why the unwinder failed */
#if 0
/* Shed more debugging info for stack unwinder improvement */
if (this_map) {
_LOG(tfd, 1,
"Relative PC=%#x from %s not contained in EXIDX\n",
saved_vrs.core.r[R_PC] - this_map->start, this_map->name);
}
_LOG(tfd, 1, "PC=%#x SP=%#x\n",
saved_vrs.core.r[R_PC], saved_vrs.core.r[R_SP]);
#endif
code = _URC_FAILURE;
break;
}
/* The dwarf unwinder assumes the context structure holds things
like the function and LSDA pointers. The ARM implementation
caches these in the exception header (UCB). To avoid
rewriting everything we make the virtual IP register point at
the UCB. */
_Unwind_SetGR((_Unwind_Context *)&saved_vrs, 12, (_Unwind_Ptr) ucbp);
/* Call log function. */
if (log_function ((_Unwind_Context *) &saved_vrs, tfd, stack_level,
map, sp_list, at_fault) != _URC_NO_REASON) {
code = _URC_FAILURE;
break;
}
stack_level++;
/* Call the pr to decide what to do. */
code = ((personality_routine_with_ptrace) UCB_PR_ADDR (ucbp))(
_US_VIRTUAL_UNWIND_FRAME | _US_FORCE_UNWIND, ucbp,
(void *) &saved_vrs, pid);
/*
* In theory the unwinding process will stop when the end of stack is
* reached or there is no unwinding information for the code address.
* To add another level of guarantee that the unwinding process
* will terminate we will stop it when the STACK_CONTENT_DEPTH is reached.
*/
} while (code != _URC_END_OF_STACK && code != _URC_FAILURE &&
stack_level < STACK_CONTENT_DEPTH);
return stack_level;
}
/* Derived version to use ptrace */
/* Common implementation for ARM ABI defined personality routines.
ID is the index of the personality routine, other arguments are as defined
by __aeabi_unwind_cpp_pr{0,1,2}. */
static _Unwind_Reason_Code
unwind_pr_common_with_ptrace (_Unwind_State state,
_Unwind_Control_Block *ucbp,
_Unwind_Context *context,
int id,
pid_t pid)
{
__gnu_unwind_state uws;
_uw *data;
int phase2_call_unexpected_after_unwind = 0;
state &= _US_ACTION_MASK;
data = (_uw *) ucbp->pr_cache.ehtp;
uws.data = get_remote_word(pid, data);
data++;
uws.next = data;
if (id == 0)
{
uws.data <<= 8;
uws.words_left = 0;
uws.bytes_left = 3;
}
else
{
uws.words_left = (uws.data >> 16) & 0xff;
uws.data <<= 16;
uws.bytes_left = 2;
data += uws.words_left;
}
/* Restore the saved pointer. */
if (state == _US_UNWIND_FRAME_RESUME)
data = (_uw *) ucbp->cleanup_cache.bitpattern[0];
if ((ucbp->pr_cache.additional & 1) == 0)
{
/* Process descriptors. */
while (get_remote_word(pid, data)) {
/**********************************************************************
* The original code here seems to deal with exceptions that are not
* applicable in our toolchain, thus there is no way to test it for now.
* Instead of leaving it here and causing potential instability in
* debuggerd, we'd better punt here and leave the stack unwound.
* In the future when we discover cases where the stack should be unwound
* further but is not, we can revisit the code here.
**********************************************************************/
return _URC_FAILURE;
}
/* Finished processing this descriptor. */
}
if (unwind_execute_with_ptrace (context, &uws, pid) != _URC_OK)
return _URC_FAILURE;
if (phase2_call_unexpected_after_unwind)
{
/* Enter __cxa_unexpected as if called from the call site. */
_Unwind_SetGR (context, R_LR, _Unwind_GetGR (context, R_PC));
_Unwind_SetGR (context, R_PC, (_uw) &__cxa_call_unexpected);
return _URC_INSTALL_CONTEXT;
}
return _URC_CONTINUE_UNWIND;
}
/* ABI defined personality routine entry points. */
static _Unwind_Reason_Code
unwind_cpp_pr0_with_ptrace (_Unwind_State state,
_Unwind_Control_Block *ucbp,
_Unwind_Context *context,
pid_t pid)
{
return unwind_pr_common_with_ptrace (state, ucbp, context, 0, pid);
}
static _Unwind_Reason_Code
unwind_cpp_pr1_with_ptrace (_Unwind_State state,
_Unwind_Control_Block *ucbp,
_Unwind_Context *context,
pid_t pid)
{
return unwind_pr_common_with_ptrace (state, ucbp, context, 1, pid);
}
static _Unwind_Reason_Code
unwind_cpp_pr2_with_ptrace (_Unwind_State state,
_Unwind_Control_Block *ucbp,
_Unwind_Context *context,
pid_t pid)
{
return unwind_pr_common_with_ptrace (state, ucbp, context, 2, pid);
}

78
debuggerd/utility.c Normal file
View file

@ -0,0 +1,78 @@
/* system/debuggerd/utility.c
**
** Copyright 2008, 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 <sys/ptrace.h>
#include <sys/exec_elf.h>
#include <assert.h>
#include <errno.h>
#include "utility.h"
/* Get a word from pid using ptrace. The result is the return value. */
int get_remote_word(int pid, void *src)
{
return ptrace(PTRACE_PEEKTEXT, pid, src, NULL);
}
/* Handy routine to read aggregated data from pid using ptrace. The read
* values are written to the dest locations directly.
*/
void get_remote_struct(int pid, void *src, void *dst, size_t size)
{
unsigned int i;
for (i = 0; i+4 <= size; i+=4) {
*(int *)(dst+i) = ptrace(PTRACE_PEEKTEXT, pid, src+i, NULL);
}
if (i < size) {
int val;
assert((size - i) < 4);
val = ptrace(PTRACE_PEEKTEXT, pid, src+i, NULL);
while (i < size) {
((unsigned char *)dst)[i] = val & 0xff;
i++;
val >>= 8;
}
}
}
/* Map a pc address to the name of the containing ELF file */
const char *map_to_name(mapinfo *mi, unsigned pc, const char* def)
{
while(mi) {
if((pc >= mi->start) && (pc < mi->end)){
return mi->name;
}
mi = mi->next;
}
return def;
}
/* Find the containing map info for the pc */
const mapinfo *pc_to_mapinfo(mapinfo *mi, unsigned pc)
{
while(mi) {
if((pc >= mi->start) && (pc < mi->end)){
return mi;
}
mi = mi->next;
}
return NULL;
}

56
debuggerd/utility.h Normal file
View file

@ -0,0 +1,56 @@
/* system/debuggerd/utility.h
**
** Copyright 2008, 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 __utility_h
#define __utility_h
#include <stddef.h>
#include <stdbool.h>
#ifndef PT_ARM_EXIDX
#define PT_ARM_EXIDX 0x70000001 /* .ARM.exidx segment */
#endif
#define STACK_CONTENT_DEPTH 32
typedef struct mapinfo {
struct mapinfo *next;
unsigned start;
unsigned end;
unsigned exidx_start;
unsigned exidx_end;
char name[];
} mapinfo;
/* Get a word from pid using ptrace. The result is the return value. */
extern int get_remote_word(int pid, void *src);
/* Handy routine to read aggregated data from pid using ptrace. The read
* values are written to the dest locations directly.
*/
extern void get_remote_struct(int pid, void *src, void *dst, size_t size);
/* Find the containing map for the pc */
const mapinfo *pc_to_mapinfo (mapinfo *mi, unsigned pc);
/* Map a pc address to the name of the containing ELF file */
const char *map_to_name(mapinfo *mi, unsigned pc, const char* def);
/* Log information onto the tombstone */
extern void _LOG(int tfd, bool in_tombstone_only, const char *fmt, ...);
#endif

57
fastboot/Android.mk Normal file
View file

@ -0,0 +1,57 @@
# Copyright (C) 2007 Google Inc.
#
# 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.
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_C_INCLUDES := $(LOCAL_PATH)/../mkbootimg
LOCAL_SRC_FILES := protocol.c engine.c bootimg.c fastboot.c
LOCAL_MODULE := fastboot
ifeq ($(HOST_OS),linux)
LOCAL_SRC_FILES += usb_linux.c util_linux.c
endif
ifeq ($(HOST_OS),darwin)
LOCAL_SRC_FILES += usb_osx.c util_osx.c
LOCAL_LDLIBS += -lpthread -framework CoreFoundation -framework IOKit \
-framework Carbon
endif
ifeq ($(HOST_OS),windows)
LOCAL_SRC_FILES += usb_windows.c util_windows.c
EXTRA_STATIC_LIBS := AdbWinApi
LOCAL_C_INCLUDES += /usr/include/w32api/ddk development/host/windows/usb/api
ifeq ($(strip $(USE_CYGWIN)),)
LOCAL_LDLIBS += -lws2_32
USE_SYSDEPS_WIN32 := 1
endif
endif
LOCAL_STATIC_LIBRARIES := $(EXTRA_STATIC_LIBS) libzipfile libunz
include $(BUILD_HOST_EXECUTABLE)
$(call dist-for-goals,user userdebug droid,$(LOCAL_BUILT_MODULE))
ifeq ($(HOST_OS),linux)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := usbtest.c usb_linux.c
LOCAL_MODULE := usbtest
include $(BUILD_HOST_EXECUTABLE)
endif
ifeq ($(HOST_OS),windows)
$(LOCAL_INSTALLED_MODULE): $(HOST_OUT_EXECUTABLES)/AdbWinApi.dll
endif

85
fastboot/bootimg.c Normal file
View file

@ -0,0 +1,85 @@
/*
* Copyright (C) 2008 The Android Open Source Project
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <bootimg.h>
void bootimg_set_cmdline(boot_img_hdr *h, const char *cmdline)
{
strcpy((char*) h->cmdline, cmdline);
}
boot_img_hdr *mkbootimg(void *kernel, unsigned kernel_size,
void *ramdisk, unsigned ramdisk_size,
void *second, unsigned second_size,
unsigned page_size,
unsigned *bootimg_size)
{
unsigned kernel_actual;
unsigned ramdisk_actual;
unsigned second_actual;
unsigned page_mask;
boot_img_hdr *hdr;
page_mask = page_size - 1;
kernel_actual = (kernel_size + page_mask) & (~page_mask);
ramdisk_actual = (ramdisk_size + page_mask) & (~page_mask);
second_actual = (second_size + page_mask) & (~page_mask);
*bootimg_size = page_size + kernel_actual + ramdisk_actual + second_actual;
hdr = calloc(*bootimg_size, 1);
if(hdr == 0) {
return hdr;
}
memcpy(hdr->magic, BOOT_MAGIC, BOOT_MAGIC_SIZE);
hdr->kernel_size = kernel_size;
hdr->kernel_addr = 0x10008000;
hdr->ramdisk_size = ramdisk_size;
hdr->ramdisk_addr = 0x11000000;
hdr->second_size = second_size;
hdr->second_addr = 0x10F00000;
hdr->tags_addr = 0x10000100;
hdr->page_size = page_size;
memcpy(hdr->magic + page_size,
kernel, kernel_size);
memcpy(hdr->magic + page_size + kernel_actual,
ramdisk, ramdisk_size);
memcpy(hdr->magic + page_size + kernel_actual + ramdisk_actual,
second, second_size);
return hdr;
}

289
fastboot/engine.c Normal file
View file

@ -0,0 +1,289 @@
/*
* Copyright (C) 2008 The Android Open Source Project
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include "fastboot.h"
char *mkmsg(const char *fmt, ...)
{
char buf[256];
char *s;
va_list ap;
va_start(ap, fmt);
vsprintf(buf, fmt, ap);
va_end(ap);
s = strdup(buf);
if (s == 0) die("out of memory");
return s;
}
#define OP_DOWNLOAD 1
#define OP_COMMAND 2
#define OP_QUERY 3
#define OP_NOTICE 4
typedef struct Action Action;
struct Action
{
unsigned op;
Action *next;
char cmd[64];
void *data;
unsigned size;
const char *msg;
int (*func)(Action *a, int status, char *resp);
};
static Action *action_list = 0;
static Action *action_last = 0;
static int cb_default(Action *a, int status, char *resp)
{
if (status) {
fprintf(stderr,"FAILED (%s)\n", resp);
} else {
fprintf(stderr,"OKAY\n");
}
return status;
}
static Action *queue_action(unsigned op, const char *fmt, ...)
{
Action *a;
va_list ap;
a = calloc(1, sizeof(Action));
if (a == 0) die("out of memory");
va_start(ap, fmt);
vsprintf(a->cmd, fmt, ap);
va_end(ap);
if (action_last) {
action_last->next = a;
} else {
action_list = a;
}
action_last = a;
a->op = op;
a->func = cb_default;
return a;
}
void fb_queue_erase(const char *ptn)
{
Action *a;
a = queue_action(OP_COMMAND, "erase:%s", ptn);
a->msg = mkmsg("erasing '%s'", ptn);
}
void fb_queue_flash(const char *ptn, void *data, unsigned sz)
{
Action *a;
a = queue_action(OP_DOWNLOAD, "");
a->data = data;
a->size = sz;
a->msg = mkmsg("sending '%s' (%d KB)", ptn, sz / 1024);
a = queue_action(OP_COMMAND, "flash:%s", ptn);
a->msg = mkmsg("writing '%s'", ptn);
}
static int match(char *str, const char **value, unsigned count)
{
const char *val;
unsigned n;
int len;
for (n = 0; n < count; n++) {
const char *val = value[n];
int len = strlen(val);
int match;
if ((len > 1) && (val[len-1] == '*')) {
len--;
match = !strncmp(val, str, len);
} else {
match = !strcmp(val, str);
}
if (match) return 1;
}
return 0;
}
static int cb_check(Action *a, int status, char *resp, int invert)
{
const char **value = a->data;
unsigned count = a->size;
unsigned n;
int yes;
if (status) {
fprintf(stderr,"FAILED (%s)\n", resp);
return status;
}
yes = match(resp, value, count);
if (invert) yes = !yes;
if (yes) {
fprintf(stderr,"OKAY\n");
return 0;
}
fprintf(stderr,"FAILED\n\n");
fprintf(stderr,"Device %s is '%s'.\n", a->cmd + 7, resp);
fprintf(stderr,"Update %s '%s'",
invert ? "rejects" : "requires", value[0]);
for (n = 1; n < count; n++) {
fprintf(stderr," or '%s'", value[n]);
}
fprintf(stderr,".\n\n");
return -1;
}
static int cb_require(Action *a, int status, char *resp)
{
return cb_check(a, status, resp, 0);
}
static int cb_reject(Action *a, int status, char *resp)
{
return cb_check(a, status, resp, 1);
}
void fb_queue_require(const char *var, int invert, unsigned nvalues, const char **value)
{
Action *a;
a = queue_action(OP_QUERY, "getvar:%s", var);
a->data = value;
a->size = nvalues;
a->msg = mkmsg("checking %s", var);
a->func = invert ? cb_reject : cb_require;
if (a->data == 0) die("out of memory");
}
static int cb_display(Action *a, int status, char *resp)
{
if (status) {
fprintf(stderr, "%s FAILED (%s)\n", a->cmd, resp);
return status;
}
fprintf(stderr, "%s: %s\n", (char*) a->data, resp);
return 0;
}
void fb_queue_display(const char *var, const char *prettyname)
{
Action *a;
a = queue_action(OP_QUERY, "getvar:%s", var);
a->data = strdup(prettyname);
if (a->data == 0) die("out of memory");
a->func = cb_display;
}
static int cb_do_nothing(Action *a, int status, char *resp)
{
fprintf(stderr,"\n");
return 0;
}
void fb_queue_reboot(void)
{
Action *a = queue_action(OP_COMMAND, "reboot");
a->func = cb_do_nothing;
a->msg = "rebooting";
}
void fb_queue_command(const char *cmd, const char *msg)
{
Action *a = queue_action(OP_COMMAND, cmd);
a->msg = msg;
}
void fb_queue_download(const char *name, void *data, unsigned size)
{
Action *a = queue_action(OP_DOWNLOAD, "");
a->data = data;
a->size = size;
a->msg = mkmsg("downloading '%s'", name);
}
void fb_queue_notice(const char *notice)
{
Action *a = queue_action(OP_NOTICE, "");
a->data = (void*) notice;
}
void fb_execute_queue(usb_handle *usb)
{
Action *a;
char resp[FB_RESPONSE_SZ+1];
int status;
a = action_list;
resp[FB_RESPONSE_SZ] = 0;
for (a = action_list; a; a = a->next) {
if (a->msg) {
fprintf(stderr,"%s... ",a->msg);
}
if (a->op == OP_DOWNLOAD) {
status = fb_download_data(usb, a->data, a->size);
status = a->func(a, status, status ? fb_get_error() : "");
if (status) break;
} else if (a->op == OP_COMMAND) {
status = fb_command(usb, a->cmd);
status = a->func(a, status, status ? fb_get_error() : "");
if (status) break;
} else if (a->op == OP_QUERY) {
status = fb_command_response(usb, a->cmd, resp);
status = a->func(a, status, status ? fb_get_error() : resp);
if (status) break;
} else if (a->op == OP_NOTICE) {
fprintf(stderr,"%s\n",(char*)a->data);
} else {
die("bogus action");
}
}
}

Binary file not shown.

657
fastboot/fastboot.c Normal file
View file

@ -0,0 +1,657 @@
/*
* Copyright (C) 2008 The Android Open Source Project
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <limits.h>
#include <ctype.h>
#include <sys/time.h>
#include <bootimg.h>
#include <zipfile/zipfile.h>
#include "fastboot.h"
static usb_handle *usb = 0;
static const char *serial = 0;
static const char *product = 0;
static const char *cmdline = 0;
static int wipe_data = 0;
void die(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 get_my_path(char *path);
char *find_item(const char *item, const char *product)
{
char *dir;
char *fn;
char path[PATH_MAX + 128];
if(!strcmp(item,"boot")) {
fn = "boot.img";
} else if(!strcmp(item,"recovery")) {
fn = "recovery.img";
} else if(!strcmp(item,"system")) {
fn = "system.img";
} else if(!strcmp(item,"userdata")) {
fn = "userdata.img";
} else if(!strcmp(item,"info")) {
fn = "android-info.txt";
} else {
fprintf(stderr,"unknown partition '%s'\n", item);
return 0;
}
if(product) {
get_my_path(path);
sprintf(path + strlen(path),
"../../../target/product/%s/%s", product, fn);
return strdup(path);
}
dir = getenv("ANDROID_PRODUCT_OUT");
if((dir == 0) || (dir[0] == 0)) {
die("neither -p product specified nor ANDROID_PRODUCT_OUT set");
return 0;
}
sprintf(path, "%s/%s", dir, fn);
return strdup(path);
}
#ifdef _WIN32
void *load_file(const char *fn, unsigned *_sz);
#else
void *load_file(const char *fn, unsigned *_sz)
{
char *data;
int sz;
int fd;
data = 0;
fd = open(fn, O_RDONLY);
if(fd < 0) return 0;
sz = lseek(fd, 0, SEEK_END);
if(sz < 0) goto oops;
if(lseek(fd, 0, SEEK_SET) != 0) goto oops;
data = (char*) malloc(sz);
if(data == 0) goto oops;
if(read(fd, data, sz) != sz) goto oops;
close(fd);
if(_sz) *_sz = sz;
return data;
oops:
close(fd);
if(data != 0) free(data);
return 0;
}
#endif
int match_fastboot(usb_ifc_info *info)
{
if((info->dev_vendor != 0x18d1) &&
(info->dev_vendor != 0x0bb4)) return -1;
if(info->ifc_class != 0xff) return -1;
if(info->ifc_subclass != 0x42) return -1;
if(info->ifc_protocol != 0x03) return -1;
// require matching serial number if a serial number is specified
// at the command line with the -s option.
if (serial && strcmp(serial, info->serial_number) != 0) return -1;
return 0;
}
int list_devices_callback(usb_ifc_info *info)
{
if (match_fastboot(info) == 0) {
char* serial = info->serial_number;
if (!serial[0]) {
serial = "????????????";
}
// output compatible with "adb devices"
printf("%s\tfastboot\n", serial);
}
return -1;
}
usb_handle *open_device(void)
{
static usb_handle *usb = 0;
int announce = 1;
if(usb) return usb;
for(;;) {
usb = usb_open(match_fastboot);
if(usb) return usb;
if(announce) {
announce = 0;
fprintf(stderr,"< waiting for device >\n");
}
sleep(1);
}
}
void list_devices(void) {
// We don't actually open a USB device here,
// just getting our callback called so we can
// list all the connected devices.
usb_open(list_devices_callback);
}
void usage(void)
{
fprintf(stderr,
/* 1234567890123456789012345678901234567890123456789012345678901234567890123456 */
"usage: fastboot [ <option> ] <command>\n"
"\n"
"commands:\n"
" update <filename> reflash device from update.zip\n"
" flashall 'flash boot' + 'flash system'\n"
" flash <partition> [ <filename> ] write a file to a flash partition\n"
" erase <partition> erase a flash partition\n"
" getvar <variable> display a bootloader variable\n"
" boot <kernel> [ <ramdisk> ] download and boot kernel\n"
" flash:raw boot <kernel> [ <ramdisk> ] create bootimage and flash it\n"
" devices list all connected devices\n"
" reboot reboot device normally\n"
" reboot-bootloader reboot device into bootloader\n"
"\n"
"options:\n"
" -w erase userdata and cache\n"
" -s <serial number> specify device serial number\n"
" -p <product> specify product name\n"
" -c <cmdline> override kernel commandline\n"
);
exit(1);
}
void *load_bootable_image(const char *kernel, const char *ramdisk,
unsigned *sz, const char *cmdline)
{
void *kdata = 0, *rdata = 0;
unsigned ksize = 0, rsize = 0;
void *bdata;
unsigned bsize;
if(kernel == 0) {
fprintf(stderr, "no image specified\n");
return 0;
}
kdata = load_file(kernel, &ksize);
if(kdata == 0) {
fprintf(stderr, "cannot load '%s'\n", kernel);
return 0;
}
/* is this actually a boot image? */
if(!memcmp(kdata, BOOT_MAGIC, BOOT_MAGIC_SIZE)) {
if(cmdline) bootimg_set_cmdline((boot_img_hdr*) kdata, cmdline);
if(ramdisk) {
fprintf(stderr, "cannot boot a boot.img *and* ramdisk\n");
return 0;
}
*sz = ksize;
return kdata;
}
if(ramdisk) {
rdata = load_file(ramdisk, &rsize);
if(rdata == 0) {
fprintf(stderr,"cannot load '%s'\n", ramdisk);
return 0;
}
}
fprintf(stderr,"creating boot image...\n");
bdata = mkbootimg(kdata, ksize, rdata, rsize, 0, 0, 2048, &bsize);
if(bdata == 0) {
fprintf(stderr,"failed to create boot.img\n");
return 0;
}
if(cmdline) bootimg_set_cmdline((boot_img_hdr*) bdata, cmdline);
fprintf(stderr,"creating boot image - %d bytes\n", bsize);
*sz = bsize;
return bdata;
}
void *unzip_file(zipfile_t zip, const char *name, unsigned *sz)
{
void *data;
zipentry_t entry;
unsigned datasz;
entry = lookup_zipentry(zip, name);
if (entry == NULL) {
fprintf(stderr, "archive does not contain '%s'\n", name);
return 0;
}
*sz = get_zipentry_size(entry);
datasz = *sz * 1.001;
data = malloc(datasz);
if(data == 0) {
fprintf(stderr, "failed to allocate %d bytes\n", *sz);
return 0;
}
if (decompress_zipentry(entry, data, datasz)) {
fprintf(stderr, "failed to unzip '%s' from archive\n", name);
free(data);
return 0;
}
return data;
}
static char *strip(char *s)
{
int n;
while(*s && isspace(*s)) s++;
n = strlen(s);
while(n-- > 0) {
if(!isspace(s[n])) break;
s[n] = 0;
}
return s;
}
#define MAX_OPTIONS 32
static int setup_requirement_line(char *name)
{
char *val[MAX_OPTIONS];
const char **out;
unsigned n, count;
char *x;
int invert = 0;
if (!strncmp(name, "reject ", 7)) {
name += 7;
invert = 1;
} else if (!strncmp(name, "require ", 8)) {
name += 8;
invert = 0;
}
x = strchr(name, '=');
if (x == 0) return 0;
*x = 0;
val[0] = x + 1;
for(count = 1; count < MAX_OPTIONS; count++) {
x = strchr(val[count - 1],'|');
if (x == 0) break;
*x = 0;
val[count] = x + 1;
}
name = strip(name);
for(n = 0; n < count; n++) val[n] = strip(val[n]);
name = strip(name);
if (name == 0) return -1;
/* work around an unfortunate name mismatch */
if (!strcmp(name,"board")) name = "product";
out = malloc(sizeof(char*) * count);
if (out == 0) return -1;
for(n = 0; n < count; n++) {
out[n] = strdup(strip(val[n]));
if (out[n] == 0) return -1;
}
fb_queue_require(name, invert, n, out);
return 0;
}
static void setup_requirements(char *data, unsigned sz)
{
char *s;
s = data;
while (sz-- > 0) {
if(*s == '\n') {
*s++ = 0;
if (setup_requirement_line(data)) {
die("out of memory");
}
data = s;
} else {
s++;
}
}
}
void queue_info_dump(void)
{
fb_queue_notice("--------------------------------------------");
fb_queue_display("version-bootloader", "Bootloader Version...");
fb_queue_display("version-baseband", "Baseband Version.....");
fb_queue_display("serialno", "Serial Number........");
fb_queue_notice("--------------------------------------------");
}
void do_update_signature(zipfile_t zip, char *fn)
{
void *data;
unsigned sz;
data = unzip_file(zip, fn, &sz);
if (data == 0) return;
fb_queue_download("signature", data, sz);
fb_queue_command("signature", "installing signature");
}
void do_update(char *fn)
{
void *zdata;
unsigned zsize;
void *data;
unsigned sz;
zipfile_t zip;
queue_info_dump();
zdata = load_file(fn, &zsize);
if (zdata == 0) die("failed to load '%s'", fn);
zip = init_zipfile(zdata, zsize);
if(zip == 0) die("failed to access zipdata in '%s'");
data = unzip_file(zip, "android-info.txt", &sz);
if (data == 0) {
char *tmp;
/* fallback for older zipfiles */
data = unzip_file(zip, "android-product.txt", &sz);
if ((data == 0) || (sz < 1)) {
die("update package has no android-info.txt or android-product.txt");
}
tmp = malloc(sz + 128);
if (tmp == 0) die("out of memory");
sprintf(tmp,"board=%sversion-baseband=0.66.04.19\n",(char*)data);
data = tmp;
sz = strlen(tmp);
}
setup_requirements(data, sz);
data = unzip_file(zip, "boot.img", &sz);
if (data == 0) die("update package missing boot.img");
do_update_signature(zip, "boot.sig");
fb_queue_flash("boot", data, sz);
data = unzip_file(zip, "recovery.img", &sz);
if (data != 0) {
do_update_signature(zip, "recovery.sig");
fb_queue_flash("recovery", data, sz);
}
data = unzip_file(zip, "system.img", &sz);
if (data == 0) die("update package missing system.img");
do_update_signature(zip, "system.sig");
fb_queue_flash("system", data, sz);
}
void do_send_signature(char *fn)
{
void *data;
unsigned sz;
char *xtn;
xtn = strrchr(fn, '.');
if (!xtn) return;
if (strcmp(xtn, ".img")) return;
strcpy(xtn,".sig");
data = load_file(fn, &sz);
strcpy(xtn,".img");
if (data == 0) return;
fb_queue_download("signature", data, sz);
fb_queue_command("signature", "installing signature");
}
void do_flashall(void)
{
char *fname;
void *data;
unsigned sz;
queue_info_dump();
fname = find_item("info", product);
if (fname == 0) die("cannot find android-info.txt");
data = load_file(fname, &sz);
if (data == 0) die("could not load android-info.txt");
setup_requirements(data, sz);
fname = find_item("boot", product);
data = load_file(fname, &sz);
if (data == 0) die("could not load boot.img");
do_send_signature(fname);
fb_queue_flash("boot", data, sz);
fname = find_item("recovery", product);
data = load_file(fname, &sz);
if (data != 0) {
do_send_signature(fname);
fb_queue_flash("recovery", data, sz);
}
fname = find_item("system", product);
data = load_file(fname, &sz);
if (data == 0) die("could not load system.img");
do_send_signature(fname);
fb_queue_flash("system", data, sz);
}
#define skip(n) do { argc -= (n); argv += (n); } while (0)
#define require(n) do { if (argc < (n)) usage(); } while (0)
int do_oem_command(int argc, char **argv)
{
int i;
char command[256];
if (argc <= 1) return 0;
command[0] = 0;
while(1) {
strcat(command,*argv);
skip(1);
if(argc == 0) break;
strcat(command," ");
}
fb_queue_command(command,"");
return 0;
}
int main(int argc, char **argv)
{
int wants_wipe = 0;
int wants_reboot = 0;
int wants_reboot_bootloader = 0;
void *data;
unsigned sz;
skip(1);
if (argc == 0) {
usage();
return 0;
}
if (!strcmp(*argv, "devices")) {
list_devices();
return 0;
}
while (argc > 0) {
if(!strcmp(*argv, "-w")) {
wants_wipe = 1;
skip(1);
} else if(!strcmp(*argv, "-s")) {
require(2);
serial = argv[1];
skip(2);
} else if(!strcmp(*argv, "-p")) {
require(2);
product = argv[1];
skip(2);
} else if(!strcmp(*argv, "-c")) {
require(2);
cmdline = argv[1];
skip(2);
} else if(!strcmp(*argv, "getvar")) {
require(2);
fb_queue_display(argv[1], argv[1]);
skip(2);
} else if(!strcmp(*argv, "erase")) {
require(2);
fb_queue_erase(argv[1]);
skip(2);
} else if(!strcmp(*argv, "signature")) {
require(2);
data = load_file(argv[1], &sz);
if (data == 0) die("could not load '%s'", argv[1]);
if (sz != 256) die("signature must be 256 bytes");
fb_queue_download("signature", data, sz);
fb_queue_command("signature", "installing signature");
skip(2);
} else if(!strcmp(*argv, "reboot")) {
wants_reboot = 1;
skip(1);
} else if(!strcmp(*argv, "reboot-bootloader")) {
wants_reboot_bootloader = 1;
skip(1);
} else if(!strcmp(*argv, "boot")) {
char *kname = 0;
char *rname = 0;
skip(1);
if (argc > 0) {
kname = argv[0];
skip(1);
}
if (argc > 0) {
rname = argv[0];
skip(1);
}
data = load_bootable_image(kname, rname, &sz, cmdline);
if (data == 0) return 1;
fb_queue_download("boot.img", data, sz);
fb_queue_command("boot", "booting");
} else if(!strcmp(*argv, "flash")) {
char *pname = argv[1];
char *fname = 0;
require(2);
if (argc > 2) {
fname = argv[2];
skip(3);
} else {
fname = find_item(pname, product);
skip(2);
}
if (fname == 0) die("cannot determine image filename for '%s'", pname);
data = load_file(fname, &sz);
if (data == 0) die("cannot load '%s'\n", fname);
fb_queue_flash(pname, data, sz);
} else if(!strcmp(*argv, "flash:raw")) {
char *pname = argv[1];
char *kname = argv[2];
char *rname = 0;
require(3);
if(argc > 3) {
rname = argv[3];
skip(4);
} else {
skip(3);
}
data = load_bootable_image(kname, rname, &sz, cmdline);
if (data == 0) die("cannot load bootable image");
fb_queue_flash(pname, data, sz);
} else if(!strcmp(*argv, "flashall")) {
skip(1);
do_flashall();
wants_reboot = 1;
} else if(!strcmp(*argv, "update")) {
if (argc > 1) {
do_update(argv[1]);
skip(2);
} else {
do_update("update.zip");
skip(1);
}
wants_reboot = 1;
} else if(!strcmp(*argv, "oem")) {
argc = do_oem_command(argc, argv);
} else {
usage();
}
}
if (wants_wipe) {
fb_queue_erase("userdata");
fb_queue_erase("cache");
}
if (wants_reboot) {
fb_queue_reboot();
} else if (wants_reboot_bootloader) {
fb_queue_command("reboot-bootloader", "rebooting into bootloader");
}
usb = open_device();
fb_execute_queue(usb);
return 0;
}

57
fastboot/fastboot.h Normal file
View file

@ -0,0 +1,57 @@
/*
* Copyright (C) 2008 The Android Open Source Project
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef _FASTBOOT_H_
#define _FASTBOOT_H_
#include "usb.h"
/* protocol.c - fastboot protocol */
int fb_command(usb_handle *usb, const char *cmd);
int fb_command_response(usb_handle *usb, const char *cmd, char *response);
int fb_download_data(usb_handle *usb, const void *data, unsigned size);
char *fb_get_error(void);
#define FB_COMMAND_SZ 64
#define FB_RESPONSE_SZ 64
/* engine.c - high level command queue engine */
void fb_queue_flash(const char *ptn, void *data, unsigned sz);;
void fb_queue_erase(const char *ptn);
void fb_queue_require(const char *var, int invert, unsigned nvalues, const char **value);
void fb_queue_display(const char *var, const char *prettyname);
void fb_queue_reboot(void);
void fb_queue_command(const char *cmd, const char *msg);
void fb_queue_download(const char *name, void *data, unsigned size);
void fb_queue_notice(const char *notice);
void fb_execute_queue(usb_handle *usb);
/* util stuff */
void die(const char *fmt, ...);
#endif

25
fastboot/genkey.sh Executable file
View file

@ -0,0 +1,25 @@
#!/bin/bash
if [ $# -ne 2 ]
then
echo "Usage: $0 alias \"pass phrase\""
exit -1
fi
# Generate a 2048 bit RSA key with public exponent 3.
# Encrypt private key with provided password.
openssl genrsa -3 -out $1.pem -passout pass:"$2" 2048
# Create a self-signed cert for this key.
openssl req -new -x509 -key $1.pem -passin pass:"$2" \
-out $1-cert.pem \
-batch -days 10000
# Create a PKCS12 store containing the generated private key.
# Protect the keystore and the private key with the provided password.
openssl pkcs12 -export -in $1-cert.pem -inkey $1.pem -passin pass:"$2" \
-out $1.p12 -name $1 -passout pass:"$2"
rm $1.pem
rm $1-cert.pem

9
fastboot/p12topem.sh Executable file
View file

@ -0,0 +1,9 @@
#!/bin/bash
if [ $# -ne 2 ]
then
echo "Usage: $0 alias passphrase"
exit -1
fi
openssl pkcs12 -passin pass:"$2" -passout pass:"$2" -in $1.p12 -out $1.pem

181
fastboot/protocol.c Normal file
View file

@ -0,0 +1,181 @@
/*
* Copyright (C) 2008 The Android Open Source Project
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "fastboot.h"
static char ERROR[128];
char *fb_get_error(void)
{
return ERROR;
}
static int check_response(usb_handle *usb, unsigned size,
unsigned data_okay, char *response)
{
unsigned char status[65];
int r;
for(;;) {
r = usb_read(usb, status, 64);
if(r < 0) {
sprintf(ERROR, "status read failed (%s)", strerror(errno));
usb_close(usb);
return -1;
}
status[r] = 0;
if(r < 4) {
sprintf(ERROR, "status malformed (%d bytes)", r);
usb_close(usb);
return -1;
}
if(!memcmp(status, "INFO", 4)) {
fprintf(stderr,"%s\n", status);
continue;
}
if(!memcmp(status, "OKAY", 4)) {
if(response) {
strcpy(response, (char*) status + 4);
}
return 0;
}
if(!memcmp(status, "FAIL", 4)) {
if(r > 4) {
sprintf(ERROR, "remote: %s", status + 4);
} else {
strcpy(ERROR, "remote failure");
}
return -1;
}
if(!memcmp(status, "DATA", 4) && data_okay){
unsigned dsize = strtoul((char*) status + 4, 0, 16);
if(dsize > size) {
strcpy(ERROR, "data size too large");
usb_close(usb);
return -1;
}
return dsize;
}
strcpy(ERROR,"unknown status code");
usb_close(usb);
break;
}
return -1;
}
static int _command_send(usb_handle *usb, const char *cmd,
const void *data, unsigned size,
char *response)
{
int cmdsize = strlen(cmd);
int r;
if(response) {
response[0] = 0;
}
if(cmdsize > 64) {
sprintf(ERROR,"command too large");
return -1;
}
if(usb_write(usb, cmd, cmdsize) != cmdsize) {
sprintf(ERROR,"command write failed (%s)", strerror(errno));
usb_close(usb);
return -1;
}
if(data == 0) {
return check_response(usb, size, 0, response);
}
r = check_response(usb, size, 1, 0);
if(r < 0) {
return -1;
}
size = r;
if(size) {
r = usb_write(usb, data, size);
if(r < 0) {
sprintf(ERROR, "data transfer failure (%s)", strerror(errno));
usb_close(usb);
return -1;
}
if(r != ((int) size)) {
sprintf(ERROR, "data transfer failure (short transfer)");
usb_close(usb);
return -1;
}
}
r = check_response(usb, 0, 0, 0);
if(r < 0) {
return -1;
} else {
return size;
}
}
int fb_command(usb_handle *usb, const char *cmd)
{
return _command_send(usb, cmd, 0, 0, 0);
}
int fb_command_response(usb_handle *usb, const char *cmd, char *response)
{
return _command_send(usb, cmd, 0, 0, response);
}
int fb_download_data(usb_handle *usb, const void *data, unsigned size)
{
char cmd[64];
int r;
sprintf(cmd, "download:%08x", size);
r = _command_send(usb, cmd, data, size, 0);
if(r < 0) {
return -1;
} else {
return 0;
}
}

10
fastboot/signfile.sh Executable file
View file

@ -0,0 +1,10 @@
#!/bin/bash
if [ $# -ne 3 ]
then
echo "Usage: $0 alias filename passpharse"
exit -1
fi
openssl dgst -passin pass:"$3" -binary -sha1 -sign $1.pem $2 > $2.sign

64
fastboot/usb.h Normal file
View file

@ -0,0 +1,64 @@
/*
* Copyright (C) 2008 The Android Open Source Project
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef _USB_H_
#define _USB_H_
typedef struct usb_handle usb_handle;
typedef struct usb_ifc_info usb_ifc_info;
struct usb_ifc_info
{
/* from device descriptor */
unsigned short dev_vendor;
unsigned short dev_product;
unsigned char dev_class;
unsigned char dev_subclass;
unsigned char dev_protocol;
unsigned char ifc_class;
unsigned char ifc_subclass;
unsigned char ifc_protocol;
unsigned char has_bulk_in;
unsigned char has_bulk_out;
char serial_number[256];
};
typedef int (*ifc_match_func)(usb_ifc_info *ifc);
usb_handle *usb_open(ifc_match_func callback);
int usb_close(usb_handle *h);
int usb_read(usb_handle *h, void *_data, int len);
int usb_write(usb_handle *h, const void *_data, int len);
#endif

373
fastboot/usb_linux.c Normal file
View file

@ -0,0 +1,373 @@
/*
* Copyright (C) 2008 The Android Open Source Project
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <dirent.h>
#include <fcntl.h>
#include <errno.h>
#include <pthread.h>
#include <ctype.h>
#include <linux/usbdevice_fs.h>
#include <linux/usbdevice_fs.h>
#include <linux/version.h>
#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 20)
#include <linux/usb/ch9.h>
#else
#include <linux/usb_ch9.h>
#endif
#include <asm/byteorder.h>
#include "usb.h"
#if TRACE_USB
#define DBG1(x...) fprintf(stderr, x)
#define DBG(x...) fprintf(stderr, x)
#else
#define DBG(x...)
#define DBG1(x...)
#endif
struct usb_handle
{
char fname[64];
int desc;
unsigned char ep_in;
unsigned char ep_out;
};
static inline int badname(const char *name)
{
while(*name) {
if(!isdigit(*name++)) return 1;
}
return 0;
}
static int check(void *_desc, int len, unsigned type, int size)
{
unsigned char *desc = _desc;
if(len < size) return -1;
if(desc[0] < size) return -1;
if(desc[0] > len) return -1;
if(desc[1] != type) return -1;
return 0;
}
static int filter_usb_device(int fd, char *ptr, int len, ifc_match_func callback,
int *ept_in_id, int *ept_out_id, int *ifc_id)
{
struct usb_device_descriptor *dev;
struct usb_config_descriptor *cfg;
struct usb_interface_descriptor *ifc;
struct usb_endpoint_descriptor *ept;
struct usb_ifc_info info;
int in, out;
unsigned i;
unsigned e;
if(check(ptr, len, USB_DT_DEVICE, USB_DT_DEVICE_SIZE))
return -1;
dev = (void*) ptr;
len -= dev->bLength;
ptr += dev->bLength;
if(check(ptr, len, USB_DT_CONFIG, USB_DT_CONFIG_SIZE))
return -1;
cfg = (void*) ptr;
len -= cfg->bLength;
ptr += cfg->bLength;
info.dev_vendor = dev->idVendor;
info.dev_product = dev->idProduct;
info.dev_class = dev->bDeviceClass;
info.dev_subclass = dev->bDeviceSubClass;
info.dev_protocol = dev->bDeviceProtocol;
// read device serial number (if there is one)
info.serial_number[0] = 0;
if (dev->iSerialNumber) {
struct usbdevfs_ctrltransfer ctrl;
__u16 buffer[128];
int result;
memset(buffer, 0, sizeof(buffer));
ctrl.bRequestType = USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_DEVICE;
ctrl.bRequest = USB_REQ_GET_DESCRIPTOR;
ctrl.wValue = (USB_DT_STRING << 8) | dev->iSerialNumber;
ctrl.wIndex = 0;
ctrl.wLength = sizeof(buffer);
ctrl.data = buffer;
result = ioctl(fd, USBDEVFS_CONTROL, &ctrl);
if (result > 0) {
int i;
// skip first word, and copy the rest to the serial string, changing shorts to bytes.
result /= 2;
for (i = 1; i < result; i++)
info.serial_number[i - 1] = buffer[i];
info.serial_number[i - 1] = 0;
}
}
for(i = 0; i < cfg->bNumInterfaces; i++) {
if(check(ptr, len, USB_DT_INTERFACE, USB_DT_INTERFACE_SIZE))
return -1;
ifc = (void*) ptr;
len -= ifc->bLength;
ptr += ifc->bLength;
in = -1;
out = -1;
info.ifc_class = ifc->bInterfaceClass;
info.ifc_subclass = ifc->bInterfaceSubClass;
info.ifc_protocol = ifc->bInterfaceProtocol;
for(e = 0; e < ifc->bNumEndpoints; e++) {
if(check(ptr, len, USB_DT_ENDPOINT, USB_DT_ENDPOINT_SIZE))
return -1;
ept = (void*) ptr;
len -= ept->bLength;
ptr += ept->bLength;
if((ept->bmAttributes & 0x03) != 0x02)
continue;
if(ept->bEndpointAddress & 0x80) {
in = ept->bEndpointAddress;
} else {
out = ept->bEndpointAddress;
}
}
info.has_bulk_in = (in != -1);
info.has_bulk_out = (out != -1);
if(callback(&info) == 0) {
*ept_in_id = in;
*ept_out_id = out;
*ifc_id = ifc->bInterfaceNumber;
return 0;
}
}
return -1;
}
static usb_handle *find_usb_device(const char *base, ifc_match_func callback)
{
usb_handle *usb = 0;
char busname[64], devname[64];
char desc[1024];
int n, in, out, ifc;
DIR *busdir, *devdir;
struct dirent *de;
int fd;
busdir = opendir(base);
if(busdir == 0) return 0;
while((de = readdir(busdir)) && (usb == 0)) {
if(badname(de->d_name)) continue;
sprintf(busname, "%s/%s", base, de->d_name);
devdir = opendir(busname);
if(devdir == 0) continue;
// DBG("[ scanning %s ]\n", busname);
while((de = readdir(devdir)) && (usb == 0)) {
if(badname(de->d_name)) continue;
sprintf(devname, "%s/%s", busname, de->d_name);
// DBG("[ scanning %s ]\n", devname);
if((fd = open(devname, O_RDWR)) < 0) {
continue;
}
n = read(fd, desc, sizeof(desc));
if(filter_usb_device(fd, desc, n, callback, &in, &out, &ifc) == 0){
usb = calloc(1, sizeof(usb_handle));
strcpy(usb->fname, devname);
usb->ep_in = in;
usb->ep_out = out;
usb->desc = fd;
n = ioctl(fd, USBDEVFS_CLAIMINTERFACE, &ifc);
if(n != 0) {
close(fd);
free(usb);
usb = 0;
continue;
}
} else {
close(fd);
}
}
closedir(devdir);
}
closedir(busdir);
return usb;
}
int usb_write(usb_handle *h, const void *_data, int len)
{
unsigned char *data = (unsigned char*) _data;
unsigned count = 0;
struct usbdevfs_bulktransfer bulk;
int n;
if(h->ep_out == 0) {
return -1;
}
if(len == 0) {
bulk.ep = h->ep_out;
bulk.len = 0;
bulk.data = data;
bulk.timeout = 0;
n = ioctl(h->desc, USBDEVFS_BULK, &bulk);
if(n != 0) {
fprintf(stderr,"ERROR: n = %d, errno = %d (%s)\n",
n, errno, strerror(errno));
return -1;
}
return 0;
}
while(len > 0) {
int xfer;
xfer = (len > 4096) ? 4096 : len;
bulk.ep = h->ep_out;
bulk.len = xfer;
bulk.data = data;
bulk.timeout = 0;
n = ioctl(h->desc, USBDEVFS_BULK, &bulk);
if(n != xfer) {
DBG("ERROR: n = %d, errno = %d (%s)\n",
n, errno, strerror(errno));
return -1;
}
count += xfer;
len -= xfer;
data += xfer;
}
return count;
}
int usb_read(usb_handle *h, void *_data, int len)
{
unsigned char *data = (unsigned char*) _data;
unsigned count = 0;
struct usbdevfs_bulktransfer bulk;
int n;
if(h->ep_in == 0) {
return -1;
}
while(len > 0) {
int xfer = (len > 4096) ? 4096 : len;
bulk.ep = h->ep_in;
bulk.len = xfer;
bulk.data = data;
bulk.timeout = 0;
DBG("[ usb read %d fd = %d], fname=%s\n", xfer, h->desc, h->fname);
n = ioctl(h->desc, USBDEVFS_BULK, &bulk);
DBG("[ usb read %d ] = %d, fname=%s\n", xfer, n, h->fname);
if(n < 0) {
DBG1("ERROR: n = %d, errno = %d (%s)\n",
n, errno, strerror(errno));
return -1;
}
count += n;
len -= n;
data += n;
if(n < xfer) {
break;
}
}
return count;
}
void usb_kick(usb_handle *h)
{
int fd;
fd = h->desc;
h->desc = -1;
if(fd >= 0) {
close(fd);
DBG("[ usb closed %d ]\n", fd);
}
}
int usb_close(usb_handle *h)
{
int fd;
fd = h->desc;
h->desc = -1;
if(fd >= 0) {
close(fd);
DBG("[ usb closed %d ]\n", fd);
}
return 0;
}
usb_handle *usb_open(ifc_match_func callback)
{
return find_usb_device("/dev/bus/usb", callback);
}

538
fastboot/usb_osx.c Normal file
View file

@ -0,0 +1,538 @@
/*
* Copyright (C) 2008 The Android Open Source Project
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <stdio.h>
#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/IOKitLib.h>
#include <IOKit/IOCFPlugIn.h>
#include <IOKit/usb/IOUSBLib.h>
#include <IOKit/IOMessage.h>
#include <mach/mach_port.h>
#include "usb.h"
/*
* Internal helper functions and associated definitions.
*/
#if TRACE_USB
#define WARN(x...) fprintf(stderr, x)
#else
#define WARN(x...)
#endif
#define ERR(x...) fprintf(stderr, "ERROR: " x)
/** An open usb device */
struct usb_handle
{
int success;
ifc_match_func callback;
usb_ifc_info info;
UInt8 bulkIn;
UInt8 bulkOut;
IOUSBInterfaceInterface190 **interface;
unsigned int zero_mask;
};
/** Try out all the interfaces and see if there's a match. Returns 0 on
* success, -1 on failure. */
static int try_interfaces(IOUSBDeviceInterface **dev, usb_handle *handle) {
IOReturn kr;
IOUSBFindInterfaceRequest request;
io_iterator_t iterator;
io_service_t usbInterface;
IOCFPlugInInterface **plugInInterface;
IOUSBInterfaceInterface190 **interface = NULL;
HRESULT result;
SInt32 score;
UInt8 interfaceNumEndpoints;
UInt8 endpoint;
UInt8 configuration;
// Placing the constant KIOUSBFindInterfaceDontCare into the following
// fields of the IOUSBFindInterfaceRequest structure will allow us to
// find all of the interfaces
request.bInterfaceClass = kIOUSBFindInterfaceDontCare;
request.bInterfaceSubClass = kIOUSBFindInterfaceDontCare;
request.bInterfaceProtocol = kIOUSBFindInterfaceDontCare;
request.bAlternateSetting = kIOUSBFindInterfaceDontCare;
// SetConfiguration will kill an existing UMS connection, so let's
// not do this if not necessary.
configuration = 0;
(*dev)->GetConfiguration(dev, &configuration);
if (configuration != 1)
(*dev)->SetConfiguration(dev, 1);
// Get an iterator for the interfaces on the device
kr = (*dev)->CreateInterfaceIterator(dev, &request, &iterator);
if (kr != 0) {
ERR("Couldn't create a device interface iterator: (%08x)\n", kr);
return -1;
}
while ((usbInterface = IOIteratorNext(iterator))) {
// Create an intermediate plugin
kr = IOCreatePlugInInterfaceForService(
usbInterface,
kIOUSBInterfaceUserClientTypeID,
kIOCFPlugInInterfaceID,
&plugInInterface,
&score);
// No longer need the usbInterface object now that we have the plugin
(void) IOObjectRelease(usbInterface);
if ((kr != 0) || (!plugInInterface)) {
WARN("Unable to create plugin (%08x)\n", kr);
continue;
}
// Now create the interface interface for the interface
result = (*plugInInterface)->QueryInterface(
plugInInterface,
CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID),
(LPVOID) &interface);
// No longer need the intermediate plugin
(*plugInInterface)->Release(plugInInterface);
if (result || !interface) {
ERR("Couldn't create interface interface: (%08x)\n",
(unsigned int) result);
// continue so we can try the next interface
continue;
}
/*
* Now open the interface. This will cause the pipes
* associated with the endpoints in the interface descriptor
* to be instantiated.
*/
/*
* TODO: Earlier comments here indicated that it was a bad
* idea to just open any interface, because opening "mass
* storage endpoints" is bad. However, the only way to find
* out if an interface does bulk in or out is to open it, and
* the framework in this application wants to be told about
* bulk in / out before deciding whether it actually wants to
* use the interface. Maybe something needs to be done about
* this situation.
*/
kr = (*interface)->USBInterfaceOpen(interface);
if (kr != 0) {
WARN("Could not open interface: (%08x)\n", kr);
(void) (*interface)->Release(interface);
// continue so we can try the next interface
continue;
}
// Get the number of endpoints associated with this interface.
kr = (*interface)->GetNumEndpoints(interface, &interfaceNumEndpoints);
if (kr != 0) {
ERR("Unable to get number of endpoints: (%08x)\n", kr);
goto next_interface;
}
// Get interface class, subclass and protocol
if ((*interface)->GetInterfaceClass(interface, &handle->info.ifc_class) != 0 ||
(*interface)->GetInterfaceSubClass(interface, &handle->info.ifc_subclass) != 0 ||
(*interface)->GetInterfaceProtocol(interface, &handle->info.ifc_protocol) != 0)
{
ERR("Unable to get interface class, subclass and protocol\n");
goto next_interface;
}
handle->info.has_bulk_in = 0;
handle->info.has_bulk_out = 0;
// Iterate over the endpoints for this interface and see if there
// are any that do bulk in/out.
for (endpoint = 0; endpoint <= interfaceNumEndpoints; endpoint++) {
UInt8 transferType;
UInt16 maxPacketSize;
UInt8 interval;
UInt8 number;
UInt8 direction;
kr = (*interface)->GetPipeProperties(interface, endpoint,
&direction,
&number, &transferType, &maxPacketSize, &interval);
if (kr == 0) {
if (transferType != kUSBBulk) {
continue;
}
if (direction == kUSBIn) {
handle->info.has_bulk_in = 1;
handle->bulkIn = endpoint;
} else if (direction == kUSBOut) {
handle->info.has_bulk_out = 1;
handle->bulkOut = endpoint;
}
if (handle->info.ifc_protocol == 0x01) {
handle->zero_mask = maxPacketSize - 1;
}
} else {
ERR("could not get pipe properties\n");
}
if (handle->info.has_bulk_in && handle->info.has_bulk_out) {
break;
}
}
if (handle->callback(&handle->info) == 0) {
handle->interface = interface;
handle->success = 1;
/*
* Clear both the endpoints, because it has been observed
* that the Mac may otherwise (incorrectly) start out with
* them in bad state.
*/
if (handle->info.has_bulk_in) {
kr = (*interface)->ClearPipeStallBothEnds(interface,
handle->bulkIn);
if (kr != 0) {
ERR("could not clear input pipe; result %d", kr);
return -1;
}
}
if (handle->info.has_bulk_out) {
kr = (*interface)->ClearPipeStallBothEnds(interface,
handle->bulkOut);
if (kr != 0) {
ERR("could not clear output pipe; result %d", kr);
return -1;
}
}
return 0;
}
next_interface:
(*interface)->USBInterfaceClose(interface);
(*interface)->Release(interface);
}
return 0;
}
/** Try out the given device and see if there's a match. Returns 0 on
* success, -1 on failure.
*/
static int try_device(io_service_t device, usb_handle *handle) {
kern_return_t kr;
IOCFPlugInInterface **plugin = NULL;
IOUSBDeviceInterface182 **dev = NULL;
SInt32 score;
HRESULT result;
UInt8 serialIndex;
// Create an intermediate plugin.
kr = IOCreatePlugInInterfaceForService(device,
kIOUSBDeviceUserClientTypeID,
kIOCFPlugInInterfaceID,
&plugin, &score);
if ((kr != 0) || (plugin == NULL)) {
ERR("Unable to create a plug-in (%08x)\n", kr);
goto error;
}
// Now create the device interface.
result = (*plugin)->QueryInterface(plugin,
CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID), (LPVOID) &dev);
if ((result != 0) || (dev == NULL)) {
ERR("Couldn't create a device interface (%08x)\n", (int) result);
goto error;
}
/*
* We don't need the intermediate interface after the device interface
* is created.
*/
IODestroyPlugInInterface(plugin);
// So, we have a device, finally. Grab its vitals.
kr = (*dev)->GetDeviceVendor(dev, &handle->info.dev_vendor);
if (kr != 0) {
ERR("GetDeviceVendor");
goto error;
}
kr = (*dev)->GetDeviceProduct(dev, &handle->info.dev_product);
if (kr != 0) {
ERR("GetDeviceProduct");
goto error;
}
kr = (*dev)->GetDeviceClass(dev, &handle->info.dev_class);
if (kr != 0) {
ERR("GetDeviceClass");
goto error;
}
kr = (*dev)->GetDeviceSubClass(dev, &handle->info.dev_subclass);
if (kr != 0) {
ERR("GetDeviceSubClass");
goto error;
}
kr = (*dev)->GetDeviceProtocol(dev, &handle->info.dev_protocol);
if (kr != 0) {
ERR("GetDeviceProtocol");
goto error;
}
kr = (*dev)->USBGetSerialNumberStringIndex(dev, &serialIndex);
if (serialIndex > 0) {
IOUSBDevRequest req;
UInt16 buffer[256];
req.bmRequestType = USBmakebmRequestType(kUSBIn, kUSBStandard, kUSBDevice);
req.bRequest = kUSBRqGetDescriptor;
req.wValue = (kUSBStringDesc << 8) | serialIndex;
req.wIndex = 0;
req.pData = buffer;
req.wLength = sizeof(buffer);
kr = (*dev)->DeviceRequest(dev, &req);
if (kr == kIOReturnSuccess && req.wLenDone > 0) {
int i, count;
// skip first word, and copy the rest to the serial string, changing shorts to bytes.
count = (req.wLenDone - 1) / 2;
for (i = 0; i < count; i++)
handle->info.serial_number[i] = buffer[i + 1];
handle->info.serial_number[i] = 0;
}
} else {
// device has no serial number
handle->info.serial_number[0] = 0;
}
if (try_interfaces(dev, handle)) {
goto error;
}
(*dev)->Release(dev);
return 0;
error:
if (dev != NULL) {
(*dev)->Release(dev);
}
return -1;
}
/** Initializes the USB system. Returns 0 on success, -1 on error. */
static int init_usb(ifc_match_func callback, usb_handle **handle) {
int ret = -1;
CFMutableDictionaryRef matchingDict;
kern_return_t result;
io_iterator_t iterator;
usb_handle h;
h.success = 0;
h.callback = callback;
/*
* Create our matching dictionary to find appropriate devices.
* IOServiceAddMatchingNotification consumes the reference, so we
* do not need to release it.
*/
matchingDict = IOServiceMatching(kIOUSBDeviceClassName);
if (matchingDict == NULL) {
ERR("Couldn't create USB matching dictionary.\n");
return -1;
}
result = IOServiceGetMatchingServices(
kIOMasterPortDefault, matchingDict, &iterator);
if (result != 0) {
ERR("Could not create iterator.");
return -1;
}
for (;;) {
if (! IOIteratorIsValid(iterator)) {
/*
* Apple documentation advises resetting the iterator if
* it should become invalid during iteration.
*/
IOIteratorReset(iterator);
continue;
}
io_service_t device = IOIteratorNext(iterator);
if (device == 0) {
break;
}
usb_ifc_info info;
if (try_device(device, &h) != 0) {
IOObjectRelease(device);
ret = -1;
break;
}
if (h.success) {
*handle = calloc(1, sizeof(usb_handle));
memcpy(*handle, &h, sizeof(usb_handle));
ret = 0;
break;
}
IOObjectRelease(device);
}
IOObjectRelease(iterator);
return ret;
}
/*
* Definitions of this file's public functions.
*/
usb_handle *usb_open(ifc_match_func callback) {
usb_handle *handle = NULL;
if (init_usb(callback, &handle) < 0) {
/* Something went wrong initializing USB. */
return NULL;
}
return handle;
}
int usb_close(usb_handle *h) {
/* TODO: Something better here? */
return 0;
}
int usb_read(usb_handle *h, void *data, int len) {
IOReturn result;
UInt32 numBytes = len;
if (len == 0) {
return 0;
}
if (h == NULL) {
return -1;
}
if (h->interface == NULL) {
ERR("usb_read interface was null\n");
return -1;
}
if (h->bulkIn == 0) {
ERR("bulkIn endpoint not assigned\n");
return -1;
}
result = (*h->interface)->ReadPipe(
h->interface, h->bulkIn, data, &numBytes);
if (result == 0) {
return (int) numBytes;
} else {
ERR("usb_read failed with status %x\n", result);
}
return -1;
}
int usb_write(usb_handle *h, const void *data, int len) {
IOReturn result;
if (len == 0) {
return 0;
}
if (h == NULL) {
return -1;
}
if (h->interface == NULL) {
ERR("usb_write interface was null\n");
return -1;
}
if (h->bulkOut == 0) {
ERR("bulkOut endpoint not assigned\n");
return -1;
}
result = (*h->interface)->WritePipe(
h->interface, h->bulkOut, (void *)data, len);
#if 0
if ((result == 0) && (h->zero_mask)) {
/* we need 0-markers and our transfer */
if(!(len & h->zero_mask)) {
result = (*h->interface)->WritePipe(
h->interface, h->bulkOut, (void *)data, 0);
}
}
#endif
if (result != 0) {
ERR("usb_write failed with status %x\n", result);
return -1;
}
return len;
}

375
fastboot/usb_windows.c Normal file
View file

@ -0,0 +1,375 @@
/*
* Copyright (C) 2008 The Android Open Source Project
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <windows.h>
#include <winerror.h>
#include <errno.h>
#include <usb100.h>
#include <adb_api.h>
#include <stdio.h>
#include "usb.h"
//#define TRACE_USB 1
#if TRACE_USB
#define DBG(x...) fprintf(stderr, x)
#else
#define DBG(x...)
#endif
/** Structure usb_handle describes our connection to the usb device via
AdbWinApi.dll. This structure is returned from usb_open() routine and
is expected in each subsequent call that is accessing the device.
*/
struct usb_handle {
/// Handle to USB interface
ADBAPIHANDLE adb_interface;
/// Handle to USB read pipe (endpoint)
ADBAPIHANDLE adb_read_pipe;
/// Handle to USB write pipe (endpoint)
ADBAPIHANDLE adb_write_pipe;
/// Interface name
char* interface_name;
};
/// Class ID assigned to the device by androidusb.sys
static const GUID usb_class_id = ANDROID_USB_CLASS_ID;
/// Checks if interface (device) matches certain criteria
int recognized_device(usb_handle* handle, ifc_match_func callback);
/// Opens usb interface (device) by interface (device) name.
usb_handle* do_usb_open(const wchar_t* interface_name);
/// Writes data to the opened usb handle
int usb_write(usb_handle* handle, const void* data, int len);
/// Reads data using the opened usb handle
int usb_read(usb_handle *handle, void* data, int len);
/// Cleans up opened usb handle
void usb_cleanup_handle(usb_handle* handle);
/// Cleans up (but don't close) opened usb handle
void usb_kick(usb_handle* handle);
/// Closes opened usb handle
int usb_close(usb_handle* handle);
usb_handle* do_usb_open(const wchar_t* interface_name) {
// Allocate our handle
usb_handle* ret = (usb_handle*)malloc(sizeof(usb_handle));
if (NULL == ret)
return NULL;
// Create interface.
ret->adb_interface = AdbCreateInterfaceByName(interface_name);
if (NULL == ret->adb_interface) {
free(ret);
errno = GetLastError();
return NULL;
}
// Open read pipe (endpoint)
ret->adb_read_pipe =
AdbOpenDefaultBulkReadEndpoint(ret->adb_interface,
AdbOpenAccessTypeReadWrite,
AdbOpenSharingModeReadWrite);
if (NULL != ret->adb_read_pipe) {
// Open write pipe (endpoint)
ret->adb_write_pipe =
AdbOpenDefaultBulkWriteEndpoint(ret->adb_interface,
AdbOpenAccessTypeReadWrite,
AdbOpenSharingModeReadWrite);
if (NULL != ret->adb_write_pipe) {
// Save interface name
unsigned long name_len = 0;
// First get expected name length
AdbGetInterfaceName(ret->adb_interface,
NULL,
&name_len,
true);
if (0 != name_len) {
ret->interface_name = (char*)malloc(name_len);
if (NULL != ret->interface_name) {
// Now save the name
if (AdbGetInterfaceName(ret->adb_interface,
ret->interface_name,
&name_len,
true)) {
// We're done at this point
return ret;
}
} else {
SetLastError(ERROR_OUTOFMEMORY);
}
}
}
}
// Something went wrong.
errno = GetLastError();
usb_cleanup_handle(ret);
free(ret);
SetLastError(errno);
return NULL;
}
int usb_write(usb_handle* handle, const void* data, int len) {
unsigned long time_out = 500 + len * 8;
unsigned long written = 0;
unsigned count = 0;
int ret;
DBG("usb_write %d\n", len);
if (NULL != handle) {
// Perform write
while(len > 0) {
int xfer = (len > 4096) ? 4096 : len;
ret = AdbWriteEndpointSync(handle->adb_write_pipe,
(void*)data,
(unsigned long)xfer,
&written,
time_out);
errno = GetLastError();
DBG("AdbWriteEndpointSync returned %d, errno: %d\n", ret, errno);
if (ret == 0) {
// assume ERROR_INVALID_HANDLE indicates we are disconnected
if (errno == ERROR_INVALID_HANDLE)
usb_kick(handle);
return -1;
}
count += written;
len -= written;
data += written;
if (len == 0)
return count;
}
} else {
DBG("usb_write NULL handle\n");
SetLastError(ERROR_INVALID_HANDLE);
}
DBG("usb_write failed: %d\n", errno);
return -1;
}
int usb_read(usb_handle *handle, void* data, int len) {
unsigned long time_out = 500 + len * 8;
unsigned long read = 0;
int ret;
DBG("usb_read %d\n", len);
if (NULL != handle) {
while (1) {
int xfer = (len > 4096) ? 4096 : len;
ret = AdbReadEndpointSync(handle->adb_read_pipe,
(void*)data,
(unsigned long)xfer,
&read,
time_out);
errno = GetLastError();
DBG("usb_read got: %ld, expected: %d, errno: %d\n", read, xfer, errno);
if (ret) {
return read;
} else if (errno != ERROR_SEM_TIMEOUT) {
// assume ERROR_INVALID_HANDLE indicates we are disconnected
if (errno == ERROR_INVALID_HANDLE)
usb_kick(handle);
break;
}
// else we timed out - try again
}
} else {
DBG("usb_read NULL handle\n");
SetLastError(ERROR_INVALID_HANDLE);
}
DBG("usb_read failed: %d\n", errno);
return -1;
}
void usb_cleanup_handle(usb_handle* handle) {
if (NULL != handle) {
if (NULL != handle->interface_name)
free(handle->interface_name);
if (NULL != handle->adb_write_pipe)
AdbCloseHandle(handle->adb_write_pipe);
if (NULL != handle->adb_read_pipe)
AdbCloseHandle(handle->adb_read_pipe);
if (NULL != handle->adb_interface)
AdbCloseHandle(handle->adb_interface);
handle->interface_name = NULL;
handle->adb_write_pipe = NULL;
handle->adb_read_pipe = NULL;
handle->adb_interface = NULL;
}
}
void usb_kick(usb_handle* handle) {
if (NULL != handle) {
usb_cleanup_handle(handle);
} else {
SetLastError(ERROR_INVALID_HANDLE);
errno = ERROR_INVALID_HANDLE;
}
}
int usb_close(usb_handle* handle) {
DBG("usb_close\n");
if (NULL != handle) {
// Cleanup handle
usb_cleanup_handle(handle);
free(handle);
}
return 0;
}
int recognized_device(usb_handle* handle, ifc_match_func callback) {
struct usb_ifc_info info;
USB_DEVICE_DESCRIPTOR device_desc;
USB_INTERFACE_DESCRIPTOR interf_desc;
if (NULL == handle)
return 0;
// Check vendor and product id first
if (!AdbGetUsbDeviceDescriptor(handle->adb_interface,
&device_desc)) {
return 0;
}
// Then check interface properties
if (!AdbGetUsbInterfaceDescriptor(handle->adb_interface,
&interf_desc)) {
return 0;
}
// Must have two endpoints
if (2 != interf_desc.bNumEndpoints) {
return 0;
}
info.dev_vendor = device_desc.idVendor;
info.dev_product = device_desc.idProduct;
info.dev_class = device_desc.bDeviceClass;
info.dev_subclass = device_desc.bDeviceSubClass;
info.dev_protocol = device_desc.bDeviceProtocol;
info.ifc_class = interf_desc.bInterfaceClass;
info.ifc_subclass = interf_desc.bInterfaceSubClass;
info.ifc_protocol = interf_desc.bInterfaceProtocol;
// read serial number (if there is one)
unsigned long serial_number_len = sizeof(info.serial_number);
if (!AdbGetSerialNumber(handle->adb_interface, info.serial_number,
&serial_number_len, true)) {
info.serial_number[0] = 0;
}
if (callback(&info) == 0) {
return 1;
}
return 0;
}
static usb_handle *find_usb_device(ifc_match_func callback) {
usb_handle* handle = NULL;
char entry_buffer[2048];
char interf_name[2048];
AdbInterfaceInfo* next_interface = (AdbInterfaceInfo*)(&entry_buffer[0]);
unsigned long entry_buffer_size = sizeof(entry_buffer);
char* copy_name;
// Enumerate all present and active interfaces.
ADBAPIHANDLE enum_handle =
AdbEnumInterfaces(usb_class_id, true, true, true);
if (NULL == enum_handle)
return NULL;
while (AdbNextInterface(enum_handle, next_interface, &entry_buffer_size)) {
// TODO(vchtchetkine): FIXME - temp hack converting wchar_t into char.
// It would be better to change AdbNextInterface so it will return
// interface name as single char string.
const wchar_t* wchar_name = next_interface->device_name;
for(copy_name = interf_name;
L'\0' != *wchar_name;
wchar_name++, copy_name++) {
*copy_name = (char)(*wchar_name);
}
*copy_name = '\0';
handle = do_usb_open(next_interface->device_name);
if (NULL != handle) {
// Lets see if this interface (device) belongs to us
if (recognized_device(handle, callback)) {
// found it!
break;
} else {
usb_cleanup_handle(handle);
free(handle);
handle = NULL;
}
}
entry_buffer_size = sizeof(entry_buffer);
}
AdbCloseHandle(enum_handle);
return handle;
}
usb_handle *usb_open(ifc_match_func callback)
{
return find_usb_device(callback);
}
// called from fastboot.c
void sleep(int seconds)
{
Sleep(seconds * 1000);
}

212
fastboot/usbtest.c Normal file
View file

@ -0,0 +1,212 @@
/*
* Copyright (C) 2008 The Android Open Source Project
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/time.h>
#include "usb.h"
static unsigned arg_size = 4096;
static unsigned arg_count = 4096;
long long NOW(void)
{
struct timeval tv;
gettimeofday(&tv, 0);
return (((long long) tv.tv_sec) * ((long long) 1000000)) +
(((long long) tv.tv_usec));
}
int printifc(usb_ifc_info *info)
{
printf("dev: csp=%02x/%02x/%02x v=%04x p=%04x ",
info->dev_class, info->dev_subclass, info->dev_protocol,
info->dev_vendor, info->dev_product);
printf("ifc: csp=%02x/%02x/%02x%s%s\n",
info->ifc_class, info->ifc_subclass, info->ifc_protocol,
info->has_bulk_in ? " in" : "",
info->has_bulk_out ? " out" : "");
return -1;
}
int match_null(usb_ifc_info *info)
{
if(info->dev_vendor != 0x18d1) return -1;
if(info->ifc_class != 0xff) return -1;
if(info->ifc_subclass != 0xfe) return -1;
if(info->ifc_protocol != 0x01) return -1;
return 0;
}
int match_zero(usb_ifc_info *info)
{
if(info->dev_vendor != 0x18d1) return -1;
if(info->ifc_class != 0xff) return -1;
if(info->ifc_subclass != 0xfe) return -1;
if(info->ifc_protocol != 0x02) return -1;
return 0;
}
int match_loop(usb_ifc_info *info)
{
if(info->dev_vendor != 0x18d1) return -1;
if(info->ifc_class != 0xff) return -1;
if(info->ifc_subclass != 0xfe) return -1;
if(info->ifc_protocol != 0x03) return -1;
return 0;
}
int test_null(usb_handle *usb)
{
int i;
unsigned char buf[4096];
memset(buf, 0xee, 4096);
long long t0, t1;
t0 = NOW();
for(i = 0; i < arg_count; i++) {
if(usb_write(usb, buf, arg_size) != arg_size) {
fprintf(stderr,"write failed (%s)\n", strerror(errno));
return -1;
}
}
t1 = NOW();
fprintf(stderr,"%d bytes in %lld uS\n", arg_count * arg_size, (t1 - t0));
return 0;
}
int test_zero(usb_handle *usb)
{
int i;
unsigned char buf[4096];
long long t0, t1;
t0 = NOW();
for(i = 0; i < arg_count; i++) {
if(usb_read(usb, buf, arg_size) != arg_size) {
fprintf(stderr,"read failed (%s)\n", strerror(errno));
return -1;
}
}
t1 = NOW();
fprintf(stderr,"%d bytes in %lld uS\n", arg_count * arg_size, (t1 - t0));
return 0;
}
struct
{
const char *cmd;
ifc_match_func match;
int (*test)(usb_handle *usb);
const char *help;
} tests[] = {
{ "list", printifc, 0, "list interfaces" },
{ "send", match_null, test_null, "send to null interface" },
{ "recv", match_zero, test_zero, "recv from zero interface" },
{ "loop", match_loop, 0, "exercise loopback interface" },
{},
};
int usage(void)
{
int i;
fprintf(stderr,"usage: usbtest <testname>\n\navailable tests:\n");
for(i = 0; tests[i].cmd; i++) {
fprintf(stderr," %-8s %s\n", tests[i].cmd, tests[i].help);
}
return -1;
}
int process_args(int argc, char **argv)
{
while(argc-- > 0) {
char *arg = *argv++;
if(!strncmp(arg,"count=",6)) {
arg_count = atoi(arg + 6);
} else if(!strncmp(arg,"size=",5)) {
arg_size = atoi(arg + 5);
} else {
fprintf(stderr,"unknown argument: %s\n", arg);
return -1;
}
}
if(arg_count == 0) {
fprintf(stderr,"count may not be zero\n");
return -1;
}
if(arg_size > 4096) {
fprintf(stderr,"size may not be greater than 4096\n");
return -1;
}
return 0;
}
int main(int argc, char **argv)
{
usb_handle *usb;
int i;
if(argc < 2)
return usage();
if(argc > 2) {
if(process_args(argc - 2, argv + 2))
return -1;
}
for(i = 0; tests[i].cmd; i++) {
if(!strcmp(argv[1], tests[i].cmd)) {
usb = usb_open(tests[i].match);
if(tests[i].test) {
if(usb == 0) {
fprintf(stderr,"usbtest: %s: could not find interface\n",
tests[i].cmd);
return -1;
}
if(tests[i].test(usb)) {
fprintf(stderr,"usbtest: %s: FAIL\n", tests[i].cmd);
return -1;
} else {
fprintf(stderr,"usbtest: %s: OKAY\n", tests[i].cmd);
}
}
return 0;
}
}
return usage();
}

52
fastboot/util_linux.c Normal file
View file

@ -0,0 +1,52 @@
/*
* Copyright (C) 2008 The Android Open Source Project
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <limits.h>
void get_my_path(char *path)
{
char proc[64];
char *x;
sprintf(proc, "/proc/%d/exe", getpid());
int err = readlink(proc, path, PATH_MAX - 1);
if(err <= 0) {
path[0] = 0;
} else {
path[err] = 0;
x = strrchr(path,'/');
if(x) x[1] = 0;
}
}

47
fastboot/util_osx.c Normal file
View file

@ -0,0 +1,47 @@
/*
* Copyright (C) 2008 The Android Open Source Project
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <utils/executablepath.h>
#import <Carbon/Carbon.h>
#include <unistd.h>
void get_my_path(char s[PATH_MAX])
{
char *x;
ProcessSerialNumber psn;
GetCurrentProcess(&psn);
CFDictionaryRef dict;
dict = ProcessInformationCopyDictionary(&psn, 0xffffffff);
CFStringRef value = (CFStringRef)CFDictionaryGetValue(dict,
CFSTR("CFBundleExecutable"));
CFStringGetCString(value, s, PATH_MAX - 1, kCFStringEncodingUTF8);
x = strrchr(s, '/');
if(x) x[1] = 0;
}

93
fastboot/util_windows.c Normal file
View file

@ -0,0 +1,93 @@
/*
* Copyright (C) 2008 The Android Open Source Project
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <limits.h>
#include <windows.h>
void get_my_path(char exe[PATH_MAX])
{
char* r;
GetModuleFileName( NULL, exe, PATH_MAX-1 );
exe[PATH_MAX-1] = 0;
r = strrchr( exe, '\\' );
if (r)
*r = 0;
}
void *load_file(const char *fn, unsigned *_sz)
{
HANDLE file;
char *data;
DWORD file_size;
file = CreateFile( fn,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
0,
NULL );
if (file == INVALID_HANDLE_VALUE)
return NULL;
file_size = GetFileSize( file, NULL );
data = NULL;
if (file_size > 0) {
data = (char*) malloc( file_size );
if (data == NULL) {
fprintf(stderr, "load_file: could not allocate %ld bytes\n", file_size );
file_size = 0;
} else {
DWORD out_bytes;
if ( !ReadFile( file, data, file_size, &out_bytes, NULL ) ||
out_bytes != file_size )
{
fprintf(stderr, "load_file: could not read %ld bytes from '%s'\n", file_size, fn);
free(data);
data = NULL;
file_size = 0;
}
}
}
CloseHandle( file );
*_sz = (unsigned) file_size;
return data;
}

View file

@ -0,0 +1,260 @@
/*
* Copyright (C) 2005 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.
*/
/*
* Android config -- "Darwin". Used for PPC Mac OS X.
*
* TODO: split this into "x86" and "ppc" versions
*/
#ifndef _ANDROID_CONFIG_H
#define _ANDROID_CONFIG_H
/*
* ===========================================================================
* !!! IMPORTANT !!!
* ===========================================================================
*
* This file is included by ALL C/C++ source files. Don't put anything in
* here unless you are absolutely certain it can't go anywhere else.
*
* Any C++ stuff must be wrapped with "#ifdef __cplusplus". Do not use "//"
* comments.
*/
/*
* Threading model. Choose one:
*
* HAVE_PTHREADS - use the pthreads library.
* HAVE_WIN32_THREADS - use Win32 thread primitives.
* -- combine HAVE_CREATETHREAD, HAVE_CREATEMUTEX, and HAVE__BEGINTHREADEX
*/
#define HAVE_PTHREADS
/*
* Do we have the futex syscall?
*/
/* #define HAVE_FUTEX */
/*
* Process creation model. Choose one:
*
* HAVE_FORKEXEC - use fork() and exec()
* HAVE_WIN32_PROC - use CreateProcess()
*/
#define HAVE_FORKEXEC
/*
* Process out-of-memory adjustment. Set if running on Linux,
* where we can write to /proc/<pid>/oom_adj to modify the out-of-memory
* badness adjustment.
*/
/* #define HAVE_OOM_ADJ */
/*
* IPC model. Choose one:
*
* HAVE_SYSV_IPC - use the classic SysV IPC mechanisms (semget, shmget).
* HAVE_MACOSX_IPC - use Macintosh IPC mechanisms (sem_open, mmap).
* HAVE_WIN32_IPC - use Win32 IPC (CreateSemaphore, CreateFileMapping).
* HAVE_ANDROID_IPC - use Android versions (?, mmap).
*/
#define HAVE_MACOSX_IPC
/*
* Memory-mapping model. Choose one:
*
* HAVE_POSIX_FILEMAP - use the Posix sys/mmap.h
* HAVE_WIN32_FILEMAP - use Win32 filemaps
*/
#define HAVE_POSIX_FILEMAP
/*
* Define this if you have <termio.h>
*/
#define HAVE_TERMIO_H
/*
* Define this if you build against MSVCRT.DLL
*/
/* #define HAVE_MS_C_RUNTIME */
/*
* Define this if you have sys/uio.h
*/
#define HAVE_SYS_UIO_H
/*
* Define this if your platforms implements symbolic links
* in its filesystems
*/
#define HAVE_SYMLINKS
/*
* Define this if we have localtime_r().
*/
#define HAVE_LOCALTIME_R
/*
* Define this if we have gethostbyname_r().
*/
/* #define HAVE_GETHOSTBYNAME_R */
/*
* Define this if we have ioctl().
*/
/* #define HAVE_IOCTL */
/*
* Define this if we want to use WinSock.
*/
/* #define HAVE_WINSOCK */
/*
* Define this if have clock_gettime() and friends
*/
/* #define HAVE_POSIX_CLOCKS */
/*
* Define this if we have pthread_cond_timedwait_monotonic() and
* clock_gettime(CLOCK_MONOTONIC).
*/
/* #define HAVE_TIMEDWAIT_MONOTONIC */
/*
* Endianness of the target machine. Choose one:
*
* HAVE_ENDIAN_H -- have endian.h header we can include.
* HAVE_LITTLE_ENDIAN -- we are little endian.
* HAVE_BIG_ENDIAN -- we are big endian.
*/
#if (defined(__ppc__) || defined(__ppc64__))
# define HAVE_BIG_ENDIAN
#elif defined(__i386__)
# define HAVE_LITTLE_ENDIAN
#endif
/*
* We need to choose between 32-bit and 64-bit off_t. All of our code should
* agree on the same size. For desktop systems, use 64-bit values,
* because some of our libraries (e.g. wxWidgets) expect to be built that way.
*/
#define _FILE_OFFSET_BITS 64
#define _LARGEFILE_SOURCE 1
/*
* Defined if we have the backtrace() call for retrieving a stack trace.
* Needed for CallStack to operate; if not defined, CallStack is
* non-functional.
*/
#define HAVE_BACKTRACE 0
/*
* Defined if we have the dladdr() call for retrieving the symbol associated
* with a memory address. If not defined, stack crawls will not have symbolic
* information.
*/
#define HAVE_DLADDR 0
/*
* Defined if we have the cxxabi.h header for demangling C++ symbols. If
* not defined, stack crawls will be displayed with raw mangled symbols
*/
#define HAVE_CXXABI 0
/*
* Defined if we have the gettid() system call.
*/
/* #define HAVE_GETTID */
/*
* Add any extra platform-specific defines here.
*/
#define _THREAD_SAFE
/*
* Define if we have <malloc.h> header
*/
/* #define HAVE_MALLOC_H */
/*
* Define if tm struct has tm_gmtoff field
*/
#define HAVE_TM_GMTOFF 1
/*
* Define if dirent struct has d_type field
*/
#define HAVE_DIRENT_D_TYPE 1
/*
* Define if we have madvise() in <sys/mman.h>
*/
#define HAVE_MADVISE 1
/*
* Define if we include <sys/mount.h> for statfs()
*/
#define INCLUDE_SYS_MOUNT_FOR_STATFS 1
/*
* What CPU architecture does this platform use?
*/
#if (defined(__ppc__) || defined(__ppc64__))
# define ARCH_PPC
#elif defined(__i386__)
# define ARCH_X86
#endif
/*
* sprintf() format string for shared library naming.
*/
#define OS_SHARED_LIB_FORMAT_STR "lib%s.dylib"
/*
* type for the third argument to mincore().
*/
#define MINCORE_POINTER_TYPE char *
/*
* The default path separator for the platform
*/
#define OS_PATH_SEPARATOR '/'
/*
* Is the filesystem case sensitive?
*
* For tools apps, we'll treat is as not case sensitive.
*/
/* #define OS_CASE_SENSITIVE */
/*
* Define if <sys/socket.h> exists.
*/
#define HAVE_SYS_SOCKET_H 1
/*
* Define if the strlcpy() function exists on the system.
*/
#define HAVE_STRLCPY 1
/*
* Define if writev() exists
*/
#define HAVE_WRITEV 1
#endif /*_ANDROID_CONFIG_H*/

View file

@ -0,0 +1,294 @@
/*
* Copyright (C) 2005 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.
*/
/*
* Android config -- "android-arm". Used for ARM device builds.
*/
#ifndef _ANDROID_CONFIG_H
#define _ANDROID_CONFIG_H
/*
* ===========================================================================
* !!! IMPORTANT !!!
* ===========================================================================
*
* This file is included by ALL C/C++ source files. Don't put anything in
* here unless you are absolutely certain it can't go anywhere else.
*
* Any C++ stuff must be wrapped with "#ifdef __cplusplus". Do not use "//"
* comments.
*/
/*
* Threading model. Choose one:
*
* HAVE_PTHREADS - use the pthreads library.
* HAVE_WIN32_THREADS - use Win32 thread primitives.
* -- combine HAVE_CREATETHREAD, HAVE_CREATEMUTEX, and HAVE__BEGINTHREADEX
*/
#define HAVE_PTHREADS
/*
* Do we have the futex syscall?
*/
#define HAVE_FUTEX
/*
* Process creation model. Choose one:
*
* HAVE_FORKEXEC - use fork() and exec()
* HAVE_WIN32_PROC - use CreateProcess()
*/
#define HAVE_FORKEXEC
/*
* Process out-of-memory adjustment. Set if running on Linux,
* where we can write to /proc/<pid>/oom_adj to modify the out-of-memory
* badness adjustment.
*/
#define HAVE_OOM_ADJ
/*
* IPC model. Choose one:
*
* HAVE_SYSV_IPC - use the classic SysV IPC mechanisms (semget, shmget).
* HAVE_MACOSX_IPC - use Macintosh IPC mechanisms (sem_open, mmap).
* HAVE_WIN32_IPC - use Win32 IPC (CreateSemaphore, CreateFileMapping).
* HAVE_ANDROID_IPC - use Android versions (?, mmap).
*/
#define HAVE_ANDROID_IPC
/*
* Memory-mapping model. Choose one:
*
* HAVE_POSIX_FILEMAP - use the Posix sys/mmap.h
* HAVE_WIN32_FILEMAP - use Win32 filemaps
*/
#define HAVE_POSIX_FILEMAP
/*
* Define this if you have <termio.h>
*/
#define HAVE_TERMIO_H
/*
* Define this if you build against MSVCRT.DLL
*/
/* #define HAVE_MS_C_RUNTIME */
/*
* Define this if you have sys/uio.h
*/
#define HAVE_SYS_UIO_H
/*
* Define this if your platforms implements symbolic links
* in its filesystems
*/
#define HAVE_SYMLINKS
/*
* Define this if we have localtime_r().
*/
/* #define HAVE_LOCALTIME_R */
/*
* Define this if we have gethostbyname_r().
*/
/* #define HAVE_GETHOSTBYNAME_R */
/*
* Define this if we have ioctl().
*/
#define HAVE_IOCTL
/*
* Define this if we want to use WinSock.
*/
/* #define HAVE_WINSOCK */
/*
* Define this if have clock_gettime() and friends
*/
#define HAVE_POSIX_CLOCKS
/*
* Define this if we have pthread_cond_timedwait_monotonic() and
* clock_gettime(CLOCK_MONOTONIC).
*/
#define HAVE_TIMEDWAIT_MONOTONIC
/*
* Define this if we have linux style epoll()
*/
#define HAVE_EPOLL
/*
* Endianness of the target machine. Choose one:
*
* HAVE_ENDIAN_H -- have endian.h header we can include.
* HAVE_LITTLE_ENDIAN -- we are little endian.
* HAVE_BIG_ENDIAN -- we are big endian.
*/
#define HAVE_ENDIAN_H
#define HAVE_LITTLE_ENDIAN
/*
* We need to choose between 32-bit and 64-bit off_t. All of our code should
* agree on the same size. For desktop systems, use 64-bit values,
* because some of our libraries (e.g. wxWidgets) expect to be built that way.
*/
/* #define _FILE_OFFSET_BITS 64 */
/* #define _LARGEFILE_SOURCE 1 */
/*
* Defined if we have the backtrace() call for retrieving a stack trace.
* Needed for CallStack to operate; if not defined, CallStack is
* non-functional.
*/
#define HAVE_BACKTRACE 0
/*
* Defined if we have the dladdr() call for retrieving the symbol associated
* with a memory address. If not defined, stack crawls will not have symbolic
* information.
*/
#define HAVE_DLADDR 0
/*
* Defined if we have the cxxabi.h header for demangling C++ symbols. If
* not defined, stack crawls will be displayed with raw mangled symbols
*/
#define HAVE_CXXABI 0
/*
* Defined if we have the gettid() system call.
*/
#define HAVE_GETTID
/*
* Defined if we have the sched_setscheduler() call
*/
#define HAVE_SCHED_SETSCHEDULER
/*
* Add any extra platform-specific defines here.
*/
#define __linux__
/*
* Define if we have <malloc.h> header
*/
#define HAVE_MALLOC_H
/*
* Define if we're running on *our* linux on device or emulator.
*/
#define HAVE_ANDROID_OS 1
/*
* Define if we have Linux-style non-filesystem Unix Domain Sockets
*/
#define HAVE_LINUX_LOCAL_SOCKET_NAMESPACE 1
/*
* Define if we have Linux's inotify in <sys/inotify.h>.
*/
#define HAVE_INOTIFY 1
/*
* Define if we have madvise() in <sys/mman.h>
*/
#define HAVE_MADVISE 1
/*
* Define if tm struct has tm_gmtoff field
*/
#define HAVE_TM_GMTOFF 1
/*
* Define if dirent struct has d_type field
*/
#define HAVE_DIRENT_D_TYPE 1
/*
* Define if libc includes Android system properties implementation.
*/
#define HAVE_LIBC_SYSTEM_PROPERTIES 1
/*
* Define if system provides a system property server (should be
* mutually exclusive with HAVE_LIBC_SYSTEM_PROPERTIES).
*/
/* #define HAVE_SYSTEM_PROPERTY_SERVER */
/*
* What CPU architecture does this platform use?
*/
#define ARCH_ARM
/*
* sprintf() format string for shared library naming.
*/
#define OS_SHARED_LIB_FORMAT_STR "lib%s.so"
/*
* Do we have __memcmp16()?
*/
#define HAVE__MEMCMP16 1
/*
* type for the third argument to mincore().
*/
#define MINCORE_POINTER_TYPE unsigned char *
/*
* Do we have the sigaction flag SA_NOCLDWAIT?
*/
#define HAVE_SA_NOCLDWAIT
/*
* The default path separator for the platform
*/
#define OS_PATH_SEPARATOR '/'
/*
* Is the filesystem case sensitive?
*/
#define OS_CASE_SENSITIVE
/*
* Define if <sys/socket.h> exists.
*/
#define HAVE_SYS_SOCKET_H 1
/*
* Define if the strlcpy() function exists on the system.
*/
#define HAVE_STRLCPY 1
/*
* Define if prctl() exists
*/
#define HAVE_PRCTL 1
/*
* Define if writev() exists
*/
#define HAVE_WRITEV 1
#endif /* _ANDROID_CONFIG_H */

View file

@ -0,0 +1,286 @@
/*
* Copyright (C) 2005 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.
*/
/*
* Android config -- "Linux". Used for desktop x86 Linux.
*/
#ifndef _ANDROID_CONFIG_H
#define _ANDROID_CONFIG_H
/*
* ===========================================================================
* !!! IMPORTANT !!!
* ===========================================================================
*
* This file is included by ALL C/C++ source files. Don't put anything in
* here unless you are absolutely certain it can't go anywhere else.
*
* Any C++ stuff must be wrapped with "#ifdef __cplusplus". Do not use "//"
* comments.
*/
/*
* Threading model. Choose one:
*
* HAVE_PTHREADS - use the pthreads library.
* HAVE_WIN32_THREADS - use Win32 thread primitives.
* -- combine HAVE_CREATETHREAD, HAVE_CREATEMUTEX, and HAVE__BEGINTHREADEX
*/
#define HAVE_PTHREADS
/*
* Do we have the futex syscall?
*/
#define HAVE_FUTEX
/*
* Process creation model. Choose one:
*
* HAVE_FORKEXEC - use fork() and exec()
* HAVE_WIN32_PROC - use CreateProcess()
*/
#define HAVE_FORKEXEC
/*
* Process out-of-memory adjustment. Set if running on Linux,
* where we can write to /proc/<pid>/oom_adj to modify the out-of-memory
* badness adjustment.
*/
#define HAVE_OOM_ADJ
/*
* IPC model. Choose one:
*
* HAVE_SYSV_IPC - use the classic SysV IPC mechanisms (semget, shmget).
* HAVE_MACOSX_IPC - use Macintosh IPC mechanisms (sem_open, mmap).
* HAVE_WIN32_IPC - use Win32 IPC (CreateSemaphore, CreateFileMapping).
* HAVE_ANDROID_IPC - use Android versions (?, mmap).
*/
#define HAVE_SYSV_IPC
/*
* Memory-mapping model. Choose one:
*
* HAVE_POSIX_FILEMAP - use the Posix sys/mmap.h
* HAVE_WIN32_FILEMAP - use Win32 filemaps
*/
#define HAVE_POSIX_FILEMAP
/*
* Define this if you have <termio.h>
*/
#define HAVE_TERMIO_H
/*
* Define this if you build against MSVCRT.DLL
*/
/* #define HAVE_MS_C_RUNTIME */
/*
* Define this if you have sys/uio.h
*/
#define HAVE_SYS_UIO_H
/*
* Define this if your platforms implements symbolic links
* in its filesystems
*/
#define HAVE_SYMLINKS
/*
* Define this if we have localtime_r().
*/
#define HAVE_LOCALTIME_R
/*
* Define this if we have gethostbyname_r().
*/
#define HAVE_GETHOSTBYNAME_R
/*
* Define this if we have ioctl().
*/
#define HAVE_IOCTL
/*
* Define this if we want to use WinSock.
*/
/* #define HAVE_WINSOCK */
/*
* Define this if have clock_gettime() and friends
*
* Desktop Linux has this in librt, but it's broken in goobuntu, yielding
* mildly or wildly inaccurate results.
*/
/*#define HAVE_POSIX_CLOCKS*/
/*
* Define this if we have pthread_cond_timedwait_monotonic() and
* clock_gettime(CLOCK_MONOTONIC).
*/
/* #define HAVE_TIMEDWAIT_MONOTONIC */
/*
* Define this if we have linux style epoll()
*/
#define HAVE_EPOLL
/*
* Endianness of the target machine. Choose one:
*
* HAVE_ENDIAN_H -- have endian.h header we can include.
* HAVE_LITTLE_ENDIAN -- we are little endian.
* HAVE_BIG_ENDIAN -- we are big endian.
*/
#define HAVE_ENDIAN_H
#define HAVE_LITTLE_ENDIAN
/*
* We need to choose between 32-bit and 64-bit off_t. All of our code should
* agree on the same size. For desktop systems, use 64-bit values,
* because some of our libraries (e.g. wxWidgets) expect to be built that way.
*/
#define _FILE_OFFSET_BITS 64
#define _LARGEFILE_SOURCE 1
/*
* Defined if we have the backtrace() call for retrieving a stack trace.
* Needed for CallStack to operate; if not defined, CallStack is
* non-functional.
*/
#define HAVE_BACKTRACE 1
/*
* Defined if we have the dladdr() call for retrieving the symbol associated
* with a memory address. If not defined, stack crawls will not have symbolic
* information.
*/
#define HAVE_DLADDR 1
/*
* Defined if we have the cxxabi.h header for demangling C++ symbols. If
* not defined, stack crawls will be displayed with raw mangled symbols
*/
#define HAVE_CXXABI 0
/*
* Defined if we have the gettid() system call.
*/
/* #define HAVE_GETTID */
/*
* Defined if we have the sched_setscheduler() call
*/
#define HAVE_SCHED_SETSCHEDULER
/*
* Add any extra platform-specific defines here.
*/
/*
* Define if we have <malloc.h> header
*/
#define HAVE_MALLOC_H
/*
* Define if we have Linux-style non-filesystem Unix Domain Sockets
*/
/*
* What CPU architecture does this platform use?
*/
#define ARCH_X86
/*
* Define if we have Linux's inotify in <sys/inotify.h>.
*/
/*#define HAVE_INOTIFY 1*/
/*
* Define if we have madvise() in <sys/mman.h>
*/
#define HAVE_MADVISE 1
/*
* Define if tm struct has tm_gmtoff field
*/
#define HAVE_TM_GMTOFF 1
/*
* Define if dirent struct has d_type field
*/
#define HAVE_DIRENT_D_TYPE 1
/*
* Define if libc includes Android system properties implementation.
*/
/* #define HAVE_LIBC_SYSTEM_PROPERTIES */
/*
* Define if system provides a system property server (should be
* mutually exclusive with HAVE_LIBC_SYSTEM_PROPERTIES).
*/
#define HAVE_SYSTEM_PROPERTY_SERVER
/*
* sprintf() format string for shared library naming.
*/
#define OS_SHARED_LIB_FORMAT_STR "lib%s.so"
/*
* type for the third argument to mincore().
*/
#define MINCORE_POINTER_TYPE unsigned char *
/*
* Do we have the sigaction flag SA_NOCLDWAIT?
*/
#define HAVE_SA_NOCLDWAIT
/*
* The default path separator for the platform
*/
#define OS_PATH_SEPARATOR '/'
/*
* Is the filesystem case sensitive?
*/
#define OS_CASE_SENSITIVE
/*
* Define if <sys/socket.h> exists.
*/
#define HAVE_SYS_SOCKET_H 1
/*
* Define if the strlcpy() function exists on the system.
*/
/* #define HAVE_STRLCPY 1 */
/*
* Define if prctl() exists
*/
#define HAVE_PRCTL 1
/*
* Define if writev() exists
*/
#define HAVE_WRITEV 1
#endif /*_ANDROID_CONFIG_H*/

View file

@ -0,0 +1,290 @@
/*
* Copyright (C) 2005 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.
*/
/*
* Android config -- "CYGWIN_NT-5.1".
*
* Cygwin has pthreads, but GDB seems to get confused if you use it to
* create threads. By "confused", I mean it freezes up the first time the
* debugged process creates a thread, even if you use CreateThread. The
* mere presence of pthreads linkage seems to cause problems.
*/
#ifndef _ANDROID_CONFIG_H
#define _ANDROID_CONFIG_H
/*
* ===========================================================================
* !!! IMPORTANT !!!
* ===========================================================================
*
* This file is included by ALL C/C++ source files. Don't put anything in
* here unless you are absolutely certain it can't go anywhere else.
*
* Any C++ stuff must be wrapped with "#ifdef __cplusplus". Do not use "//"
* comments.
*/
/*
* Threading model. Choose one:
*
* HAVE_PTHREADS - use the pthreads library.
* HAVE_WIN32_THREADS - use Win32 thread primitives.
*/
#define HAVE_WIN32_THREADS
/*
* Do we have the futex syscall?
*/
/* #define HAVE_FUTEX */
/*
* Process creation model. Choose one:
*
* HAVE_FORKEXEC - use fork() and exec()
* HAVE_WIN32_PROC - use CreateProcess()
*/
#ifdef __CYGWIN__
# define HAVE_FORKEXEC
#else
# define HAVE_WIN32_PROC
#endif
/*
* Process out-of-memory adjustment. Set if running on Linux,
* where we can write to /proc/<pid>/oom_adj to modify the out-of-memory
* badness adjustment.
*/
/* #define HAVE_OOM_ADJ */
/*
* IPC model. Choose one:
*
* HAVE_SYSV_IPC - use the classic SysV IPC mechanisms (semget, shmget).
* HAVE_MACOSX_IPC - use Macintosh IPC mechanisms (sem_open, mmap).
* HAVE_WIN32_IPC - use Win32 IPC (CreateSemaphore, CreateFileMapping).
* HAVE_ANDROID_IPC - use Android versions (?, mmap).
*/
#define HAVE_WIN32_IPC
/*
* Memory-mapping model. Choose one:
*
* HAVE_POSIX_FILEMAP - use the Posix sys/mmap.h
* HAVE_WIN32_FILEMAP - use Win32 filemaps
*/
#ifdef __CYGWIN__
#define HAVE_POSIX_FILEMAP
#else
#define HAVE_WIN32_FILEMAP
#endif
/*
* Define this if you have <termio.h>
*/
#ifdef __CYGWIN__
# define HAVE_TERMIO_H
#endif
/*
* Define this if you build against MSVCRT.DLL
*/
#ifndef __CYGWIN__
# define HAVE_MS_C_RUNTIME
#endif
/*
* Define this if you have sys/uio.h
*/
#ifdef __CYGWIN__
#define HAVE_SYS_UIO_H
#endif
/*
* Define this if we have localtime_r().
*/
/* #define HAVE_LOCALTIME_R */
/*
* Define this if we have gethostbyname_r().
*/
/* #define HAVE_GETHOSTBYNAME_R */
/*
* Define this if we have ioctl().
*/
/* #define HAVE_IOCTL */
/*
* Define this if we want to use WinSock.
*/
#ifndef __CYGWIN__
#define HAVE_WINSOCK
#endif
/*
* Define this if your platforms implements symbolic links
* in its filesystems
*/
/* #define HAVE_SYMLINKS */
/*
* Define this if have clock_gettime() and friends
*/
/* #define HAVE_POSIX_CLOCKS */
/*
* Endianness of the target machine. Choose one:
*
* HAVE_ENDIAN_H -- have endian.h header we can include.
* HAVE_LITTLE_ENDIAN -- we are little endian.
* HAVE_BIG_ENDIAN -- we are big endian.
*/
#ifdef __CYGWIN__
#define HAVE_ENDIAN_H
#endif
#define HAVE_LITTLE_ENDIAN
/*
* We need to choose between 32-bit and 64-bit off_t. All of our code should
* agree on the same size. For desktop systems, use 64-bit values,
* because some of our libraries (e.g. wxWidgets) expect to be built that way.
*/
#define _FILE_OFFSET_BITS 64
#define _LARGEFILE_SOURCE 1
/*
* Defined if we have the backtrace() call for retrieving a stack trace.
* Needed for CallStack to operate; if not defined, CallStack is
* non-functional.
*/
#define HAVE_BACKTRACE 0
/*
* Defined if we have the dladdr() call for retrieving the symbol associated
* with a memory address. If not defined, stack crawls will not have symbolic
* information.
*/
#define HAVE_DLADDR 0
/*
* Defined if we have the cxxabi.h header for demangling C++ symbols. If
* not defined, stack crawls will be displayed with raw mangled symbols
*/
#define HAVE_CXXABI 0
/*
* Define if tm struct has tm_gmtoff field
*/
/* #define HAVE_TM_GMTOFF 1 */
/*
* Define if dirent struct has d_type field
*/
/* #define HAVE_DIRENT_D_TYPE 1 */
/*
* Define if libc includes Android system properties implementation.
*/
/* #define HAVE_LIBC_SYSTEM_PROPERTIES */
/*
* Define if system provides a system property server (should be
* mutually exclusive with HAVE_LIBC_SYSTEM_PROPERTIES).
*/
/* #define HAVE_SYSTEM_PROPERTY_SERVER */
/*
* Define if we have madvise() in <sys/mman.h>
*/
/*#define HAVE_MADVISE 1*/
/*
* Add any extra platform-specific defines here.
*/
#define WIN32 1 /* stock Cygwin doesn't define these */
#define _WIN32 1
#define _WIN32_WINNT 0x0500 /* admit to using >= Win2K */
#define HAVE_WINDOWS_PATHS /* needed by simulator */
/*
* What CPU architecture does this platform use?
*/
#define ARCH_X86
/*
* sprintf() format string for shared library naming.
*/
#define OS_SHARED_LIB_FORMAT_STR "lib%s.dll"
/*
* type for the third argument to mincore().
*/
#define MINCORE_POINTER_TYPE unsigned char *
/*
* The default path separator for the platform
*/
#define OS_PATH_SEPARATOR '\\'
/*
* Is the filesystem case sensitive?
*/
/* #define OS_CASE_SENSITIVE */
/*
* Define if <sys/socket.h> exists.
* Cygwin has it, but not MinGW.
*/
#ifdef USE_MINGW
/* #define HAVE_SYS_SOCKET_H */
#else
#define HAVE_SYS_SOCKET_H 1
#endif
/*
* Define if the strlcpy() function exists on the system.
*/
/* #define HAVE_STRLCPY 1 */
/*
* Define if <winsock2.h> exists.
* Only MinGW has it.
*/
#ifdef USE_MINGW
#define HAVE_WINSOCK2_H 1
#else
/* #define HAVE_WINSOCK2_H */
#endif
/*
* Various definitions missing in MinGW
*/
#ifdef USE_MINGW
#define S_IRGRP 0
#define sleep _sleep
#endif
/*
* Define if writev() exists.
*/
/* #define HAVE_WRITEV */
#endif /*_ANDROID_CONFIG_H*/

70
include/ctest/ctest.h Normal file
View file

@ -0,0 +1,70 @@
/*
* 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.
*/
/**
* Very simple unit testing framework.
*/
#ifndef __CUTILS_TEST_H
#define __CUTILS_TEST_H
#ifdef __cplusplus
extern "C" {
#endif
/**
* Adds a test to the test suite.
*/
#define addTest(test) addNamedTest(#test, &test)
/**
* Asserts that a condition is true. The test fails if it isn't.
*/
#define assertTrue(value, message) assertTrueWithSource(value, __FILE__, __LINE__, message);
/**
* Asserts that a condition is false. The test fails if the value is true.
*/
#define assertFalse(value, message) assertTrueWithSource(!value, __FILE__, __LINE__, message);
/** Fails a test with the given message. */
#define fail(message) assertTrueWithSource(0, __FILE__, __LINE__, message);
/**
* Asserts that two values are ==.
*/
#define assertSame(a, b) assertTrueWithSource(a == b, __FILE__, __LINE__, "Expected same value.");
/**
* Asserts that two values are !=.
*/
#define assertNotSame(a, b) assertTrueWithSource(a != b, __FILE__, __LINE__,\
"Expected different values");
/**
* Runs a test suite.
*/
void runTests(void);
// Do not call these functions directly. Use macros above instead.
void addNamedTest(const char* name, void (*test)(void));
void assertTrueWithSource(int value, const char* file, int line, char* message);
#ifdef __cplusplus
}
#endif
#endif /* __CUTILS_TEST_H */

35
include/cutils/adb_networking.h Executable file
View file

@ -0,0 +1,35 @@
/*
* Copyright (C) 2006 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_NETWORKING_H
#define _ADB_NETWORKING_H 1
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#ifdef __cplusplus
extern "C" {
#endif
extern int adb_networking_connect_fd(int fd, struct sockaddr_in *p_address);
extern int adb_networking_gethostbyname(const char *name, struct in_addr *p_out_addr);
#ifdef __cplusplus
}
#endif
#endif /*_ADB_NETWORKING_H*/

67
include/cutils/array.h Normal file
View file

@ -0,0 +1,67 @@
/*
* 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.
*/
/**
* A pointer array which intelligently expands its capacity ad needed.
*/
#ifndef __ARRAY_H
#define __ARRAY_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stdlib.h>
/** An array. */
typedef struct Array Array;
/** Constructs a new array. Returns NULL if we ran out of memory. */
Array* arrayCreate();
/** Frees an array. Does not free elements themselves. */
void arrayFree(Array* array);
/** Adds a pointer. Returns 0 is successful, < 0 otherwise. */
int arrayAdd(Array* array, void* pointer);
/** Gets the pointer at the specified index. */
void* arrayGet(Array* array, int index);
/** Removes the pointer at the given index and returns it. */
void* arrayRemove(Array* array, int index);
/** Sets pointer at the given index. Returns old pointer. */
void* arraySet(Array* array, int index, void* pointer);
/** Sets the array size. Sets new pointers to NULL. Returns 0 if successful, < 0 otherwise . */
int arraySetSize(Array* array, int size);
/** Returns the size of the given array. */
int arraySize(Array* array);
/**
* Returns a pointer to a C-style array which will be valid until this array
* changes.
*/
const void** arrayUnwrap(Array* array);
#ifdef __cplusplus
}
#endif
#endif /* __ARRAY_H */

42
include/cutils/ashmem.h Normal file
View file

@ -0,0 +1,42 @@
/* cutils/ashmem.h
**
** Copyright 2008 The Android Open Source Project
**
** This file is dual licensed. It may be redistributed and/or modified
** under the terms of the Apache 2.0 License OR version 2 of the GNU
** General Public License.
*/
#ifndef _CUTILS_ASHMEM_H
#define _CUTILS_ASHMEM_H
#ifdef __cplusplus
extern "C" {
#endif
int ashmem_create_region(const char *name, size_t size);
int ashmem_set_prot_region(int fd, int prot);
int ashmem_pin_region(int fd, size_t offset, size_t len);
int ashmem_unpin_region(int fd, size_t offset, size_t len);
#ifdef __cplusplus
}
#endif
#ifndef __ASHMEMIOC /* in case someone included <linux/ashmem.h> too */
#define ASHMEM_NAME_LEN 256
#define ASHMEM_NAME_DEF "dev/ashmem"
/* Return values from ASHMEM_PIN: Was the mapping purged while unpinned? */
#define ASHMEM_NOT_PURGED 0
#define ASHMEM_WAS_PURGED 1
/* Return values from ASHMEM_UNPIN: Is the mapping now pinned or unpinned? */
#define ASHMEM_IS_UNPINNED 0
#define ASHMEM_IS_PINNED 1
#endif /* ! __ASHMEMIOC */
#endif /* _CUTILS_ASHMEM_H */

79
include/cutils/atomic.h Normal file
View file

@ -0,0 +1,79 @@
/*
* 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 ANDROID_CUTILS_ATOMIC_H
#define ANDROID_CUTILS_ATOMIC_H
#include <stdint.h>
#include <sys/types.h>
#ifdef __cplusplus
extern "C" {
#endif
/*
* NOTE: memory shared between threads is synchronized by all atomic operations
* below, this means that no explicit memory barrier is required: all reads or
* writes issued before android_atomic_* operations are guaranteed to complete
* before the atomic operation takes place.
*/
void android_atomic_write(int32_t value, volatile int32_t* addr);
/*
* all these atomic operations return the previous value
*/
int32_t android_atomic_inc(volatile int32_t* addr);
int32_t android_atomic_dec(volatile int32_t* addr);
int32_t android_atomic_add(int32_t value, volatile int32_t* addr);
int32_t android_atomic_and(int32_t value, volatile int32_t* addr);
int32_t android_atomic_or(int32_t value, volatile int32_t* addr);
int32_t android_atomic_swap(int32_t value, volatile int32_t* addr);
/*
* NOTE: Two "quasiatomic" operations on the exact same memory address
* are guaranteed to operate atomically with respect to each other,
* but no guarantees are made about quasiatomic operations mixed with
* non-quasiatomic operations on the same address, nor about
* quasiatomic operations that are performed on partially-overlapping
* memory.
*/
int64_t android_quasiatomic_swap_64(int64_t value, volatile int64_t* addr);
int64_t android_quasiatomic_read_64(volatile int64_t* addr);
/*
* cmpxchg return a non zero value if the exchange was NOT performed,
* in other words if oldvalue != *addr
*/
int android_atomic_cmpxchg(int32_t oldvalue, int32_t newvalue,
volatile int32_t* addr);
int android_quasiatomic_cmpxchg_64(int64_t oldvalue, int64_t newvalue,
volatile int64_t* addr);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // ANDROID_CUTILS_ATOMIC_H

View file

@ -0,0 +1,61 @@
/*
* Copyright (C) 2006 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 __CUTILS_CONFIG_UTILS_H
#define __CUTILS_CONFIG_UTILS_H
#ifdef __cplusplus
extern "C" {
#endif
typedef struct cnode cnode;
struct cnode
{
cnode *next;
cnode *first_child;
cnode *last_child;
const char *name;
const char *value;
};
/* parse a text string into a config node tree */
void config_load(cnode *root, char *data);
/* parse a file into a config node tree */
void config_load_file(cnode *root, const char *fn);
/* create a single config node */
cnode* config_node(const char *name, const char *value);
/* locate a named child of a config node */
cnode* config_find(cnode *root, const char *name);
/* look up a child by name and return the boolean value */
int config_bool(cnode *root, const char *name, int _default);
/* look up a child by name and return the string value */
const char* config_str(cnode *root, const char *name, const char *_default);
/* add a named child to a config node (or modify it if it already exists) */
void config_set(cnode *root, const char *name, const char *value);
#ifdef __cplusplus
}
#endif
#endif

34
include/cutils/cpu_info.h Normal file
View file

@ -0,0 +1,34 @@
/*
* 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 __CUTILS_CPU_INFO_H
#define __CUTILS_CPU_INFO_H
#ifdef __cplusplus
extern "C" {
#endif
/* returns a string contiaining an ASCII representation of the CPU serial number,
** or NULL if cpu info not available.
** The string is a static variable, so don't call free() on it.
*/
extern const char* get_cpu_serial_number(void);
#ifdef __cplusplus
}
#endif
#endif /* __CUTILS_CPU_INFO_H */

26
include/cutils/dir_hash.h Normal file
View file

@ -0,0 +1,26 @@
/*
* 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.
*/
typedef enum {
SHA_1,
} HashAlgorithm;
int get_file_hash(HashAlgorithm algorithm, const char *path,
char *output_string, size_t max_output_string);
int get_recursive_hash_manifest(HashAlgorithm algorithm,
const char *directory_path,
char **output_string);

View file

@ -0,0 +1,50 @@
/*
* 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 _LIBS_CUTILS_EVENTTAGMAP_H
#define _LIBS_CUTILS_EVENTTAGMAP_H
#ifdef __cplusplus
extern "C" {
#endif
#define EVENT_TAG_MAP_FILE "/system/etc/event-log-tags"
struct EventTagMap;
typedef struct EventTagMap EventTagMap;
/*
* Open the specified file as an event log tag map.
*
* Returns NULL on failure.
*/
EventTagMap* android_openEventTagMap(const char* fileName);
/*
* Close the map.
*/
void android_closeEventTagMap(EventTagMap* map);
/*
* Look up a tag by index. Returns the tag string, or NULL if not found.
*/
const char* android_lookupEventTag(const EventTagMap* map, int tag);
#ifdef __cplusplus
}
#endif
#endif /*_LIBS_CUTILS_EVENTTAGMAP_H*/

74
include/cutils/fdevent.h Normal file
View file

@ -0,0 +1,74 @@
/*
* Copyright (C) 2006 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 __FDEVENT_H
#define __FDEVENT_H
/* events that may be observed */
#define FDE_READ 0x0001
#define FDE_WRITE 0x0002
#define FDE_ERROR 0x0004
/* features that may be set (via the events set/add/del interface) */
#define FDE_DONT_CLOSE 0x0080
typedef struct fdevent fdevent;
typedef void (*fd_func)(int fd, unsigned events, void *userdata);
/* Allocate and initialize a new fdevent object
*/
fdevent *fdevent_create(int fd, fd_func func, void *arg);
/* Uninitialize and deallocate an fdevent object that was
** created by fdevent_create()
*/
void fdevent_destroy(fdevent *fde);
/* Initialize an fdevent object that was externally allocated
*/
void fdevent_install(fdevent *fde, int fd, fd_func func, void *arg);
/* Uninitialize an fdevent object that was initialized by
** fdevent_install()
*/
void fdevent_remove(fdevent *item);
/* Change which events should cause notifications
*/
void fdevent_set(fdevent *fde, unsigned events);
void fdevent_add(fdevent *fde, unsigned events);
void fdevent_del(fdevent *fde, unsigned events);
/* loop forever, handling events.
*/
void fdevent_loop();
struct fdevent
{
fdevent *next;
fdevent *prev;
int fd;
unsigned short state;
unsigned short events;
fd_func func;
void *arg;
};
#endif

150
include/cutils/hashmap.h Normal file
View file

@ -0,0 +1,150 @@
/*
* 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.
*/
/**
* Hash map.
*/
#ifndef __HASHMAP_H
#define __HASHMAP_H
#include <stdbool.h>
#include <stdlib.h>
#ifdef __cplusplus
extern "C" {
#endif
/** A hash map. */
typedef struct Hashmap Hashmap;
/**
* Creates a new hash map. Returns NULL if memory allocation fails.
*
* @param initialCapacity number of expected entries
* @param hash function which hashes keys
* @param equals function which compares keys for equality
*/
Hashmap* hashmapCreate(size_t initialCapacity,
int (*hash)(void* key), bool (*equals)(void* keyA, void* keyB));
/**
* Frees the hash map. Does not free the keys or values themselves.
*/
void hashmapFree(Hashmap* map);
/**
* Hashes the memory pointed to by key with the given size. Useful for
* implementing hash functions.
*/
int hashmapHash(void* key, size_t keySize);
/**
* Puts value for the given key in the map. Returns pre-existing value if
* any.
*
* If memory allocation fails, this function returns NULL, the map's size
* does not increase, and errno is set to ENOMEM.
*/
void* hashmapPut(Hashmap* map, void* key, void* value);
/**
* Gets a value from the map. Returns NULL if no entry for the given key is
* found or if the value itself is NULL.
*/
void* hashmapGet(Hashmap* map, void* key);
/**
* Returns true if the map contains an entry for the given key.
*/
bool hashmapContainsKey(Hashmap* map, void* key);
/**
* Gets the value for a key. If a value is not found, this function gets a
* value and creates an entry using the given callback.
*
* If memory allocation fails, the callback is not called, this function
* returns NULL, and errno is set to ENOMEM.
*/
void* hashmapMemoize(Hashmap* map, void* key,
void* (*initialValue)(void* key, void* context), void* context);
/**
* Removes an entry from the map. Returns the removed value or NULL if no
* entry was present.
*/
void* hashmapRemove(Hashmap* map, void* key);
/**
* Gets the number of entries in this map.
*/
size_t hashmapSize(Hashmap* map);
/**
* Invokes the given callback on each entry in the map. Stops iterating if
* the callback returns false.
*/
void hashmapForEach(Hashmap* map,
bool (*callback)(void* key, void* value, void* context),
void* context);
/**
* Concurrency support.
*/
/**
* Locks the hash map so only the current thread can access it.
*/
void hashmapLock(Hashmap* map);
/**
* Unlocks the hash map so other threads can access it.
*/
void hashmapUnlock(Hashmap* map);
/**
* Key utilities.
*/
/**
* Hashes int keys. 'key' is a pointer to int.
*/
int hashmapIntHash(void* key);
/**
* Compares two int keys for equality.
*/
bool hashmapIntEquals(void* keyA, void* keyB);
/**
* For debugging.
*/
/**
* Gets current capacity.
*/
size_t hashmapCurrentCapacity(Hashmap* map);
/**
* Counts the number of entry collisions.
*/
size_t hashmapCountCollisions(Hashmap* map);
#ifdef __cplusplus
}
#endif
#endif /* __HASHMAP_H */

43
include/cutils/jstring.h Normal file
View file

@ -0,0 +1,43 @@
/*
* Copyright (C) 2006 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 __CUTILS_STRING16_H
#define __CUTILS_STRING16_H
#include <stdint.h>
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef uint16_t char16_t;
extern char * strndup16to8 (const char16_t* s, size_t n);
extern size_t strnlen16to8 (const char16_t* s, size_t n);
extern char * strncpy16to8 (char *dest, const char16_t*s, size_t n);
extern char16_t * strdup8to16 (const char* s, size_t *out_len);
extern size_t strlen8to16 (const char* utf8Str);
extern char16_t * strcpy8to16 (char16_t *dest, const char*s, size_t *out_len);
extern char16_t * strcpylen8to16 (char16_t *dest, const char*s, int length,
size_t *out_len);
#ifdef __cplusplus
}
#endif
#endif /* __CUTILS_STRING16_H */

346
include/cutils/log.h Normal file
View file

@ -0,0 +1,346 @@
/*
* Copyright (C) 2005 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.
*/
//
// C/C++ logging functions. See the logging documentation for API details.
//
// We'd like these to be available from C code (in case we import some from
// somewhere), so this has a C interface.
//
// The output will be correct when the log file is shared between multiple
// threads and/or multiple processes so long as the operating system
// supports O_APPEND. These calls have mutex-protected data structures
// and so are NOT reentrant. Do not use LOG in a signal handler.
//
#ifndef _LIBS_CUTILS_LOG_H
#define _LIBS_CUTILS_LOG_H
#include <stdio.h>
#include <time.h>
#include <sys/types.h>
#include <unistd.h>
#ifdef HAVE_PTHREADS
#include <pthread.h>
#endif
#include <stdarg.h>
#include <cutils/uio.h>
#include <cutils/logd.h>
#ifdef __cplusplus
extern "C" {
#endif
// ---------------------------------------------------------------------
/*
* Normally we strip LOGV (VERBOSE messages) from release builds.
* You can modify this (for example with "#define LOG_NDEBUG 0"
* at the top of your source file) to change that behavior.
*/
#ifndef LOG_NDEBUG
#ifdef NDEBUG
#define LOG_NDEBUG 1
#else
#define LOG_NDEBUG 0
#endif
#endif
/*
* This is the local tag used for the following simplified
* logging macros. You can change this preprocessor definition
* before using the other macros to change the tag.
*/
#ifndef LOG_TAG
#define LOG_TAG NULL
#endif
// ---------------------------------------------------------------------
/*
* Simplified macro to send a verbose log message using the current LOG_TAG.
*/
#ifndef LOGV
#if LOG_NDEBUG
#define LOGV(...) ((void)0)
#else
#define LOGV(...) ((void)LOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__))
#endif
#endif
#define CONDITION(cond) (__builtin_expect((cond)!=0, 0))
#ifndef LOGV_IF
#if LOG_NDEBUG
#define LOGV_IF(cond, ...) ((void)0)
#else
#define LOGV_IF(cond, ...) \
( (CONDITION(cond)) \
? ((void)LOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) \
: (void)0 )
#endif
#endif
/*
* Simplified macro to send a debug log message using the current LOG_TAG.
*/
#ifndef LOGD
#define LOGD(...) ((void)LOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__))
#endif
#ifndef LOGD_IF
#define LOGD_IF(cond, ...) \
( (CONDITION(cond)) \
? ((void)LOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__)) \
: (void)0 )
#endif
/*
* Simplified macro to send an info log message using the current LOG_TAG.
*/
#ifndef LOGI
#define LOGI(...) ((void)LOG(LOG_INFO, LOG_TAG, __VA_ARGS__))
#endif
#ifndef LOGI_IF
#define LOGI_IF(cond, ...) \
( (CONDITION(cond)) \
? ((void)LOG(LOG_INFO, LOG_TAG, __VA_ARGS__)) \
: (void)0 )
#endif
/*
* Simplified macro to send a warning log message using the current LOG_TAG.
*/
#ifndef LOGW
#define LOGW(...) ((void)LOG(LOG_WARN, LOG_TAG, __VA_ARGS__))
#endif
#ifndef LOGW_IF
#define LOGW_IF(cond, ...) \
( (CONDITION(cond)) \
? ((void)LOG(LOG_WARN, LOG_TAG, __VA_ARGS__)) \
: (void)0 )
#endif
/*
* Simplified macro to send an error log message using the current LOG_TAG.
*/
#ifndef LOGE
#define LOGE(...) ((void)LOG(LOG_ERROR, LOG_TAG, __VA_ARGS__))
#endif
#ifndef LOGE_IF
#define LOGE_IF(cond, ...) \
( (CONDITION(cond)) \
? ((void)LOG(LOG_ERROR, LOG_TAG, __VA_ARGS__)) \
: (void)0 )
#endif
// ---------------------------------------------------------------------
/*
* Conditional based on whether the current LOG_TAG is enabled at
* verbose priority.
*/
#ifndef IF_LOGV
#if LOG_NDEBUG
#define IF_LOGV() if (false)
#else
#define IF_LOGV() IF_LOG(LOG_VERBOSE, LOG_TAG)
#endif
#endif
/*
* Conditional based on whether the current LOG_TAG is enabled at
* debug priority.
*/
#ifndef IF_LOGD
#define IF_LOGD() IF_LOG(LOG_DEBUG, LOG_TAG)
#endif
/*
* Conditional based on whether the current LOG_TAG is enabled at
* info priority.
*/
#ifndef IF_LOGI
#define IF_LOGI() IF_LOG(LOG_INFO, LOG_TAG)
#endif
/*
* Conditional based on whether the current LOG_TAG is enabled at
* warn priority.
*/
#ifndef IF_LOGW
#define IF_LOGW() IF_LOG(LOG_WARN, LOG_TAG)
#endif
/*
* Conditional based on whether the current LOG_TAG is enabled at
* error priority.
*/
#ifndef IF_LOGE
#define IF_LOGE() IF_LOG(LOG_ERROR, LOG_TAG)
#endif
// ---------------------------------------------------------------------
/*
* Log a fatal error. If the given condition fails, this stops program
* execution like a normal assertion, but also generating the given message.
* It is NOT stripped from release builds. Note that the condition test
* is -inverted- from the normal assert() semantics.
*/
#define LOG_ALWAYS_FATAL_IF(cond, ...) \
( (CONDITION(cond)) \
? ((void)android_printAssert(#cond, LOG_TAG, __VA_ARGS__)) \
: (void)0 )
#define LOG_ALWAYS_FATAL(...) \
( ((void)android_printAssert(NULL, LOG_TAG, __VA_ARGS__)) )
/*
* Versions of LOG_ALWAYS_FATAL_IF and LOG_ALWAYS_FATAL that
* are stripped out of release builds.
*/
#if LOG_NDEBUG
#define LOG_FATAL_IF(cond, ...) ((void)0)
#define LOG_FATAL(...) ((void)0)
#else
#define LOG_FATAL_IF(cond, ...) LOG_ALWAYS_FATAL_IF(cond, __VA_ARGS__)
#define LOG_FATAL(...) LOG_ALWAYS_FATAL(__VA_ARGS__)
#endif
/*
* Assertion that generates a log message when the assertion fails.
* Stripped out of release builds. Uses the current LOG_TAG.
*/
#define LOG_ASSERT(cond, ...) LOG_FATAL_IF(!(cond), __VA_ARGS__)
//#define LOG_ASSERT(cond) LOG_FATAL_IF(!(cond), "Assertion failed: " #cond)
// ---------------------------------------------------------------------
/*
* Basic log message macro.
*
* Example:
* LOG(LOG_WARN, NULL, "Failed with error %d", errno);
*
* The second argument may be NULL or "" to indicate the "global" tag.
*/
#ifndef LOG
#define LOG(priority, tag, ...) \
LOG_PRI(ANDROID_##priority, tag, __VA_ARGS__)
#endif
/*
* Log macro that allows you to specify a number for the priority.
*/
#ifndef LOG_PRI
#define LOG_PRI(priority, tag, ...) \
android_printLog(priority, tag, __VA_ARGS__)
#endif
/*
* Log macro that allows you to pass in a varargs ("args" is a va_list).
*/
#ifndef LOG_PRI_VA
#define LOG_PRI_VA(priority, tag, fmt, args) \
android_vprintLog(priority, NULL, tag, fmt, args)
#endif
/*
* Conditional given a desired logging priority and tag.
*/
#ifndef IF_LOG
#define IF_LOG(priority, tag) \
if (android_testLog(ANDROID_##priority, tag))
#endif
// ---------------------------------------------------------------------
/*
* Event logging.
*/
/*
* Event log entry types. These must match up with the declarations in
* java/android/android/util/EventLog.java.
*/
typedef enum {
EVENT_TYPE_INT = 0,
EVENT_TYPE_LONG = 1,
EVENT_TYPE_STRING = 2,
EVENT_TYPE_LIST = 3,
} AndroidEventLogType;
#define LOG_EVENT_INT(_tag, _value) { \
int intBuf = _value; \
(void) android_btWriteLog(_tag, EVENT_TYPE_INT, &intBuf, \
sizeof(intBuf)); \
}
#define LOG_EVENT_LONG(_tag, _value) { \
long long longBuf = _value; \
(void) android_btWriteLog(_tag, EVENT_TYPE_LONG, &longBuf, \
sizeof(longBuf)); \
}
#define LOG_EVENT_STRING(_tag, _value) \
((void) 0) /* not implemented -- must combine len with string */
/* TODO: something for LIST */
/*
* ===========================================================================
*
* The stuff in the rest of this file should not be used directly.
*/
#define android_printLog(prio, tag, fmt...) \
__android_log_print(prio, tag, fmt)
#define android_vprintLog(prio, cond, tag, fmt...) \
__android_log_vprint(prio, tag, fmt)
#define android_printAssert(cond, tag, fmt...) \
__android_log_assert(cond, tag, fmt)
#define android_writeLog(prio, tag, text) \
__android_log_write(prio, tag, text)
#define android_bWriteLog(tag, payload, len) \
__android_log_bwrite(tag, payload, len)
#define android_btWriteLog(tag, type, payload, len) \
__android_log_btwrite(tag, type, payload, len)
// TODO: remove these prototypes and their users
#define android_testLog(prio, tag) (1)
#define android_writevLog(vec,num) do{}while(0)
#define android_write1Log(str,len) do{}while (0)
#define android_setMinPriority(tag, prio) do{}while(0)
//#define android_logToCallback(func) do{}while(0)
#define android_logToFile(tag, file) (0)
#define android_logToFd(tag, fd) (0)
#ifdef __cplusplus
}
#endif
#endif // _LIBS_CUTILS_LOG_H

78
include/cutils/logd.h Normal file
View file

@ -0,0 +1,78 @@
/*
* Copyright (C) 2006 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 _ANDROID_CUTILS_LOGD_H
#define _ANDROID_CUTILS_LOGD_H
#include <time.h>
#include <stdio.h>
#include <unistd.h>
#include <stdint.h>
#include <sys/types.h>
#ifdef HAVE_PTHREADS
#include <pthread.h>
#endif
#include <cutils/uio.h>
#include <stdarg.h>
#ifdef __cplusplus
extern "C" {
#endif
/*
* Priority values, in ascending priority order.
*/
typedef enum android_LogPriority {
ANDROID_LOG_UNKNOWN = 0,
ANDROID_LOG_DEFAULT, /* only for SetMinPriority() */
ANDROID_LOG_VERBOSE,
ANDROID_LOG_DEBUG,
ANDROID_LOG_INFO,
ANDROID_LOG_WARN,
ANDROID_LOG_ERROR,
ANDROID_LOG_FATAL,
ANDROID_LOG_SILENT, /* only for SetMinPriority(); must be last */
} android_LogPriority;
int __android_log_write(int prio, const char *tag, const char *text);
int __android_log_vprint(int prio, const char *tag,
const char *fmt, va_list ap);
int __android_log_bwrite(int32_t tag, const void *payload, size_t len);
int __android_log_btwrite(int32_t tag, char type, const void *payload,
size_t len);
int __android_log_print(int prio, const char *tag, const char *fmt, ...)
#if defined(__GNUC__)
__attribute__ ((format(printf, 3, 4)))
#endif
;
void __android_log_assert(const char *cond, const char *tag,
const char *fmt, ...)
#if defined(__GNUC__)
__attribute__ ((noreturn))
__attribute__ ((format(printf, 3, 4)))
#endif
;
#ifdef __cplusplus
}
#endif
#endif /* _LOGD_H */

156
include/cutils/logprint.h Normal file
View file

@ -0,0 +1,156 @@
/*
* Copyright (C) 2006 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 _LOGPRINT_H
#define _LOGPRINT_H
#include <utils/Log.h>
#include <utils/logger.h>
#include <cutils/event_tag_map.h>
#include <pthread.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
FORMAT_OFF = 0,
FORMAT_BRIEF,
FORMAT_PROCESS,
FORMAT_TAG,
FORMAT_THREAD,
FORMAT_RAW,
FORMAT_TIME,
FORMAT_THREADTIME,
FORMAT_LONG,
} AndroidLogPrintFormat;
typedef struct AndroidLogFormat_t AndroidLogFormat;
typedef struct AndroidLogEntry_t {
time_t tv_sec;
long tv_nsec;
android_LogPriority priority;
pid_t pid;
pthread_t tid;
const char * tag;
size_t messageLen;
const char * message;
} AndroidLogEntry;
AndroidLogFormat *android_log_format_new();
void android_log_format_free(AndroidLogFormat *p_format);
void android_log_setPrintFormat(AndroidLogFormat *p_format,
AndroidLogPrintFormat format);
/**
* Returns FORMAT_OFF on invalid string
*/
AndroidLogPrintFormat android_log_formatFromString(const char *s);
/**
* filterExpression: a single filter expression
* eg "AT:d"
*
* returns 0 on success and -1 on invalid expression
*
* Assumes single threaded execution
*
*/
int android_log_addFilterRule(AndroidLogFormat *p_format,
const char *filterExpression);
/**
* filterString: a whitespace-separated set of filter expressions
* eg "AT:d *:i"
*
* returns 0 on success and -1 on invalid expression
*
* Assumes single threaded execution
*
*/
int android_log_addFilterString(AndroidLogFormat *p_format,
const char *filterString);
/**
* returns 1 if this log line should be printed based on its priority
* and tag, and 0 if it should not
*/
int android_log_shouldPrintLine (
AndroidLogFormat *p_format, const char *tag, android_LogPriority pri);
/**
* Splits a wire-format buffer into an AndroidLogEntry
* entry allocated by caller. Pointers will point directly into buf
*
* Returns 0 on success and -1 on invalid wire format (entry will be
* in unspecified state)
*/
int android_log_processLogBuffer(struct logger_entry *buf,
AndroidLogEntry *entry);
/**
* Like android_log_processLogBuffer, but for binary logs.
*
* If "map" is non-NULL, it will be used to convert the log tag number
* into a string.
*/
int android_log_processBinaryLogBuffer(struct logger_entry *buf,
AndroidLogEntry *entry, const EventTagMap* map, char* messageBuf,
int messageBufLen);
/**
* Formats a log message into a buffer
*
* Uses defaultBuffer if it can, otherwise malloc()'s a new buffer
* If return value != defaultBuffer, caller must call free()
* Returns NULL on malloc error
*/
char *android_log_formatLogLine (
AndroidLogFormat *p_format,
char *defaultBuffer,
size_t defaultBufferSize,
const AndroidLogEntry *p_line,
size_t *p_outLength);
/**
* Either print or do not print log line, based on filter
*
* Assumes single threaded execution
*
*/
int android_log_filterAndPrintLogLine(
AndroidLogFormat *p_format,
int fd,
const AndroidLogEntry *entry);
#ifdef __cplusplus
}
#endif
#endif /*_LOGPRINT_H*/

42
include/cutils/memory.h Normal file
View file

@ -0,0 +1,42 @@
/*
* Copyright (C) 2006 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 ANDROID_CUTILS_MEMORY_H
#define ANDROID_CUTILS_MEMORY_H
#include <stdint.h>
#include <sys/types.h>
#ifdef __cplusplus
extern "C" {
#endif
/* size is given in bytes and must be multiple of 2 */
void android_memset16(uint16_t* dst, uint16_t value, size_t size);
/* size is given in bytes and must be multiple of 4 */
void android_memset32(uint32_t* dst, uint32_t value, size_t size);
#if !HAVE_STRLCPY
/* Declaration of strlcpy() for platforms that don't already have it. */
size_t strlcpy(char *dst, const char *src, size_t size);
#endif
#ifdef __cplusplus
} // extern "C"
#endif
#endif // ANDROID_CUTILS_MEMORY_H

48
include/cutils/misc.h Normal file
View file

@ -0,0 +1,48 @@
/*
* Copyright (C) 2006 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 __CUTILS_MISC_H
#define __CUTILS_MISC_H
#ifdef __cplusplus
extern "C" {
#endif
/* Load an entire file into a malloc'd chunk of memory
* that is length_of_file + 1 (null terminator). If
* sz is non-zero, return the size of the file via sz.
* Returns 0 on failure.
*/
extern void *load_file(const char *fn, unsigned *sz);
/* Connects your process to the system debugger daemon
* so that on a crash it may be logged or interactively
* debugged (depending on system settings).
*/
extern void debuggerd_connect(void);
/* This is the range of UIDs (and GIDs) that are reserved
* for assigning to applications.
*/
#define FIRST_APPLICATION_UID 10000
#define LAST_APPLICATION_UID 99999
#ifdef __cplusplus
}
#endif
#endif /* __CUTILS_MISC_H */

124
include/cutils/mq.h Normal file
View file

@ -0,0 +1,124 @@
/*
* 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.
*/
/**
* IPC messaging library.
*/
#ifndef __MQ_H
#define __MQ_H
#ifdef __cplusplus
extern "C" {
#endif
/** A message. */
typedef struct MqMessage MqMessage;
/** A destination to which messages can be sent. */
typedef struct MqDestination MqDestination;
/* Array of bytes. */
typedef struct MqBytes MqBytes;
/**
* Hears messages.
*
* @param destination to which the message was sent
* @param message the message to hear
*/
typedef void MqMessageListener(MqDestination* destination, MqMessage* message);
/**
* Hears a destination close.
*
* @param destination that closed
*/
typedef void MqCloseListener(MqDestination* destination);
/** Message functions. */
/**
* Creates a new Message.
*
* @param header as defined by user
* @param body as defined by user
* @param replyTo destination to which replies should be sent, NULL if none
*/
MqMessage* mqCreateMessage(MqBytes header, MqBytes body,
MqDestination* replyTo);
/** Sends a message to a destination. */
void mqSendMessage(MqMessage* message, MqDestination* destination);
/** Destination functions. */
/**
* Creates a new destination. Acquires a reference implicitly.
*
* @param messageListener function to call when a message is recieved
* @param closeListener function to call when the destination closes
* @param userData user-specific data to associate with the destination.
* Retrieve using mqGetDestinationUserData().
*/
MqDestination* mqCreateDestination(MqMessageListener* messageListener,
MqCloseListener* closeListener, void* userData);
/**
* Gets user data which was associated with the given destination at
* construction time.
*
* It is only valid to call this function in the same process that the
* given destination was created in.
* This function returns a null pointer if you call it on a destination
* created in a remote process.
*/
void* mqGetUserData(MqDestination* destination);
/**
* Returns 1 if the destination was created in this process, or 0 if
* the destination was created in a different process, in which case you have
* a remote stub.
*/
int mqIsDestinationLocal(MqDestination* destination);
/**
* Increments the destination's reference count.
*/
void mqKeepDestination(MqDesintation* destination);
/**
* Decrements the destination's reference count.
*/
void mqFreeDestination(MqDestination* desintation);
/** Registry API. */
/**
* Gets the destination bound to a name.
*/
MqDestination* mqGetDestination(char* name);
/**
* Binds a destination to a name.
*/
void mqPutDestination(char* name, MqDestination* desintation);
#ifdef __cplusplus
}
#endif
#endif /* __MQ_H */

117
include/cutils/mspace.h Normal file
View file

@ -0,0 +1,117 @@
/*
* Copyright (C) 2006 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.
*/
/* A wrapper file for dlmalloc.h that defines prototypes for the
* mspace_*() functions, which provide an interface for creating
* multiple heaps.
*/
#ifndef MSPACE_H_
#define MSPACE_H_
/* It's a pain getting the mallinfo stuff to work
* with Linux, OSX, and klibc, so just turn it off
* for now.
* TODO: make mallinfo work
*/
#define NO_MALLINFO 1
/* Allow setting the maximum heap footprint.
*/
#define USE_MAX_ALLOWED_FOOTPRINT 1
#define USE_CONTIGUOUS_MSPACES 1
#if USE_CONTIGUOUS_MSPACES
#define HAVE_MMAP 0
#define HAVE_MORECORE 1
#define MORECORE_CONTIGUOUS 0
#endif
#define MSPACES 1
#define ONLY_MSPACES 1
#include "../../../../bionic/libc/bionic/dlmalloc.h"
#ifdef __cplusplus
extern "C" {
#endif
/*
mspace_usable_size(void* p);
Returns the number of bytes you can actually use in
an allocated chunk, which may be more than you requested (although
often not) due to alignment and minimum size constraints.
You can use this many bytes without worrying about
overwriting other allocated objects. This is not a particularly great
programming practice. mspace_usable_size can be more useful in
debugging and assertions, for example:
p = mspace_malloc(msp, n);
assert(mspace_usable_size(msp, p) >= 256);
*/
size_t mspace_usable_size(mspace, const void*);
#if USE_CONTIGUOUS_MSPACES
/*
Similar to create_mspace(), but the underlying memory is
guaranteed to be contiguous. No more than max_capacity
bytes is ever allocated to the mspace.
*/
mspace create_contiguous_mspace(size_t starting_capacity, size_t max_capacity,
int locked);
/*
Identical to create_contiguous_mspace, but labels the mapping 'mspace/name'
instead of 'mspace'
*/
mspace create_contiguous_mspace_with_name(size_t starting_capacity,
size_t max_capacity, int locked, const char *name);
size_t destroy_contiguous_mspace(mspace msp);
#endif
/*
Call the handler for each block in the specified mspace.
chunkptr and chunklen refer to the heap-level chunk including
the chunk overhead, and userptr and userlen refer to the
user-usable part of the chunk. If the chunk is free, userptr
will be NULL and userlen will be 0. userlen is not guaranteed
to be the same value passed into malloc() for a given chunk;
it is >= the requested size.
*/
void mspace_walk_heap(mspace msp,
void(*handler)(const void *chunkptr, size_t chunklen,
const void *userptr, size_t userlen, void *arg), void *harg);
/*
mspace_walk_free_pages(handler, harg)
Calls the provided handler on each free region in the specified
mspace. The memory between start and end are guaranteed not to
contain any important data, so the handler is free to alter the
contents in any way. This can be used to advise the OS that large
free regions may be swapped out.
The value in harg will be passed to each call of the handler.
*/
void mspace_walk_free_pages(mspace msp,
void(*handler)(void *start, void *end, void *arg), void *harg);
#ifdef __cplusplus
}; /* end of extern "C" */
#endif
#endif /* MSPACE_H_ */

View file

@ -0,0 +1,42 @@
/*
* Copyright (C) 2008 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.
*/
/**
* Gives the current process a name.
*/
#ifndef __PROCESS_NAME_H
#define __PROCESS_NAME_H
#ifdef __cplusplus
extern "C" {
#endif
/**
* Sets the current process name.
*
* Warning: This leaks a string every time you call it. Use judiciously!
*/
void set_process_name(const char* process_name);
/** Gets the current process name. */
const char* get_process_name(void);
#ifdef __cplusplus
}
#endif
#endif /* __PROCESS_NAME_H */

View file

@ -0,0 +1,70 @@
/*
* Copyright (C) 2006 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 __CUTILS_PROPERTIES_H
#define __CUTILS_PROPERTIES_H
#ifdef __cplusplus
extern "C" {
#endif
/* System properties are *small* name value pairs managed by the
** property service. If your data doesn't fit in the provided
** space it is not appropriate for a system property.
**
** WARNING: system/bionic/include/sys/system_properties.h also defines
** these, but with different names. (TODO: fix that)
*/
#define PROPERTY_KEY_MAX 32
#define PROPERTY_VALUE_MAX 92
/* property_get: returns the length of the value which will never be
** greater than PROPERTY_VALUE_MAX - 1 and will always be zero terminated.
** (the length does not include the terminating zero).
**
** If the property read fails or returns an empty value, the default
** value is used (if nonnull).
*/
int property_get(const char *key, char *value, const char *default_value);
/* property_set: returns 0 on success, < 0 on failure
*/
int property_set(const char *key, const char *value);
int property_list(void (*propfn)(const char *key, const char *value, void *cookie), void *cookie);
#ifdef HAVE_SYSTEM_PROPERTY_SERVER
/*
* We have an external property server instead of built-in libc support.
* Used by the simulator.
*/
#define SYSTEM_PROPERTY_PIPE_NAME "/tmp/android-sysprop"
enum {
kSystemPropertyUnknown = 0,
kSystemPropertyGet,
kSystemPropertySet,
kSystemPropertyList
};
#endif /*HAVE_SYSTEM_PROPERTY_SERVER*/
#ifdef __cplusplus
}
#endif
#endif

View file

@ -0,0 +1,43 @@
/*
* Copyright (C) 2006 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.
*/
/*
* A simple utility for reading fixed records out of a stream fd
*/
#ifndef _CUTILS_RECORD_STREAM_H
#define _CUTILS_RECORD_STREAM_H
#ifdef __cplusplus
extern "C" {
#endif
typedef struct RecordStream RecordStream;
extern RecordStream *record_stream_new(int fd, size_t maxRecordLen);
extern void record_stream_free(RecordStream *p_rs);
extern int record_stream_get_next (RecordStream *p_rs, void ** p_outRecord,
size_t *p_outRecordLen);
#ifdef __cplusplus
}
#endif
#endif /*_CUTILS_RECORD_STREAM_H*/

130
include/cutils/selector.h Normal file
View file

@ -0,0 +1,130 @@
/*
* 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.
*/
/**
* Framework for multiplexing I/O. A selector manages a set of file
* descriptors and calls out to user-provided callback functions to read and
* write data and handle errors.
*/
#ifndef __SELECTOR_H
#define __SELECTOR_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stdbool.h>
/**
* Manages SelectableFds and invokes their callbacks at appropriate times.
*/
typedef struct Selector Selector;
/**
* A selectable descriptor. Contains callbacks which the selector can invoke
* before calling select(), when the descriptor is readable or writable, and
* when the descriptor contains out-of-band data. Simply set a callback to
* NULL if you're not interested in that particular event.
*
* A selectable descriptor can indicate that it needs to be removed from the
* selector by setting the 'remove' flag. The selector will remove the
* descriptor at a later time and invoke the onRemove() callback.
*
* SelectableFd fields should only be modified from the selector loop.
*/
typedef struct SelectableFd SelectableFd;
struct SelectableFd {
/** The file descriptor itself. */
int fd;
/** Pointer to user-specific data. Can be NULL. */
void* data;
/**
* Set this flag when you no longer wish to be selected. The selector
* will invoke onRemove() when the descriptor is actually removed.
*/
bool remove;
/**
* Invoked by the selector before calling select. You can set up other
* callbacks from here as necessary.
*/
void (*beforeSelect)(SelectableFd* self);
/**
* Invoked by the selector when the descriptor has data available. Set to
* NULL to indicate that you're not interested in reading.
*/
void (*onReadable)(SelectableFd* self);
/**
* Invoked by the selector when the descriptor can accept data. Set to
* NULL to indicate that you're not interested in writing.
*/
void (*onWritable)(SelectableFd* self);
/**
* Invoked by the selector when out-of-band (OOB) data is available. Set to
* NULL to indicate that you're not interested in OOB data.
*/
void (*onExcept)(SelectableFd* self);
/**
* Invoked by the selector after the descriptor is removed from the
* selector but before the selector frees the SelectableFd memory.
*/
void (*onRemove)(SelectableFd* self);
/**
* The selector which selected this fd. Set by the selector itself.
*/
Selector* selector;
};
/**
* Creates a new selector.
*/
Selector* selectorCreate(void);
/**
* Creates a new selectable fd, adds it to the given selector and returns a
* pointer. Outside of 'selector' and 'fd', all fields are set to 0 or NULL
* by default.
*
* The selectable fd should only be modified from the selector loop thread.
*/
SelectableFd* selectorAdd(Selector* selector, int fd);
/**
* Wakes up the selector even though no I/O events occurred. Use this
* to indicate that you're ready to write to a descriptor.
*/
void selectorWakeUp(Selector* selector);
/**
* Loops continuously selecting file descriptors and firing events.
* Does not return.
*/
void selectorLoop(Selector* selector);
#ifdef __cplusplus
}
#endif
#endif /* __SELECTOR_H */

100
include/cutils/sockets.h Normal file
View file

@ -0,0 +1,100 @@
/*
* Copyright (C) 2006 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 __CUTILS_SOCKETS_H
#define __CUTILS_SOCKETS_H
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#ifdef HAVE_WINSOCK
#include <winsock2.h>
typedef int socklen_t;
#elif HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#define ANDROID_SOCKET_ENV_PREFIX "ANDROID_SOCKET_"
#define ANDROID_SOCKET_DIR "/dev/socket"
#ifdef __cplusplus
extern "C" {
#endif
/*
* android_get_control_socket - simple helper function to get the file
* descriptor of our init-managed Unix domain socket. `name' is the name of the
* socket, as given in init.rc. Returns -1 on error.
*
* This is inline and not in libcutils proper because we want to use this in
* third-party daemons with minimal modification.
*/
static inline int android_get_control_socket(const char *name)
{
char key[64] = ANDROID_SOCKET_ENV_PREFIX;
const char *val;
int fd;
/* build our environment variable, counting cycles like a wolf ... */
#if HAVE_STRLCPY
strlcpy(key + sizeof(ANDROID_SOCKET_ENV_PREFIX) - 1,
name,
sizeof(key) - sizeof(ANDROID_SOCKET_ENV_PREFIX));
#else /* for the host, which may lack the almightly strncpy ... */
strncpy(key + sizeof(ANDROID_SOCKET_ENV_PREFIX) - 1,
name,
sizeof(key) - sizeof(ANDROID_SOCKET_ENV_PREFIX));
key[sizeof(key)-1] = '\0';
#endif
val = getenv(key);
if (!val)
return -1;
errno = 0;
fd = strtol(val, NULL, 10);
if (errno)
return -1;
return fd;
}
/*
* See also android.os.LocalSocketAddress.Namespace
*/
// Linux "abstract" (non-filesystem) namespace
#define ANDROID_SOCKET_NAMESPACE_ABSTRACT 0
// Android "reserved" (/dev/socket) namespace
#define ANDROID_SOCKET_NAMESPACE_RESERVED 1
// Normal filesystem namespace
#define ANDROID_SOCKET_NAMESPACE_FILESYSTEM 2
extern int socket_loopback_client(int port, int type);
extern int socket_network_client(const char *host, int port, int type);
extern int socket_loopback_server(int port, int type);
extern int socket_local_server(const char *name, int namespaceId, int type);
extern int socket_local_server_bind(int s, const char *name, int namespaceId);
extern int socket_local_client_connect(int fd,
const char *name, int namespaceId, int type);
extern int socket_local_client(const char *name, int namespaceId, int type);
extern int socket_inaddr_any_server(int port, int type);
#ifdef __cplusplus
}
#endif
#endif /* __CUTILS_SOCKETS_H */

146
include/cutils/threads.h Normal file
View file

@ -0,0 +1,146 @@
/*
* 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 _LIBS_CUTILS_THREADS_H
#define _LIBS_CUTILS_THREADS_H
#ifdef __cplusplus
extern "C" {
#endif
/***********************************************************************/
/***********************************************************************/
/***** *****/
/***** local thread storage *****/
/***** *****/
/***********************************************************************/
/***********************************************************************/
#ifdef HAVE_PTHREADS
#include <pthread.h>
typedef struct {
pthread_mutex_t lock;
int has_tls;
pthread_key_t tls;
} thread_store_t;
#define THREAD_STORE_INITIALIZER { PTHREAD_MUTEX_INITIALIZER, 0, 0 }
#elif defined HAVE_WIN32_THREADS
#include <windows.h>
typedef struct {
int lock_init;
int has_tls;
DWORD tls;
CRITICAL_SECTION lock;
} thread_store_t;
#define THREAD_STORE_INITIALIZER { 0, 0, 0, {0, 0, 0, 0, 0, 0} }
#else
# error "no thread_store_t implementation for your platform !!"
#endif
typedef void (*thread_store_destruct_t)(void* value);
extern void* thread_store_get(thread_store_t* store);
extern void thread_store_set(thread_store_t* store,
void* value,
thread_store_destruct_t destroy);
/***********************************************************************/
/***********************************************************************/
/***** *****/
/***** mutexes *****/
/***** *****/
/***********************************************************************/
/***********************************************************************/
#ifdef HAVE_PTHREADS
typedef pthread_mutex_t mutex_t;
#define MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER
static __inline__ void mutex_lock(mutex_t* lock)
{
pthread_mutex_lock(lock);
}
static __inline__ void mutex_unlock(mutex_t* lock)
{
pthread_mutex_unlock(lock);
}
static __inline__ int mutex_init(mutex_t* lock)
{
return pthread_mutex_init(lock, NULL);
}
static __inline__ void mutex_destroy(mutex_t* lock)
{
pthread_mutex_destroy(lock);
}
#endif
#ifdef HAVE_WIN32_THREADS
typedef struct {
int init;
CRITICAL_SECTION lock[1];
} mutex_t;
#define MUTEX_INITIALIZER { 0, {{ NULL, 0, 0, NULL, NULL, 0 }} }
static __inline__ void mutex_lock(mutex_t* lock)
{
if (!lock->init) {
lock->init = 1;
InitializeCriticalSection( lock->lock );
lock->init = 2;
} else while (lock->init != 2)
Sleep(10);
EnterCriticalSection(lock->lock);
}
static __inline__ void mutex_unlock(mutex_t* lock)
{
LeaveCriticalSection(lock->lock);
}
static __inline__ int mutex_init(mutex_t* lock)
{
InitializeCriticalSection(lock->lock);
lock->init = 2;
return 0;
}
static __inline__ void mutex_destroy(mutex_t* lock)
{
if (lock->init) {
lock->init = 0;
DeleteCriticalSection(lock->lock);
}
}
#endif
#ifdef __cplusplus
}
#endif
#endif /* _LIBS_CUTILS_THREADS_H */

32
include/cutils/tztime.h Normal file
View file

@ -0,0 +1,32 @@
/*
* Copyright (C) 2006 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 _CUTILS_TZTIME_H
#define _CUTILS_TZTIME_H
#ifdef __cplusplus
extern "C" {
#endif
time_t mktime_tz(struct tm * const tmp, char const * tz);
void localtime_tz(const time_t * const timep, struct tm * tmp, const char* tz);
#ifdef __cplusplus
}
#endif
#endif /* __CUTILS_TZTIME_H */

Some files were not shown because too many files have changed in this diff Show more