Merge "support "sideload over ADB" mode"
This commit is contained in:
+4
-2
@@ -10,7 +10,8 @@ LOCAL_SRC_FILES := \
|
||||
roots.cpp \
|
||||
ui.cpp \
|
||||
screen_ui.cpp \
|
||||
verifier.cpp
|
||||
verifier.cpp \
|
||||
adb_install.cpp
|
||||
|
||||
LOCAL_MODULE := recovery
|
||||
|
||||
@@ -40,7 +41,7 @@ else
|
||||
LOCAL_STATIC_LIBRARIES += $(TARGET_RECOVERY_UI_LIB)
|
||||
endif
|
||||
LOCAL_STATIC_LIBRARIES += libext4_utils libz
|
||||
LOCAL_STATIC_LIBRARIES += libminzip libunz libmtdutils libmincrypt
|
||||
LOCAL_STATIC_LIBRARIES += libminzip libunz libmtdutils libmincrypt libminadbd
|
||||
LOCAL_STATIC_LIBRARIES += libminui libpixelflinger_static libpng libcutils
|
||||
LOCAL_STATIC_LIBRARIES += libstdc++ libc
|
||||
|
||||
@@ -67,6 +68,7 @@ include $(BUILD_EXECUTABLE)
|
||||
include $(commands_recovery_local_path)/minui/Android.mk
|
||||
include $(commands_recovery_local_path)/minelf/Android.mk
|
||||
include $(commands_recovery_local_path)/minzip/Android.mk
|
||||
include $(commands_recovery_local_path)/minadbd/Android.mk
|
||||
include $(commands_recovery_local_path)/mtdutils/Android.mk
|
||||
include $(commands_recovery_local_path)/tools/Android.mk
|
||||
include $(commands_recovery_local_path)/edify/Android.mk
|
||||
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Copyright (C) 2012 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 <unistd.h>
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/stat.h>
|
||||
#include <signal.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "ui.h"
|
||||
#include "cutils/properties.h"
|
||||
#include "install.h"
|
||||
#include "common.h"
|
||||
#include "adb_install.h"
|
||||
extern "C" {
|
||||
#include "minadbd/adb.h"
|
||||
}
|
||||
|
||||
static RecoveryUI* ui = NULL;
|
||||
|
||||
static void
|
||||
set_usb_driver(bool enabled) {
|
||||
int fd = open("/sys/class/android_usb/android0/enable", O_WRONLY);
|
||||
if (fd < 0) {
|
||||
ui->Print("failed to open driver control: %s\n", strerror(errno));
|
||||
return;
|
||||
}
|
||||
if (write(fd, enabled ? "1" : "0", 1) < 0) {
|
||||
ui->Print("failed to set driver control: %s\n", strerror(errno));
|
||||
}
|
||||
if (close(fd) < 0) {
|
||||
ui->Print("failed to close driver control: %s\n", strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
stop_adbd() {
|
||||
property_set("ctl.stop", "adbd");
|
||||
set_usb_driver(false);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
maybe_restart_adbd() {
|
||||
char value[PROPERTY_VALUE_MAX+1];
|
||||
int len = property_get("ro.debuggable", value, NULL);
|
||||
if (len == 1 && value[0] == '1') {
|
||||
ui->Print("Restarting adbd...\n");
|
||||
set_usb_driver(true);
|
||||
property_set("ctl.start", "adbd");
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
apply_from_adb(RecoveryUI* ui_, int* wipe_cache, const char* install_file) {
|
||||
ui = ui_;
|
||||
|
||||
stop_adbd();
|
||||
set_usb_driver(true);
|
||||
|
||||
ui->Print("\n\nNow send the package you want to apply\n"
|
||||
"to the device with \"adb sideload <filename>\"...\n");
|
||||
|
||||
pid_t child;
|
||||
if ((child = fork()) == 0) {
|
||||
execl("/sbin/recovery", "recovery", "--adbd", NULL);
|
||||
_exit(-1);
|
||||
}
|
||||
int status;
|
||||
// TODO(dougz): there should be a way to cancel waiting for a
|
||||
// package (by pushing some button combo on the device). For now
|
||||
// you just have to 'adb sideload' a file that's not a valid
|
||||
// package, like "/dev/null".
|
||||
waitpid(child, &status, 0);
|
||||
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
|
||||
ui->Print("status %d\n", WEXITSTATUS(status));
|
||||
}
|
||||
|
||||
set_usb_driver(false);
|
||||
maybe_restart_adbd();
|
||||
|
||||
struct stat st;
|
||||
if (stat(ADB_SIDELOAD_FILENAME, &st) != 0) {
|
||||
if (errno == ENOENT) {
|
||||
ui->Print("No package received.\n");
|
||||
} else {
|
||||
ui->Print("Error reading package:\n %s\n", strerror(errno));
|
||||
}
|
||||
return INSTALL_ERROR;
|
||||
}
|
||||
return install_package(ADB_SIDELOAD_FILENAME, wipe_cache, install_file);
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (C) 2012 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_INSTALL_H
|
||||
#define _ADB_INSTALL_H
|
||||
|
||||
class RecoveryUI;
|
||||
|
||||
int apply_from_adb(RecoveryUI* h, int* wipe_cache, const char* install_file);
|
||||
|
||||
#endif
|
||||
+4
-6
@@ -26,8 +26,7 @@ static const char* HEADERS[] = { "Volume up/down to move highlight;",
|
||||
NULL };
|
||||
|
||||
static const char* ITEMS[] = {"reboot system now",
|
||||
"apply update from external storage",
|
||||
"apply update from cache",
|
||||
"apply update from ADB",
|
||||
"wipe data/factory reset",
|
||||
"wipe cache partition",
|
||||
NULL };
|
||||
@@ -72,10 +71,9 @@ class DefaultDevice : public Device {
|
||||
BuiltinAction InvokeMenuItem(int menu_position) {
|
||||
switch (menu_position) {
|
||||
case 0: return REBOOT;
|
||||
case 1: return APPLY_EXT;
|
||||
case 2: return APPLY_CACHE;
|
||||
case 3: return WIPE_DATA;
|
||||
case 4: return WIPE_CACHE;
|
||||
case 1: return APPLY_ADB_SIDELOAD;
|
||||
case 2: return WIPE_DATA;
|
||||
case 3: return WIPE_CACHE;
|
||||
default: return NO_ACTION;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ class Device {
|
||||
virtual int HandleMenuKey(int key, int visible) = 0;
|
||||
|
||||
enum BuiltinAction { NO_ACTION, REBOOT, APPLY_EXT, APPLY_CACHE,
|
||||
WIPE_DATA, WIPE_CACHE };
|
||||
APPLY_ADB_SIDELOAD, WIPE_DATA, WIPE_CACHE };
|
||||
|
||||
// Perform a recovery action selected from the menu.
|
||||
// 'menu_position' will be the item number of the selected menu
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
# Copyright 2005 The Android Open Source Project
|
||||
#
|
||||
# Android.mk for adb
|
||||
#
|
||||
|
||||
LOCAL_PATH:= $(call my-dir)
|
||||
|
||||
# minadbd library
|
||||
# =========================================================
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_SRC_FILES := \
|
||||
adb.c \
|
||||
fdevent.c \
|
||||
transport.c \
|
||||
transport_usb.c \
|
||||
sockets.c \
|
||||
services.c \
|
||||
usb_linux_client.c \
|
||||
utils.c
|
||||
|
||||
LOCAL_CFLAGS := -O2 -g -DADB_HOST=0 -Wall -Wno-unused-parameter
|
||||
LOCAL_CFLAGS += -D_XOPEN_SOURCE -D_GNU_SOURCE
|
||||
|
||||
LOCAL_MODULE := libminadbd
|
||||
|
||||
LOCAL_STATIC_LIBRARIES := libcutils libc
|
||||
include $(BUILD_STATIC_LIBRARY)
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
The contents of this directory are copied from system/core/adb, with
|
||||
the following changes:
|
||||
|
||||
adb.c
|
||||
- much support for host mode and non-linux OS's stripped out; this
|
||||
version only runs as adbd on the device.
|
||||
- does not setuid/setgid itself (always stays root)
|
||||
- only uses USB transport
|
||||
- references to JDWP removed
|
||||
- main() removed
|
||||
|
||||
adb.h
|
||||
- minor changes to match adb.c changes
|
||||
|
||||
sockets.c
|
||||
- references to JDWP removed
|
||||
|
||||
services.c
|
||||
- all services except echo_service (which is commented out) removed
|
||||
- all host mode support removed
|
||||
- sideload_service() added; this is the only service supported. It
|
||||
receives a single blob of data, writes it to a fixed filename, and
|
||||
makes the process exit.
|
||||
|
||||
Android.mk
|
||||
- only builds in adbd mode; builds as static library instead of a
|
||||
standalone executable.
|
||||
+1151
File diff suppressed because it is too large
Load Diff
+443
@@ -0,0 +1,443 @@
|
||||
/*
|
||||
* 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>
|
||||
|
||||
#include "transport.h" /* readx(), writex() */
|
||||
#include "fdevent.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 29 // Increment this when we want to force users to start a new adb server
|
||||
|
||||
typedef struct amessage amessage;
|
||||
typedef struct apacket apacket;
|
||||
typedef struct asocket asocket;
|
||||
typedef struct alistener alistener;
|
||||
typedef struct aservice aservice;
|
||||
typedef struct atransport atransport;
|
||||
typedef struct adisconnect adisconnect;
|
||||
typedef struct usb_handle usb_handle;
|
||||
|
||||
struct amessage {
|
||||
unsigned command; /* command identifier constant */
|
||||
unsigned arg0; /* first argument */
|
||||
unsigned arg1; /* second argument */
|
||||
unsigned data_length; /* length of payload (0 is allowed) */
|
||||
unsigned data_check; /* checksum of data payload */
|
||||
unsigned magic; /* command ^ 0xffffffff */
|
||||
};
|
||||
|
||||
struct apacket
|
||||
{
|
||||
apacket *next;
|
||||
|
||||
unsigned len;
|
||||
unsigned char *ptr;
|
||||
|
||||
amessage msg;
|
||||
unsigned char data[MAX_PAYLOAD];
|
||||
};
|
||||
|
||||
/* An asocket represents one half of a connection between a local and
|
||||
** remote entity. A local asocket is bound to a file descriptor. A
|
||||
** remote asocket is bound to the protocol engine.
|
||||
*/
|
||||
struct asocket {
|
||||
/* chain pointers for the local/remote list of
|
||||
** asockets that this asocket lives in
|
||||
*/
|
||||
asocket *next;
|
||||
asocket *prev;
|
||||
|
||||
/* the unique identifier for this asocket
|
||||
*/
|
||||
unsigned id;
|
||||
|
||||
/* flag: set when the socket's peer has closed
|
||||
** but packets are still queued for delivery
|
||||
*/
|
||||
int closing;
|
||||
|
||||
/* the asocket we are connected to
|
||||
*/
|
||||
|
||||
asocket *peer;
|
||||
|
||||
/* For local asockets, the fde is used to bind
|
||||
** us to our fd event system. For remote asockets
|
||||
** these fields are not used.
|
||||
*/
|
||||
fdevent fde;
|
||||
int fd;
|
||||
|
||||
/* queue of apackets waiting to be written
|
||||
*/
|
||||
apacket *pkt_first;
|
||||
apacket *pkt_last;
|
||||
|
||||
/* enqueue is called by our peer when it has data
|
||||
** for us. It should return 0 if we can accept more
|
||||
** data or 1 if not. If we return 1, we must call
|
||||
** peer->ready() when we once again are ready to
|
||||
** receive data.
|
||||
*/
|
||||
int (*enqueue)(asocket *s, apacket *pkt);
|
||||
|
||||
/* ready is called by the peer when it is ready for
|
||||
** us to send data via enqueue again
|
||||
*/
|
||||
void (*ready)(asocket *s);
|
||||
|
||||
/* close is called by the peer when it has gone away.
|
||||
** we are not allowed to make any further calls on the
|
||||
** peer once our close method is called.
|
||||
*/
|
||||
void (*close)(asocket *s);
|
||||
|
||||
/* socket-type-specific extradata */
|
||||
void *extra;
|
||||
|
||||
/* A socket is bound to atransport */
|
||||
atransport *transport;
|
||||
};
|
||||
|
||||
|
||||
/* the adisconnect structure is used to record a callback that
|
||||
** will be called whenever a transport is disconnected (e.g. by the user)
|
||||
** this should be used to cleanup objects that depend on the
|
||||
** transport (e.g. remote sockets, listeners, etc...)
|
||||
*/
|
||||
struct adisconnect
|
||||
{
|
||||
void (*func)(void* opaque, atransport* t);
|
||||
void* opaque;
|
||||
adisconnect* next;
|
||||
adisconnect* prev;
|
||||
};
|
||||
|
||||
|
||||
/* a transport object models the connection to a remote device or emulator
|
||||
** there is one transport per connected device/emulator. a "local transport"
|
||||
** connects through TCP (for the emulator), while a "usb transport" through
|
||||
** USB (for real devices)
|
||||
**
|
||||
** note that kTransportHost doesn't really correspond to a real transport
|
||||
** object, it's a special value used to indicate that a client wants to
|
||||
** connect to a service implemented within the ADB server itself.
|
||||
*/
|
||||
typedef enum transport_type {
|
||||
kTransportUsb,
|
||||
kTransportLocal,
|
||||
kTransportAny,
|
||||
kTransportHost,
|
||||
} transport_type;
|
||||
|
||||
struct atransport
|
||||
{
|
||||
atransport *next;
|
||||
atransport *prev;
|
||||
|
||||
int (*read_from_remote)(apacket *p, atransport *t);
|
||||
int (*write_to_remote)(apacket *p, atransport *t);
|
||||
void (*close)(atransport *t);
|
||||
void (*kick)(atransport *t);
|
||||
|
||||
int fd;
|
||||
int transport_socket;
|
||||
fdevent transport_fde;
|
||||
int ref_count;
|
||||
unsigned sync_token;
|
||||
int connection_state;
|
||||
transport_type type;
|
||||
|
||||
/* usb handle or socket fd as needed */
|
||||
usb_handle *usb;
|
||||
int sfd;
|
||||
|
||||
/* used to identify transports for clients */
|
||||
char *serial;
|
||||
char *product;
|
||||
int adb_port; // Use for emulators (local transport)
|
||||
|
||||
/* 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, size_t maxLen);
|
||||
int launch_server(int server_port);
|
||||
int adb_main();
|
||||
|
||||
|
||||
/* 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 */
|
||||
#if ADB_HOST
|
||||
int get_available_local_transport_index();
|
||||
#endif
|
||||
int init_socket_transport(atransport *t, int s, int port, int local);
|
||||
void init_usb_transport(atransport *t, usb_handle *usb, int state);
|
||||
|
||||
/* 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, int local);
|
||||
|
||||
/* these should only be used for the "adb disconnect" command */
|
||||
void unregister_transport(atransport *t);
|
||||
void unregister_all_tcp_transports();
|
||||
|
||||
void register_usb_transport(usb_handle *h, const char *serial, unsigned writeable);
|
||||
|
||||
/* this should only be used for transports with connection_state == CS_NOPERM */
|
||||
void unregister_usb_transport(usb_handle *usb);
|
||||
|
||||
atransport *find_transport(const char *serial);
|
||||
#if ADB_HOST
|
||||
atransport* find_emulator_transport_by_adb_port(int adb_port);
|
||||
#endif
|
||||
|
||||
int service_to_fd(const char *name);
|
||||
#if ADB_HOST
|
||||
asocket *host_service_to_socket(const char* name, const char *serial);
|
||||
#endif
|
||||
|
||||
#if !ADB_HOST
|
||||
typedef enum {
|
||||
BACKUP,
|
||||
RESTORE
|
||||
} BackupOperation;
|
||||
int backup_service(BackupOperation operation, char* args);
|
||||
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);
|
||||
|
||||
/* 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, /* 0x001 */
|
||||
TRACE_SOCKETS,
|
||||
TRACE_PACKETS,
|
||||
TRACE_TRANSPORT,
|
||||
TRACE_RWX, /* 0x010 */
|
||||
TRACE_USB,
|
||||
TRACE_SYNC,
|
||||
TRACE_SYSDEPS,
|
||||
TRACE_JDWP, /* 0x100 */
|
||||
TRACE_SERVICES,
|
||||
} AdbTrace;
|
||||
|
||||
#if ADB_TRACE
|
||||
|
||||
extern int adb_trace_mask;
|
||||
extern unsigned char adb_trace_output_count;
|
||||
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) { \
|
||||
int save_errno = errno; \
|
||||
adb_mutex_lock(&D_lock); \
|
||||
fprintf(stderr, "%s::%s():", \
|
||||
__FILE__, __FUNCTION__); \
|
||||
errno = save_errno; \
|
||||
fprintf(stderr, __VA_ARGS__ ); \
|
||||
fflush(stderr); \
|
||||
adb_mutex_unlock(&D_lock); \
|
||||
errno = save_errno; \
|
||||
} \
|
||||
} while (0)
|
||||
# define DR(...) \
|
||||
do { \
|
||||
if (ADB_TRACING) { \
|
||||
int save_errno = errno; \
|
||||
adb_mutex_lock(&D_lock); \
|
||||
errno = save_errno; \
|
||||
fprintf(stderr, __VA_ARGS__ ); \
|
||||
fflush(stderr); \
|
||||
adb_mutex_unlock(&D_lock); \
|
||||
errno = save_errno; \
|
||||
} \
|
||||
} while (0)
|
||||
#else
|
||||
# define D(...) ((void)0)
|
||||
# define DR(...) ((void)0)
|
||||
# define ADB_TRACING 0
|
||||
#endif
|
||||
|
||||
|
||||
#if !TRACE_PACKETS
|
||||
#define print_packet(tag,p) do {} while (0)
|
||||
#endif
|
||||
|
||||
#if ADB_HOST_ON_TARGET
|
||||
/* adb and adbd are coexisting on the target, so use 5038 for adb
|
||||
* to avoid conflicting with adbd's usage of 5037
|
||||
*/
|
||||
# define DEFAULT_ADB_PORT 5038
|
||||
#else
|
||||
# define DEFAULT_ADB_PORT 5037
|
||||
#endif
|
||||
|
||||
#define DEFAULT_ADB_LOCAL_TRANSPORT_PORT 5555
|
||||
|
||||
#define ADB_CLASS 0xff
|
||||
#define ADB_SUBCLASS 0x42
|
||||
#define ADB_PROTOCOL 0x1
|
||||
|
||||
|
||||
void local_init(int port);
|
||||
int local_connect(int port);
|
||||
int local_connect_arbitrary_ports(int console_port, int adb_port);
|
||||
|
||||
/* usb host/client interface */
|
||||
void usb_init();
|
||||
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 */
|
||||
#if ADB_HOST
|
||||
int is_adb_interface(int vid, int pid, int usb_class, int usb_subclass, int usb_protocol);
|
||||
#endif
|
||||
|
||||
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_NOPERM 5 /* Insufficient permissions to communicate with the device */
|
||||
#define CS_SIDELOAD 6
|
||||
|
||||
extern int HOST;
|
||||
extern int SHELL_EXIT_NOTIFY_FD;
|
||||
|
||||
#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);
|
||||
|
||||
#define ADB_SIDELOAD_FILENAME "/tmp/update.zip"
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,695 @@
|
||||
/* http://frotznet.googlecode.com/svn/trunk/utils/fdevent.c
|
||||
**
|
||||
** Copyright 2006, Brian Swetland <swetland@frotz.net>
|
||||
**
|
||||
** 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/ioctl.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "fdevent.h"
|
||||
#include "transport.h"
|
||||
#include "sysdeps.h"
|
||||
|
||||
|
||||
/* !!! Do not enable DEBUG for the adb that will run as the server:
|
||||
** both stdout and stderr are used to communicate between the client
|
||||
** and server. Any extra output will cause failures.
|
||||
*/
|
||||
#define DEBUG 0 /* non-0 will break adb server */
|
||||
|
||||
// This socket is used when a subproc shell service exists.
|
||||
// It wakes up the fdevent_loop() and cause the correct handling
|
||||
// of the shell's pseudo-tty master. I.e. force close it.
|
||||
int SHELL_EXIT_NOTIFY_FD = -1;
|
||||
|
||||
static void fatal(const char *fn, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
fprintf(stderr, "%s:", fn);
|
||||
vfprintf(stderr, fmt, ap);
|
||||
va_end(ap);
|
||||
abort();
|
||||
}
|
||||
|
||||
#define FATAL(x...) fatal(__FUNCTION__, x)
|
||||
|
||||
#if DEBUG
|
||||
#define D(...) \
|
||||
do { \
|
||||
adb_mutex_lock(&D_lock); \
|
||||
int save_errno = errno; \
|
||||
fprintf(stderr, "%s::%s():", __FILE__, __FUNCTION__); \
|
||||
errno = save_errno; \
|
||||
fprintf(stderr, __VA_ARGS__); \
|
||||
adb_mutex_unlock(&D_lock); \
|
||||
errno = save_errno; \
|
||||
} while(0)
|
||||
static void dump_fde(fdevent *fde, const char *info)
|
||||
{
|
||||
adb_mutex_lock(&D_lock);
|
||||
fprintf(stderr,"FDE #%03d %c%c%c %s\n", fde->fd,
|
||||
fde->state & FDE_READ ? 'R' : ' ',
|
||||
fde->state & FDE_WRITE ? 'W' : ' ',
|
||||
fde->state & FDE_ERROR ? 'E' : ' ',
|
||||
info);
|
||||
adb_mutex_unlock(&D_lock);
|
||||
}
|
||||
#else
|
||||
#define D(...) ((void)0)
|
||||
#define dump_fde(fde, info) do { } while(0)
|
||||
#endif
|
||||
|
||||
#define FDE_EVENTMASK 0x00ff
|
||||
#define FDE_STATEMASK 0xff00
|
||||
|
||||
#define FDE_ACTIVE 0x0100
|
||||
#define FDE_PENDING 0x0200
|
||||
#define FDE_CREATED 0x0400
|
||||
|
||||
static void fdevent_plist_enqueue(fdevent *node);
|
||||
static void fdevent_plist_remove(fdevent *node);
|
||||
static fdevent *fdevent_plist_dequeue(void);
|
||||
static void fdevent_subproc_event_func(int fd, unsigned events, void *userdata);
|
||||
|
||||
static fdevent list_pending = {
|
||||
.next = &list_pending,
|
||||
.prev = &list_pending,
|
||||
};
|
||||
|
||||
static fdevent **fd_table = 0;
|
||||
static int fd_table_max = 0;
|
||||
|
||||
#ifdef CRAPTASTIC
|
||||
//HAVE_EPOLL
|
||||
|
||||
#include <sys/epoll.h>
|
||||
|
||||
static int epoll_fd = -1;
|
||||
|
||||
static void fdevent_init()
|
||||
{
|
||||
/* XXX: what's a good size for the passed in hint? */
|
||||
epoll_fd = epoll_create(256);
|
||||
|
||||
if(epoll_fd < 0) {
|
||||
perror("epoll_create() failed");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* mark for close-on-exec */
|
||||
fcntl(epoll_fd, F_SETFD, FD_CLOEXEC);
|
||||
}
|
||||
|
||||
static void fdevent_connect(fdevent *fde)
|
||||
{
|
||||
struct epoll_event ev;
|
||||
|
||||
memset(&ev, 0, sizeof(ev));
|
||||
ev.events = 0;
|
||||
ev.data.ptr = fde;
|
||||
|
||||
#if 0
|
||||
if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fde->fd, &ev)) {
|
||||
perror("epoll_ctl() failed\n");
|
||||
exit(1);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void fdevent_disconnect(fdevent *fde)
|
||||
{
|
||||
struct epoll_event ev;
|
||||
|
||||
memset(&ev, 0, sizeof(ev));
|
||||
ev.events = 0;
|
||||
ev.data.ptr = fde;
|
||||
|
||||
/* technically we only need to delete if we
|
||||
** were actively monitoring events, but let's
|
||||
** be aggressive and do it anyway, just in case
|
||||
** something's out of sync
|
||||
*/
|
||||
epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fde->fd, &ev);
|
||||
}
|
||||
|
||||
static void fdevent_update(fdevent *fde, unsigned events)
|
||||
{
|
||||
struct epoll_event ev;
|
||||
int active;
|
||||
|
||||
active = (fde->state & FDE_EVENTMASK) != 0;
|
||||
|
||||
memset(&ev, 0, sizeof(ev));
|
||||
ev.events = 0;
|
||||
ev.data.ptr = fde;
|
||||
|
||||
if(events & FDE_READ) ev.events |= EPOLLIN;
|
||||
if(events & FDE_WRITE) ev.events |= EPOLLOUT;
|
||||
if(events & FDE_ERROR) ev.events |= (EPOLLERR | EPOLLHUP);
|
||||
|
||||
fde->state = (fde->state & FDE_STATEMASK) | events;
|
||||
|
||||
if(active) {
|
||||
/* we're already active. if we're changing to *no*
|
||||
** events being monitored, we need to delete, otherwise
|
||||
** we need to just modify
|
||||
*/
|
||||
if(ev.events) {
|
||||
if(epoll_ctl(epoll_fd, EPOLL_CTL_MOD, fde->fd, &ev)) {
|
||||
perror("epoll_ctl() failed\n");
|
||||
exit(1);
|
||||
}
|
||||
} else {
|
||||
if(epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fde->fd, &ev)) {
|
||||
perror("epoll_ctl() failed\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* we're not active. if we're watching events, we need
|
||||
** to add, otherwise we can just do nothing
|
||||
*/
|
||||
if(ev.events) {
|
||||
if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fde->fd, &ev)) {
|
||||
perror("epoll_ctl() failed\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void fdevent_process()
|
||||
{
|
||||
struct epoll_event events[256];
|
||||
fdevent *fde;
|
||||
int i, n;
|
||||
|
||||
n = epoll_wait(epoll_fd, events, 256, -1);
|
||||
|
||||
if(n < 0) {
|
||||
if(errno == EINTR) return;
|
||||
perror("epoll_wait");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
for(i = 0; i < n; i++) {
|
||||
struct epoll_event *ev = events + i;
|
||||
fde = ev->data.ptr;
|
||||
|
||||
if(ev->events & EPOLLIN) {
|
||||
fde->events |= FDE_READ;
|
||||
}
|
||||
if(ev->events & EPOLLOUT) {
|
||||
fde->events |= FDE_WRITE;
|
||||
}
|
||||
if(ev->events & (EPOLLERR | EPOLLHUP)) {
|
||||
fde->events |= FDE_ERROR;
|
||||
}
|
||||
if(fde->events) {
|
||||
if(fde->state & FDE_PENDING) continue;
|
||||
fde->state |= FDE_PENDING;
|
||||
fdevent_plist_enqueue(fde);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#else /* USE_SELECT */
|
||||
|
||||
#ifdef HAVE_WINSOCK
|
||||
#include <winsock2.h>
|
||||
#else
|
||||
#include <sys/select.h>
|
||||
#endif
|
||||
|
||||
static fd_set read_fds;
|
||||
static fd_set write_fds;
|
||||
static fd_set error_fds;
|
||||
|
||||
static int select_n = 0;
|
||||
|
||||
static void fdevent_init(void)
|
||||
{
|
||||
FD_ZERO(&read_fds);
|
||||
FD_ZERO(&write_fds);
|
||||
FD_ZERO(&error_fds);
|
||||
}
|
||||
|
||||
static void fdevent_connect(fdevent *fde)
|
||||
{
|
||||
if(fde->fd >= select_n) {
|
||||
select_n = fde->fd + 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void fdevent_disconnect(fdevent *fde)
|
||||
{
|
||||
int i, n;
|
||||
|
||||
FD_CLR(fde->fd, &read_fds);
|
||||
FD_CLR(fde->fd, &write_fds);
|
||||
FD_CLR(fde->fd, &error_fds);
|
||||
|
||||
for(n = 0, i = 0; i < select_n; i++) {
|
||||
if(fd_table[i] != 0) n = i;
|
||||
}
|
||||
select_n = n + 1;
|
||||
}
|
||||
|
||||
static void fdevent_update(fdevent *fde, unsigned events)
|
||||
{
|
||||
if(events & FDE_READ) {
|
||||
FD_SET(fde->fd, &read_fds);
|
||||
} else {
|
||||
FD_CLR(fde->fd, &read_fds);
|
||||
}
|
||||
if(events & FDE_WRITE) {
|
||||
FD_SET(fde->fd, &write_fds);
|
||||
} else {
|
||||
FD_CLR(fde->fd, &write_fds);
|
||||
}
|
||||
if(events & FDE_ERROR) {
|
||||
FD_SET(fde->fd, &error_fds);
|
||||
} else {
|
||||
FD_CLR(fde->fd, &error_fds);
|
||||
}
|
||||
|
||||
fde->state = (fde->state & FDE_STATEMASK) | events;
|
||||
}
|
||||
|
||||
/* Looks at fd_table[] for bad FDs and sets bit in fds.
|
||||
** Returns the number of bad FDs.
|
||||
*/
|
||||
static int fdevent_fd_check(fd_set *fds)
|
||||
{
|
||||
int i, n = 0;
|
||||
fdevent *fde;
|
||||
|
||||
for(i = 0; i < select_n; i++) {
|
||||
fde = fd_table[i];
|
||||
if(fde == 0) continue;
|
||||
if(fcntl(i, F_GETFL, NULL) < 0) {
|
||||
FD_SET(i, fds);
|
||||
n++;
|
||||
// fde->state |= FDE_DONT_CLOSE;
|
||||
|
||||
}
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
#if !DEBUG
|
||||
static inline void dump_all_fds(const char *extra_msg) {}
|
||||
#else
|
||||
static void dump_all_fds(const char *extra_msg)
|
||||
{
|
||||
int i;
|
||||
fdevent *fde;
|
||||
// per fd: 4 digits (but really: log10(FD_SETSIZE)), 1 staus, 1 blank
|
||||
char msg_buff[FD_SETSIZE*6 + 1], *pb=msg_buff;
|
||||
size_t max_chars = FD_SETSIZE * 6 + 1;
|
||||
int printed_out;
|
||||
#define SAFE_SPRINTF(...) \
|
||||
do { \
|
||||
printed_out = snprintf(pb, max_chars, __VA_ARGS__); \
|
||||
if (printed_out <= 0) { \
|
||||
D("... snprintf failed.\n"); \
|
||||
return; \
|
||||
} \
|
||||
if (max_chars < (unsigned int)printed_out) { \
|
||||
D("... snprintf out of space.\n"); \
|
||||
return; \
|
||||
} \
|
||||
pb += printed_out; \
|
||||
max_chars -= printed_out; \
|
||||
} while(0)
|
||||
|
||||
for(i = 0; i < select_n; i++) {
|
||||
fde = fd_table[i];
|
||||
SAFE_SPRINTF("%d", i);
|
||||
if(fde == 0) {
|
||||
SAFE_SPRINTF("? ");
|
||||
continue;
|
||||
}
|
||||
if(fcntl(i, F_GETFL, NULL) < 0) {
|
||||
SAFE_SPRINTF("b");
|
||||
}
|
||||
SAFE_SPRINTF(" ");
|
||||
}
|
||||
D("%s fd_table[]->fd = {%s}\n", extra_msg, msg_buff);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void fdevent_process()
|
||||
{
|
||||
int i, n;
|
||||
fdevent *fde;
|
||||
unsigned events;
|
||||
fd_set rfd, wfd, efd;
|
||||
|
||||
memcpy(&rfd, &read_fds, sizeof(fd_set));
|
||||
memcpy(&wfd, &write_fds, sizeof(fd_set));
|
||||
memcpy(&efd, &error_fds, sizeof(fd_set));
|
||||
|
||||
dump_all_fds("pre select()");
|
||||
|
||||
n = select(select_n, &rfd, &wfd, &efd, NULL);
|
||||
int saved_errno = errno;
|
||||
D("select() returned n=%d, errno=%d\n", n, n<0?saved_errno:0);
|
||||
|
||||
dump_all_fds("post select()");
|
||||
|
||||
if(n < 0) {
|
||||
switch(saved_errno) {
|
||||
case EINTR: return;
|
||||
case EBADF:
|
||||
// Can't trust the FD sets after an error.
|
||||
FD_ZERO(&wfd);
|
||||
FD_ZERO(&efd);
|
||||
FD_ZERO(&rfd);
|
||||
break;
|
||||
default:
|
||||
D("Unexpected select() error=%d\n", saved_errno);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if(n <= 0) {
|
||||
// We fake a read, as the rest of the code assumes
|
||||
// that errors will be detected at that point.
|
||||
n = fdevent_fd_check(&rfd);
|
||||
}
|
||||
|
||||
for(i = 0; (i < select_n) && (n > 0); i++) {
|
||||
events = 0;
|
||||
if(FD_ISSET(i, &rfd)) { events |= FDE_READ; n--; }
|
||||
if(FD_ISSET(i, &wfd)) { events |= FDE_WRITE; n--; }
|
||||
if(FD_ISSET(i, &efd)) { events |= FDE_ERROR; n--; }
|
||||
|
||||
if(events) {
|
||||
fde = fd_table[i];
|
||||
if(fde == 0)
|
||||
FATAL("missing fde for fd %d\n", i);
|
||||
|
||||
fde->events |= events;
|
||||
|
||||
D("got events fde->fd=%d events=%04x, state=%04x\n",
|
||||
fde->fd, fde->events, fde->state);
|
||||
if(fde->state & FDE_PENDING) continue;
|
||||
fde->state |= FDE_PENDING;
|
||||
fdevent_plist_enqueue(fde);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static void fdevent_register(fdevent *fde)
|
||||
{
|
||||
if(fde->fd < 0) {
|
||||
FATAL("bogus negative fd (%d)\n", fde->fd);
|
||||
}
|
||||
|
||||
if(fde->fd >= fd_table_max) {
|
||||
int oldmax = fd_table_max;
|
||||
if(fde->fd > 32000) {
|
||||
FATAL("bogus huuuuge fd (%d)\n", fde->fd);
|
||||
}
|
||||
if(fd_table_max == 0) {
|
||||
fdevent_init();
|
||||
fd_table_max = 256;
|
||||
}
|
||||
while(fd_table_max <= fde->fd) {
|
||||
fd_table_max *= 2;
|
||||
}
|
||||
fd_table = realloc(fd_table, sizeof(fdevent*) * fd_table_max);
|
||||
if(fd_table == 0) {
|
||||
FATAL("could not expand fd_table to %d entries\n", fd_table_max);
|
||||
}
|
||||
memset(fd_table + oldmax, 0, sizeof(int) * (fd_table_max - oldmax));
|
||||
}
|
||||
|
||||
fd_table[fde->fd] = fde;
|
||||
}
|
||||
|
||||
static void fdevent_unregister(fdevent *fde)
|
||||
{
|
||||
if((fde->fd < 0) || (fde->fd >= fd_table_max)) {
|
||||
FATAL("fd out of range (%d)\n", fde->fd);
|
||||
}
|
||||
|
||||
if(fd_table[fde->fd] != fde) {
|
||||
FATAL("fd_table out of sync [%d]\n", fde->fd);
|
||||
}
|
||||
|
||||
fd_table[fde->fd] = 0;
|
||||
|
||||
if(!(fde->state & FDE_DONT_CLOSE)) {
|
||||
dump_fde(fde, "close");
|
||||
adb_close(fde->fd);
|
||||
}
|
||||
}
|
||||
|
||||
static void fdevent_plist_enqueue(fdevent *node)
|
||||
{
|
||||
fdevent *list = &list_pending;
|
||||
|
||||
node->next = list;
|
||||
node->prev = list->prev;
|
||||
node->prev->next = node;
|
||||
list->prev = node;
|
||||
}
|
||||
|
||||
static void fdevent_plist_remove(fdevent *node)
|
||||
{
|
||||
node->prev->next = node->next;
|
||||
node->next->prev = node->prev;
|
||||
node->next = 0;
|
||||
node->prev = 0;
|
||||
}
|
||||
|
||||
static fdevent *fdevent_plist_dequeue(void)
|
||||
{
|
||||
fdevent *list = &list_pending;
|
||||
fdevent *node = list->next;
|
||||
|
||||
if(node == list) return 0;
|
||||
|
||||
list->next = node->next;
|
||||
list->next->prev = list;
|
||||
node->next = 0;
|
||||
node->prev = 0;
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
static void fdevent_call_fdfunc(fdevent* fde)
|
||||
{
|
||||
unsigned events = fde->events;
|
||||
fde->events = 0;
|
||||
if(!(fde->state & FDE_PENDING)) return;
|
||||
fde->state &= (~FDE_PENDING);
|
||||
dump_fde(fde, "callback");
|
||||
fde->func(fde->fd, events, fde->arg);
|
||||
}
|
||||
|
||||
static void fdevent_subproc_event_func(int fd, unsigned ev, void *userdata)
|
||||
{
|
||||
|
||||
D("subproc handling on fd=%d ev=%04x\n", fd, ev);
|
||||
|
||||
// Hook oneself back into the fde's suitable for select() on read.
|
||||
if((fd < 0) || (fd >= fd_table_max)) {
|
||||
FATAL("fd %d out of range for fd_table \n", fd);
|
||||
}
|
||||
fdevent *fde = fd_table[fd];
|
||||
fdevent_add(fde, FDE_READ);
|
||||
|
||||
if(ev & FDE_READ){
|
||||
int subproc_fd;
|
||||
|
||||
if(readx(fd, &subproc_fd, sizeof(subproc_fd))) {
|
||||
FATAL("Failed to read the subproc's fd from fd=%d\n", fd);
|
||||
}
|
||||
if((subproc_fd < 0) || (subproc_fd >= fd_table_max)) {
|
||||
D("subproc_fd %d out of range 0, fd_table_max=%d\n",
|
||||
subproc_fd, fd_table_max);
|
||||
return;
|
||||
}
|
||||
fdevent *subproc_fde = fd_table[subproc_fd];
|
||||
if(!subproc_fde) {
|
||||
D("subproc_fd %d cleared from fd_table\n", subproc_fd);
|
||||
return;
|
||||
}
|
||||
if(subproc_fde->fd != subproc_fd) {
|
||||
// Already reallocated?
|
||||
D("subproc_fd %d != fd_table[].fd %d\n", subproc_fd, subproc_fde->fd);
|
||||
return;
|
||||
}
|
||||
|
||||
subproc_fde->force_eof = 1;
|
||||
|
||||
int rcount = 0;
|
||||
ioctl(subproc_fd, FIONREAD, &rcount);
|
||||
D("subproc with fd=%d has rcount=%d err=%d\n",
|
||||
subproc_fd, rcount, errno);
|
||||
|
||||
if(rcount) {
|
||||
// If there is data left, it will show up in the select().
|
||||
// This works because there is no other thread reading that
|
||||
// data when in this fd_func().
|
||||
return;
|
||||
}
|
||||
|
||||
D("subproc_fde.state=%04x\n", subproc_fde->state);
|
||||
subproc_fde->events |= FDE_READ;
|
||||
if(subproc_fde->state & FDE_PENDING) {
|
||||
return;
|
||||
}
|
||||
subproc_fde->state |= FDE_PENDING;
|
||||
fdevent_call_fdfunc(subproc_fde);
|
||||
}
|
||||
}
|
||||
|
||||
fdevent *fdevent_create(int fd, fd_func func, void *arg)
|
||||
{
|
||||
fdevent *fde = (fdevent*) malloc(sizeof(fdevent));
|
||||
if(fde == 0) return 0;
|
||||
fdevent_install(fde, fd, func, arg);
|
||||
fde->state |= FDE_CREATED;
|
||||
return fde;
|
||||
}
|
||||
|
||||
void fdevent_destroy(fdevent *fde)
|
||||
{
|
||||
if(fde == 0) return;
|
||||
if(!(fde->state & FDE_CREATED)) {
|
||||
FATAL("fde %p not created by fdevent_create()\n", fde);
|
||||
}
|
||||
fdevent_remove(fde);
|
||||
}
|
||||
|
||||
void fdevent_install(fdevent *fde, int fd, fd_func func, void *arg)
|
||||
{
|
||||
memset(fde, 0, sizeof(fdevent));
|
||||
fde->state = FDE_ACTIVE;
|
||||
fde->fd = fd;
|
||||
fde->force_eof = 0;
|
||||
fde->func = func;
|
||||
fde->arg = arg;
|
||||
|
||||
#ifndef HAVE_WINSOCK
|
||||
fcntl(fd, F_SETFL, O_NONBLOCK);
|
||||
#endif
|
||||
fdevent_register(fde);
|
||||
dump_fde(fde, "connect");
|
||||
fdevent_connect(fde);
|
||||
fde->state |= FDE_ACTIVE;
|
||||
}
|
||||
|
||||
void fdevent_remove(fdevent *fde)
|
||||
{
|
||||
if(fde->state & FDE_PENDING) {
|
||||
fdevent_plist_remove(fde);
|
||||
}
|
||||
|
||||
if(fde->state & FDE_ACTIVE) {
|
||||
fdevent_disconnect(fde);
|
||||
dump_fde(fde, "disconnect");
|
||||
fdevent_unregister(fde);
|
||||
}
|
||||
|
||||
fde->state = 0;
|
||||
fde->events = 0;
|
||||
}
|
||||
|
||||
|
||||
void fdevent_set(fdevent *fde, unsigned events)
|
||||
{
|
||||
events &= FDE_EVENTMASK;
|
||||
|
||||
if((fde->state & FDE_EVENTMASK) == events) return;
|
||||
|
||||
if(fde->state & FDE_ACTIVE) {
|
||||
fdevent_update(fde, events);
|
||||
dump_fde(fde, "update");
|
||||
}
|
||||
|
||||
fde->state = (fde->state & FDE_STATEMASK) | events;
|
||||
|
||||
if(fde->state & FDE_PENDING) {
|
||||
/* if we're pending, make sure
|
||||
** we don't signal an event that
|
||||
** is no longer wanted.
|
||||
*/
|
||||
fde->events &= (~events);
|
||||
if(fde->events == 0) {
|
||||
fdevent_plist_remove(fde);
|
||||
fde->state &= (~FDE_PENDING);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void fdevent_add(fdevent *fde, unsigned events)
|
||||
{
|
||||
fdevent_set(
|
||||
fde, (fde->state & FDE_EVENTMASK) | (events & FDE_EVENTMASK));
|
||||
}
|
||||
|
||||
void fdevent_del(fdevent *fde, unsigned events)
|
||||
{
|
||||
fdevent_set(
|
||||
fde, (fde->state & FDE_EVENTMASK) & (~(events & FDE_EVENTMASK)));
|
||||
}
|
||||
|
||||
void fdevent_subproc_setup()
|
||||
{
|
||||
int s[2];
|
||||
|
||||
if(adb_socketpair(s)) {
|
||||
FATAL("cannot create shell-exit socket-pair\n");
|
||||
}
|
||||
SHELL_EXIT_NOTIFY_FD = s[0];
|
||||
fdevent *fde;
|
||||
fde = fdevent_create(s[1], fdevent_subproc_event_func, NULL);
|
||||
if(!fde)
|
||||
FATAL("cannot create fdevent for shell-exit handler\n");
|
||||
fdevent_add(fde, FDE_READ);
|
||||
}
|
||||
|
||||
void fdevent_loop()
|
||||
{
|
||||
fdevent *fde;
|
||||
fdevent_subproc_setup();
|
||||
|
||||
for(;;) {
|
||||
D("--- ---- waiting for events\n");
|
||||
|
||||
fdevent_process();
|
||||
|
||||
while((fde = fdevent_plist_dequeue())) {
|
||||
fdevent_call_fdfunc(fde);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
#include <stdint.h> /* for int64_t */
|
||||
|
||||
/* events that may be observed */
|
||||
#define FDE_READ 0x0001
|
||||
#define FDE_WRITE 0x0002
|
||||
#define FDE_ERROR 0x0004
|
||||
#define FDE_TIMEOUT 0x0008
|
||||
|
||||
/* 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
|
||||
* Note: use FD_TIMER as 'fd' to create a fd-less object
|
||||
* (used to implement timers).
|
||||
*/
|
||||
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);
|
||||
|
||||
void fdevent_set_timeout(fdevent *fde, int64_t timeout_ms);
|
||||
|
||||
/* loop forever, handling events.
|
||||
*/
|
||||
void fdevent_loop();
|
||||
|
||||
struct fdevent
|
||||
{
|
||||
fdevent *next;
|
||||
fdevent *prev;
|
||||
|
||||
int fd;
|
||||
int force_eof;
|
||||
|
||||
unsigned short state;
|
||||
unsigned short events;
|
||||
|
||||
fd_func func;
|
||||
void *arg;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,26 @@
|
||||
/* the list of mutexes used by adb */
|
||||
/* #ifndef __MUTEX_LIST_H
|
||||
* Do not use an include-guard. This file is included once to declare the locks
|
||||
* and once in win32 to actually do the runtime initialization.
|
||||
*/
|
||||
#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)
|
||||
|
||||
// Sadly logging to /data/adb/adb-... is not thread safe.
|
||||
// After modifying adb.h::D() to count invocations:
|
||||
// DEBUG(jpa):0:Handling main()
|
||||
// DEBUG(jpa):1:[ usb_init - starting thread ]
|
||||
// (Oopsies, no :2:, and matching message is also gone.)
|
||||
// DEBUG(jpa):3:[ usb_thread - opening device ]
|
||||
// DEBUG(jpa):4:jdwp control socket started (10)
|
||||
ADB_MUTEX(D_lock)
|
||||
|
||||
#undef ADB_MUTEX
|
||||
@@ -0,0 +1,161 @@
|
||||
/*
|
||||
* 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"
|
||||
#include "fdevent.h"
|
||||
|
||||
#define TRACE_TAG TRACE_SERVICES
|
||||
#include "adb.h"
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static void sideload_service(int s, void *cookie)
|
||||
{
|
||||
unsigned char buf[4096];
|
||||
unsigned count = (unsigned) cookie;
|
||||
int fd;
|
||||
|
||||
fprintf(stderr, "sideload_service invoked\n");
|
||||
|
||||
fd = adb_creat(ADB_SIDELOAD_FILENAME, 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);
|
||||
|
||||
if (count == 0) {
|
||||
fprintf(stderr, "adbd exiting after successful sideload\n");
|
||||
sleep(1);
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#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];
|
||||
}
|
||||
|
||||
int service_to_fd(const char *name)
|
||||
{
|
||||
int ret = -1;
|
||||
|
||||
if (!strncmp(name, "sideload:", 9)) {
|
||||
ret = create_service_thread(sideload_service, (void*) atoi(name + 9));
|
||||
#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;
|
||||
}
|
||||
@@ -0,0 +1,830 @@
|
||||
/*
|
||||
* 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,
|
||||
};
|
||||
|
||||
/* the the list of currently closing local sockets.
|
||||
** these have no peer anymore, but still packets to
|
||||
** write to their fd.
|
||||
*/
|
||||
static asocket local_socket_closing_list = {
|
||||
.next = &local_socket_closing_list,
|
||||
.prev = &local_socket_closing_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; s = s->next) {
|
||||
if (s->id == id) {
|
||||
result = s;
|
||||
break;
|
||||
}
|
||||
}
|
||||
adb_mutex_unlock(&socket_list_lock);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void
|
||||
insert_local_socket(asocket* s, asocket* list)
|
||||
{
|
||||
s->next = list;
|
||||
s->prev = s->next->prev;
|
||||
s->prev->next = s;
|
||||
s->next->prev = s;
|
||||
}
|
||||
|
||||
|
||||
void install_local_socket(asocket *s)
|
||||
{
|
||||
adb_mutex_lock(&socket_list_lock);
|
||||
|
||||
s->id = local_socket_next_id++;
|
||||
insert_local_socket(s, &local_socket_list);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
// be sure to hold the socket list lock when calling this
|
||||
static void local_socket_destroy(asocket *s)
|
||||
{
|
||||
apacket *p, *n;
|
||||
D("LS(%d): destroying fde.fd=%d\n", s->id, s->fde.fd);
|
||||
|
||||
/* 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);
|
||||
}
|
||||
remove_socket(s);
|
||||
free(s);
|
||||
}
|
||||
|
||||
|
||||
static void local_socket_close_locked(asocket *s)
|
||||
{
|
||||
D("entered. LS(%d) fd=%d\n", s->id, s->fd);
|
||||
if(s->peer) {
|
||||
D("LS(%d): closing peer. peer->id=%d peer->fd=%d\n",
|
||||
s->id, s->peer->id, s->peer->fd);
|
||||
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);
|
||||
}
|
||||
s->peer = 0;
|
||||
}
|
||||
|
||||
/* If we are already closing, or if there are no
|
||||
** pending packets, destroy immediately
|
||||
*/
|
||||
if (s->closing || s->pkt_first == NULL) {
|
||||
int id = s->id;
|
||||
local_socket_destroy(s);
|
||||
D("LS(%d): closed\n", id);
|
||||
return;
|
||||
}
|
||||
|
||||
/* otherwise, put on the closing list
|
||||
*/
|
||||
D("LS(%d): closing\n", s->id);
|
||||
s->closing = 1;
|
||||
fdevent_del(&s->fde, FDE_READ);
|
||||
remove_socket(s);
|
||||
D("LS(%d): put on socket_closing_list fd=%d\n", s->id, s->fd);
|
||||
insert_local_socket(s, &local_socket_closing_list);
|
||||
}
|
||||
|
||||
static void local_socket_event_func(int fd, unsigned ev, void *_s)
|
||||
{
|
||||
asocket *s = _s;
|
||||
|
||||
D("LS(%d): event_func(fd=%d(==%d), ev=%04x)\n", s->id, s->fd, fd, ev);
|
||||
|
||||
/* put the FDE_WRITE processing before the FDE_READ
|
||||
** in order to simplify the code.
|
||||
*/
|
||||
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) {
|
||||
/* returning here is ok because FDE_READ will
|
||||
** be processed in the next iteration loop
|
||||
*/
|
||||
if(errno == EAGAIN) return;
|
||||
if(errno == EINTR) continue;
|
||||
}
|
||||
D(" closing after write because r=%d and errno is %d\n", r, errno);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/* if we sent the last packet of a closing socket,
|
||||
** we can now destroy it.
|
||||
*/
|
||||
if (s->closing) {
|
||||
D(" closing because 'closing' is set after write\n");
|
||||
s->close(s);
|
||||
return;
|
||||
}
|
||||
|
||||
/* 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);
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
D("LS(%d): post adb_read(fd=%d,...) r=%d (errno=%d) avail=%d\n", s->id, s->fd, r, r<0?errno:0, 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;
|
||||
}
|
||||
D("LS(%d): fd=%d post avail loop. r=%d is_eof=%d forced_eof=%d\n",
|
||||
s->id, s->fd, r, is_eof, s->fde.force_eof);
|
||||
if((avail == MAX_PAYLOAD) || (s->peer == 0)) {
|
||||
put_apacket(p);
|
||||
} else {
|
||||
p->len = MAX_PAYLOAD - avail;
|
||||
|
||||
r = s->peer->enqueue(s->peer, p);
|
||||
D("LS(%d): fd=%d post peer->enqueue(). r=%d\n", s->id, s->fd, r);
|
||||
|
||||
if(r < 0) {
|
||||
/* error return means they closed us as a side-effect
|
||||
** and we must return immediately.
|
||||
**
|
||||
** note that if we still have buffered packets, the
|
||||
** socket will be placed on the closing socket list.
|
||||
** this handler function will be called again
|
||||
** to process FDE_WRITE events.
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
||||
/* Don't allow a forced eof if data is still there */
|
||||
if((s->fde.force_eof && !r) || is_eof) {
|
||||
D(" closing because is_eof=%d r=%d s->fde.force_eof=%d\n", is_eof, r, s->fde.force_eof);
|
||||
s->close(s);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
D("LS(%d): FDE_ERROR (fd=%d)\n", s->id, s->fd);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
asocket *create_local_socket(int fd)
|
||||
{
|
||||
asocket *s = calloc(1, sizeof(asocket));
|
||||
if (s == NULL) fatal("cannot allocate socket");
|
||||
s->fd = fd;
|
||||
s->enqueue = local_socket_enqueue;
|
||||
s->ready = local_socket_ready;
|
||||
s->close = local_socket_close;
|
||||
install_local_socket(s);
|
||||
|
||||
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;
|
||||
|
||||
fd = service_to_fd(name);
|
||||
if(fd < 0) return 0;
|
||||
|
||||
s = create_local_socket(fd);
|
||||
D("LS(%d): bound to '%s' via %d\n", s->id, name, fd);
|
||||
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("entered remote_socket_enqueue RS(%d) WRITE fd=%d peer.fd=%d\n",
|
||||
s->id, s->fd, s->peer->fd);
|
||||
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("entered remote_socket_ready RS(%d) OKAY fd=%d peer.fd=%d\n",
|
||||
s->id, s->fd, s->peer->fd);
|
||||
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("entered remote_socket_close RS(%d) CLOSE fd=%d peer->fd=%d\n",
|
||||
s->id, s->fd, s->peer?s->peer->fd:-1);
|
||||
apacket *p = get_apacket();
|
||||
p->msg.command = A_CLSE;
|
||||
if(s->peer) {
|
||||
p->msg.arg0 = s->peer->id;
|
||||
s->peer->peer = 0;
|
||||
D("RS(%d) peer->close()ing peer->id=%d peer->fd=%d\n",
|
||||
s->id, s->peer->id, s->peer->fd);
|
||||
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 == NULL) 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 RS(%d) fd=%d\n", s->id, s->fd);
|
||||
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;
|
||||
}
|
||||
|
||||
/* skip_host_serial return the position in a string
|
||||
skipping over the 'serial' parameter in the ADB protocol,
|
||||
where parameter string may be a host:port string containing
|
||||
the protocol delimiter (colon). */
|
||||
char *skip_host_serial(char *service) {
|
||||
char *first_colon, *serial_end;
|
||||
|
||||
first_colon = strchr(service, ':');
|
||||
if (!first_colon) {
|
||||
/* No colon in service string. */
|
||||
return NULL;
|
||||
}
|
||||
serial_end = first_colon;
|
||||
if (isdigit(serial_end[1])) {
|
||||
serial_end++;
|
||||
while ((*serial_end) && isdigit(*serial_end)) {
|
||||
serial_end++;
|
||||
}
|
||||
if ((*serial_end) != ':') {
|
||||
// Something other than numbers was found, reset the end.
|
||||
serial_end = first_colon;
|
||||
}
|
||||
}
|
||||
return serial_end;
|
||||
}
|
||||
|
||||
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:" and could be a host:port string.
|
||||
serial_end = skip_host_serial(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);
|
||||
s->peer = 0;
|
||||
}
|
||||
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 == NULL) fatal("cannot allocate socket");
|
||||
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);
|
||||
}
|
||||
@@ -0,0 +1,495 @@
|
||||
/*
|
||||
* 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 */
|
||||
/* For win32, adb_sysdeps_init() will do the mutex runtime initialization. */
|
||||
#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_shutdown(int fd);
|
||||
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 "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;
|
||||
int force_eof;
|
||||
|
||||
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 "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) 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
|
||||
|
||||
/* declare all mutexes */
|
||||
#define ADB_MUTEX(x) extern adb_mutex_t x;
|
||||
#include "mutex_list.h"
|
||||
|
||||
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_shutdown(int fd)
|
||||
{
|
||||
return shutdown(fd, SHUT_RDWR);
|
||||
}
|
||||
#undef shutdown
|
||||
#define shutdown ____xxx_shutdown
|
||||
|
||||
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)
|
||||
{
|
||||
int fd;
|
||||
|
||||
fd = accept(serverfd, addr, addrlen);
|
||||
if (fd >= 0)
|
||||
close_on_exec(fd);
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
#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 */
|
||||
+1081
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright (C) 2011 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 __TRANSPORT_H
|
||||
#define __TRANSPORT_H
|
||||
|
||||
/* 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);
|
||||
#endif /* __TRANSPORT_H */
|
||||
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
* 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"
|
||||
|
||||
#if ADB_HOST
|
||||
#include "usb_vendors.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_BIG_ENDIAN
|
||||
#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, int state)
|
||||
{
|
||||
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 = state;
|
||||
t->type = kTransportUsb;
|
||||
t->usb = h;
|
||||
|
||||
#if ADB_HOST
|
||||
HOST = 1;
|
||||
#else
|
||||
HOST = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if ADB_HOST
|
||||
int is_adb_interface(int vid, int pid, int usb_class, int usb_subclass, int usb_protocol)
|
||||
{
|
||||
unsigned i;
|
||||
for (i = 0; i < vendorIdCount; i++) {
|
||||
if (vid == vendorIds[i]) {
|
||||
if (usb_class == ADB_CLASS && usb_subclass == ADB_SUBCLASS &&
|
||||
usb_protocol == ADB_PROTOCOL) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,157 @@
|
||||
/*
|
||||
* 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, 1);
|
||||
}
|
||||
|
||||
// never gets here
|
||||
return 0;
|
||||
}
|
||||
|
||||
int usb_write(usb_handle *h, const void *data, int len)
|
||||
{
|
||||
int n;
|
||||
|
||||
D("about to write (fd=%d, len=%d)\n", h->fd, len);
|
||||
n = adb_write(h->fd, data, len);
|
||||
if(n != len) {
|
||||
D("ERROR: fd = %d, n = %d, errno = %d (%s)\n",
|
||||
h->fd, n, errno, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
D("[ done fd=%d ]\n", h->fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int usb_read(usb_handle *h, void *data, int len)
|
||||
{
|
||||
int n;
|
||||
|
||||
D("about to read (fd=%d, len=%d)\n", h->fd, len);
|
||||
n = adb_read(h->fd, data, len);
|
||||
if(n != len) {
|
||||
D("ERROR: fd = %d, n = %d, errno = %d (%s)\n",
|
||||
h->fd, n, errno, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
D("[ done fd=%d ]\n", h->fd);
|
||||
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;
|
||||
}
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* 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 "utils.h"
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
char*
|
||||
buff_addc (char* buff, char* buffEnd, int c)
|
||||
{
|
||||
int avail = buffEnd - buff;
|
||||
|
||||
if (avail <= 0) /* already in overflow mode */
|
||||
return buff;
|
||||
|
||||
if (avail == 1) { /* overflowing, the last byte is reserved for zero */
|
||||
buff[0] = 0;
|
||||
return buff + 1;
|
||||
}
|
||||
|
||||
buff[0] = (char) c; /* add char and terminating zero */
|
||||
buff[1] = 0;
|
||||
return buff + 1;
|
||||
}
|
||||
|
||||
char*
|
||||
buff_adds (char* buff, char* buffEnd, const char* s)
|
||||
{
|
||||
int slen = strlen(s);
|
||||
|
||||
return buff_addb(buff, buffEnd, s, slen);
|
||||
}
|
||||
|
||||
char*
|
||||
buff_addb (char* buff, char* buffEnd, const void* data, int len)
|
||||
{
|
||||
int avail = (buffEnd - buff);
|
||||
|
||||
if (avail <= 0 || len <= 0) /* already overflowing */
|
||||
return buff;
|
||||
|
||||
if (len > avail)
|
||||
len = avail;
|
||||
|
||||
memcpy(buff, data, len);
|
||||
|
||||
buff += len;
|
||||
|
||||
/* ensure there is a terminating zero */
|
||||
if (buff >= buffEnd) { /* overflow */
|
||||
buff[-1] = 0;
|
||||
} else
|
||||
buff[0] = 0;
|
||||
|
||||
return buff;
|
||||
}
|
||||
|
||||
char*
|
||||
buff_add (char* buff, char* buffEnd, const char* format, ... )
|
||||
{
|
||||
int avail;
|
||||
|
||||
avail = (buffEnd - buff);
|
||||
|
||||
if (avail > 0) {
|
||||
va_list args;
|
||||
int nn;
|
||||
|
||||
va_start(args, format);
|
||||
nn = vsnprintf( buff, avail, format, args);
|
||||
va_end(args);
|
||||
|
||||
if (nn < 0) {
|
||||
/* some C libraries return -1 in case of overflow,
|
||||
* but they will also do that if the format spec is
|
||||
* invalid. We assume ADB is not buggy enough to
|
||||
* trigger that last case. */
|
||||
nn = avail;
|
||||
}
|
||||
else if (nn > avail) {
|
||||
nn = avail;
|
||||
}
|
||||
|
||||
buff += nn;
|
||||
|
||||
/* ensure that there is a terminating zero */
|
||||
if (buff >= buffEnd)
|
||||
buff[-1] = 0;
|
||||
else
|
||||
buff[0] = 0;
|
||||
}
|
||||
return buff;
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
#ifndef _ADB_UTILS_H
|
||||
#define _ADB_UTILS_H
|
||||
|
||||
/* bounded buffer functions */
|
||||
|
||||
/* all these functions are used to append data to a bounded buffer.
|
||||
*
|
||||
* after each operation, the buffer is guaranteed to be zero-terminated,
|
||||
* even in the case of an overflow. they all return the new buffer position
|
||||
* which allows one to use them in succession, only checking for overflows
|
||||
* at the end. For example:
|
||||
*
|
||||
* BUFF_DECL(temp,p,end,1024);
|
||||
* char* p;
|
||||
*
|
||||
* p = buff_addc(temp, end, '"');
|
||||
* p = buff_adds(temp, end, string);
|
||||
* p = buff_addc(temp, end, '"');
|
||||
*
|
||||
* if (p >= end) {
|
||||
* overflow detected. note that 'temp' is
|
||||
* zero-terminated for safety.
|
||||
* }
|
||||
* return strdup(temp);
|
||||
*/
|
||||
|
||||
/* tries to add a character to the buffer, in case of overflow
|
||||
* this will only write a terminating zero and return buffEnd.
|
||||
*/
|
||||
char* buff_addc (char* buff, char* buffEnd, int c);
|
||||
|
||||
/* tries to add a string to the buffer */
|
||||
char* buff_adds (char* buff, char* buffEnd, const char* s);
|
||||
|
||||
/* tries to add a bytes to the buffer. the input can contain zero bytes,
|
||||
* but a terminating zero will always be appended at the end anyway
|
||||
*/
|
||||
char* buff_addb (char* buff, char* buffEnd, const void* data, int len);
|
||||
|
||||
/* tries to add a formatted string to a bounded buffer */
|
||||
char* buff_add (char* buff, char* buffEnd, const char* format, ... );
|
||||
|
||||
/* convenience macro used to define a bounded buffer, as well as
|
||||
* a 'cursor' and 'end' variables all in one go.
|
||||
*
|
||||
* note: this doesn't place an initial terminating zero in the buffer,
|
||||
* you need to use one of the buff_ functions for this. or simply
|
||||
* do _cursor[0] = 0 manually.
|
||||
*/
|
||||
#define BUFF_DECL(_buff,_cursor,_end,_size) \
|
||||
char _buff[_size], *_cursor=_buff, *_end = _cursor + (_size)
|
||||
|
||||
#endif /* _ADB_UTILS_H */
|
||||
@@ -40,6 +40,10 @@
|
||||
#include "ui.h"
|
||||
#include "screen_ui.h"
|
||||
#include "device.h"
|
||||
#include "adb_install.h"
|
||||
extern "C" {
|
||||
#include "minadbd/adb.h"
|
||||
}
|
||||
|
||||
static const struct option OPTIONS[] = {
|
||||
{ "send_intent", required_argument, NULL, 's' },
|
||||
@@ -725,6 +729,21 @@ prompt_and_wait(Device* device) {
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case Device::APPLY_ADB_SIDELOAD:
|
||||
ensure_path_mounted(CACHE_ROOT);
|
||||
status = apply_from_adb(ui, &wipe_cache, TEMPORARY_INSTALL_FILE);
|
||||
if (status >= 0) {
|
||||
if (status != INSTALL_SUCCESS) {
|
||||
ui->SetBackground(RecoveryUI::ERROR);
|
||||
ui->Print("Installation aborted.\n");
|
||||
} else if (!ui->IsTextVisible()) {
|
||||
return; // reboot if logs aren't visible
|
||||
} else {
|
||||
ui->Print("\nInstall from ADB complete.\n");
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -741,6 +760,19 @@ main(int argc, char **argv) {
|
||||
// If these fail, there's not really anywhere to complain...
|
||||
freopen(TEMPORARY_LOG_FILE, "a", stdout); setbuf(stdout, NULL);
|
||||
freopen(TEMPORARY_LOG_FILE, "a", stderr); setbuf(stderr, NULL);
|
||||
|
||||
// If this binary is started with the single argument "--adbd",
|
||||
// instead of being the normal recovery binary, it turns into kind
|
||||
// of a stripped-down version of adbd that only supports the
|
||||
// 'sideload' command. Note this must be a real argument, not
|
||||
// anything in the command file or bootloader control block; the
|
||||
// only way recovery should be run with this argument is when it
|
||||
// starts a copy of itself from the apply_from_adb() function.
|
||||
if (argc == 2 && strcmp(argv[1], "--adbd") == 0) {
|
||||
adb_main();
|
||||
return 0;
|
||||
}
|
||||
|
||||
printf("Starting recovery on %s", ctime(&start));
|
||||
|
||||
Device* device = make_device();
|
||||
|
||||
Reference in New Issue
Block a user