Initial commit: added autotoolized adb (from Android 2.3.7 tree)
authorPaulK <contact@paulk.fr>
Sat, 7 Apr 2012 13:58:09 +0000 (15:58 +0200)
committerPaulK <contact@paulk.fr>
Sat, 7 Apr 2012 13:58:09 +0000 (15:58 +0200)
153 files changed:
AUTHORS [new file with mode: 0644]
COPYING [new file with mode: 0644]
ChangeLog [new file with mode: 0644]
INSTALL [new file with mode: 0644]
Makefile.am [new file with mode: 0644]
NEWS [new file with mode: 0644]
README [new file with mode: 0644]
adb/Android.mk [new file with mode: 0644]
adb/MODULE_LICENSE_APACHE2 [new file with mode: 0644]
adb/Makefile.am [new file with mode: 0644]
adb/NOTICE [new file with mode: 0644]
adb/OVERVIEW.TXT [new file with mode: 0644]
adb/SERVICES.TXT [new file with mode: 0644]
adb/adb.c [new file with mode: 0644]
adb/adb.h [new file with mode: 0644]
adb/adb_client.c [new file with mode: 0644]
adb/adb_client.h [new file with mode: 0644]
adb/commandline.c [new file with mode: 0644]
adb/console.c [new file with mode: 0644]
adb/fdevent.c [new file with mode: 0644]
adb/fdevent.h [new file with mode: 0644]
adb/file_sync_client.c [new file with mode: 0644]
adb/file_sync_service.c [new file with mode: 0644]
adb/file_sync_service.h [new file with mode: 0644]
adb/framebuffer_service.c [new file with mode: 0644]
adb/get_my_path_darwin.c [new file with mode: 0644]
adb/get_my_path_freebsd.c [new file with mode: 0644]
adb/get_my_path_linux.c [new file with mode: 0644]
adb/get_my_path_windows.c [new file with mode: 0644]
adb/jdwp_service.c [new file with mode: 0644]
adb/log_service.c [new file with mode: 0644]
adb/mutex_list.h [new file with mode: 0644]
adb/protocol.txt [new file with mode: 0644]
adb/remount_service.c [new file with mode: 0644]
adb/services.c [new file with mode: 0644]
adb/sockets.c [new file with mode: 0644]
adb/sockets.dia [new file with mode: 0644]
adb/sysdeps.h [new file with mode: 0644]
adb/sysdeps_win32.c [new file with mode: 0644]
adb/test_track_devices.c [new file with mode: 0644]
adb/test_track_jdwp.c [new file with mode: 0644]
adb/transport.c [new file with mode: 0644]
adb/transport_local.c [new file with mode: 0644]
adb/transport_usb.c [new file with mode: 0644]
adb/usb_libusb.c [new file with mode: 0644]
adb/usb_linux.c [new file with mode: 0644]
adb/usb_linux_client.c [new file with mode: 0644]
adb/usb_osx.c [new file with mode: 0644]
adb/usb_vendors.c [new file with mode: 0644]
adb/usb_vendors.h [new file with mode: 0644]
adb/usb_windows.c [new file with mode: 0644]
adb/utils.c [new file with mode: 0644]
adb/utils.h [new file with mode: 0644]
autogen.sh [new file with mode: 0755]
bionic/dlmalloc.c [new file with mode: 0644]
configure.ac [new file with mode: 0644]
include/android/log.h [new file with mode: 0644]
include/cutils/abort_socket.h [new file with mode: 0644]
include/cutils/array.h [new file with mode: 0644]
include/cutils/ashmem.h [new file with mode: 0644]
include/cutils/atomic-arm.h [new file with mode: 0644]
include/cutils/atomic-inline.h [new file with mode: 0644]
include/cutils/atomic-x86.h [new file with mode: 0644]
include/cutils/atomic.h [new file with mode: 0644]
include/cutils/compiler.h [new file with mode: 0644]
include/cutils/config_utils.h [new file with mode: 0644]
include/cutils/cpu_info.h [new file with mode: 0644]
include/cutils/dir_hash.h [new file with mode: 0644]
include/cutils/event_tag_map.h [new file with mode: 0644]
include/cutils/hashmap.h [new file with mode: 0644]
include/cutils/iosched_policy.h [new file with mode: 0644]
include/cutils/jstring.h [new file with mode: 0644]
include/cutils/log.h [new file with mode: 0644]
include/cutils/logd.h [new file with mode: 0644]
include/cutils/logger.h [new file with mode: 0644]
include/cutils/logprint.h [new file with mode: 0644]
include/cutils/memory.h [new file with mode: 0644]
include/cutils/misc.h [new file with mode: 0644]
include/cutils/mq.h [new file with mode: 0644]
include/cutils/mspace.h [new file with mode: 0644]
include/cutils/native_handle.h [new file with mode: 0644]
include/cutils/open_memstream.h [new file with mode: 0644]
include/cutils/process_name.h [new file with mode: 0644]
include/cutils/properties.h [new file with mode: 0644]
include/cutils/record_stream.h [new file with mode: 0644]
include/cutils/sched_policy.h [new file with mode: 0644]
include/cutils/selector.h [new file with mode: 0644]
include/cutils/sockets.h [new file with mode: 0644]
include/cutils/threads.h [new file with mode: 0644]
include/cutils/tztime.h [new file with mode: 0644]
include/cutils/uio.h [new file with mode: 0644]
include/cutils/zygote.h [new file with mode: 0644]
include/zipfile/zipfile.h [new file with mode: 0644]
libcutils/Android.mk [new file with mode: 0644]
libcutils/MODULE_LICENSE_APACHE2 [new file with mode: 0644]
libcutils/Makefile.am [new file with mode: 0644]
libcutils/NOTICE [new file with mode: 0644]
libcutils/abort_socket.c [new file with mode: 0644]
libcutils/arch-arm/memset32.S [new file with mode: 0644]
libcutils/arch-x86/android_memset16.S [new file with mode: 0644]
libcutils/arch-x86/android_memset32.S [new file with mode: 0644]
libcutils/arch-x86/cache_wrapper.S [new file with mode: 0644]
libcutils/arch-x86/sse2-memset16-atom.S [new file with mode: 0644]
libcutils/arch-x86/sse2-memset32-atom.S [new file with mode: 0644]
libcutils/array.c [new file with mode: 0644]
libcutils/ashmem-dev.c [new file with mode: 0644]
libcutils/ashmem-host.c [new file with mode: 0644]
libcutils/atomic-android-sh.c [new file with mode: 0644]
libcutils/atomic.c [new file with mode: 0644]
libcutils/buffer.c [new file with mode: 0644]
libcutils/buffer.h [new file with mode: 0644]
libcutils/config_utils.c [new file with mode: 0644]
libcutils/cpu_info.c [new file with mode: 0644]
libcutils/dir_hash.c [new file with mode: 0644]
libcutils/dlmalloc_stubs.c [new file with mode: 0644]
libcutils/hashmap.c [new file with mode: 0644]
libcutils/iosched_policy.c [new file with mode: 0644]
libcutils/load_file.c [new file with mode: 0644]
libcutils/loghack.h [new file with mode: 0644]
libcutils/memory.c [new file with mode: 0644]
libcutils/mq.c [new file with mode: 0644]
libcutils/mspace.c [new file with mode: 0644]
libcutils/native_handle.c [new file with mode: 0644]
libcutils/open_memstream.c [new file with mode: 0644]
libcutils/private.h [new file with mode: 0644]
libcutils/process_name.c [new file with mode: 0644]
libcutils/properties.c [new file with mode: 0644]
libcutils/record_stream.c [new file with mode: 0644]
libcutils/sched_policy.c [new file with mode: 0644]
libcutils/selector.c [new file with mode: 0644]
libcutils/socket_inaddr_any_server.c [new file with mode: 0644]
libcutils/socket_local.h [new file with mode: 0644]
libcutils/socket_local_client.c [new file with mode: 0644]
libcutils/socket_local_server.c [new file with mode: 0644]
libcutils/socket_loopback_client.c [new file with mode: 0644]
libcutils/socket_loopback_server.c [new file with mode: 0644]
libcutils/socket_network_client.c [new file with mode: 0644]
libcutils/strdup16to8.c [new file with mode: 0644]
libcutils/strdup8to16.c [new file with mode: 0644]
libcutils/threads.c [new file with mode: 0644]
libcutils/tzfile.h [new file with mode: 0644]
libcutils/tzstrftime.c [new file with mode: 0644]
libcutils/tztime.c [new file with mode: 0644]
libcutils/uio.c [new file with mode: 0644]
libcutils/zygote.c [new file with mode: 0644]
libzipfile/Android.mk [new file with mode: 0644]
libzipfile/MODULE_LICENSE_APACHE2 [new file with mode: 0644]
libzipfile/Makefile.am [new file with mode: 0644]
libzipfile/NOTICE [new file with mode: 0644]
libzipfile/centraldir.c [new file with mode: 0644]
libzipfile/private.h [new file with mode: 0644]
libzipfile/test_zipfile.c [new file with mode: 0644]
libzipfile/zipfile.c [new file with mode: 0644]

diff --git a/AUTHORS b/AUTHORS
new file mode 100644 (file)
index 0000000..58c91d9
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1 @@
+The Android Open Source Project
\ No newline at end of file
diff --git a/COPYING b/COPYING
new file mode 100644 (file)
index 0000000..430d3d6
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,23 @@
+ Copyright 2008, The Android Open Source Project
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+     * Redistributions of source code must retain the above copyright
+       notice, this list of conditions and the following disclaimer.
+     * Redistributions in binary form must reproduce the above copyright
+       notice, this list of conditions and the following disclaimer in the
+       documentation and/or other materials provided with the distribution.
+     * Neither the name of Google Inc. nor the names of its contributors may
+       be used to endorse or promote products derived from this software
+       without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY Google Inc. ``AS IS'' AND ANY EXPRESS OR 
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 
+ EVENT SHALL Google Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 
+ OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
+ WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 
+ OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/ChangeLog b/ChangeLog
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/INSTALL b/INSTALL
new file mode 100644 (file)
index 0000000..2550dab
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,302 @@
+Installation Instructions
+*************************
+
+Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005,
+2006, 2007, 2008, 2009 Free Software Foundation, Inc.
+
+   This file is free documentation; the Free Software Foundation gives
+unlimited permission to copy, distribute and modify it.
+
+Basic Installation
+==================
+
+   Briefly, the shell commands `./configure; make; make install' should
+configure, build, and install this package.  The following
+more-detailed instructions are generic; see the `README' file for
+instructions specific to this package.
+
+   The `configure' shell script attempts to guess correct values for
+various system-dependent variables used during compilation.  It uses
+those values to create a `Makefile' in each directory of the package.
+It may also create one or more `.h' files containing system-dependent
+definitions.  Finally, it creates a shell script `config.status' that
+you can run in the future to recreate the current configuration, and a
+file `config.log' containing compiler output (useful mainly for
+debugging `configure').
+
+   It can also use an optional file (typically called `config.cache'
+and enabled with `--cache-file=config.cache' or simply `-C') that saves
+the results of its tests to speed up reconfiguring.  Caching is
+disabled by default to prevent problems with accidental use of stale
+cache files.
+
+   If you need to do unusual things to compile the package, please try
+to figure out how `configure' could check whether to do them, and mail
+diffs or instructions to the address given in the `README' so they can
+be considered for the next release.  If you are using the cache, and at
+some point `config.cache' contains results you don't want to keep, you
+may remove or edit it.
+
+   The file `configure.ac' (or `configure.in') is used to create
+`configure' by a program called `autoconf'.  You need `configure.ac' if
+you want to change it or regenerate `configure' using a newer version
+of `autoconf'.
+
+The simplest way to compile this package is:
+
+  1. `cd' to the directory containing the package's source code and type
+     `./configure' to configure the package for your system.
+
+     Running `configure' might take a while.  While running, it prints
+     some messages telling which features it is checking for.
+
+  2. Type `make' to compile the package.
+
+  3. Optionally, type `make check' to run any self-tests that come with
+     the package.
+
+  4. Type `make install' to install the programs and any data files and
+     documentation.
+
+  5. You can remove the program binaries and object files from the
+     source code directory by typing `make clean'.  To also remove the
+     files that `configure' created (so you can compile the package for
+     a different kind of computer), type `make distclean'.  There is
+     also a `make maintainer-clean' target, but that is intended mainly
+     for the package's developers.  If you use it, you may have to get
+     all sorts of other programs in order to regenerate files that came
+     with the distribution.
+
+  6. Often, you can also type `make uninstall' to remove the installed
+     files again.
+
+Compilers and Options
+=====================
+
+   Some systems require unusual options for compilation or linking that
+the `configure' script does not know about.  Run `./configure --help'
+for details on some of the pertinent environment variables.
+
+   You can give `configure' initial values for configuration parameters
+by setting variables in the command line or in the environment.  Here
+is an example:
+
+     ./configure CC=c99 CFLAGS=-g LIBS=-lposix
+
+   *Note Defining Variables::, for more details.
+
+Compiling For Multiple Architectures
+====================================
+
+   You can compile the package for more than one kind of computer at the
+same time, by placing the object files for each architecture in their
+own directory.  To do this, you can use GNU `make'.  `cd' to the
+directory where you want the object files and executables to go and run
+the `configure' script.  `configure' automatically checks for the
+source code in the directory that `configure' is in and in `..'.
+
+   With a non-GNU `make', it is safer to compile the package for one
+architecture at a time in the source code directory.  After you have
+installed the package for one architecture, use `make distclean' before
+reconfiguring for another architecture.
+
+   On MacOS X 10.5 and later systems, you can create libraries and
+executables that work on multiple system types--known as "fat" or
+"universal" binaries--by specifying multiple `-arch' options to the
+compiler but only a single `-arch' option to the preprocessor.  Like
+this:
+
+     ./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \
+                 CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \
+                 CPP="gcc -E" CXXCPP="g++ -E"
+
+   This is not guaranteed to produce working output in all cases, you
+may have to build one architecture at a time and combine the results
+using the `lipo' tool if you have problems.
+
+Installation Names
+==================
+
+   By default, `make install' installs the package's commands under
+`/usr/local/bin', include files under `/usr/local/include', etc.  You
+can specify an installation prefix other than `/usr/local' by giving
+`configure' the option `--prefix=PREFIX'.
+
+   You can specify separate installation prefixes for
+architecture-specific files and architecture-independent files.  If you
+pass the option `--exec-prefix=PREFIX' to `configure', the package uses
+PREFIX as the prefix for installing programs and libraries.
+Documentation and other data files still use the regular prefix.
+
+   In addition, if you use an unusual directory layout you can give
+options like `--bindir=DIR' to specify different values for particular
+kinds of files.  Run `configure --help' for a list of the directories
+you can set and what kinds of files go in them.
+
+   If the package supports it, you can cause programs to be installed
+with an extra prefix or suffix on their names by giving `configure' the
+option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
+
+Optional Features
+=================
+
+   Some packages pay attention to `--enable-FEATURE' options to
+`configure', where FEATURE indicates an optional part of the package.
+They may also pay attention to `--with-PACKAGE' options, where PACKAGE
+is something like `gnu-as' or `x' (for the X Window System).  The
+`README' should mention any `--enable-' and `--with-' options that the
+package recognizes.
+
+   For packages that use the X Window System, `configure' can usually
+find the X include and library files automatically, but if it doesn't,
+you can use the `configure' options `--x-includes=DIR' and
+`--x-libraries=DIR' to specify their locations.
+
+Particular systems
+==================
+
+   On HP-UX, the default C compiler is not ANSI C compatible.  If GNU
+CC is not installed, it is recommended to use the following options in
+order to use an ANSI C compiler:
+
+     ./configure CC="cc -Ae -D_XOPEN_SOURCE=500"
+
+and if that doesn't work, install pre-built binaries of GCC for HP-UX.
+
+   On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot
+parse its `<wchar.h>' header file.  The option `-nodtk' can be used as
+a workaround.  If GNU CC is not installed, it is therefore recommended
+to try
+
+     ./configure CC="cc"
+
+and if that doesn't work, try
+
+     ./configure CC="cc -nodtk"
+
+   On Solaris, don't put `/usr/ucb' early in your `PATH'.  This
+directory contains several dysfunctional programs; working variants of
+these programs are available in `/usr/bin'.  So, if you need `/usr/ucb'
+in your `PATH', put it _after_ `/usr/bin'.
+
+   On Haiku, software installed for all users goes in `/boot/common',
+not `/usr/local'.  It is recommended to use the following options:
+
+     ./configure --prefix=/boot/common
+
+Specifying the System Type
+==========================
+
+   There may be some features `configure' cannot figure out
+automatically, but needs to determine by the type of machine the package
+will run on.  Usually, assuming the package is built to be run on the
+_same_ architectures, `configure' can figure that out, but if it prints
+a message saying it cannot guess the machine type, give it the
+`--build=TYPE' option.  TYPE can either be a short name for the system
+type, such as `sun4', or a canonical name which has the form:
+
+     CPU-COMPANY-SYSTEM
+
+where SYSTEM can have one of these forms:
+
+     OS
+     KERNEL-OS
+
+   See the file `config.sub' for the possible values of each field.  If
+`config.sub' isn't included in this package, then this package doesn't
+need to know the machine type.
+
+   If you are _building_ compiler tools for cross-compiling, you should
+use the option `--target=TYPE' to select the type of system they will
+produce code for.
+
+   If you want to _use_ a cross compiler, that generates code for a
+platform different from the build platform, you should specify the
+"host" platform (i.e., that on which the generated programs will
+eventually be run) with `--host=TYPE'.
+
+Sharing Defaults
+================
+
+   If you want to set default values for `configure' scripts to share,
+you can create a site shell script called `config.site' that gives
+default values for variables like `CC', `cache_file', and `prefix'.
+`configure' looks for `PREFIX/share/config.site' if it exists, then
+`PREFIX/etc/config.site' if it exists.  Or, you can set the
+`CONFIG_SITE' environment variable to the location of the site script.
+A warning: not all `configure' scripts look for a site script.
+
+Defining Variables
+==================
+
+   Variables not defined in a site shell script can be set in the
+environment passed to `configure'.  However, some packages may run
+configure again during the build, and the customized values of these
+variables may be lost.  In order to avoid this problem, you should set
+them in the `configure' command line, using `VAR=value'.  For example:
+
+     ./configure CC=/usr/local2/bin/gcc
+
+causes the specified `gcc' to be used as the C compiler (unless it is
+overridden in the site shell script).
+
+Unfortunately, this technique does not work for `CONFIG_SHELL' due to
+an Autoconf bug.  Until the bug is fixed you can use this workaround:
+
+     CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash
+
+`configure' Invocation
+======================
+
+   `configure' recognizes the following options to control how it
+operates.
+
+`--help'
+`-h'
+     Print a summary of all of the options to `configure', and exit.
+
+`--help=short'
+`--help=recursive'
+     Print a summary of the options unique to this package's
+     `configure', and exit.  The `short' variant lists options used
+     only in the top level, while the `recursive' variant lists options
+     also present in any nested packages.
+
+`--version'
+`-V'
+     Print the version of Autoconf used to generate the `configure'
+     script, and exit.
+
+`--cache-file=FILE'
+     Enable the cache: use and save the results of the tests in FILE,
+     traditionally `config.cache'.  FILE defaults to `/dev/null' to
+     disable caching.
+
+`--config-cache'
+`-C'
+     Alias for `--cache-file=config.cache'.
+
+`--quiet'
+`--silent'
+`-q'
+     Do not print messages saying which checks are being made.  To
+     suppress all normal output, redirect it to `/dev/null' (any error
+     messages will still be shown).
+
+`--srcdir=DIR'
+     Look for the package's source code in directory DIR.  Usually
+     `configure' can determine that directory automatically.
+
+`--prefix=DIR'
+     Use DIR as the installation prefix.  *Note Installation Names::
+     for more details, including other options available for fine-tuning
+     the installation locations.
+
+`--no-create'
+`-n'
+     Run the configure checks, but stop before creating any output
+     files.
+
+`configure' also accepts some other, not widely useful, options.  Run
+`configure --help' for more details.
+
diff --git a/Makefile.am b/Makefile.am
new file mode 100644 (file)
index 0000000..4521681
--- /dev/null
@@ -0,0 +1,10 @@
+SUBDIRS = libzipfile libcutils adb
+
+MAINTAINERCLEANFILES = Makefile.in aclocal.m4 config.guess \
+                       config.h.in config.sub configure install-sh \
+                      ltconfig ltmain.sh missing mkinstalldirs \
+                      stamp-h.in acconfig.h depcomp
+
+EXTRA_DIST = README AUTHORS COPYING autogen.sh
+
+
diff --git a/NEWS b/NEWS
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/adb/Android.mk b/adb/Android.mk
new file mode 100644 (file)
index 0000000..eafd36d
--- /dev/null
@@ -0,0 +1,166 @@
+# Copyright 2005 The Android Open Source Project
+#
+# Android.mk for adb
+#
+
+LOCAL_PATH:= $(call my-dir)
+
+# adb host tool
+# =========================================================
+include $(CLEAR_VARS)
+
+# Default to a virtual (sockets) usb interface
+USB_SRCS :=
+EXTRA_SRCS :=
+
+ifeq ($(HOST_OS),linux)
+  USB_SRCS := usb_linux.c
+  EXTRA_SRCS := get_my_path_linux.c
+  LOCAL_LDLIBS += -lrt -lpthread
+endif
+
+ifeq ($(HOST_OS),darwin)
+  USB_SRCS := usb_osx.c
+  EXTRA_SRCS := get_my_path_darwin.c
+  LOCAL_LDLIBS += -lpthread -framework CoreFoundation -framework IOKit -framework Carbon
+endif
+
+ifeq ($(HOST_OS),freebsd)
+  USB_SRCS := usb_libusb.c
+  EXTRA_SRCS := get_my_path_freebsd.c
+  LOCAL_LDLIBS += -lpthread -lusb
+endif
+
+ifeq ($(HOST_OS),windows)
+  USB_SRCS := usb_windows.c
+  EXTRA_SRCS := get_my_path_windows.c
+  EXTRA_STATIC_LIBS := AdbWinApi
+  ifneq ($(strip $(USE_CYGWIN)),)
+    # Pure cygwin case
+    LOCAL_LDLIBS += -lpthread
+    LOCAL_C_INCLUDES += /usr/include/w32api/ddk
+  endif
+  ifneq ($(strip $(USE_MINGW)),)
+    # MinGW under Linux case
+    LOCAL_LDLIBS += -lws2_32
+    USE_SYSDEPS_WIN32 := 1
+    LOCAL_C_INCLUDES += /usr/i586-mingw32msvc/include/ddk
+  endif
+  LOCAL_C_INCLUDES += development/host/windows/usb/api/
+endif
+
+LOCAL_SRC_FILES := \
+       adb.c \
+       console.c \
+       transport.c \
+       transport_local.c \
+       transport_usb.c \
+       commandline.c \
+       adb_client.c \
+       sockets.c \
+       services.c \
+       file_sync_client.c \
+       $(EXTRA_SRCS) \
+       $(USB_SRCS) \
+       utils.c \
+       usb_vendors.c
+
+
+ifneq ($(USE_SYSDEPS_WIN32),)
+  LOCAL_SRC_FILES += sysdeps_win32.c
+else
+  LOCAL_SRC_FILES += fdevent.c
+endif
+
+LOCAL_CFLAGS += -O2 -g -DADB_HOST=1  -Wall -Wno-unused-parameter
+LOCAL_CFLAGS += -D_XOPEN_SOURCE -D_GNU_SOURCE
+LOCAL_MODULE := adb
+
+LOCAL_STATIC_LIBRARIES := libzipfile libunz $(EXTRA_STATIC_LIBS)
+ifeq ($(USE_SYSDEPS_WIN32),)
+       LOCAL_STATIC_LIBRARIES += libcutils
+endif
+
+include $(BUILD_HOST_EXECUTABLE)
+
+$(call dist-for-goals,droid,$(LOCAL_BUILT_MODULE))
+
+ifeq ($(HOST_OS),windows)
+$(LOCAL_INSTALLED_MODULE): \
+    $(HOST_OUT_EXECUTABLES)/AdbWinApi.dll \
+    $(HOST_OUT_EXECUTABLES)/AdbWinUsbApi.dll
+endif
+
+
+# adbd device daemon
+# =========================================================
+
+# build adbd in all non-simulator builds
+BUILD_ADBD := false
+ifneq ($(TARGET_SIMULATOR),true)
+    BUILD_ADBD := true
+endif
+
+# build adbd for the Linux simulator build
+# so we can use it to test the adb USB gadget driver on x86
+#ifeq ($(HOST_OS),linux)
+#    BUILD_ADBD := true
+#endif
+
+
+ifeq ($(BUILD_ADBD),true)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+       adb.c \
+       fdevent.c \
+       transport.c \
+       transport_local.c \
+       transport_usb.c \
+       sockets.c \
+       services.c \
+       file_sync_service.c \
+       jdwp_service.c \
+       framebuffer_service.c \
+       remount_service.c \
+       usb_linux_client.c \
+       log_service.c \
+       utils.c
+
+LOCAL_CFLAGS := -O2 -g -DADB_HOST=0 -Wall -Wno-unused-parameter
+LOCAL_CFLAGS += -D_XOPEN_SOURCE -D_GNU_SOURCE
+
+# TODO: This should probably be board specific, whether or not the kernel has
+# the gadget driver; rather than relying on the architecture type.
+ifeq ($(TARGET_ARCH),arm)
+LOCAL_CFLAGS += -DANDROID_GADGET=1
+endif
+
+ifneq ($(TARGET_RECOVERY_PRE_COMMAND),)
+       LOCAL_CFLAGS += -DRECOVERY_PRE_COMMAND='$(TARGET_RECOVERY_PRE_COMMAND)'
+endif
+
+ifeq ($(BOARD_USE_SCREENCAP),true)
+       LOCAL_CFLAGS += -DUSE_SCREENCAP
+endif
+
+ifeq ($(BOARD_ALWAYS_INSECURE),true)
+       LOCAL_CFLAGS += -DBOARD_ALWAYS_INSECURE
+endif
+
+LOCAL_MODULE := adbd
+
+LOCAL_FORCE_STATIC_EXECUTABLE := true
+LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT_SBIN)
+LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_SBIN_UNSTRIPPED)
+
+ifeq ($(TARGET_SIMULATOR),true)
+  LOCAL_STATIC_LIBRARIES := libcutils
+  LOCAL_LDLIBS += -lpthread
+  include $(BUILD_HOST_EXECUTABLE)
+else
+  LOCAL_STATIC_LIBRARIES := libcutils libc
+  include $(BUILD_EXECUTABLE)
+endif
+
+endif
diff --git a/adb/MODULE_LICENSE_APACHE2 b/adb/MODULE_LICENSE_APACHE2
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/adb/Makefile.am b/adb/Makefile.am
new file mode 100644 (file)
index 0000000..a603860
--- /dev/null
@@ -0,0 +1,34 @@
+AUTOMAKE_OPTIONS     = 1.4 foreign
+MAINTAINERCLEANFILES = Makefile.in
+
+AM_CPPFLAGS = \
+-I. \
+-I$(top_builddir) \
+-I$(top_srcdir) \
+-I$(top_srcdir)/include \
+-I$(top_builddir)/include
+
+bin_PROGRAMS = adb
+
+adb_SOURCES = \
+adb.c \
+console.c \
+transport.c \
+transport_local.c \
+transport_usb.c \
+commandline.c \
+adb_client.c \
+sockets.c \
+services.c \
+fdevent.c \
+file_sync_client.c \
+usb_linux.c \
+get_my_path_linux.c \
+utils.c \
+usb_vendors.c
+
+adb_LDADD = $(top_builddir)/libzipfile/libzipfile.la  \
+                 $(top_builddir)/libcutils/libcutils.la 
+adb_CFLAGS = -DADB_HOST=1 -DHAVE_FORKEXEC
+adb_LDFLAGS = -lpthread
+
diff --git a/adb/NOTICE b/adb/NOTICE
new file mode 100644 (file)
index 0000000..9ffcc08
--- /dev/null
@@ -0,0 +1,191 @@
+
+   Copyright (c) 2006-2009, The Android Open Source Project
+   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.
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
diff --git a/adb/OVERVIEW.TXT b/adb/OVERVIEW.TXT
new file mode 100644 (file)
index 0000000..c40695a
--- /dev/null
@@ -0,0 +1,139 @@
+Implementation notes regarding ADB.
+
+I. General Overview:
+
+The Android Debug Bridge (ADB) is used to:
+
+- keep track of all Android devices and emulators instances
+  connected to or running on a given host developer machine
+
+- implement various control commands (e.g. "adb shell", "adb pull", etc..)
+  for the benefit of clients (command-line users, or helper programs like
+  DDMS). These commands are what is called a 'service' in ADB.
+
+As a whole, everything works through the following components:
+
+  1. The ADB server
+
+    This is a background process that runs on the host machine. Its purpose
+    if to sense the USB ports to know when devices are attached/removed,
+    as well as when emulator instances start/stop.
+
+    It thus maintains a list of "connected devices" and assigns a 'state'
+    to each one of them: OFFLINE, BOOTLOADER, RECOVERY or ONLINE (more on
+    this below).
+
+    The ADB server is really one giant multiplexing loop whose purpose is
+    to orchestrate the exchange of data (packets, really) between clients,
+    services and devices.
+
+
+  2. The ADB daemon (adbd)
+
+    The 'adbd' program runs as a background process within an Android device
+    or emulated system. Its purpose is to connect to the ADB server
+    (through USB for devices, through TCP for emulators) and provide a
+    few services for clients that run on the host.
+
+    The ADB server considers that a device is ONLINE when it has successfully
+    connected to the adbd program within it. Otherwise, the device is OFFLINE,
+    meaning that the ADB server detected a new device/emulator, but could not
+    connect to the adbd daemon.
+
+    the BOOTLOADER and RECOVERY states correspond to alternate states of
+    devices when they are in the bootloader or recovery mode.
+
+  3. The ADB command-line client
+
+    The 'adb' command-line program is used to run adb commands from a shell
+    or a script. It first tries to locate the ADB server on the host machine,
+    and will start one automatically if none is found.
+
+    then, the client sends its service requests to the ADB server. It doesn't
+    need to know.
+
+    Currently, a single 'adb' binary is used for both the server and client.
+    this makes distribution and starting the server easier.
+
+
+  4. Services
+
+    There are essentially two kinds of services that a client can talk to.
+
+    Host Services:
+      these services run within the ADB Server and thus do not need to
+      communicate with a device at all. A typical example is "adb devices"
+      which is used to return the list of currently known devices and their
+      state. They are a few couple other services though.
+
+    Local Services:
+      these services either run within the adbd daemon, or are started by
+      it on the device. The ADB server is used to multiplex streams
+      between the client and the service running in adbd. In this case
+      its role is to initiate the connection, then of being a pass-through
+      for the data.
+
+
+II. Protocol details:
+
+  1. Client <-> Server protocol:
+
+    This details the protocol used between ADB clients and the ADB
+    server itself. The ADB server listens on TCP:localhost:5037.
+
+    A client sends a request using the following format:
+
+        1. A 4-byte hexadecimal string giving the length of the payload
+        2. Followed by the payload itself.
+
+    For example, to query the ADB server for its internal version number,
+    the client will do the following:
+
+        1. Connect to tcp:localhost:5037
+        2. Send the string "000Chost:version" to the corresponding socket
+
+    The 'host:' prefix is used to indicate that the request is addressed
+    to the server itself (we will talk about other kinds of requests later).
+    The content length is encoded in ASCII for easier debugging.
+
+    The server should answer a request with one of the following:
+
+        1. For success, the 4-byte "OKAY" string
+
+        2. For failure, the 4-byte "FAIL" string, followed by a
+           4-byte hex length, followed by a string giving the reason
+           for failure.
+
+        3. As a special exception, for 'host:version', a 4-byte
+           hex string corresponding to the server's internal version number
+
+    Note that the connection is still alive after an OKAY, which allows the
+    client to make other requests. But in certain cases, an OKAY will even
+    change the state of the connection. 
+
+    For example, the case of the 'host:transport:<serialnumber>' request,
+    where '<serialnumber>' is used to identify a given device/emulator; after
+    the "OKAY" answer, all further requests made by the client will go
+    directly to the corresponding adbd daemon.
+
+    The file SERVICES.TXT lists all services currently implemented by ADB.
+
+
+  2. Transports:
+
+    An ADB transport models a connection between the ADB server and one device
+    or emulator. There are currently two kinds of transports:
+
+       - USB transports, for physical devices through USB
+
+       - Local transports, for emulators running on the host, connected to
+         the server through TCP
+
+    In theory, it should be possible to write a local transport that proxies
+    a connection between an ADB server and a device/emulator connected to/
+    running on another machine. This hasn't been done yet though.
+
+    Each transport can carry one or more multiplexed streams between clients
+    and the device/emulator they point to. The ADB server must handle
+    unexpected transport disconnections (e.g. when a device is physically
+    unplugged) properly.
diff --git a/adb/SERVICES.TXT b/adb/SERVICES.TXT
new file mode 100644 (file)
index 0000000..be4d50b
--- /dev/null
@@ -0,0 +1,236 @@
+This file tries to document all requests a client can make
+to the ADB server of an adbd daemon. See the OVERVIEW.TXT document
+to understand what's going on here.
+
+HOST SERVICES:
+
+host:version
+    Ask the ADB server for its internal version number.
+
+    As a special exception, the server will respond with a 4-byte
+    hex string corresponding to its internal version number, without
+    any OKAY or FAIL.
+
+host:kill
+    Ask the ADB server to quit immediately. This is used when the
+    ADB client detects that an obsolete server is running after an
+    upgrade.
+
+host:devices
+    Ask to return the list of available Android devices and their
+    state. After the OKAY, this is followed by a 4-byte hex len,
+    and a string that will be dumped as-is by the client, then
+    the connection is closed
+
+host:track-devices
+    This is a variant of host:devices which doesn't close the
+    connection. Instead, a new device list description is sent
+    each time a device is added/removed or the state of a given
+    device changes (hex4 + content). This allows tools like DDMS
+    to track the state of connected devices in real-time without
+    polling the server repeatedly.
+
+host:emulator:<port>
+    This is a special query that is sent to the ADB server when a
+    new emulator starts up. <port> is a decimal number corresponding
+    to the emulator's ADB control port, i.e. the TCP port that the
+    emulator will forward automatically to the adbd daemon running
+    in the emulator system.
+
+    This mechanism allows the ADB server to know when new emulator
+    instances start.
+
+host:transport:<serial-number>
+    Ask to switch the connection to the device/emulator identified by
+    <serial-number>. After the OKAY response, every client request will
+    be sent directly to the adbd daemon running on the device.
+    (Used to implement the -s option)
+
+host:transport-usb
+    Ask to switch the connection to one device connected through USB
+    to the host machine. This will fail if there are more than one such
+    devices. (Used to implement the -d convenience option)
+
+host:transport-local
+    Ask to switch the connection to one emulator connected through TCP.
+    This will fail if there is more than one such emulator instance
+    running. (Used to implement the -e convenience option)
+
+host:transport-any
+    Another host:transport variant. Ask to switch the connection to
+    either the device or emulator connect to/running on the host.
+    Will fail if there is more than one such device/emulator available.
+    (Used when neither -s, -d or -e are provided)
+
+host-serial:<serial-number>:<request>
+    This is a special form of query, where the 'host-serial:<serial-number>:'
+    prefix can be used to indicate that the client is asking the ADB server
+    for information related to a specific device. <request> can be in one
+    of the format described below.
+
+host-usb:<request>
+    A variant of host-serial used to target the single USB device connected
+    to the host. This will fail if there is none or more than one.
+
+host-local:<request>
+    A variant of host-serial used to target the single emulator instance
+    running on the host. This will fail if there is none or more than one.
+
+host:<request>
+    When asking for information related to a device, 'host:' can also be
+    interpreted as 'any single device or emulator connected to/running on
+    the host'.
+
+<host-prefix>:get-product
+    XXX
+
+<host-prefix>:get-serialno
+    Returns the serial number of the corresponding device/emulator.
+    Note that emulator serial numbers are of the form "emulator-5554"
+
+<host-prefix>:get-state
+    Returns the state of a given device as a string.
+
+<host-prefix>:forward:<local>;<remote>
+    Asks the ADB server to forward local connections from <local>
+    to the <remote> address on a given device.
+
+    There, <host-prefix> can be one of the
+    host-serial/host-usb/host-local/host prefixes as described previously
+    and indicates which device/emulator to target.
+
+    the format of <local> is one of:
+
+        tcp:<port>      -> TCP connection on localhost:<port>
+        local:<path>    -> Unix local domain socket on <path>
+
+    the format of <remote> is one of:
+
+        tcp:<port>      -> TCP localhost:<port> on device
+        local:<path>    -> Unix local domain socket on device
+        jdwp:<pid>      -> JDWP thread on VM process <pid>
+
+    or even any one of the local services described below.
+
+
+
+LOCAL SERVICES:
+
+All the queries below assumed that you already switched the transport
+to a real device, or that you have used a query prefix as described
+above.
+
+shell:command arg1 arg2 ...
+    Run 'command arg1 arg2 ...' in a shell on the device, and return
+    its output and error streams. Note that arguments must be separated
+    by spaces. If an argument contains a space, it must be quoted with
+    double-quotes. Arguments cannot contain double quotes or things
+    will go very wrong.
+
+    Note that this is the non-interactive version of "adb shell"
+
+shell:
+    Start an interactive shell session on the device. Redirect
+    stdin/stdout/stderr as appropriate. Note that the ADB server uses
+    this to implement "adb shell", but will also cook the input before
+    sending it to the device (see interactive_shell() in commandline.c)
+
+remount:
+    Ask adbd to remount the device's filesystem in read-write mode,
+    instead of read-only. This is usually necessary before performing
+    an "adb sync" or "adb push" request.
+
+    This request may not succeed on certain builds which do not allow
+    that.
+
+dev:<path>
+    Opens a device file and connects the client directly to it for
+    read/write purposes. Useful for debugging, but may require special
+    privileges and thus may not run on all devices. <path> is a full
+    path from the root of the filesystem.
+
+tcp:<port>
+    Tries to connect to tcp port <port> on localhost.
+
+tcp:<port>:<server-name>
+    Tries to connect to tcp port <port> on machine <server-name> from
+    the device. This can be useful to debug some networking/proxy
+    issues that can only be revealed on the device itself.
+
+local:<path>
+    Tries to connect to a Unix domain socket <path> on the device
+
+localreserved:<path>
+localabstract:<path>
+localfilesystem:<path>
+    Variants of local:<path> that are used to access other Android
+    socket namespaces.
+
+log:<name>
+    Opens one of the system logs (/dev/log/<name>) and allows the client
+    to read them directly. Used to implement 'adb logcat'. The stream
+    will be read-only for the client.
+
+framebuffer:
+    This service is used to send snapshots of the framebuffer to a client.
+    It requires sufficient privileges but works as follow:
+
+      After the OKAY, the service sends 16-byte binary structure
+      containing the following fields (little-endian format):
+
+            depth:   uint32_t:    framebuffer depth
+            size:    uint32_t:    framebuffer size in bytes
+            width:   uint32_t:    framebuffer width in pixels
+            height:  uint32_t:    framebuffer height in pixels
+
+      With the current implementation, depth is always 16, and
+      size is always width*height*2
+
+      Then, each time the client wants a snapshot, it should send
+      one byte through the channel, which will trigger the service
+      to send it 'size' bytes of framebuffer data.
+
+      If the adbd daemon doesn't have sufficient privileges to open
+      the framebuffer device, the connection is simply closed immediately.
+
+dns:<server-name>
+    This service is an exception because it only runs within the ADB server.
+    It is used to implement USB networking, i.e. to provide a network connection
+    to the device through the host machine (note: this is the exact opposite of
+    network tethering).
+
+    It is used to perform a gethostbyname(<address>) on the host and return
+    the corresponding IP address as a 4-byte string.
+
+recover:<size>
+    This service is used to upload a recovery image to the device. <size>
+    must be a number corresponding to the size of the file. The service works
+    by:
+
+       - creating a file named /tmp/update
+       - reading 'size' bytes from the client and writing them to /tmp/update
+       - when everything is read successfully, create a file named /tmp/update.start
+
+    This service can only work when the device is in recovery mode. Otherwise,
+    the /tmp directory doesn't exist and the connection will be closed immediately.
+
+jdwp:<pid>
+    Connects to the JDWP thread running in the VM of process <pid>.
+
+track-jdwp
+    This is used to send the list of JDWP pids periodically to the client.
+    The format of the returned data is the following:
+
+        <hex4>:    the length of all content as a 4-char hexadecimal string
+        <content>: a series of ASCII lines of the following format:
+                        <pid> "\n"
+
+    This service is used by DDMS to know which debuggable processes are running
+    on the device/emulator.
+
+    Note that there is no single-shot service to retrieve the list only once.
+
+sync:
+    This starts the file synchronisation service, used to implement "adb push"
+    and "adb pull". Since this service is pretty complex, it will be detailed
+    in a companion document named SYNC.TXT
diff --git a/adb/adb.c b/adb/adb.c
new file mode 100644 (file)
index 0000000..e2e1f95
--- /dev/null
+++ b/adb/adb.c
@@ -0,0 +1,1282 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define  TRACE_TAG   TRACE_ADB
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <string.h>
+#include <time.h>
+#include <sys/time.h>
+
+#include "sysdeps.h"
+#include "adb.h"
+
+#if !ADB_HOST
+#include <private/android_filesystem_config.h>
+#include <linux/capability.h>
+#include <linux/prctl.h>
+#else
+#include "usb_vendors.h"
+#endif
+
+
+int HOST = 0;
+
+static const char *adb_device_banner = "device";
+
+void fatal(const char *fmt, ...)
+{
+    va_list ap;
+    va_start(ap, fmt);
+    fprintf(stderr, "error: ");
+    vfprintf(stderr, fmt, ap);
+    fprintf(stderr, "\n");
+    va_end(ap);
+    exit(-1);
+}
+
+void fatal_errno(const char *fmt, ...)
+{
+    va_list ap;
+    va_start(ap, fmt);
+    fprintf(stderr, "error: %s: ", strerror(errno));
+    vfprintf(stderr, fmt, ap);
+    fprintf(stderr, "\n");
+    va_end(ap);
+    exit(-1);
+}
+
+int   adb_trace_mask;
+
+/* read a comma/space/colum/semi-column separated list of tags
+ * from the ADB_TRACE environment variable and build the trace
+ * mask from it. note that '1' and 'all' are special cases to
+ * enable all tracing
+ */
+void  adb_trace_init(void)
+{
+    const char*  p = getenv("ADB_TRACE");
+    const char*  q;
+
+    static const struct {
+        const char*  tag;
+        int           flag;
+    } tags[] = {
+        { "1", 0 },
+        { "all", 0 },
+        { "adb", TRACE_ADB },
+        { "sockets", TRACE_SOCKETS },
+        { "packets", TRACE_PACKETS },
+        { "rwx", TRACE_RWX },
+        { "usb", TRACE_USB },
+        { "sync", TRACE_SYNC },
+        { "sysdeps", TRACE_SYSDEPS },
+        { "transport", TRACE_TRANSPORT },
+        { "jdwp", TRACE_JDWP },
+        { NULL, 0 }
+    };
+
+    if (p == NULL)
+            return;
+
+    /* use a comma/column/semi-colum/space separated list */
+    while (*p) {
+        int  len, tagn;
+
+        q = strpbrk(p, " ,:;");
+        if (q == NULL) {
+            q = p + strlen(p);
+        }
+        len = q - p;
+
+        for (tagn = 0; tags[tagn].tag != NULL; tagn++)
+        {
+            int  taglen = strlen(tags[tagn].tag);
+
+            if (len == taglen && !memcmp(tags[tagn].tag, p, len) )
+            {
+                int  flag = tags[tagn].flag;
+                if (flag == 0) {
+                    adb_trace_mask = ~0;
+                    return;
+                }
+                adb_trace_mask |= (1 << flag);
+                break;
+            }
+        }
+        p = q;
+        if (*p)
+            p++;
+    }
+}
+
+
+apacket *get_apacket(void)
+{
+    apacket *p = malloc(sizeof(apacket));
+    if(p == 0) fatal("failed to allocate an apacket");
+    memset(p, 0, sizeof(apacket) - MAX_PAYLOAD);
+    return p;
+}
+
+void put_apacket(apacket *p)
+{
+    free(p);
+}
+
+void handle_online(void)
+{
+    D("adb: online\n");
+}
+
+void handle_offline(atransport *t)
+{
+    D("adb: offline\n");
+    //Close the associated usb
+    run_transport_disconnects(t);
+}
+
+#if TRACE_PACKETS
+#define DUMPMAX 32
+void print_packet(const char *label, apacket *p)
+{
+    char *tag;
+    char *x;
+    unsigned count;
+
+    switch(p->msg.command){
+    case A_SYNC: tag = "SYNC"; break;
+    case A_CNXN: tag = "CNXN" ; break;
+    case A_OPEN: tag = "OPEN"; break;
+    case A_OKAY: tag = "OKAY"; break;
+    case A_CLSE: tag = "CLSE"; break;
+    case A_WRTE: tag = "WRTE"; break;
+    default: tag = "????"; break;
+    }
+
+    fprintf(stderr, "%s: %s %08x %08x %04x \"",
+            label, tag, p->msg.arg0, p->msg.arg1, p->msg.data_length);
+    count = p->msg.data_length;
+    x = (char*) p->data;
+    if(count > DUMPMAX) {
+        count = DUMPMAX;
+        tag = "\n";
+    } else {
+        tag = "\"\n";
+    }
+    while(count-- > 0){
+        if((*x >= ' ') && (*x < 127)) {
+            fputc(*x, stderr);
+        } else {
+            fputc('.', stderr);
+        }
+        x++;
+    }
+    fprintf(stderr, tag);
+}
+#endif
+
+static void send_ready(unsigned local, unsigned remote, atransport *t)
+{
+    D("Calling send_ready \n");
+    apacket *p = get_apacket();
+    p->msg.command = A_OKAY;
+    p->msg.arg0 = local;
+    p->msg.arg1 = remote;
+    send_packet(p, t);
+}
+
+static void send_close(unsigned local, unsigned remote, atransport *t)
+{
+    D("Calling send_close \n");
+    apacket *p = get_apacket();
+    p->msg.command = A_CLSE;
+    p->msg.arg0 = local;
+    p->msg.arg1 = remote;
+    send_packet(p, t);
+}
+
+static void send_connect(atransport *t)
+{
+    D("Calling send_connect \n");
+    apacket *cp = get_apacket();
+    cp->msg.command = A_CNXN;
+    cp->msg.arg0 = A_VERSION;
+    cp->msg.arg1 = MAX_PAYLOAD;
+    snprintf((char*) cp->data, sizeof cp->data, "%s::",
+            HOST ? "host" : adb_device_banner);
+    cp->msg.data_length = strlen((char*) cp->data) + 1;
+    send_packet(cp, t);
+#if ADB_HOST
+        /* XXX why sleep here? */
+    // allow the device some time to respond to the connect message
+    adb_sleep_ms(1000);
+#endif
+}
+
+static char *connection_state_name(atransport *t)
+{
+    if (t == NULL) {
+        return "unknown";
+    }
+
+    switch(t->connection_state) {
+    case CS_BOOTLOADER:
+        return "bootloader";
+    case CS_DEVICE:
+        return "device";
+    case CS_OFFLINE:
+        return "offline";
+    default:
+        return "unknown";
+    }
+}
+
+void parse_banner(char *banner, atransport *t)
+{
+    char *type, *product, *end;
+
+    D("parse_banner: %s\n", banner);
+    type = banner;
+    product = strchr(type, ':');
+    if(product) {
+        *product++ = 0;
+    } else {
+        product = "";
+    }
+
+        /* remove trailing ':' */
+    end = strchr(product, ':');
+    if(end) *end = 0;
+
+        /* save product name in device structure */
+    if (t->product == NULL) {
+        t->product = strdup(product);
+    } else if (strcmp(product, t->product) != 0) {
+        free(t->product);
+        t->product = strdup(product);
+    }
+
+    if(!strcmp(type, "bootloader")){
+        D("setting connection_state to CS_BOOTLOADER\n");
+        t->connection_state = CS_BOOTLOADER;
+        update_transports();
+        return;
+    }
+
+    if(!strcmp(type, "device")) {
+        D("setting connection_state to CS_DEVICE\n");
+        t->connection_state = CS_DEVICE;
+        update_transports();
+        return;
+    }
+
+    if(!strcmp(type, "recovery")) {
+        D("setting connection_state to CS_RECOVERY\n");
+        t->connection_state = CS_RECOVERY;
+        update_transports();
+        return;
+    }
+
+    t->connection_state = CS_HOST;
+}
+
+void handle_packet(apacket *p, atransport *t)
+{
+    asocket *s;
+
+    D("handle_packet() %d\n", p->msg.command);
+
+    print_packet("recv", p);
+
+    switch(p->msg.command){
+    case A_SYNC:
+        if(p->msg.arg0){
+            send_packet(p, t);
+            if(HOST) send_connect(t);
+        } else {
+            t->connection_state = CS_OFFLINE;
+            handle_offline(t);
+            send_packet(p, t);
+        }
+        return;
+
+    case A_CNXN: /* CONNECT(version, maxdata, "system-id-string") */
+            /* XXX verify version, etc */
+        if(t->connection_state != CS_OFFLINE) {
+            t->connection_state = CS_OFFLINE;
+            handle_offline(t);
+        }
+        parse_banner((char*) p->data, t);
+        handle_online();
+        if(!HOST) send_connect(t);
+        break;
+
+    case A_OPEN: /* OPEN(local-id, 0, "destination") */
+        if(t->connection_state != CS_OFFLINE) {
+            char *name = (char*) p->data;
+            name[p->msg.data_length > 0 ? p->msg.data_length - 1 : 0] = 0;
+            s = create_local_service_socket(name);
+            if(s == 0) {
+                send_close(0, p->msg.arg0, t);
+            } else {
+                s->peer = create_remote_socket(p->msg.arg0, t);
+                s->peer->peer = s;
+                send_ready(s->id, s->peer->id, t);
+                s->ready(s);
+            }
+        }
+        break;
+
+    case A_OKAY: /* READY(local-id, remote-id, "") */
+        if(t->connection_state != CS_OFFLINE) {
+            if((s = find_local_socket(p->msg.arg1))) {
+                if(s->peer == 0) {
+                    s->peer = create_remote_socket(p->msg.arg0, t);
+                    s->peer->peer = s;
+                }
+                s->ready(s);
+            }
+        }
+        break;
+
+    case A_CLSE: /* CLOSE(local-id, remote-id, "") */
+        if(t->connection_state != CS_OFFLINE) {
+            if((s = find_local_socket(p->msg.arg1))) {
+                s->close(s);
+            }
+        }
+        break;
+
+    case A_WRTE:
+        if(t->connection_state != CS_OFFLINE) {
+            if((s = find_local_socket(p->msg.arg1))) {
+                unsigned rid = p->msg.arg0;
+                p->len = p->msg.data_length;
+
+                if(s->enqueue(s, p) == 0) {
+                    D("Enqueue the socket\n");
+                    send_ready(s->id, rid, t);
+                }
+                return;
+            }
+        }
+        break;
+
+    default:
+        printf("handle_packet: what is %08x?!\n", p->msg.command);
+    }
+
+    put_apacket(p);
+}
+
+alistener listener_list = {
+    .next = &listener_list,
+    .prev = &listener_list,
+};
+
+static void ss_listener_event_func(int _fd, unsigned ev, void *_l)
+{
+    asocket *s;
+
+    if(ev & FDE_READ) {
+        struct sockaddr addr;
+        socklen_t alen;
+        int fd;
+
+        alen = sizeof(addr);
+        fd = adb_socket_accept(_fd, &addr, &alen);
+        if(fd < 0) return;
+
+        adb_socket_setbufsize(fd, CHUNK_SIZE);
+
+        s = create_local_socket(fd);
+        if(s) {
+            connect_to_smartsocket(s);
+            return;
+        }
+
+        adb_close(fd);
+    }
+}
+
+static void listener_event_func(int _fd, unsigned ev, void *_l)
+{
+    alistener *l = _l;
+    asocket *s;
+
+    if(ev & FDE_READ) {
+        struct sockaddr addr;
+        socklen_t alen;
+        int fd;
+
+        alen = sizeof(addr);
+        fd = adb_socket_accept(_fd, &addr, &alen);
+        if(fd < 0) return;
+
+        s = create_local_socket(fd);
+        if(s) {
+            s->transport = l->transport;
+            connect_to_remote(s, l->connect_to);
+            return;
+        }
+
+        adb_close(fd);
+    }
+}
+
+static void  free_listener(alistener*  l)
+{
+    if (l->next) {
+        l->next->prev = l->prev;
+        l->prev->next = l->next;
+        l->next = l->prev = l;
+    }
+
+    // closes the corresponding fd
+    fdevent_remove(&l->fde);
+
+    if (l->local_name)
+        free((char*)l->local_name);
+
+    if (l->connect_to)
+        free((char*)l->connect_to);
+
+    if (l->transport) {
+        remove_transport_disconnect(l->transport, &l->disconnect);
+    }
+    free(l);
+}
+
+static void listener_disconnect(void*  _l, atransport*  t)
+{
+    alistener*  l = _l;
+
+    free_listener(l);
+}
+
+int local_name_to_fd(const char *name)
+{
+    int port;
+
+    if(!strncmp("tcp:", name, 4)){
+        int  ret;
+        port = atoi(name + 4);
+        ret = socket_loopback_server(port, SOCK_STREAM);
+        return ret;
+    }
+#ifndef HAVE_WIN32_IPC  /* no Unix-domain sockets on Win32 */
+    // It's non-sensical to support the "reserved" space on the adb host side
+    if(!strncmp(name, "local:", 6)) {
+        return socket_local_server(name + 6,
+                ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM);
+    } else if(!strncmp(name, "localabstract:", 14)) {
+        return socket_local_server(name + 14,
+                ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM);
+    } else if(!strncmp(name, "localfilesystem:", 16)) {
+        return socket_local_server(name + 16,
+                ANDROID_SOCKET_NAMESPACE_FILESYSTEM, SOCK_STREAM);
+    }
+
+#endif
+    printf("unknown local portname '%s'\n", name);
+    return -1;
+}
+
+static int remove_listener(const char *local_name, const char *connect_to, atransport* transport)
+{
+    alistener *l;
+
+    for (l = listener_list.next; l != &listener_list; l = l->next) {
+        if (!strcmp(local_name, l->local_name) &&
+            !strcmp(connect_to, l->connect_to) &&
+            l->transport && l->transport == transport) {
+
+            listener_disconnect(l, transport);
+            return 0;
+        }
+    }
+
+    return -1;
+}
+
+static int install_listener(const char *local_name, const char *connect_to, atransport* transport)
+{
+    alistener *l;
+
+    //printf("install_listener('%s','%s')\n", local_name, connect_to);
+
+    for(l = listener_list.next; l != &listener_list; l = l->next){
+        if(strcmp(local_name, l->local_name) == 0) {
+            char *cto;
+
+                /* can't repurpose a smartsocket */
+            if(l->connect_to[0] == '*') {
+                return -1;
+            }
+
+            cto = strdup(connect_to);
+            if(cto == 0) {
+                return -1;
+            }
+
+            //printf("rebinding '%s' to '%s'\n", local_name, connect_to);
+            free((void*) l->connect_to);
+            l->connect_to = cto;
+            if (l->transport != transport) {
+                remove_transport_disconnect(l->transport, &l->disconnect);
+                l->transport = transport;
+                add_transport_disconnect(l->transport, &l->disconnect);
+            }
+            return 0;
+        }
+    }
+
+    if((l = calloc(1, sizeof(alistener))) == 0) goto nomem;
+    if((l->local_name = strdup(local_name)) == 0) goto nomem;
+    if((l->connect_to = strdup(connect_to)) == 0) goto nomem;
+
+
+    l->fd = local_name_to_fd(local_name);
+    if(l->fd < 0) {
+        free((void*) l->local_name);
+        free((void*) l->connect_to);
+        free(l);
+        printf("cannot bind '%s'\n", local_name);
+        return -2;
+    }
+
+    close_on_exec(l->fd);
+    if(!strcmp(l->connect_to, "*smartsocket*")) {
+        fdevent_install(&l->fde, l->fd, ss_listener_event_func, l);
+    } else {
+        fdevent_install(&l->fde, l->fd, listener_event_func, l);
+    }
+    fdevent_set(&l->fde, FDE_READ);
+
+    l->next = &listener_list;
+    l->prev = listener_list.prev;
+    l->next->prev = l;
+    l->prev->next = l;
+    l->transport = transport;
+
+    if (transport) {
+        l->disconnect.opaque = l;
+        l->disconnect.func   = listener_disconnect;
+        add_transport_disconnect(transport, &l->disconnect);
+    }
+    return 0;
+
+nomem:
+    fatal("cannot allocate listener");
+    return 0;
+}
+
+#ifdef HAVE_FORKEXEC
+static void sigchld_handler(int n)
+{
+    int status;
+    while(waitpid(-1, &status, WNOHANG) > 0) ;
+}
+#endif
+
+#ifdef HAVE_WIN32_PROC
+static BOOL WINAPI ctrlc_handler(DWORD type)
+{
+    exit(STATUS_CONTROL_C_EXIT);
+    return TRUE;
+}
+#endif
+
+static void adb_cleanup(void)
+{
+    usb_cleanup();
+}
+
+void start_logging(void)
+{
+#ifdef HAVE_WIN32_PROC
+    char    temp[ MAX_PATH ];
+    FILE*   fnul;
+    FILE*   flog;
+
+    GetTempPath( sizeof(temp) - 8, temp );
+    strcat( temp, "adb.log" );
+
+    /* Win32 specific redirections */
+    fnul = fopen( "NUL", "rt" );
+    if (fnul != NULL)
+        stdin[0] = fnul[0];
+
+    flog = fopen( temp, "at" );
+    if (flog == NULL)
+        flog = fnul;
+
+    setvbuf( flog, NULL, _IONBF, 0 );
+
+    stdout[0] = flog[0];
+    stderr[0] = flog[0];
+    fprintf(stderr,"--- adb starting (pid %d) ---\n", getpid());
+#else
+    int fd;
+
+    fd = unix_open("/dev/null", O_RDONLY);
+    dup2(fd, 0);
+
+    fd = unix_open("/tmp/adb.log", O_WRONLY | O_CREAT | O_APPEND, 0640);
+    if(fd < 0) {
+        fd = unix_open("/dev/null", O_WRONLY);
+    }
+    dup2(fd, 1);
+    dup2(fd, 2);
+    fprintf(stderr,"--- adb starting (pid %d) ---\n", getpid());
+#endif
+}
+
+#if !ADB_HOST
+void start_device_log(void)
+{
+    int fd;
+    char    path[PATH_MAX];
+    struct tm now;
+    time_t t;
+    char value[PROPERTY_VALUE_MAX];
+
+    // read the trace mask from persistent property persist.adb.trace_mask
+    // give up if the property is not set or cannot be parsed
+    property_get("persist.adb.trace_mask", value, "");
+    if (sscanf(value, "%x", &adb_trace_mask) != 1)
+        return;
+
+    adb_mkdir("/data/adb", 0775);
+    tzset();
+    time(&t);
+    localtime_r(&t, &now);
+    strftime(path, sizeof(path),
+                "/data/adb/adb-%Y-%m-%d-%H-%M-%S.txt",
+                &now);
+    fd = unix_open(path, O_WRONLY | O_CREAT | O_TRUNC, 0640);
+    if (fd < 0)
+        return;
+
+    // redirect stdout and stderr to the log file
+    dup2(fd, 1);
+    dup2(fd, 2);
+    fprintf(stderr,"--- adb starting (pid %d) ---\n", getpid());
+
+    fd = unix_open("/dev/null", O_RDONLY);
+    dup2(fd, 0);
+}
+#endif
+
+#if ADB_HOST
+int launch_server(int server_port)
+{
+#ifdef HAVE_WIN32_PROC
+    /* we need to start the server in the background                    */
+    /* we create a PIPE that will be used to wait for the server's "OK" */
+    /* message since the pipe handles must be inheritable, we use a     */
+    /* security attribute                                               */
+    HANDLE                pipe_read, pipe_write;
+    SECURITY_ATTRIBUTES   sa;
+    STARTUPINFO           startup;
+    PROCESS_INFORMATION   pinfo;
+    char                  program_path[ MAX_PATH ];
+    int                   ret;
+
+    sa.nLength = sizeof(sa);
+    sa.lpSecurityDescriptor = NULL;
+    sa.bInheritHandle = TRUE;
+
+    /* create pipe, and ensure its read handle isn't inheritable */
+    ret = CreatePipe( &pipe_read, &pipe_write, &sa, 0 );
+    if (!ret) {
+        fprintf(stderr, "CreatePipe() failure, error %ld\n", GetLastError() );
+        return -1;
+    }
+
+    SetHandleInformation( pipe_read, HANDLE_FLAG_INHERIT, 0 );
+
+    ZeroMemory( &startup, sizeof(startup) );
+    startup.cb = sizeof(startup);
+    startup.hStdInput  = GetStdHandle( STD_INPUT_HANDLE );
+    startup.hStdOutput = pipe_write;
+    startup.hStdError  = GetStdHandle( STD_ERROR_HANDLE );
+    startup.dwFlags    = STARTF_USESTDHANDLES;
+
+    ZeroMemory( &pinfo, sizeof(pinfo) );
+
+    /* get path of current program */
+    GetModuleFileName( NULL, program_path, sizeof(program_path) );
+
+    ret = CreateProcess(
+            program_path,                              /* program path  */
+            "adb fork-server server",
+                                    /* the fork-server argument will set the
+                                       debug = 2 in the child           */
+            NULL,                   /* process handle is not inheritable */
+            NULL,                    /* thread handle is not inheritable */
+            TRUE,                          /* yes, inherit some handles */
+            DETACHED_PROCESS, /* the new process doesn't have a console */
+            NULL,                     /* use parent's environment block */
+            NULL,                    /* use parent's starting directory */
+            &startup,                 /* startup info, i.e. std handles */
+            &pinfo );
+
+    CloseHandle( pipe_write );
+
+    if (!ret) {
+        fprintf(stderr, "CreateProcess failure, error %ld\n", GetLastError() );
+        CloseHandle( pipe_read );
+        return -1;
+    }
+
+    CloseHandle( pinfo.hProcess );
+    CloseHandle( pinfo.hThread );
+
+    /* wait for the "OK\n" message */
+    {
+        char  temp[3];
+        DWORD  count;
+
+        ret = ReadFile( pipe_read, temp, 3, &count, NULL );
+        CloseHandle( pipe_read );
+        if ( !ret ) {
+            fprintf(stderr, "could not read ok from ADB Server, error = %ld\n", GetLastError() );
+            return -1;
+        }
+        if (count != 3 || temp[0] != 'O' || temp[1] != 'K' || temp[2] != '\n') {
+            fprintf(stderr, "ADB server didn't ACK\n" );
+            return -1;
+        }
+    }
+#elif defined(HAVE_FORKEXEC)
+    char    path[PATH_MAX];
+    int     fd[2];
+
+    // set up a pipe so the child can tell us when it is ready.
+    // fd[0] will be parent's end, and fd[1] will get mapped to stderr in the child.
+    if (pipe(fd)) {
+        fprintf(stderr, "pipe failed in launch_server, errno: %d\n", errno);
+        return -1;
+    }
+    get_my_path(path, PATH_MAX);
+    pid_t pid = fork();
+    if(pid < 0) return -1;
+
+    if (pid == 0) {
+        // child side of the fork
+
+        // redirect stderr to the pipe
+        // we use stderr instead of stdout due to stdout's buffering behavior.
+        adb_close(fd[0]);
+        dup2(fd[1], STDERR_FILENO);
+        adb_close(fd[1]);
+
+        // child process
+        int result = execl(path, "adb", "fork-server", "server", NULL);
+        // this should not return
+        fprintf(stderr, "OOPS! execl returned %d, errno: %d\n", result, errno);
+    } else  {
+        // parent side of the fork
+
+        char  temp[3];
+
+        temp[0] = 'A'; temp[1] = 'B'; temp[2] = 'C';
+        // wait for the "OK\n" message
+        adb_close(fd[1]);
+        int ret = adb_read(fd[0], temp, 3);
+        adb_close(fd[0]);
+        if (ret < 0) {
+            fprintf(stderr, "could not read ok from ADB Server, errno = %d\n", errno);
+            return -1;
+        }
+        if (ret != 3 || temp[0] != 'O' || temp[1] != 'K' || temp[2] != '\n') {
+            fprintf(stderr, "ADB server didn't ACK\n" );
+            return -1;
+        }
+
+        setsid();
+    }
+#else
+#error "cannot implement background server start on this platform"
+#endif
+    return 0;
+}
+#endif
+
+/* Constructs a local name of form tcp:port.
+ * target_str points to the target string, it's content will be overwritten.
+ * target_size is the capacity of the target string.
+ * server_port is the port number to use for the local name.
+ */
+void build_local_name(char* target_str, size_t target_size, int server_port)
+{
+  snprintf(target_str, target_size, "tcp:%d", server_port);
+}
+
+int adb_main(int is_daemon, int server_port)
+{
+#if !ADB_HOST
+    int secure = 0;
+    int port;
+    char value[PROPERTY_VALUE_MAX];
+#endif
+
+    atexit(adb_cleanup);
+#ifdef HAVE_WIN32_PROC
+    SetConsoleCtrlHandler( ctrlc_handler, TRUE );
+#elif defined(HAVE_FORKEXEC)
+    signal(SIGCHLD, sigchld_handler);
+    signal(SIGPIPE, SIG_IGN);
+#endif
+
+    init_transport_registration();
+
+
+#if ADB_HOST
+    HOST = 1;
+    usb_vendors_init();
+    usb_init();
+    local_init(DEFAULT_ADB_LOCAL_TRANSPORT_PORT);
+
+    char local_name[30];
+    build_local_name(local_name, sizeof(local_name), server_port);
+    if(install_listener(local_name, "*smartsocket*", NULL)) {
+        exit(1);
+    }
+#else
+    /* run adbd in secure mode if ro.secure is set and
+    ** we are not in the emulator
+    */
+    property_get("ro.kernel.qemu", value, "");
+    if (strcmp(value, "1") != 0) {
+        property_get("ro.secure", value, "");
+        if (strcmp(value, "1") == 0) {
+            // don't run as root if ro.secure is set...
+#ifndef BOARD_ALWAYS_INSECURE
+            secure = 1;
+#endif
+
+            // ... except we allow running as root in userdebug builds if the 
+            // service.adb.root property has been set by the "adb root" command
+            property_get("ro.debuggable", value, "");
+            if (strcmp(value, "1") == 0) {
+                property_get("service.adb.root", value, "");
+                if (strcmp(value, "1") == 0) {
+                    secure = 0;
+                }
+            }
+        }
+    }
+
+    /* don't listen on a port (default 5037) if running in secure mode */
+    /* don't run as root if we are running in secure mode */
+    if (secure) {
+        struct __user_cap_header_struct header;
+        struct __user_cap_data_struct cap;
+
+        if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) != 0) {
+            exit(1);
+        }
+
+        /* add extra groups:
+        ** AID_ADB to access the USB driver
+        ** AID_LOG to read system logs (adb logcat)
+        ** AID_INPUT to diagnose input issues (getevent)
+        ** AID_INET to diagnose network issues (netcfg, ping)
+        ** AID_GRAPHICS to access the frame buffer
+        ** AID_NET_BT and AID_NET_BT_ADMIN to diagnose bluetooth (hcidump)
+        ** AID_SDCARD_RW to allow writing to the SD card
+        ** AID_MOUNT to allow unmounting the SD card before rebooting
+        */
+        gid_t groups[] = { AID_ADB, AID_LOG, AID_INPUT, AID_INET, AID_GRAPHICS,
+                           AID_NET_BT, AID_NET_BT_ADMIN, AID_SDCARD_RW, AID_MOUNT };
+        if (setgroups(sizeof(groups)/sizeof(groups[0]), groups) != 0) {
+            exit(1);
+        }
+
+        /* then switch user and group to "shell" */
+        if (setgid(AID_SHELL) != 0) {
+            exit(1);
+        }
+        if (setuid(AID_SHELL) != 0) {
+            exit(1);
+        }
+
+        /* set CAP_SYS_BOOT capability, so "adb reboot" will succeed */
+        header.version = _LINUX_CAPABILITY_VERSION;
+        header.pid = 0;
+        cap.effective = cap.permitted = (1 << CAP_SYS_BOOT);
+        cap.inheritable = 0;
+        capset(&header, &cap);
+
+        D("Local port disabled\n");
+    } else {
+        char local_name[30];
+        build_local_name(local_name, sizeof(local_name), server_port);
+        if(install_listener(local_name, "*smartsocket*", NULL)) {
+            exit(1);
+        }
+    }
+
+        /* for the device, start the usb transport if the
+        ** android usb device exists and the "service.adb.tcp.port" and
+        ** "persist.adb.tcp.port" properties are not set.
+        ** Otherwise start the network transport.
+        */
+    property_get("service.adb.tcp.port", value, "");
+    if (!value[0])
+        property_get("persist.adb.tcp.port", value, "");
+    if (sscanf(value, "%d", &port) == 1 && port > 0) {
+        // listen on TCP port specified by service.adb.tcp.port property
+        local_init(port);
+    } else if (access("/dev/android_adb", F_OK) == 0) {
+        // listen on USB
+        usb_init();
+    } else {
+        // listen on default port
+        local_init(DEFAULT_ADB_LOCAL_TRANSPORT_PORT);
+    }
+    init_jdwp();
+#endif
+
+    if (is_daemon)
+    {
+        // inform our parent that we are up and running.
+#ifdef HAVE_WIN32_PROC
+        DWORD  count;
+        WriteFile( GetStdHandle( STD_OUTPUT_HANDLE ), "OK\n", 3, &count, NULL );
+#elif defined(HAVE_FORKEXEC)
+        fprintf(stderr, "OK\n");
+#endif
+        start_logging();
+    }
+
+    fdevent_loop();
+
+    usb_cleanup();
+
+    return 0;
+}
+
+#if ADB_HOST
+void connect_device(char* host, char* buffer, int buffer_size)
+{
+    int port, fd;
+    char* portstr = strchr(host, ':');
+    char hostbuf[100];
+    char serial[100];
+
+    strncpy(hostbuf, host, sizeof(hostbuf) - 1);
+    if (portstr) {
+        if (portstr - host >= sizeof(hostbuf)) {
+            snprintf(buffer, buffer_size, "bad host name %s", host);
+            return;
+        }
+        // zero terminate the host at the point we found the colon
+        hostbuf[portstr - host] = 0;
+        if (sscanf(portstr + 1, "%d", &port) == 0) {
+            snprintf(buffer, buffer_size, "bad port number %s", portstr);
+            return;
+        }
+    } else {
+        port = DEFAULT_ADB_LOCAL_TRANSPORT_PORT;
+    }
+
+    snprintf(serial, sizeof(serial), "%s:%d", hostbuf, port);
+    if (find_transport(serial)) {
+        snprintf(buffer, buffer_size, "already connected to %s", serial);
+        return;
+    }
+
+    fd = socket_network_client(hostbuf, port, SOCK_STREAM);
+    if (fd < 0) {
+        snprintf(buffer, buffer_size, "unable to connect to %s:%d", host, port);
+        return;
+    }
+
+    D("client: connected on remote on fd %d\n", fd);
+    close_on_exec(fd);
+    disable_tcp_nagle(fd);
+    register_socket_transport(fd, serial, port, 0);
+    snprintf(buffer, buffer_size, "connected to %s", serial);
+}
+
+void connect_emulator(char* port_spec, char* buffer, int buffer_size)
+{
+    char* port_separator = strchr(port_spec, ',');
+    if (!port_separator) {
+        snprintf(buffer, buffer_size,
+                "unable to parse '%s' as <console port>,<adb port>",
+                port_spec);
+        return;
+    }
+
+    // Zero-terminate console port and make port_separator point to 2nd port.
+    *port_separator++ = 0;
+    int console_port = strtol(port_spec, NULL, 0);
+    int adb_port = strtol(port_separator, NULL, 0);
+    if (!(console_port > 0 && adb_port > 0)) {
+        *(port_separator - 1) = ',';
+        snprintf(buffer, buffer_size,
+                "Invalid port numbers: Expected positive numbers, got '%s'",
+                port_spec);
+        return;
+    }
+
+    /* Check if the emulator is already known.
+     * Note: There's a small but harmless race condition here: An emulator not
+     * present just yet could be registered by another invocation right
+     * after doing this check here. However, local_connect protects
+     * against double-registration too. From here, a better error message
+     * can be produced. In the case of the race condition, the very specific
+     * error message won't be shown, but the data doesn't get corrupted. */
+    atransport* known_emulator = find_emulator_transport_by_adb_port(adb_port);
+    if (known_emulator != NULL) {
+        snprintf(buffer, buffer_size,
+                "Emulator on port %d already registered.", adb_port);
+        return;
+    }
+
+    /* Check if more emulators can be registered. Similar unproblematic
+     * race condition as above. */
+    int candidate_slot = get_available_local_transport_index();
+    if (candidate_slot < 0) {
+        snprintf(buffer, buffer_size, "Cannot accept more emulators.");
+        return;
+    }
+
+    /* Preconditions met, try to connect to the emulator. */
+    if (!local_connect_arbitrary_ports(console_port, adb_port)) {
+        snprintf(buffer, buffer_size,
+                "Connected to emulator on ports %d,%d", console_port, adb_port);
+    } else {
+        snprintf(buffer, buffer_size,
+                "Could not connect to emulator on ports %d,%d",
+                console_port, adb_port);
+    }
+}
+#endif
+
+int handle_host_request(char *service, transport_type ttype, char* serial, int reply_fd, asocket *s)
+{
+    atransport *transport = NULL;
+    char buf[4096];
+
+    if(!strcmp(service, "kill")) {
+        fprintf(stderr,"adb server killed by remote request\n");
+        fflush(stdout);
+        adb_write(reply_fd, "OKAY", 4);
+        usb_cleanup();
+        exit(0);
+    }
+
+#if ADB_HOST
+    // "transport:" is used for switching transport with a specified serial number
+    // "transport-usb:" is used for switching transport to the only USB transport
+    // "transport-local:" is used for switching transport to the only local transport
+    // "transport-any:" is used for switching transport to the only transport
+    if (!strncmp(service, "transport", strlen("transport"))) {
+        char* error_string = "unknown failure";
+        transport_type type = kTransportAny;
+
+        if (!strncmp(service, "transport-usb", strlen("transport-usb"))) {
+            type = kTransportUsb;
+        } else if (!strncmp(service, "transport-local", strlen("transport-local"))) {
+            type = kTransportLocal;
+        } else if (!strncmp(service, "transport-any", strlen("transport-any"))) {
+            type = kTransportAny;
+        } else if (!strncmp(service, "transport:", strlen("transport:"))) {
+            service += strlen("transport:");
+            serial = strdup(service);
+        }
+
+        transport = acquire_one_transport(CS_ANY, type, serial, &error_string);
+
+        if (transport) {
+            s->transport = transport;
+            adb_write(reply_fd, "OKAY", 4);
+        } else {
+            sendfailmsg(reply_fd, error_string);
+        }
+        return 1;
+    }
+
+    // return a list of all connected devices
+    if (!strcmp(service, "devices")) {
+        char buffer[4096];
+        memset(buf, 0, sizeof(buf));
+        memset(buffer, 0, sizeof(buffer));
+        D("Getting device list \n");
+        list_transports(buffer, sizeof(buffer));
+        snprintf(buf, sizeof(buf), "OKAY%04x%s",(unsigned)strlen(buffer),buffer);
+        D("Wrote device list \n");
+        writex(reply_fd, buf, strlen(buf));
+        return 0;
+    }
+
+    // add a new TCP transport, device or emulator
+    if (!strncmp(service, "connect:", 8)) {
+        char buffer[4096];
+        char* host = service + 8;
+        if (!strncmp(host, "emu:", 4)) {
+            connect_emulator(host + 4, buffer, sizeof(buffer));
+        } else {
+            connect_device(host, buffer, sizeof(buffer));
+        }
+        // Send response for emulator and device
+        snprintf(buf, sizeof(buf), "OKAY%04x%s",(unsigned)strlen(buffer), buffer);
+        writex(reply_fd, buf, strlen(buf));
+        return 0;
+    }
+
+    // remove TCP transport
+    if (!strncmp(service, "disconnect:", 11)) {
+        char buffer[4096];
+        memset(buffer, 0, sizeof(buffer));
+        char* serial = service + 11;
+        if (serial[0] == 0) {
+            // disconnect from all TCP devices
+            unregister_all_tcp_transports();
+        } else {
+            char hostbuf[100];
+            // assume port 5555 if no port is specified
+            if (!strchr(serial, ':')) {
+                snprintf(hostbuf, sizeof(hostbuf) - 1, "%s:5555", serial);
+                serial = hostbuf;
+            }
+            atransport *t = find_transport(serial);
+
+            if (t) {
+                unregister_transport(t);
+            } else {
+                snprintf(buffer, sizeof(buffer), "No such device %s", serial);
+            }
+        }
+
+        snprintf(buf, sizeof(buf), "OKAY%04x%s",(unsigned)strlen(buffer), buffer);
+        writex(reply_fd, buf, strlen(buf));
+        return 0;
+    }
+
+    // returns our value for ADB_SERVER_VERSION
+    if (!strcmp(service, "version")) {
+        char version[12];
+        snprintf(version, sizeof version, "%04x", ADB_SERVER_VERSION);
+        snprintf(buf, sizeof buf, "OKAY%04x%s", (unsigned)strlen(version), version);
+        writex(reply_fd, buf, strlen(buf));
+        return 0;
+    }
+
+    if(!strncmp(service,"get-serialno",strlen("get-serialno"))) {
+        char *out = "unknown";
+         transport = acquire_one_transport(CS_ANY, ttype, serial, NULL);
+       if (transport && transport->serial) {
+            out = transport->serial;
+        }
+        snprintf(buf, sizeof buf, "OKAY%04x%s",(unsigned)strlen(out),out);
+        writex(reply_fd, buf, strlen(buf));
+        return 0;
+    }
+    // indicates a new emulator instance has started
+    if (!strncmp(service,"emulator:",9)) {
+        int  port = atoi(service+9);
+        local_connect(port);
+        /* we don't even need to send a reply */
+        return 0;
+    }
+#endif // ADB_HOST
+
+    if(!strncmp(service,"forward:",8) || !strncmp(service,"killforward:",12)) {
+        char *local, *remote, *err;
+        int r;
+        atransport *transport;
+
+        int createForward = strncmp(service,"kill",4);
+
+        local = service + (createForward ? 8 : 12);
+        remote = strchr(local,';');
+        if(remote == 0) {
+            sendfailmsg(reply_fd, "malformed forward spec");
+            return 0;
+        }
+
+        *remote++ = 0;
+        if((local[0] == 0) || (remote[0] == 0) || (remote[0] == '*')){
+            sendfailmsg(reply_fd, "malformed forward spec");
+            return 0;
+        }
+
+        transport = acquire_one_transport(CS_ANY, ttype, serial, &err);
+        if (!transport) {
+            sendfailmsg(reply_fd, err);
+            return 0;
+        }
+
+        if (createForward) {
+            r = install_listener(local, remote, transport);
+        } else {
+            r = remove_listener(local, remote, transport);
+        }
+        if(r == 0) {
+                /* 1st OKAY is connect, 2nd OKAY is status */
+            writex(reply_fd, "OKAYOKAY", 8);
+            return 0;
+        }
+
+        if (createForward) {
+            sendfailmsg(reply_fd, (r == -1) ? "cannot rebind smartsocket" : "cannot bind socket");
+        } else {
+            sendfailmsg(reply_fd, "cannot remove listener");
+        }
+        return 0;
+    }
+
+    if(!strncmp(service,"get-state",strlen("get-state"))) {
+        transport = acquire_one_transport(CS_ANY, ttype, serial, NULL);
+        char *state = connection_state_name(transport);
+        snprintf(buf, sizeof buf, "OKAY%04x%s",(unsigned)strlen(state),state);
+        writex(reply_fd, buf, strlen(buf));
+        return 0;
+    }
+    return -1;
+}
+
+#if !ADB_HOST
+int recovery_mode = 0;
+#endif
+
+int main(int argc, char **argv)
+{
+    adb_trace_init();
+#if ADB_HOST
+    adb_sysdeps_init();
+    return adb_commandline(argc - 1, argv + 1);
+#else
+    if((argc > 1) && (!strcmp(argv[1],"recovery"))) {
+        adb_device_banner = "recovery";
+        recovery_mode = 1;
+    }
+
+    start_device_log();
+    return adb_main(0, DEFAULT_ADB_PORT);
+#endif
+}
diff --git a/adb/adb.h b/adb/adb.h
new file mode 100644 (file)
index 0000000..3d2a77b
--- /dev/null
+++ b/adb/adb.h
@@ -0,0 +1,414 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __ADB_H
+#define __ADB_H
+
+#include <limits.h>
+
+#define MAX_PAYLOAD 4096
+
+#define A_SYNC 0x434e5953
+#define A_CNXN 0x4e584e43
+#define A_OPEN 0x4e45504f
+#define A_OKAY 0x59414b4f
+#define A_CLSE 0x45534c43
+#define A_WRTE 0x45545257
+
+#define A_VERSION 0x01000000        // ADB protocol version
+
+#define ADB_VERSION_MAJOR 1         // Used for help/version information
+#define ADB_VERSION_MINOR 0         // Used for help/version information
+
+#define ADB_SERVER_VERSION    26    // 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(int is_daemon, int server_port);
+
+
+/* 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
+int       init_jdwp(void);
+asocket*  create_jdwp_service_socket();
+asocket*  create_jdwp_tracker_service_socket();
+int       create_jdwp_connection_fd(int  jdwp_pid);
+#endif
+
+#if !ADB_HOST
+void framebuffer_service(int fd, void *cookie);
+void log_service(int fd, void *cookie);
+void remount_service(int fd, void *cookie);
+char * get_log_file_path(const char * log_name);
+#endif
+
+/* packet allocator */
+apacket *get_apacket(void);
+void put_apacket(apacket *p);
+
+int check_header(apacket *p);
+int check_data(apacket *p);
+
+/* convenience wrappers around read/write that will retry on
+** EINTR and/or short read/write.  Returns 0 on success, -1
+** on error or EOF.
+*/
+int readx(int fd, void *ptr, size_t len);
+int writex(int fd, const void *ptr, size_t len);
+
+/* define ADB_TRACE to 1 to enable tracing support, or 0 to disable it */
+
+#define  ADB_TRACE    1
+
+/* IMPORTANT: if you change the following list, don't
+ * forget to update the corresponding 'tags' table in
+ * the adb_trace_init() function implemented in adb.c
+ */
+typedef enum {
+    TRACE_ADB = 0,
+    TRACE_SOCKETS,
+    TRACE_PACKETS,
+    TRACE_TRANSPORT,
+    TRACE_RWX,
+    TRACE_USB,
+    TRACE_SYNC,
+    TRACE_SYSDEPS,
+    TRACE_JDWP,
+} AdbTrace;
+
+#if ADB_TRACE
+
+  int     adb_trace_mask;
+
+  void    adb_trace_init(void);
+
+#  define ADB_TRACING  ((adb_trace_mask & (1 << TRACE_TAG)) != 0)
+
+  /* you must define TRACE_TAG before using this macro */
+  #define  D(...)                                      \
+        do {                                           \
+            if (ADB_TRACING)                           \
+                fprintf(stderr, __VA_ARGS__ );         \
+        } while (0)
+#else
+#  define  D(...)          ((void)0)
+#  define  ADB_TRACING     0
+#endif
+
+
+#if !TRACE_PACKETS
+#define print_packet(tag,p) do {} while (0)
+#endif
+
+#define DEFAULT_ADB_PORT 5037
+#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 */
+
+extern int HOST;
+
+#define CHUNK_SIZE (64*1024)
+
+int sendfailmsg(int fd, const char *reason);
+int handle_host_request(char *service, transport_type ttype, char* serial, int reply_fd, asocket *s);
+
+#endif
diff --git a/adb/adb_client.c b/adb/adb_client.c
new file mode 100644 (file)
index 0000000..882810a
--- /dev/null
@@ -0,0 +1,324 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <zipfile/zipfile.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "sysdeps.h"
+
+#define  TRACE_TAG  TRACE_ADB
+#include "adb_client.h"
+
+static transport_type __adb_transport = kTransportAny;
+static const char* __adb_serial = NULL;
+
+static int __adb_server_port = DEFAULT_ADB_PORT;
+
+void adb_set_transport(transport_type type, const char* serial)
+{
+    __adb_transport = type;
+    __adb_serial = serial;
+}
+
+void adb_set_tcp_specifics(int server_port)
+{
+    __adb_server_port = server_port;
+}
+
+int  adb_get_emulator_console_port(void)
+{
+    const char*   serial = __adb_serial;
+    int           port;
+
+    if (serial == NULL) {
+        /* if no specific device was specified, we need to look at */
+        /* the list of connected devices, and extract an emulator  */
+        /* name from it. two emulators is an error                 */
+        char*  tmp = adb_query("host:devices");
+        char*  p   = tmp;
+        if(!tmp) {
+            printf("no emulator connected\n");
+            return -1;
+        }
+        while (*p) {
+            char*  q = strchr(p, '\n');
+            if (q != NULL)
+                *q++ = 0;
+            else
+                q = p + strlen(p);
+
+            if (!memcmp(p, LOCAL_CLIENT_PREFIX, sizeof(LOCAL_CLIENT_PREFIX)-1)) {
+                if (serial != NULL) {  /* more than one emulator listed */
+                    free(tmp);
+                    return -2;
+                }
+                serial = p;
+            }
+
+            p = q;
+        }
+        free(tmp);
+
+        if (serial == NULL)
+            return -1;  /* no emulator found */
+    }
+    else {
+        if (memcmp(serial, LOCAL_CLIENT_PREFIX, sizeof(LOCAL_CLIENT_PREFIX)-1) != 0)
+            return -1;  /* not an emulator */
+    }
+
+    serial += sizeof(LOCAL_CLIENT_PREFIX)-1;
+    port    = strtol(serial, NULL, 10);
+    return port;
+}
+
+static char __adb_error[256] = { 0 };
+
+const char *adb_error(void)
+{
+    return __adb_error;
+}
+
+static int switch_socket_transport(int fd)
+{
+    char service[64];
+    char tmp[5];
+    int len;
+
+    if (__adb_serial)
+        snprintf(service, sizeof service, "host:transport:%s", __adb_serial);
+    else {
+        char* transport_type = "???";
+
+         switch (__adb_transport) {
+            case kTransportUsb:
+                transport_type = "transport-usb";
+                break;
+            case kTransportLocal:
+                transport_type = "transport-local";
+                break;
+            case kTransportAny:
+                transport_type = "transport-any";
+                break;
+            case kTransportHost:
+                // no switch necessary
+                return 0;
+                break;
+        }
+
+        snprintf(service, sizeof service, "host:%s", transport_type);
+    }
+    len = strlen(service);
+    snprintf(tmp, sizeof tmp, "%04x", len);
+
+    if(writex(fd, tmp, 4) || writex(fd, service, len)) {
+        strcpy(__adb_error, "write failure during connection");
+        adb_close(fd);
+        return -1;
+    }
+    D("Switch transport in progress\n");
+
+    if(adb_status(fd)) {
+        adb_close(fd);
+        D("Switch transport failed\n");
+        return -1;
+    }
+    D("Switch transport success\n");
+    return 0;
+}
+
+int adb_status(int fd)
+{
+    unsigned char buf[5];
+    unsigned len;
+
+    if(readx(fd, buf, 4)) {
+        strcpy(__adb_error, "protocol fault (no status)");
+        return -1;
+    }
+
+    if(!memcmp(buf, "OKAY", 4)) {
+        return 0;
+    }
+
+    if(memcmp(buf, "FAIL", 4)) {
+        sprintf(__adb_error,
+                "protocol fault (status %02x %02x %02x %02x?!)",
+                buf[0], buf[1], buf[2], buf[3]);
+        return -1;
+    }
+
+    if(readx(fd, buf, 4)) {
+        strcpy(__adb_error, "protocol fault (status len)");
+        return -1;
+    }
+    buf[4] = 0;
+    len = strtoul((char*)buf, 0, 16);
+    if(len > 255) len = 255;
+    if(readx(fd, __adb_error, len)) {
+        strcpy(__adb_error, "protocol fault (status read)");
+        return -1;
+    }
+    __adb_error[len] = 0;
+    return -1;
+}
+
+int _adb_connect(const char *service)
+{
+    char tmp[5];
+    int len;
+    int fd;
+
+    D("_adb_connect: %s\n", service);
+    len = strlen(service);
+    if((len < 1) || (len > 1024)) {
+        strcpy(__adb_error, "service name too long");
+        return -1;
+    }
+    snprintf(tmp, sizeof tmp, "%04x", len);
+
+    fd = socket_loopback_client(__adb_server_port, SOCK_STREAM);
+    if(fd < 0) {
+        strcpy(__adb_error, "cannot connect to daemon");
+        return -2;
+    }
+
+    if (memcmp(service,"host",4) != 0 && switch_socket_transport(fd)) {
+        return -1;
+    }
+
+    if(writex(fd, tmp, 4) || writex(fd, service, len)) {
+        strcpy(__adb_error, "write failure during connection");
+        adb_close(fd);
+        return -1;
+    }
+
+    if(adb_status(fd)) {
+        adb_close(fd);
+        return -1;
+    }
+
+    return fd;
+}
+
+int adb_connect(const char *service)
+{
+    // first query the adb server's version
+    int fd = _adb_connect("host:version");
+
+    if(fd == -2) {
+        fprintf(stdout,"* daemon not running. starting it now on port %d *\n",
+                __adb_server_port);
+    start_server:
+        if(launch_server(__adb_server_port)) {
+            fprintf(stderr,"* failed to start daemon *\n");
+            return -1;
+        } else {
+            fprintf(stdout,"* daemon started successfully *\n");
+        }
+        /* give the server some time to start properly and detect devices */
+        adb_sleep_ms(3000);
+        // fall through to _adb_connect
+    } else {
+        // if server was running, check its version to make sure it is not out of date
+        char buf[100];
+        int n;
+        int version = ADB_SERVER_VERSION - 1;
+
+        // if we have a file descriptor, then parse version result
+        if(fd >= 0) {
+            if(readx(fd, buf, 4)) goto error;
+
+            buf[4] = 0;
+            n = strtoul(buf, 0, 16);
+            if(n > (int)sizeof(buf)) goto error;
+            if(readx(fd, buf, n)) goto error;
+            adb_close(fd);
+
+            if (sscanf(buf, "%04x", &version) != 1) goto error;
+        } else {
+            // if fd is -1, then check for "unknown host service",
+            // which would indicate a version of adb that does not support the version command
+            if (strcmp(__adb_error, "unknown host service") != 0)
+                return fd;
+        }
+
+        if(version != ADB_SERVER_VERSION) {
+            printf("adb server is out of date.  killing...\n");
+            fd = _adb_connect("host:kill");
+            adb_close(fd);
+
+            /* XXX can we better detect its death? */
+            adb_sleep_ms(2000);
+            goto start_server;
+        }
+    }
+
+    // if the command is start-server, we are done.
+    if (!strcmp(service, "host:start-server"))
+        return 0;
+
+    fd = _adb_connect(service);
+    if(fd == -2) {
+        fprintf(stderr,"** daemon still not running");
+    }
+
+    return fd;
+error:
+    adb_close(fd);
+    return -1;
+}
+
+
+int adb_command(const char *service)
+{
+    int fd = adb_connect(service);
+    if(fd < 0) {
+        return -1;
+    }
+
+    if(adb_status(fd)) {
+        adb_close(fd);
+        return -1;
+    }
+
+    return 0;
+}
+
+char *adb_query(const char *service)
+{
+    char buf[5];
+    unsigned n;
+    char *tmp;
+
+    D("adb_query: %s\n", service);
+    int fd = adb_connect(service);
+    if(fd < 0) {
+        fprintf(stderr,"error: %s\n", __adb_error);
+        return 0;
+    }
+
+    if(readx(fd, buf, 4)) goto oops;
+
+    buf[4] = 0;
+    n = strtoul(buf, 0, 16);
+    if(n > 1024) goto oops;
+
+    tmp = malloc(n + 1);
+    if(tmp == 0) goto oops;
+
+    if(readx(fd, tmp, n) == 0) {
+        tmp[n] = 0;
+        adb_close(fd);
+        return tmp;
+    }
+    free(tmp);
+
+oops:
+    adb_close(fd);
+    return 0;
+}
diff --git a/adb/adb_client.h b/adb/adb_client.h
new file mode 100644 (file)
index 0000000..40ab189
--- /dev/null
@@ -0,0 +1,53 @@
+#ifndef _ADB_CLIENT_H_
+#define _ADB_CLIENT_H_
+
+#include "adb.h"
+
+/* connect to adb, connect to the named service, and return
+** a valid fd for interacting with that service upon success
+** or a negative number on failure
+*/
+int adb_connect(const char *service);
+int _adb_connect(const char *service);
+
+/* connect to adb, connect to the named service, return 0 if
+** the connection succeeded AND the service returned OKAY
+*/
+int adb_command(const char *service);
+
+/* connect to adb, connect to the named service, return
+** a malloc'd string of its response upon success or NULL
+** on failure.
+*/
+char *adb_query(const char *service);
+
+/* Set the preferred transport to connect to.
+*/
+void adb_set_transport(transport_type type, const char* serial);
+
+/* Set TCP specifics of the transport to use
+*/
+void adb_set_tcp_specifics(int server_port);
+
+/* Return the console port of the currently connected emulator (if any)
+ * of -1 if there is no emulator, and -2 if there is more than one.
+ * assumes adb_set_transport() was alled previously...
+ */
+int  adb_get_emulator_console_port(void);
+
+/* send commands to the current emulator instance. will fail if there
+ * is zero, or more than one emulator connected (or if you use -s <serial>
+ * with a <serial> that does not designate an emulator)
+ */
+int  adb_send_emulator_command(int  argc, char**  argv);
+
+/* return verbose error string from last operation */
+const char *adb_error(void);
+
+/* read a standard adb status response (OKAY|FAIL) and
+** return 0 in the event of OKAY, -1 in the event of FAIL
+** or protocol error
+*/
+int adb_status(int fd);
+
+#endif
diff --git a/adb/commandline.c b/adb/commandline.c
new file mode 100644 (file)
index 0000000..dcba83b
--- /dev/null
@@ -0,0 +1,1272 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <ctype.h>
+#include <assert.h>
+
+#include "sysdeps.h"
+
+#ifdef HAVE_TERMIO_H
+#include <termios.h>
+#endif
+
+#define  TRACE_TAG  TRACE_ADB
+#include "adb.h"
+#include "adb_client.h"
+#include "file_sync_service.h"
+
+enum {
+    IGNORE_DATA,
+    WIPE_DATA,
+    FLASH_DATA
+};
+
+static int do_cmd(transport_type ttype, char* serial, char *cmd, ...);
+
+void get_my_path(char *s, size_t maxLen);
+int find_sync_dirs(const char *srcarg,
+        char **android_srcdir_out, char **data_srcdir_out);
+int install_app(transport_type transport, char* serial, int argc, char** argv);
+int uninstall_app(transport_type transport, char* serial, int argc, char** argv);
+
+static const char *gProductOutPath = NULL;
+
+static char *product_file(const char *extra)
+{
+    int n;
+    char *x;
+
+    if (gProductOutPath == NULL) {
+        fprintf(stderr, "adb: Product directory not specified; "
+                "use -p or define ANDROID_PRODUCT_OUT\n");
+        exit(1);
+    }
+
+    n = strlen(gProductOutPath) + strlen(extra) + 2;
+    x = malloc(n);
+    if (x == 0) {
+        fprintf(stderr, "adb: Out of memory (product_file())\n");
+        exit(1);
+    }
+
+    snprintf(x, (size_t)n, "%s" OS_PATH_SEPARATOR_STR "%s", gProductOutPath, extra);
+    return x;
+}
+
+void version(FILE * out) {
+    fprintf(out, "Android Debug Bridge version %d.%d.%d\n",
+         ADB_VERSION_MAJOR, ADB_VERSION_MINOR, ADB_SERVER_VERSION);
+}
+
+void help()
+{
+    version(stderr);
+
+    fprintf(stderr,
+        "\n"
+        " -d                            - directs command to the only connected USB device\n"
+        "                                 returns an error if more than one USB device is present.\n"
+        " -e                            - directs command to the only running emulator.\n"
+        "                                 returns an error if more than one emulator is running.\n"
+        " -s <serial number>            - directs command to the USB device or emulator with\n"
+        "                                 the given serial number. Overrides ANDROID_SERIAL\n"
+        "                                 environment variable.\n"
+        " -p <product name or path>     - simple product name like 'sooner', or\n"
+        "                                 a relative/absolute path to a product\n"
+        "                                 out directory like 'out/target/product/sooner'.\n"
+        "                                 If -p is not specified, the ANDROID_PRODUCT_OUT\n"
+        "                                 environment variable is used, which must\n"
+        "                                 be an absolute path.\n"
+        " devices                       - list all connected devices\n"
+        " connect <host>[:<port>]       - connect to a device via TCP/IP\n"
+        "                                 Port 5555 is used by default if no port number is specified.\n"
+        " disconnect [<host>[:<port>]]  - disconnect from a TCP/IP device.\n"
+        "                                 Port 5555 is used by default if no port number is specified.\n"
+        "                                 Using this ocmmand with no additional arguments\n"
+        "                                 will disconnect from all connected TCP/IP devices.\n"
+        "\n"
+        "device commands:\n"
+        "  adb push <local> <remote>    - copy file/dir to device\n"
+        "  adb pull <remote> [<local>]  - copy file/dir from device\n"
+        "  adb sync [ <directory> ]     - copy host->device only if changed\n"
+        "                                 (-l means list but don't copy)\n"
+        "                                 (see 'adb help all')\n"
+        "  adb shell                    - run remote shell interactively\n"
+        "  adb shell <command>          - run remote shell command\n"
+        "  adb emu <command>            - run emulator console command\n"
+        "  adb logcat [ <filter-spec> ] - View device log\n"
+        "  adb forward <local> <remote> - forward socket connections\n"
+        "                                 forward specs are one of: \n"
+        "                                   tcp:<port>\n"
+        "                                   localabstract:<unix domain socket name>\n"
+        "                                   localreserved:<unix domain socket name>\n"
+        "                                   localfilesystem:<unix domain socket name>\n"
+        "                                   dev:<character device name>\n"
+        "                                   jdwp:<process pid> (remote only)\n"
+        "  adb jdwp                     - list PIDs of processes hosting a JDWP transport\n"
+        "  adb install [-l] [-r] [-s] <file> - push this package file to the device and install it\n"
+        "                                 ('-l' means forward-lock the app)\n"
+        "                                 ('-r' means reinstall the app, keeping its data)\n"
+        "                                 ('-s' means install on SD card instead of internal storage)\n"
+        "  adb uninstall [-k] <package> - remove this app package from the device\n"
+        "                                 ('-k' means keep the data and cache directories)\n"
+        "  adb bugreport                - return all information from the device\n"
+        "                                 that should be included in a bug report.\n"
+        "\n"
+        "  adb help                     - show this help message\n"
+        "  adb version                  - show version num\n"
+        "\n"
+        "DATAOPTS:\n"
+        " (no option)                   - don't touch the data partition\n"
+        "  -w                           - wipe the data partition\n"
+        "  -d                           - flash the data partition\n"
+        "\n"
+        "scripting:\n"
+        "  adb wait-for-device          - block until device is online\n"
+        "  adb start-server             - ensure that there is a server running\n"
+        "  adb kill-server              - kill the server if it is running\n"
+        "  adb get-state                - prints: offline | bootloader | device\n"
+        "  adb get-serialno             - prints: <serial-number>\n"
+        "  adb status-window            - continuously print device status for a specified device\n"
+        "  adb remount                  - remounts the /system partition on the device read-write\n"
+        "  adb reboot [bootloader|recovery] - reboots the device, optionally into the bootloader or recovery program\n"
+        "  adb reboot-bootloader        - reboots the device into the bootloader\n"
+        "  adb root                     - restarts the adbd daemon with root permissions\n"
+        "  adb usb                      - restarts the adbd daemon listening on USB\n"
+        "  adb tcpip <port>             - restarts the adbd daemon listening on TCP on the specified port"
+        "\n"
+        "networking:\n"
+        "  adb ppp <tty> [parameters]   - Run PPP over USB.\n"
+        " Note: you should not automatically start a PPP connection.\n"
+        " <tty> refers to the tty for PPP stream. Eg. dev:/dev/omap_csmi_tty1\n"
+        " [parameters] - Eg. defaultroute debug dump local notty usepeerdns\n"
+        "\n"
+        "adb sync notes: adb sync [ <directory> ]\n"
+        "  <localdir> can be interpreted in several ways:\n"
+        "\n"
+        "  - If <directory> is not specified, both /system and /data partitions will be updated.\n"
+        "\n"
+        "  - If it is \"system\" or \"data\", only the corresponding partition\n"
+        "    is updated.\n"
+        "\n"
+        "environmental variables:\n"
+        "  ADB_TRACE                    - Print debug information. A comma separated list of the following values\n"
+        "                                 1 or all, adb, sockets, packets, rwx, usb, sync, sysdeps, transport, jdwp\n"
+        "  ANDROID_SERIAL               - The serial number to connect to. -s takes priority over this if given.\n"
+        "  ANDROID_LOG_TAGS             - When used with the logcat option, only these debug tags are printed.\n"
+        );
+}
+
+int usage()
+{
+    help();
+    return 1;
+}
+
+#ifdef HAVE_TERMIO_H
+static struct termios tio_save;
+
+static void stdin_raw_init(int fd)
+{
+    struct termios tio;
+
+    if(tcgetattr(fd, &tio)) return;
+    if(tcgetattr(fd, &tio_save)) return;
+
+    tio.c_lflag = 0; /* disable CANON, ECHO*, etc */
+
+        /* no timeout but request at least one character per read */
+    tio.c_cc[VTIME] = 0;
+    tio.c_cc[VMIN] = 1;
+
+    tcsetattr(fd, TCSANOW, &tio);
+    tcflush(fd, TCIFLUSH);
+}
+
+static void stdin_raw_restore(int fd)
+{
+    tcsetattr(fd, TCSANOW, &tio_save);
+    tcflush(fd, TCIFLUSH);
+}
+#endif
+
+static void read_and_dump(int fd)
+{
+    char buf[4096];
+    int len;
+
+    while(fd >= 0) {
+        len = adb_read(fd, buf, 4096);
+        if(len == 0) {
+            break;
+        }
+
+        if(len < 0) {
+            if(errno == EINTR) continue;
+            break;
+        }
+        fwrite(buf, 1, len, stdout);
+        fflush(stdout);
+    }
+}
+
+static void *stdin_read_thread(void *x)
+{
+    int fd, fdi;
+    unsigned char buf[1024];
+    int r, n;
+    int state = 0;
+
+    int *fds = (int*) x;
+    fd = fds[0];
+    fdi = fds[1];
+    free(fds);
+
+    for(;;) {
+        /* fdi is really the client's stdin, so use read, not adb_read here */
+        r = unix_read(fdi, buf, 1024);
+        if(r == 0) break;
+        if(r < 0) {
+            if(errno == EINTR) continue;
+            break;
+        }
+        for(n = 0; n < r; n++){
+            switch(buf[n]) {
+            case '\n':
+                state = 1;
+                break;
+            case '\r':
+                state = 1;
+                break;
+            case '~':
+                if(state == 1) state++;
+                break;
+            case '.':
+                if(state == 2) {
+                    fprintf(stderr,"\n* disconnect *\n");
+#ifdef HAVE_TERMIO_H
+                    stdin_raw_restore(fdi);
+#endif
+                    exit(0);
+                }
+            default:
+                state = 0;
+            }
+        }
+        r = adb_write(fd, buf, r);
+        if(r <= 0) {
+            break;
+        }
+    }
+    return 0;
+}
+
+int interactive_shell(void)
+{
+    adb_thread_t thr;
+    int fdi, fd;
+    int *fds;
+
+    fd = adb_connect("shell:");
+    if(fd < 0) {
+        fprintf(stderr,"error: %s\n", adb_error());
+        return 1;
+    }
+    fdi = 0; //dup(0);
+
+    fds = malloc(sizeof(int) * 2);
+    fds[0] = fd;
+    fds[1] = fdi;
+
+#ifdef HAVE_TERMIO_H
+    stdin_raw_init(fdi);
+#endif
+    adb_thread_create(&thr, stdin_read_thread, fds);
+    read_and_dump(fd);
+#ifdef HAVE_TERMIO_H
+    stdin_raw_restore(fdi);
+#endif
+    return 0;
+}
+
+
+static void format_host_command(char* buffer, size_t  buflen, const char* command, transport_type ttype, const char* serial)
+{
+    if (serial) {
+        snprintf(buffer, buflen, "host-serial:%s:%s", serial, command);
+    } else {
+        const char* prefix = "host";
+        if (ttype == kTransportUsb)
+            prefix = "host-usb";
+        else if (ttype == kTransportLocal)
+            prefix = "host-local";
+
+        snprintf(buffer, buflen, "%s:%s", prefix, command);
+    }
+}
+
+static void status_window(transport_type ttype, const char* serial)
+{
+    char command[4096];
+    char *state = 0;
+    char *laststate = 0;
+
+        /* silence stderr */
+#ifdef _WIN32
+    /* XXX: TODO */
+#else
+    int  fd;
+    fd = unix_open("/dev/null", O_WRONLY);
+    dup2(fd, 2);
+    adb_close(fd);
+#endif
+
+    format_host_command(command, sizeof command, "get-state", ttype, serial);
+
+    for(;;) {
+        adb_sleep_ms(250);
+
+        if(state) {
+            free(state);
+            state = 0;
+        }
+
+        state = adb_query(command);
+
+        if(state) {
+            if(laststate && !strcmp(state,laststate)){
+                continue;
+            } else {
+                if(laststate) free(laststate);
+                laststate = strdup(state);
+            }
+        }
+
+        printf("%c[2J%c[2H", 27, 27);
+        printf("Android Debug Bridge\n");
+        printf("State: %s\n", state ? state : "offline");
+        fflush(stdout);
+    }
+}
+
+/** duplicate string and quote all \ " ( ) chars + space character. */
+static char *
+dupAndQuote(const char *s)
+{
+    const char *ts;
+    size_t alloc_len;
+    char *ret;
+    char *dest;
+
+    ts = s;
+
+    alloc_len = 0;
+
+    for( ;*ts != '\0'; ts++) {
+        alloc_len++;
+        if (*ts == ' ' || *ts == '"' || *ts == '\\' || *ts == '(' || *ts == ')') {
+            alloc_len++;
+        }
+    }
+
+    ret = (char *)malloc(alloc_len + 1);
+
+    ts = s;
+    dest = ret;
+
+    for ( ;*ts != '\0'; ts++) {
+        if (*ts == ' ' || *ts == '"' || *ts == '\\' || *ts == '(' || *ts == ')') {
+            *dest++ = '\\';
+        }
+
+        *dest++ = *ts;
+    }
+
+    *dest++ = '\0';
+
+    return ret;
+}
+
+/**
+ * Run ppp in "notty" mode against a resource listed as the first parameter
+ * eg:
+ *
+ * ppp dev:/dev/omap_csmi_tty0 <ppp options>
+ *
+ */
+int ppp(int argc, char **argv)
+{
+#ifdef HAVE_WIN32_PROC
+    fprintf(stderr, "error: adb %s not implemented on Win32\n", argv[0]);
+    return -1;
+#else
+    char *adb_service_name;
+    pid_t pid;
+    int fd;
+
+    if (argc < 2) {
+        fprintf(stderr, "usage: adb %s <adb service name> [ppp opts]\n",
+                argv[0]);
+
+        return 1;
+    }
+
+    adb_service_name = argv[1];
+
+    fd = adb_connect(adb_service_name);
+
+    if(fd < 0) {
+        fprintf(stderr,"Error: Could not open adb service: %s. Error: %s\n",
+                adb_service_name, adb_error());
+        return 1;
+    }
+
+    pid = fork();
+
+    if (pid < 0) {
+        perror("from fork()");
+        return 1;
+    } else if (pid == 0) {
+        int err;
+        int i;
+        const char **ppp_args;
+
+        // copy args
+        ppp_args = (const char **) alloca(sizeof(char *) * argc + 1);
+        ppp_args[0] = "pppd";
+        for (i = 2 ; i < argc ; i++) {
+            //argv[2] and beyond become ppp_args[1] and beyond
+            ppp_args[i - 1] = argv[i];
+        }
+        ppp_args[i-1] = NULL;
+
+        // child side
+
+        dup2(fd, STDIN_FILENO);
+        dup2(fd, STDOUT_FILENO);
+        adb_close(STDERR_FILENO);
+        adb_close(fd);
+
+        err = execvp("pppd", (char * const *)ppp_args);
+
+        if (err < 0) {
+            perror("execing pppd");
+        }
+        exit(-1);
+    } else {
+        // parent side
+
+        adb_close(fd);
+        return 0;
+    }
+#endif /* !HAVE_WIN32_PROC */
+}
+
+static int send_shellcommand(transport_type transport, char* serial, char* buf)
+{
+    int fd, ret;
+
+    for(;;) {
+        fd = adb_connect(buf);
+        if(fd >= 0)
+            break;
+        fprintf(stderr,"- waiting for device -\n");
+        adb_sleep_ms(1000);
+        do_cmd(transport, serial, "wait-for-device", 0);
+    }
+
+    read_and_dump(fd);
+    ret = adb_close(fd);
+    if (ret)
+        perror("close");
+
+    return ret;
+}
+
+static int logcat(transport_type transport, char* serial, int argc, char **argv)
+{
+    char buf[4096];
+
+    char *log_tags;
+    char *quoted_log_tags;
+
+    log_tags = getenv("ANDROID_LOG_TAGS");
+    quoted_log_tags = dupAndQuote(log_tags == NULL ? "" : log_tags);
+
+    snprintf(buf, sizeof(buf),
+        "shell:export ANDROID_LOG_TAGS=\"\%s\" ; exec logcat",
+        quoted_log_tags);
+
+    free(quoted_log_tags);
+
+    argc -= 1;
+    argv += 1;
+    while(argc-- > 0) {
+        char *quoted;
+
+        quoted = dupAndQuote (*argv++);
+
+        strncat(buf, " ", sizeof(buf)-1);
+        strncat(buf, quoted, sizeof(buf)-1);
+        free(quoted);
+    }
+
+    send_shellcommand(transport, serial, buf);
+    return 0;
+}
+
+#define SENTINEL_FILE "config" OS_PATH_SEPARATOR_STR "envsetup.make"
+static int top_works(const char *top)
+{
+    if (top != NULL && adb_is_absolute_host_path(top)) {
+        char path_buf[PATH_MAX];
+        snprintf(path_buf, sizeof(path_buf),
+                "%s" OS_PATH_SEPARATOR_STR SENTINEL_FILE, top);
+        return access(path_buf, F_OK) == 0;
+    }
+    return 0;
+}
+
+static char *find_top_from(const char *indir, char path_buf[PATH_MAX])
+{
+    strcpy(path_buf, indir);
+    while (1) {
+        if (top_works(path_buf)) {
+            return path_buf;
+        }
+        char *s = adb_dirstop(path_buf);
+        if (s != NULL) {
+            *s = '\0';
+        } else {
+            path_buf[0] = '\0';
+            return NULL;
+        }
+    }
+}
+
+static char *find_top(char path_buf[PATH_MAX])
+{
+    char *top = getenv("ANDROID_BUILD_TOP");
+    if (top != NULL && top[0] != '\0') {
+        if (!top_works(top)) {
+            fprintf(stderr, "adb: bad ANDROID_BUILD_TOP value \"%s\"\n", top);
+            return NULL;
+        }
+    } else {
+        top = getenv("TOP");
+        if (top != NULL && top[0] != '\0') {
+            if (!top_works(top)) {
+                fprintf(stderr, "adb: bad TOP value \"%s\"\n", top);
+                return NULL;
+            }
+        } else {
+            top = NULL;
+        }
+    }
+
+    if (top != NULL) {
+        /* The environment pointed to a top directory that works.
+         */
+        strcpy(path_buf, top);
+        return path_buf;
+    }
+
+    /* The environment didn't help.  Walk up the tree from the CWD
+     * to see if we can find the top.
+     */
+    char dir[PATH_MAX];
+    top = find_top_from(getcwd(dir, sizeof(dir)), path_buf);
+    if (top == NULL) {
+        /* If the CWD isn't under a good-looking top, see if the
+         * executable is.
+         */
+        get_my_path(dir, PATH_MAX);
+        top = find_top_from(dir, path_buf);
+    }
+    return top;
+}
+
+/* <hint> may be:
+ * - A simple product name
+ *   e.g., "sooner"
+TODO: debug?  sooner-debug, sooner:debug?
+ * - A relative path from the CWD to the ANDROID_PRODUCT_OUT dir
+ *   e.g., "out/target/product/sooner"
+ * - An absolute path to the PRODUCT_OUT dir
+ *   e.g., "/src/device/out/target/product/sooner"
+ *
+ * Given <hint>, try to construct an absolute path to the
+ * ANDROID_PRODUCT_OUT dir.
+ */
+static const char *find_product_out_path(const char *hint)
+{
+    static char path_buf[PATH_MAX];
+
+    if (hint == NULL || hint[0] == '\0') {
+        return NULL;
+    }
+
+    /* If it's already absolute, don't bother doing any work.
+     */
+    if (adb_is_absolute_host_path(hint)) {
+        strcpy(path_buf, hint);
+        return path_buf;
+    }
+
+    /* If there are any slashes in it, assume it's a relative path;
+     * make it absolute.
+     */
+    if (adb_dirstart(hint) != NULL) {
+        if (getcwd(path_buf, sizeof(path_buf)) == NULL) {
+            fprintf(stderr, "adb: Couldn't get CWD: %s\n", strerror(errno));
+            return NULL;
+        }
+        if (strlen(path_buf) + 1 + strlen(hint) >= sizeof(path_buf)) {
+            fprintf(stderr, "adb: Couldn't assemble path\n");
+            return NULL;
+        }
+        strcat(path_buf, OS_PATH_SEPARATOR_STR);
+        strcat(path_buf, hint);
+        return path_buf;
+    }
+
+    /* It's a string without any slashes.  Try to do something with it.
+     *
+     * Try to find the root of the build tree, and build a PRODUCT_OUT
+     * path from there.
+     */
+    char top_buf[PATH_MAX];
+    const char *top = find_top(top_buf);
+    if (top == NULL) {
+        fprintf(stderr, "adb: Couldn't find top of build tree\n");
+        return NULL;
+    }
+//TODO: if we have a way to indicate debug, look in out/debug/target/...
+    snprintf(path_buf, sizeof(path_buf),
+            "%s" OS_PATH_SEPARATOR_STR
+            "out" OS_PATH_SEPARATOR_STR
+            "target" OS_PATH_SEPARATOR_STR
+            "product" OS_PATH_SEPARATOR_STR
+            "%s", top_buf, hint);
+    if (access(path_buf, F_OK) < 0) {
+        fprintf(stderr, "adb: Couldn't find a product dir "
+                "based on \"-p %s\"; \"%s\" doesn't exist\n", hint, path_buf);
+        return NULL;
+    }
+    return path_buf;
+}
+
+int adb_commandline(int argc, char **argv)
+{
+    char buf[4096];
+    int no_daemon = 0;
+    int is_daemon = 0;
+    int persist = 0;
+    int r;
+    int quote;
+    transport_type ttype = kTransportAny;
+    char* serial = NULL;
+    char* server_port_str = NULL;
+
+        /* If defined, this should be an absolute path to
+         * the directory containing all of the various system images
+         * for a particular product.  If not defined, and the adb
+         * command requires this information, then the user must
+         * specify the path using "-p".
+         */
+    gProductOutPath = getenv("ANDROID_PRODUCT_OUT");
+    if (gProductOutPath == NULL || gProductOutPath[0] == '\0') {
+        gProductOutPath = NULL;
+    }
+    // TODO: also try TARGET_PRODUCT/TARGET_DEVICE as a hint
+
+    serial = getenv("ANDROID_SERIAL");
+
+    /* Validate and assign the server port */
+    server_port_str = getenv("ANDROID_ADB_SERVER_PORT");
+    int server_port = DEFAULT_ADB_PORT;
+    if (server_port_str && strlen(server_port_str) > 0) {
+        server_port = (int) strtol(server_port_str, NULL, 0);
+        if (server_port <= 0) {
+            fprintf(stderr,
+                    "adb: Env var ANDROID_ADB_SERVER_PORT must be a positive number. Got \"%s\"\n",
+                    server_port_str);
+            return usage();
+        }
+    }
+
+    /* modifiers and flags */
+    while(argc > 0) {
+        if(!strcmp(argv[0],"nodaemon")) {
+            no_daemon = 1;
+        } else if (!strcmp(argv[0], "fork-server")) {
+            /* this is a special flag used only when the ADB client launches the ADB Server */
+            is_daemon = 1;
+        } else if(!strcmp(argv[0],"persist")) {
+            persist = 1;
+        } else if(!strncmp(argv[0], "-p", 2)) {
+            const char *product = NULL;
+            if (argv[0][2] == '\0') {
+                if (argc < 2) return usage();
+                product = argv[1];
+                argc--;
+                argv++;
+            } else {
+                product = argv[1] + 2;
+            }
+            gProductOutPath = find_product_out_path(product);
+            if (gProductOutPath == NULL) {
+                fprintf(stderr, "adb: could not resolve \"-p %s\"\n",
+                        product);
+                return usage();
+            }
+        } else if (argv[0][0]=='-' && argv[0][1]=='s') {
+            if (isdigit(argv[0][2])) {
+                serial = argv[0] + 2;
+            } else {
+                if(argc < 2 || argv[0][2] != '\0') return usage();
+                serial = argv[1];
+                argc--;
+                argv++;
+            }
+        } else if (!strcmp(argv[0],"-d")) {
+            ttype = kTransportUsb;
+        } else if (!strcmp(argv[0],"-e")) {
+            ttype = kTransportLocal;
+        } else {
+                /* out of recognized modifiers and flags */
+            break;
+        }
+        argc--;
+        argv++;
+    }
+
+    adb_set_transport(ttype, serial);
+    adb_set_tcp_specifics(server_port);
+
+    if ((argc > 0) && (!strcmp(argv[0],"server"))) {
+        if (no_daemon || is_daemon) {
+            r = adb_main(is_daemon, server_port);
+        } else {
+            r = launch_server(server_port);
+        }
+        if(r) {
+            fprintf(stderr,"* could not start server *\n");
+        }
+        return r;
+    }
+
+top:
+    if(argc == 0) {
+        return usage();
+    }
+
+    /* adb_connect() commands */
+
+    if(!strcmp(argv[0], "devices")) {
+        char *tmp;
+        snprintf(buf, sizeof buf, "host:%s", argv[0]);
+        tmp = adb_query(buf);
+        if(tmp) {
+            printf("List of devices attached \n");
+            printf("%s\n", tmp);
+            return 0;
+        } else {
+            return 1;
+        }
+    }
+
+    if(!strcmp(argv[0], "connect")) {
+        char *tmp;
+        if (argc != 2) {
+            fprintf(stderr, "Usage: adb connect <host>[:<port>]\n");
+            return 1;
+        }
+        snprintf(buf, sizeof buf, "host:connect:%s", argv[1]);
+        tmp = adb_query(buf);
+        if(tmp) {
+            printf("%s\n", tmp);
+            return 0;
+        } else {
+            return 1;
+        }
+    }
+
+    if(!strcmp(argv[0], "disconnect")) {
+        char *tmp;
+        if (argc > 2) {
+            fprintf(stderr, "Usage: adb disconnect [<host>[:<port>]]\n");
+            return 1;
+        }
+        if (argc == 2) {
+            snprintf(buf, sizeof buf, "host:disconnect:%s", argv[1]);
+        } else {
+            snprintf(buf, sizeof buf, "host:disconnect:");
+        }
+        tmp = adb_query(buf);
+        if(tmp) {
+            printf("%s\n", tmp);
+            return 0;
+        } else {
+            return 1;
+        }
+    }
+
+    if (!strcmp(argv[0], "emu")) {
+        return adb_send_emulator_command(argc, argv);
+    }
+
+    if(!strcmp(argv[0], "shell")) {
+        int r;
+        int fd;
+
+        if(argc < 2) {
+            return interactive_shell();
+        }
+
+        snprintf(buf, sizeof buf, "shell:%s", argv[1]);
+        argc -= 2;
+        argv += 2;
+        while(argc-- > 0) {
+            strcat(buf, " ");
+
+            /* quote empty strings and strings with spaces */
+            quote = (**argv == 0 || strchr(*argv, ' '));
+            if (quote)
+                strcat(buf, "\"");
+            strcat(buf, *argv++);
+            if (quote)
+                strcat(buf, "\"");
+        }
+
+        for(;;) {
+            fd = adb_connect(buf);
+            if(fd >= 0) {
+                read_and_dump(fd);
+                adb_close(fd);
+                r = 0;
+            } else {
+                fprintf(stderr,"error: %s\n", adb_error());
+                r = -1;
+            }
+
+            if(persist) {
+                fprintf(stderr,"\n- waiting for device -\n");
+                adb_sleep_ms(1000);
+                do_cmd(ttype, serial, "wait-for-device", 0);
+            } else {
+                return r;
+            }
+        }
+    }
+
+    if(!strcmp(argv[0], "kill-server")) {
+        int fd;
+        fd = _adb_connect("host:kill");
+        if(fd == -1) {
+            fprintf(stderr,"* server not running *\n");
+            return 1;
+        }
+        return 0;
+    }
+
+    if(!strcmp(argv[0], "remount") || !strcmp(argv[0], "reboot")
+            || !strcmp(argv[0], "reboot-bootloader")
+            || !strcmp(argv[0], "tcpip") || !strcmp(argv[0], "usb")
+            || !strcmp(argv[0], "root")) {
+        char command[100];
+        if (!strcmp(argv[0], "reboot-bootloader"))
+            snprintf(command, sizeof(command), "reboot:bootloader");
+        else if (argc > 1)
+            snprintf(command, sizeof(command), "%s:%s", argv[0], argv[1]);
+        else
+            snprintf(command, sizeof(command), "%s:", argv[0]);
+        int fd = adb_connect(command);
+        if(fd >= 0) {
+            read_and_dump(fd);
+            adb_close(fd);
+            return 0;
+        }
+        fprintf(stderr,"error: %s\n", adb_error());
+        return 1;
+    }
+
+    if(!strcmp(argv[0], "bugreport")) {
+        if (argc != 1) return usage();
+        do_cmd(ttype, serial, "shell", "bugreport", 0);
+        return 0;
+    }
+
+    /* adb_command() wrapper commands */
+
+    if(!strncmp(argv[0], "wait-for-", strlen("wait-for-"))) {
+        char* service = argv[0];
+        if (!strncmp(service, "wait-for-device", strlen("wait-for-device"))) {
+            if (ttype == kTransportUsb) {
+                service = "wait-for-usb";
+            } else if (ttype == kTransportLocal) {
+                service = "wait-for-local";
+            } else {
+                service = "wait-for-any";
+            }
+        }
+
+        format_host_command(buf, sizeof buf, service, ttype, serial);
+
+        if (adb_command(buf)) {
+            D("failure: %s *\n",adb_error());
+            fprintf(stderr,"error: %s\n", adb_error());
+            return 1;
+        }
+
+        /* Allow a command to be run after wait-for-device,
+            * e.g. 'adb wait-for-device shell'.
+            */
+        if(argc > 1) {
+            argc--;
+            argv++;
+            goto top;
+        }
+        return 0;
+    }
+
+    if(!strcmp(argv[0], "forward")) {
+        if(argc != 3) return usage();
+        if (serial) {
+            snprintf(buf, sizeof buf, "host-serial:%s:forward:%s;%s",serial, argv[1], argv[2]);
+        } else if (ttype == kTransportUsb) {
+            snprintf(buf, sizeof buf, "host-usb:forward:%s;%s", argv[1], argv[2]);
+        } else if (ttype == kTransportLocal) {
+            snprintf(buf, sizeof buf, "host-local:forward:%s;%s", argv[1], argv[2]);
+        } else {
+            snprintf(buf, sizeof buf, "host:forward:%s;%s", argv[1], argv[2]);
+        }
+        if(adb_command(buf)) {
+            fprintf(stderr,"error: %s\n", adb_error());
+            return 1;
+        }
+        return 0;
+    }
+
+    /* do_sync_*() commands */
+
+    if(!strcmp(argv[0], "ls")) {
+        if(argc != 2) return usage();
+        return do_sync_ls(argv[1]);
+    }
+
+    if(!strcmp(argv[0], "push")) {
+        if(argc != 3) return usage();
+        return do_sync_push(argv[1], argv[2], 0 /* no verify APK */);
+    }
+
+    if(!strcmp(argv[0], "pull")) {
+        if (argc == 2) {
+            return do_sync_pull(argv[1], ".");
+        } else if (argc == 3) {
+            return do_sync_pull(argv[1], argv[2]);
+        } else {
+            return usage();
+        }
+    }
+
+    if(!strcmp(argv[0], "install")) {
+        if (argc < 2) return usage();
+        return install_app(ttype, serial, argc, argv);
+    }
+
+    if(!strcmp(argv[0], "uninstall")) {
+        if (argc < 2) return usage();
+        return uninstall_app(ttype, serial, argc, argv);
+    }
+
+    if(!strcmp(argv[0], "sync")) {
+        char *srcarg, *android_srcpath, *data_srcpath;
+        int listonly = 0;
+
+        int ret;
+        if(argc < 2) {
+            /* No local path was specified. */
+            srcarg = NULL;
+        } else if (argc >= 2 && strcmp(argv[1], "-l") == 0) {
+            listonly = 1;
+            if (argc == 3) {
+                srcarg = argv[2];
+            } else {
+                srcarg = NULL;
+            }
+        } else if(argc == 2) {
+            /* A local path or "android"/"data" arg was specified. */
+            srcarg = argv[1];
+        } else {
+            return usage();
+        }
+        ret = find_sync_dirs(srcarg, &android_srcpath, &data_srcpath);
+        if(ret != 0) return usage();
+
+        if(android_srcpath != NULL)
+            ret = do_sync_sync(android_srcpath, "/system", listonly);
+        if(ret == 0 && data_srcpath != NULL)
+            ret = do_sync_sync(data_srcpath, "/data", listonly);
+
+        free(android_srcpath);
+        free(data_srcpath);
+        return ret;
+    }
+
+    /* passthrough commands */
+
+    if(!strcmp(argv[0],"get-state") ||
+        !strcmp(argv[0],"get-serialno"))
+    {
+        char *tmp;
+
+        format_host_command(buf, sizeof buf, argv[0], ttype, serial);
+        tmp = adb_query(buf);
+        if(tmp) {
+            printf("%s\n", tmp);
+            return 0;
+        } else {
+            return 1;
+        }
+    }
+
+    /* other commands */
+
+    if(!strcmp(argv[0],"status-window")) {
+        status_window(ttype, serial);
+        return 0;
+    }
+
+    if(!strcmp(argv[0],"logcat") || !strcmp(argv[0],"lolcat")) {
+        return logcat(ttype, serial, argc, argv);
+    }
+
+    if(!strcmp(argv[0],"ppp")) {
+        return ppp(argc, argv);
+    }
+
+    if (!strcmp(argv[0], "start-server")) {
+        return adb_connect("host:start-server");
+    }
+
+    if (!strcmp(argv[0], "jdwp")) {
+        int  fd = adb_connect("jdwp");
+        if (fd >= 0) {
+            read_and_dump(fd);
+            adb_close(fd);
+            return 0;
+        } else {
+            fprintf(stderr, "error: %s\n", adb_error());
+            return -1;
+        }
+    }
+
+    /* "adb /?" is a common idiom under Windows */
+    if(!strcmp(argv[0], "help") || !strcmp(argv[0], "/?")) {
+        help();
+        return 0;
+    }
+
+    if(!strcmp(argv[0], "version")) {
+        version(stdout);
+        return 0;
+    }
+
+    usage();
+    return 1;
+}
+
+static int do_cmd(transport_type ttype, char* serial, char *cmd, ...)
+{
+    char *argv[16];
+    int argc;
+    va_list ap;
+
+    va_start(ap, cmd);
+    argc = 0;
+
+    if (serial) {
+        argv[argc++] = "-s";
+        argv[argc++] = serial;
+    } else if (ttype == kTransportUsb) {
+        argv[argc++] = "-d";
+    } else if (ttype == kTransportLocal) {
+        argv[argc++] = "-e";
+    }
+
+    argv[argc++] = cmd;
+    while((argv[argc] = va_arg(ap, char*)) != 0) argc++;
+    va_end(ap);
+
+#if 0
+    int n;
+    fprintf(stderr,"argc = %d\n",argc);
+    for(n = 0; n < argc; n++) {
+        fprintf(stderr,"argv[%d] = \"%s\"\n", n, argv[n]);
+    }
+#endif
+
+    return adb_commandline(argc, argv);
+}
+
+int find_sync_dirs(const char *srcarg,
+        char **android_srcdir_out, char **data_srcdir_out)
+{
+    char *android_srcdir, *data_srcdir;
+
+    if(srcarg == NULL) {
+        android_srcdir = product_file("system");
+        data_srcdir = product_file("data");
+    } else {
+        /* srcarg may be "data", "system" or NULL.
+         * if srcarg is NULL, then both data and system are synced
+         */
+        if(strcmp(srcarg, "system") == 0) {
+            android_srcdir = product_file("system");
+            data_srcdir = NULL;
+        } else if(strcmp(srcarg, "data") == 0) {
+            android_srcdir = NULL;
+            data_srcdir = product_file("data");
+        } else {
+            /* It's not "system" or "data".
+             */
+            return 1;
+        }
+    }
+
+    if(android_srcdir_out != NULL)
+        *android_srcdir_out = android_srcdir;
+    else
+        free(android_srcdir);
+
+    if(data_srcdir_out != NULL)
+        *data_srcdir_out = data_srcdir;
+    else
+        free(data_srcdir);
+
+    return 0;
+}
+
+static int pm_command(transport_type transport, char* serial,
+                      int argc, char** argv)
+{
+    char buf[4096];
+
+    snprintf(buf, sizeof(buf), "shell:pm");
+
+    while(argc-- > 0) {
+        char *quoted;
+
+        quoted = dupAndQuote(*argv++);
+
+        strncat(buf, " ", sizeof(buf)-1);
+        strncat(buf, quoted, sizeof(buf)-1);
+        free(quoted);
+    }
+
+    send_shellcommand(transport, serial, buf);
+    return 0;
+}
+
+int uninstall_app(transport_type transport, char* serial, int argc, char** argv)
+{
+    /* if the user choose the -k option, we refuse to do it until devices are
+       out with the option to uninstall the remaining data somehow (adb/ui) */
+    if (argc == 3 && strcmp(argv[1], "-k") == 0)
+    {
+        printf(
+            "The -k option uninstalls the application while retaining the data/cache.\n"
+            "At the moment, there is no way to remove the remaining data.\n"
+            "You will have to reinstall the application with the same signature, and fully uninstall it.\n"
+            "If you truly wish to continue, execute 'adb shell pm uninstall -k %s'\n", argv[2]);
+        return -1;
+    }
+
+    /* 'adb uninstall' takes the same arguments as 'pm uninstall' on device */
+    return pm_command(transport, serial, argc, argv);
+}
+
+static int delete_file(transport_type transport, char* serial, char* filename)
+{
+    char buf[4096];
+    char* quoted;
+
+    snprintf(buf, sizeof(buf), "shell:rm ");
+    quoted = dupAndQuote(filename);
+    strncat(buf, quoted, sizeof(buf)-1);
+    free(quoted);
+
+    send_shellcommand(transport, serial, buf);
+    return 0;
+}
+
+int install_app(transport_type transport, char* serial, int argc, char** argv)
+{
+    struct stat st;
+    int err;
+    const char *const DATA_DEST = "/data/local/tmp/%s";
+    const char *const SD_DEST = "/sdcard/tmp/%s";
+    const char* where = DATA_DEST;
+    char to[PATH_MAX];
+    char* filename = argv[argc - 1];
+    const char* p;
+    int i;
+
+    for (i = 0; i < argc; i++) {
+        if (!strcmp(argv[i], "-s"))
+            where = SD_DEST;
+    }
+
+    p = adb_dirstop(filename);
+    if (p) {
+        p++;
+        snprintf(to, sizeof to, where, p);
+    } else {
+        snprintf(to, sizeof to, where, filename);
+    }
+    if (p[0] == '\0') {
+    }
+
+    err = stat(filename, &st);
+    if (err != 0) {
+        fprintf(stderr, "can't find '%s' to install\n", filename);
+        return 1;
+    }
+    if (!S_ISREG(st.st_mode)) {
+        fprintf(stderr, "can't install '%s' because it's not a file\n",
+                filename);
+        return 1;
+    }
+
+    if (!(err = do_sync_push(filename, to, 1 /* verify APK */))) {
+        /* file in place; tell the Package Manager to install it */
+        argv[argc - 1] = to;       /* destination name, not source location */
+        pm_command(transport, serial, argc, argv);
+        delete_file(transport, serial, to);
+    }
+
+    return err;
+}
diff --git a/adb/console.c b/adb/console.c
new file mode 100644 (file)
index 0000000..b813d33
--- /dev/null
@@ -0,0 +1,45 @@
+#include "sysdeps.h"
+#include "adb.h"
+#include "adb_client.h"
+#include <stdio.h>
+
+static int  connect_to_console(void)
+{
+    int  fd, port;
+
+    port = adb_get_emulator_console_port();
+    if (port < 0) {
+        if (port == -2)
+            fprintf(stderr, "error: more than one emulator detected. use -s option\n");
+        else
+            fprintf(stderr, "error: no emulator detected\n");
+        return -1;
+    }
+    fd = socket_loopback_client( port, SOCK_STREAM );
+    if (fd < 0) {
+        fprintf(stderr, "error: could not connect to TCP port %d\n", port);
+        return -1;
+    }
+    return  fd;
+}
+
+
+int  adb_send_emulator_command(int  argc, char**  argv)
+{
+    int   fd, nn;
+
+    fd = connect_to_console();
+    if (fd < 0)
+        return 1;
+
+#define  QUIT  "quit\n"
+
+    for (nn = 1; nn < argc; nn++) {
+        adb_write( fd, argv[nn], strlen(argv[nn]) );
+        adb_write( fd, (nn == argc-1) ? "\n" : " ", 1 );
+    }
+    adb_write( fd, QUIT, sizeof(QUIT)-1 );
+    adb_close(fd);
+
+    return 0;
+}
diff --git a/adb/fdevent.c b/adb/fdevent.c
new file mode 100644 (file)
index 0000000..c179b20
--- /dev/null
@@ -0,0 +1,506 @@
+/* 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 <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"
+
+#define TRACE(x...) fprintf(stderr,x)
+
+#define DEBUG 0
+
+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
+static void dump_fde(fdevent *fde, const char *info)
+{
+    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);
+}
+#else
+#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 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;    
+}
+
+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));
+
+    n = select(select_n, &rfd, &wfd, &efd, 0);
+
+    if(n < 0) {
+        if(errno == EINTR) return;
+        perror("select");
+        return;
+    }
+
+    for(i = 0; (i < select_n) && (n > 0); i++) {
+        events = 0;
+        if(FD_ISSET(i, &rfd)) events |= FDE_READ;
+        if(FD_ISSET(i, &wfd)) events |= FDE_WRITE;
+        if(FD_ISSET(i, &efd)) events |= FDE_ERROR;
+
+        if(events) {
+            n--;
+
+            fde = fd_table[i];
+            if(fde == 0) FATAL("missing fde for fd %d\n", i);
+
+            fde->events |= events;
+
+            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");
+    }
+
+    fd_table[fde->fd] = 0;
+
+    if(!(fde->state & FDE_DONT_CLOSE)) {
+        dump_fde(fde, "close");
+        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;
+}
+
+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->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_loop()
+{
+    fdevent *fde;
+
+    for(;;) {
+#if DEBUG
+        fprintf(stderr,"--- ---- waiting for events\n");
+#endif
+        fdevent_process();
+
+        while((fde = fdevent_plist_dequeue())) {
+            unsigned events = fde->events;
+            fde->events = 0;
+            fde->state &= (~FDE_PENDING);
+            dump_fde(fde, "callback");
+            fde->func(fde->fd, events, fde->arg);
+        }
+    }
+}
+
diff --git a/adb/fdevent.h b/adb/fdevent.h
new file mode 100644 (file)
index 0000000..6b7e7ec
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * 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;
+    unsigned short state;
+    unsigned short events;
+
+    fd_func func;
+    void *arg;
+};
+
+
+#endif
diff --git a/adb/file_sync_client.c b/adb/file_sync_client.c
new file mode 100644 (file)
index 0000000..da25ae8
--- /dev/null
@@ -0,0 +1,1023 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <time.h>
+#include <dirent.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <zipfile/zipfile.h>
+
+#include "sysdeps.h"
+#include "adb.h"
+#include "adb_client.h"
+#include "file_sync_service.h"
+
+
+static unsigned total_bytes;
+static long long start_time;
+
+static long long NOW()
+{
+    struct timeval tv;
+    gettimeofday(&tv, 0);
+    return ((long long) tv.tv_usec) +
+        1000000LL * ((long long) tv.tv_sec);
+}
+
+static void BEGIN()
+{
+    total_bytes = 0;
+    start_time = NOW();
+}
+
+static void END()
+{
+    long long t = NOW() - start_time;
+    if(total_bytes == 0) return;
+
+    if (t == 0)  /* prevent division by 0 :-) */
+        t = 1000000;
+
+    fprintf(stderr,"%lld KB/s (%d bytes in %lld.%03llds)\n",
+            ((((long long) total_bytes) * 1000000LL) / t) / 1024LL,
+            total_bytes, (t / 1000000LL), (t % 1000000LL) / 1000LL);
+}
+
+void sync_quit(int fd)
+{
+    syncmsg msg;
+
+    msg.req.id = ID_QUIT;
+    msg.req.namelen = 0;
+
+    writex(fd, &msg.req, sizeof(msg.req));
+}
+
+typedef void (*sync_ls_cb)(unsigned mode, unsigned size, unsigned time, const char *name, void *cookie);
+
+int sync_ls(int fd, const char *path, sync_ls_cb func, void *cookie)
+{
+    syncmsg msg;
+    char buf[257];
+    int len;
+
+    len = strlen(path);
+    if(len > 1024) goto fail;
+
+    msg.req.id = ID_LIST;
+    msg.req.namelen = htoll(len);
+
+    if(writex(fd, &msg.req, sizeof(msg.req)) ||
+       writex(fd, path, len)) {
+        goto fail;
+    }
+
+    for(;;) {
+        if(readx(fd, &msg.dent, sizeof(msg.dent))) break;
+        if(msg.dent.id == ID_DONE) return 0;
+        if(msg.dent.id != ID_DENT) break;
+
+        len = ltohl(msg.dent.namelen);
+        if(len > 256) break;
+
+        if(readx(fd, buf, len)) break;
+        buf[len] = 0;
+
+        func(ltohl(msg.dent.mode),
+             ltohl(msg.dent.size),
+             ltohl(msg.dent.time),
+             buf, cookie);
+    }
+
+fail:
+    adb_close(fd);
+    return -1;
+}
+
+typedef struct syncsendbuf syncsendbuf;
+
+struct syncsendbuf {
+    unsigned id;
+    unsigned size;
+    char data[SYNC_DATA_MAX];
+};
+
+static syncsendbuf send_buffer;
+
+int sync_readtime(int fd, const char *path, unsigned *timestamp)
+{
+    syncmsg msg;
+    int len = strlen(path);
+
+    msg.req.id = ID_STAT;
+    msg.req.namelen = htoll(len);
+
+    if(writex(fd, &msg.req, sizeof(msg.req)) ||
+       writex(fd, path, len)) {
+        return -1;
+    }
+
+    if(readx(fd, &msg.stat, sizeof(msg.stat))) {
+        return -1;
+    }
+
+    if(msg.stat.id != ID_STAT) {
+        return -1;
+    }
+
+    *timestamp = ltohl(msg.stat.time);
+    return 0;
+}
+
+static int sync_start_readtime(int fd, const char *path)
+{
+    syncmsg msg;
+    int len = strlen(path);
+
+    msg.req.id = ID_STAT;
+    msg.req.namelen = htoll(len);
+
+    if(writex(fd, &msg.req, sizeof(msg.req)) ||
+       writex(fd, path, len)) {
+        return -1;
+    }
+
+    return 0;
+}
+
+static int sync_finish_readtime(int fd, unsigned int *timestamp,
+                                unsigned int *mode, unsigned int *size)
+{
+    syncmsg msg;
+
+    if(readx(fd, &msg.stat, sizeof(msg.stat)))
+        return -1;
+
+    if(msg.stat.id != ID_STAT)
+        return -1;
+
+    *timestamp = ltohl(msg.stat.time);
+    *mode = ltohl(msg.stat.mode);
+    *size = ltohl(msg.stat.size);
+
+    return 0;
+}
+
+int sync_readmode(int fd, const char *path, unsigned *mode)
+{
+    syncmsg msg;
+    int len = strlen(path);
+
+    msg.req.id = ID_STAT;
+    msg.req.namelen = htoll(len);
+
+    if(writex(fd, &msg.req, sizeof(msg.req)) ||
+       writex(fd, path, len)) {
+        return -1;
+    }
+
+    if(readx(fd, &msg.stat, sizeof(msg.stat))) {
+        return -1;
+    }
+
+    if(msg.stat.id != ID_STAT) {
+        return -1;
+    }
+
+    *mode = ltohl(msg.stat.mode);
+    return 0;
+}
+
+static int write_data_file(int fd, const char *path, syncsendbuf *sbuf)
+{
+    int lfd, err = 0;
+
+    lfd = adb_open(path, O_RDONLY);
+    if(lfd < 0) {
+        fprintf(stderr,"cannot open '%s': %s\n", path, strerror(errno));
+        return -1;
+    }
+
+    sbuf->id = ID_DATA;
+    for(;;) {
+        int ret;
+
+        ret = adb_read(lfd, sbuf->data, SYNC_DATA_MAX);
+        if(!ret)
+            break;
+
+        if(ret < 0) {
+            if(errno == EINTR)
+                continue;
+            fprintf(stderr,"cannot read '%s': %s\n", path, strerror(errno));
+            break;
+        }
+
+        sbuf->size = htoll(ret);
+        if(writex(fd, sbuf, sizeof(unsigned) * 2 + ret)){
+            err = -1;
+            break;
+        }
+        total_bytes += ret;
+    }
+
+    adb_close(lfd);
+    return err;
+}
+
+static int write_data_buffer(int fd, char* file_buffer, int size, syncsendbuf *sbuf)
+{
+    int err = 0;
+    int total = 0;
+
+    sbuf->id = ID_DATA;
+    while (total < size) {
+        int count = size - total;
+        if (count > SYNC_DATA_MAX) {
+            count = SYNC_DATA_MAX;
+        }
+
+        memcpy(sbuf->data, &file_buffer[total], count);
+        sbuf->size = htoll(count);
+        if(writex(fd, sbuf, sizeof(unsigned) * 2 + count)){
+            err = -1;
+            break;
+        }
+        total += count;
+        total_bytes += count;
+    }
+
+    return err;
+}
+
+#ifdef HAVE_SYMLINKS
+static int write_data_link(int fd, const char *path, syncsendbuf *sbuf)
+{
+    int len, ret;
+
+    len = readlink(path, sbuf->data, SYNC_DATA_MAX-1);
+    if(len < 0) {
+        fprintf(stderr, "error reading link '%s': %s\n", path, strerror(errno));
+        return -1;
+    }
+    sbuf->data[len] = '\0';
+
+    sbuf->size = htoll(len + 1);
+    sbuf->id = ID_DATA;
+
+    ret = writex(fd, sbuf, sizeof(unsigned) * 2 + len + 1);
+    if(ret)
+        return -1;
+
+    total_bytes += len + 1;
+
+    return 0;
+}
+#endif
+
+static int sync_send(int fd, const char *lpath, const char *rpath,
+                     unsigned mtime, mode_t mode, int verifyApk)
+{
+    syncmsg msg;
+    int len, r;
+    syncsendbuf *sbuf = &send_buffer;
+    char* file_buffer = NULL;
+    int size = 0;
+    char tmp[64];
+
+    len = strlen(rpath);
+    if(len > 1024) goto fail;
+
+    snprintf(tmp, sizeof(tmp), ",%d", mode);
+    r = strlen(tmp);
+
+    if (verifyApk) {
+        int lfd;
+        zipfile_t zip;
+        zipentry_t entry;
+        int amt;
+
+        // if we are transferring an APK file, then sanity check to make sure
+        // we have a real zip file that contains an AndroidManifest.xml
+        // this requires that we read the entire file into memory.
+        lfd = adb_open(lpath, O_RDONLY);
+        if(lfd < 0) {
+            fprintf(stderr,"cannot open '%s': %s\n", lpath, strerror(errno));
+            return -1;
+        }
+
+        size = adb_lseek(lfd, 0, SEEK_END);
+        if (size == -1 || -1 == adb_lseek(lfd, 0, SEEK_SET)) {
+            fprintf(stderr, "error seeking in file '%s'\n", lpath);
+            adb_close(lfd);
+            return 1;
+        }
+
+        file_buffer = (char *)malloc(size);
+        if (file_buffer == NULL) {
+            fprintf(stderr, "could not allocate buffer for '%s'\n",
+                    lpath);
+            adb_close(lfd);
+            return 1;
+        }
+        amt = adb_read(lfd, file_buffer, size);
+        if (amt != size) {
+            fprintf(stderr, "error reading from file: '%s'\n", lpath);
+            adb_close(lfd);
+            free(file_buffer);
+            return 1;
+        }
+
+        adb_close(lfd);
+
+        zip = init_zipfile(file_buffer, size);
+        if (zip == NULL) {
+            fprintf(stderr, "file '%s' is not a valid zip file\n",
+                    lpath);
+            free(file_buffer);
+            return 1;
+        }
+
+        entry = lookup_zipentry(zip, "AndroidManifest.xml");
+        release_zipfile(zip);
+        if (entry == NULL) {
+            fprintf(stderr, "file '%s' does not contain AndroidManifest.xml\n",
+                    lpath);
+            free(file_buffer);
+            return 1;
+        }
+    }
+
+    msg.req.id = ID_SEND;
+    msg.req.namelen = htoll(len + r);
+
+    if(writex(fd, &msg.req, sizeof(msg.req)) ||
+       writex(fd, rpath, len) || writex(fd, tmp, r)) {
+        free(file_buffer);
+        goto fail;
+    }
+
+    if (file_buffer) {
+        write_data_buffer(fd, file_buffer, size, sbuf);
+        free(file_buffer);
+    } else if (S_ISREG(mode))
+        write_data_file(fd, lpath, sbuf);
+#ifdef HAVE_SYMLINKS
+    else if (S_ISLNK(mode))
+        write_data_link(fd, lpath, sbuf);
+#endif
+    else
+        goto fail;
+
+    msg.data.id = ID_DONE;
+    msg.data.size = htoll(mtime);
+    if(writex(fd, &msg.data, sizeof(msg.data)))
+        goto fail;
+
+    if(readx(fd, &msg.status, sizeof(msg.status)))
+        return -1;
+
+    if(msg.status.id != ID_OKAY) {
+        if(msg.status.id == ID_FAIL) {
+            len = ltohl(msg.status.msglen);
+            if(len > 256) len = 256;
+            if(readx(fd, sbuf->data, len)) {
+                return -1;
+            }
+            sbuf->data[len] = 0;
+        } else
+            strcpy(sbuf->data, "unknown reason");
+
+        fprintf(stderr,"failed to copy '%s' to '%s': %s\n", lpath, rpath, sbuf->data);
+        return -1;
+    }
+
+    return 0;
+
+fail:
+    fprintf(stderr,"protocol failure\n");
+    adb_close(fd);
+    return -1;
+}
+
+static int mkdirs(char *name)
+{
+    int ret;
+    char *x = name + 1;
+
+    for(;;) {
+        x = adb_dirstart(x);
+        if(x == 0) return 0;
+        *x = 0;
+        ret = adb_mkdir(name, 0775);
+        *x = OS_PATH_SEPARATOR;
+        if((ret < 0) && (errno != EEXIST)) {
+            return ret;
+        }
+        x++;
+    }
+    return 0;
+}
+
+int sync_recv(int fd, const char *rpath, const char *lpath)
+{
+    syncmsg msg;
+    int len;
+    int lfd = -1;
+    char *buffer = send_buffer.data;
+    unsigned id;
+
+    len = strlen(rpath);
+    if(len > 1024) return -1;
+
+    msg.req.id = ID_RECV;
+    msg.req.namelen = htoll(len);
+    if(writex(fd, &msg.req, sizeof(msg.req)) ||
+       writex(fd, rpath, len)) {
+        return -1;
+    }
+
+    if(readx(fd, &msg.data, sizeof(msg.data))) {
+        return -1;
+    }
+    id = msg.data.id;
+
+    if((id == ID_DATA) || (id == ID_DONE)) {
+        adb_unlink(lpath);
+        mkdirs((char *)lpath);
+        lfd = adb_creat(lpath, 0644);
+        if(lfd < 0) {
+            fprintf(stderr,"cannot create '%s': %s\n", lpath, strerror(errno));
+            return -1;
+        }
+        goto handle_data;
+    } else {
+        goto remote_error;
+    }
+
+    for(;;) {
+        if(readx(fd, &msg.data, sizeof(msg.data))) {
+            return -1;
+        }
+        id = msg.data.id;
+
+    handle_data:
+        len = ltohl(msg.data.size);
+        if(id == ID_DONE) break;
+        if(id != ID_DATA) goto remote_error;
+        if(len > SYNC_DATA_MAX) {
+            fprintf(stderr,"data overrun\n");
+            adb_close(lfd);
+            return -1;
+        }
+
+        if(readx(fd, buffer, len)) {
+            adb_close(lfd);
+            return -1;
+        }
+
+        if(writex(lfd, buffer, len)) {
+            fprintf(stderr,"cannot write '%s': %s\n", rpath, strerror(errno));
+            adb_close(lfd);
+            return -1;
+        }
+
+        total_bytes += len;
+    }
+
+    adb_close(lfd);
+    return 0;
+
+remote_error:
+    adb_close(lfd);
+    adb_unlink(lpath);
+
+    if(id == ID_FAIL) {
+        len = ltohl(msg.data.size);
+        if(len > 256) len = 256;
+        if(readx(fd, buffer, len)) {
+            return -1;
+        }
+        buffer[len] = 0;
+    } else {
+        memcpy(buffer, &id, 4);
+        buffer[4] = 0;
+//        strcpy(buffer,"unknown reason");
+    }
+    fprintf(stderr,"failed to copy '%s' to '%s': %s\n", rpath, lpath, buffer);
+    return 0;
+}
+
+
+
+/* --- */
+
+
+static void do_sync_ls_cb(unsigned mode, unsigned size, unsigned time,
+                          const char *name, void *cookie)
+{
+    printf("%08x %08x %08x %s\n", mode, size, time, name);
+}
+
+int do_sync_ls(const char *path)
+{
+    int fd = adb_connect("sync:");
+    if(fd < 0) {
+        fprintf(stderr,"error: %s\n", adb_error());
+        return 1;
+    }
+
+    if(sync_ls(fd, path, do_sync_ls_cb, 0)) {
+        return 1;
+    } else {
+        sync_quit(fd);
+        return 0;
+    }
+}
+
+typedef struct copyinfo copyinfo;
+
+struct copyinfo
+{
+    copyinfo *next;
+    const char *src;
+    const char *dst;
+    unsigned int time;
+    unsigned int mode;
+    unsigned int size;
+    int flag;
+    //char data[0];
+};
+
+copyinfo *mkcopyinfo(const char *spath, const char *dpath,
+                     const char *name, int isdir)
+{
+    int slen = strlen(spath);
+    int dlen = strlen(dpath);
+    int nlen = strlen(name);
+    int ssize = slen + nlen + 2;
+    int dsize = dlen + nlen + 2;
+
+    copyinfo *ci = malloc(sizeof(copyinfo) + ssize + dsize);
+    if(ci == 0) {
+        fprintf(stderr,"out of memory\n");
+        abort();
+    }
+
+    ci->next = 0;
+    ci->time = 0;
+    ci->mode = 0;
+    ci->size = 0;
+    ci->flag = 0;
+    ci->src = (const char*)(ci + 1);
+    ci->dst = ci->src + ssize;
+    snprintf((char*) ci->src, ssize, isdir ? "%s%s/" : "%s%s", spath, name);
+    snprintf((char*) ci->dst, dsize, isdir ? "%s%s/" : "%s%s", dpath, name);
+
+//    fprintf(stderr,"mkcopyinfo('%s','%s')\n", ci->src, ci->dst);
+    return ci;
+}
+
+
+static int local_build_list(copyinfo **filelist,
+                            const char *lpath, const char *rpath)
+{
+    DIR *d;
+    struct dirent *de;
+    struct stat st;
+    copyinfo *dirlist = 0;
+    copyinfo *ci, *next;
+
+//    fprintf(stderr,"local_build_list('%s','%s')\n", lpath, rpath);
+
+    d = opendir(lpath);
+    if(d == 0) {
+        fprintf(stderr,"cannot open '%s': %s\n", lpath, strerror(errno));
+        return -1;
+    }
+
+    while((de = readdir(d))) {
+        char stat_path[PATH_MAX];
+        char *name = de->d_name;
+
+        if(name[0] == '.') {
+            if(name[1] == 0) continue;
+            if((name[1] == '.') && (name[2] == 0)) continue;
+        }
+
+        /*
+         * We could use d_type if HAVE_DIRENT_D_TYPE is defined, but reiserfs
+         * always returns DT_UNKNOWN, so we just use stat() for all cases.
+         */
+        if (strlen(lpath) + strlen(de->d_name) + 1 > sizeof(stat_path))
+            continue;
+        strcpy(stat_path, lpath);
+        strcat(stat_path, de->d_name);
+        stat(stat_path, &st);
+
+        if (S_ISDIR(st.st_mode)) {
+            ci = mkcopyinfo(lpath, rpath, name, 1);
+            ci->next = dirlist;
+            dirlist = ci;
+        } else {
+            ci = mkcopyinfo(lpath, rpath, name, 0);
+            if(lstat(ci->src, &st)) {
+                closedir(d);
+                fprintf(stderr,"cannot stat '%s': %s\n", ci->src, strerror(errno));
+                return -1;
+            }
+            if(!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) {
+                fprintf(stderr, "skipping special file '%s'\n", ci->src);
+                free(ci);
+            } else {
+                ci->time = st.st_mtime;
+                ci->mode = st.st_mode;
+                ci->size = st.st_size;
+                ci->next = *filelist;
+                *filelist = ci;
+            }
+        }
+    }
+
+    closedir(d);
+
+    for(ci = dirlist; ci != 0; ci = next) {
+        next = ci->next;
+        local_build_list(filelist, ci->src, ci->dst);
+        free(ci);
+    }
+
+    return 0;
+}
+
+
+static int copy_local_dir_remote(int fd, const char *lpath, const char *rpath, int checktimestamps, int listonly)
+{
+    copyinfo *filelist = 0;
+    copyinfo *ci, *next;
+    int pushed = 0;
+    int skipped = 0;
+
+    if((lpath[0] == 0) || (rpath[0] == 0)) return -1;
+    if(lpath[strlen(lpath) - 1] != '/') {
+        int  tmplen = strlen(lpath)+2;
+        char *tmp = malloc(tmplen);
+        if(tmp == 0) return -1;
+        snprintf(tmp, tmplen, "%s/",lpath);
+        lpath = tmp;
+    }
+    if(rpath[strlen(rpath) - 1] != '/') {
+        int tmplen = strlen(rpath)+2;
+        char *tmp = malloc(tmplen);
+        if(tmp == 0) return -1;
+        snprintf(tmp, tmplen, "%s/",rpath);
+        rpath = tmp;
+    }
+
+    if(local_build_list(&filelist, lpath, rpath)) {
+        return -1;
+    }
+
+    if(checktimestamps){
+        for(ci = filelist; ci != 0; ci = ci->next) {
+            if(sync_start_readtime(fd, ci->dst)) {
+                return 1;
+            }
+        }
+        for(ci = filelist; ci != 0; ci = ci->next) {
+            unsigned int timestamp, mode, size;
+            if(sync_finish_readtime(fd, &timestamp, &mode, &size))
+                return 1;
+            if(size == ci->size) {
+                /* for links, we cannot update the atime/mtime */
+                if((S_ISREG(ci->mode & mode) && timestamp == ci->time) ||
+                    (S_ISLNK(ci->mode & mode) && timestamp >= ci->time))
+                    ci->flag = 1;
+            }
+        }
+    }
+    for(ci = filelist; ci != 0; ci = next) {
+        next = ci->next;
+        if(ci->flag == 0) {
+            fprintf(stderr,"%spush: %s -> %s\n", listonly ? "would " : "", ci->src, ci->dst);
+            if(!listonly &&
+               sync_send(fd, ci->src, ci->dst, ci->time, ci->mode, 0 /* no verify APK */)){
+                return 1;
+            }
+            pushed++;
+        } else {
+            skipped++;
+        }
+        free(ci);
+    }
+
+    fprintf(stderr,"%d file%s pushed. %d file%s skipped.\n",
+            pushed, (pushed == 1) ? "" : "s",
+            skipped, (skipped == 1) ? "" : "s");
+
+    return 0;
+}
+
+
+int do_sync_push(const char *lpath, const char *rpath, int verifyApk)
+{
+    struct stat st;
+    unsigned mode;
+    int fd;
+
+    fd = adb_connect("sync:");
+    if(fd < 0) {
+        fprintf(stderr,"error: %s\n", adb_error());
+        return 1;
+    }
+
+    if(stat(lpath, &st)) {
+        fprintf(stderr,"cannot stat '%s': %s\n", lpath, strerror(errno));
+        sync_quit(fd);
+        return 1;
+    }
+
+    if(S_ISDIR(st.st_mode)) {
+        BEGIN();
+        if(copy_local_dir_remote(fd, lpath, rpath, 0, 0)) {
+            return 1;
+        } else {
+            END();
+            sync_quit(fd);
+        }
+    } else {
+        if(sync_readmode(fd, rpath, &mode)) {
+            return 1;
+        }
+        if((mode != 0) && S_ISDIR(mode)) {
+                /* if we're copying a local file to a remote directory,
+                ** we *really* want to copy to remotedir + "/" + localfilename
+                */
+            const char *name = adb_dirstop(lpath);
+            if(name == 0) {
+                name = lpath;
+            } else {
+                name++;
+            }
+            int  tmplen = strlen(name) + strlen(rpath) + 2;
+            char *tmp = malloc(strlen(name) + strlen(rpath) + 2);
+            if(tmp == 0) return 1;
+            snprintf(tmp, tmplen, "%s/%s", rpath, name);
+            rpath = tmp;
+        }
+        BEGIN();
+        if(sync_send(fd, lpath, rpath, st.st_mtime, st.st_mode, verifyApk)) {
+            return 1;
+        } else {
+            END();
+            sync_quit(fd);
+            return 0;
+        }
+    }
+
+    return 0;
+}
+
+
+typedef struct {
+    copyinfo **filelist;
+    copyinfo **dirlist;
+    const char *rpath;
+    const char *lpath;
+} sync_ls_build_list_cb_args;
+
+void
+sync_ls_build_list_cb(unsigned mode, unsigned size, unsigned time,
+                      const char *name, void *cookie)
+{
+    sync_ls_build_list_cb_args *args = (sync_ls_build_list_cb_args *)cookie;
+    copyinfo *ci;
+
+    if (S_ISDIR(mode)) {
+        copyinfo **dirlist = args->dirlist;
+
+        /* Don't try recursing down "." or ".." */
+        if (name[0] == '.') {
+            if (name[1] == '\0') return;
+            if ((name[1] == '.') && (name[2] == '\0')) return;
+        }
+
+        ci = mkcopyinfo(args->rpath, args->lpath, name, 1);
+        ci->next = *dirlist;
+        *dirlist = ci;
+    } else if (S_ISREG(mode) || S_ISLNK(mode)) {
+        copyinfo **filelist = args->filelist;
+
+        ci = mkcopyinfo(args->rpath, args->lpath, name, 0);
+        ci->time = time;
+        ci->mode = mode;
+        ci->size = size;
+        ci->next = *filelist;
+        *filelist = ci;
+    } else {
+        fprintf(stderr, "skipping special file '%s'\n", name);
+    }
+}
+
+static int remote_build_list(int syncfd, copyinfo **filelist,
+                             const char *rpath, const char *lpath)
+{
+    copyinfo *dirlist = NULL;
+    sync_ls_build_list_cb_args args;
+
+    args.filelist = filelist;
+    args.dirlist = &dirlist;
+    args.rpath = rpath;
+    args.lpath = lpath;
+
+    /* Put the files/dirs in rpath on the lists. */
+    if (sync_ls(syncfd, rpath, sync_ls_build_list_cb, (void *)&args)) {
+        return 1;
+    }
+
+    /* Recurse into each directory we found. */
+    while (dirlist != NULL) {
+        copyinfo *next = dirlist->next;
+        if (remote_build_list(syncfd, filelist, dirlist->src, dirlist->dst)) {
+            return 1;
+        }
+        free(dirlist);
+        dirlist = next;
+    }
+
+    return 0;
+}
+
+static int copy_remote_dir_local(int fd, const char *rpath, const char *lpath,
+                                 int checktimestamps)
+{
+    copyinfo *filelist = 0;
+    copyinfo *ci, *next;
+    int pulled = 0;
+    int skipped = 0;
+
+    /* Make sure that both directory paths end in a slash. */
+    if (rpath[0] == 0 || lpath[0] == 0) return -1;
+    if (rpath[strlen(rpath) - 1] != '/') {
+        int  tmplen = strlen(rpath) + 2;
+        char *tmp = malloc(tmplen);
+        if (tmp == 0) return -1;
+        snprintf(tmp, tmplen, "%s/", rpath);
+        rpath = tmp;
+    }
+    if (lpath[strlen(lpath) - 1] != '/') {
+        int  tmplen = strlen(lpath) + 2;
+        char *tmp = malloc(tmplen);
+        if (tmp == 0) return -1;
+        snprintf(tmp, tmplen, "%s/", lpath);
+        lpath = tmp;
+    }
+
+    fprintf(stderr, "pull: building file list...\n");
+    /* Recursively build the list of files to copy. */
+    if (remote_build_list(fd, &filelist, rpath, lpath)) {
+        return -1;
+    }
+
+#if 0
+    if (checktimestamps) {
+        for (ci = filelist; ci != 0; ci = ci->next) {
+            if (sync_start_readtime(fd, ci->dst)) {
+                return 1;
+            }
+        }
+        for (ci = filelist; ci != 0; ci = ci->next) {
+            unsigned int timestamp, mode, size;
+            if (sync_finish_readtime(fd, &timestamp, &mode, &size))
+                return 1;
+            if (size == ci->size) {
+                /* for links, we cannot update the atime/mtime */
+                if ((S_ISREG(ci->mode & mode) && timestamp == ci->time) ||
+                    (S_ISLNK(ci->mode & mode) && timestamp >= ci->time))
+                    ci->flag = 1;
+            }
+        }
+    }
+#endif
+    for (ci = filelist; ci != 0; ci = next) {
+        next = ci->next;
+        if (ci->flag == 0) {
+            fprintf(stderr, "pull: %s -> %s\n", ci->src, ci->dst);
+            if (sync_recv(fd, ci->src, ci->dst)) {
+                return 1;
+            }
+            pulled++;
+        } else {
+            skipped++;
+        }
+        free(ci);
+    }
+
+    fprintf(stderr, "%d file%s pulled. %d file%s skipped.\n",
+            pulled, (pulled == 1) ? "" : "s",
+            skipped, (skipped == 1) ? "" : "s");
+
+    return 0;
+}
+
+int do_sync_pull(const char *rpath, const char *lpath)
+{
+    unsigned mode;
+    struct stat st;
+
+    int fd;
+
+    fd = adb_connect("sync:");
+    if(fd < 0) {
+        fprintf(stderr,"error: %s\n", adb_error());
+        return 1;
+    }
+
+    if(sync_readmode(fd, rpath, &mode)) {
+        return 1;
+    }
+    if(mode == 0) {
+        fprintf(stderr,"remote object '%s' does not exist\n", rpath);
+        return 1;
+    }
+
+    if(S_ISREG(mode) || S_ISLNK(mode) || S_ISCHR(mode) || S_ISBLK(mode)) {
+        if(stat(lpath, &st) == 0) {
+            if(S_ISDIR(st.st_mode)) {
+                    /* if we're copying a remote file to a local directory,
+                    ** we *really* want to copy to localdir + "/" + remotefilename
+                    */
+                const char *name = adb_dirstop(rpath);
+                if(name == 0) {
+                    name = rpath;
+                } else {
+                    name++;
+                }
+                int  tmplen = strlen(name) + strlen(lpath) + 2;
+                char *tmp = malloc(tmplen);
+                if(tmp == 0) return 1;
+                snprintf(tmp, tmplen, "%s/%s", lpath, name);
+                lpath = tmp;
+            }
+        }
+        BEGIN();
+        if(sync_recv(fd, rpath, lpath)) {
+            return 1;
+        } else {
+            END();
+            sync_quit(fd);
+            return 0;
+        }
+    } else if(S_ISDIR(mode)) {
+        BEGIN();
+        if (copy_remote_dir_local(fd, rpath, lpath, 0)) {
+            return 1;
+        } else {
+            END();
+            sync_quit(fd);
+            return 0;
+        }
+    } else {
+        fprintf(stderr,"remote object '%s' not a file or directory\n", rpath);
+        return 1;
+    }
+}
+
+int do_sync_sync(const char *lpath, const char *rpath, int listonly)
+{
+    fprintf(stderr,"syncing %s...\n",rpath);
+
+    int fd = adb_connect("sync:");
+    if(fd < 0) {
+        fprintf(stderr,"error: %s\n", adb_error());
+        return 1;
+    }
+
+    BEGIN();
+    if(copy_local_dir_remote(fd, lpath, rpath, 1, listonly)){
+        return 1;
+    } else {
+        END();
+        sync_quit(fd);
+        return 0;
+    }
+}
diff --git a/adb/file_sync_service.c b/adb/file_sync_service.c
new file mode 100644 (file)
index 0000000..a231e93
--- /dev/null
@@ -0,0 +1,412 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <utime.h>
+
+#include <errno.h>
+
+#include "sysdeps.h"
+
+#define TRACE_TAG  TRACE_SYNC
+#include "adb.h"
+#include "file_sync_service.h"
+
+static int mkdirs(char *name)
+{
+    int ret;
+    char *x = name + 1;
+
+    if(name[0] != '/') return -1;
+
+    for(;;) {
+        x = adb_dirstart(x);
+        if(x == 0) return 0;
+        *x = 0;
+        ret = adb_mkdir(name, 0775);
+        if((ret < 0) && (errno != EEXIST)) {
+            D("mkdir(\"%s\") -> %s\n", name, strerror(errno));
+            *x = '/';
+            return ret;
+        }
+        *x++ = '/';
+    }
+    return 0;
+}
+
+static int do_stat(int s, const char *path)
+{
+    syncmsg msg;
+    struct stat st;
+
+    msg.stat.id = ID_STAT;
+
+    if(lstat(path, &st)) {
+        msg.stat.mode = 0;
+        msg.stat.size = 0;
+        msg.stat.time = 0;
+    } else {
+        msg.stat.mode = htoll(st.st_mode);
+        msg.stat.size = htoll(st.st_size);
+        msg.stat.time = htoll(st.st_mtime);
+    }
+
+    return writex(s, &msg.stat, sizeof(msg.stat));
+}
+
+static int do_list(int s, const char *path)
+{
+    DIR *d;
+    struct dirent *de;
+    struct stat st;
+    syncmsg msg;
+    int len;
+
+    char tmp[1024 + 256 + 1];
+    char *fname;
+
+    len = strlen(path);
+    memcpy(tmp, path, len);
+    tmp[len] = '/';
+    fname = tmp + len + 1;
+
+    msg.dent.id = ID_DENT;
+
+    d = opendir(path);
+    if(d == 0) goto done;
+
+    while((de = readdir(d))) {
+        int len = strlen(de->d_name);
+
+            /* not supposed to be possible, but
+               if it does happen, let's not buffer overrun */
+        if(len > 256) continue;
+
+        strcpy(fname, de->d_name);
+        if(lstat(tmp, &st) == 0) {
+            msg.dent.mode = htoll(st.st_mode);
+            msg.dent.size = htoll(st.st_size);
+            msg.dent.time = htoll(st.st_mtime);
+            msg.dent.namelen = htoll(len);
+
+            if(writex(s, &msg.dent, sizeof(msg.dent)) ||
+               writex(s, de->d_name, len)) {
+                return -1;
+            }
+        }
+    }
+
+    closedir(d);
+
+done:
+    msg.dent.id = ID_DONE;
+    msg.dent.mode = 0;
+    msg.dent.size = 0;
+    msg.dent.time = 0;
+    msg.dent.namelen = 0;
+    return writex(s, &msg.dent, sizeof(msg.dent));
+}
+
+static int fail_message(int s, const char *reason)
+{
+    syncmsg msg;
+    int len = strlen(reason);
+
+    D("sync: failure: %s\n", reason);
+
+    msg.data.id = ID_FAIL;
+    msg.data.size = htoll(len);
+    if(writex(s, &msg.data, sizeof(msg.data)) ||
+       writex(s, reason, len)) {
+        return -1;
+    } else {
+        return 0;
+    }
+}
+
+static int fail_errno(int s)
+{
+    return fail_message(s, strerror(errno));
+}
+
+static int handle_send_file(int s, char *path, mode_t mode, char *buffer)
+{
+    syncmsg msg;
+    unsigned int timestamp = 0;
+    int fd;
+
+    fd = adb_open_mode(path, O_WRONLY | O_CREAT | O_EXCL, mode);
+    if(fd < 0 && errno == ENOENT) {
+        mkdirs(path);
+        fd = adb_open_mode(path, O_WRONLY | O_CREAT | O_EXCL, mode);
+    }
+    if(fd < 0 && errno == EEXIST) {
+        fd = adb_open_mode(path, O_WRONLY, mode);
+    }
+    if(fd < 0) {
+        if(fail_errno(s))
+            return -1;
+        fd = -1;
+    }
+
+    for(;;) {
+        unsigned int len;
+
+        if(readx(s, &msg.data, sizeof(msg.data)))
+            goto fail;
+
+        if(msg.data.id != ID_DATA) {
+            if(msg.data.id == ID_DONE) {
+                timestamp = ltohl(msg.data.size);
+                break;
+            }
+            fail_message(s, "invalid data message");
+            goto fail;
+        }
+        len = ltohl(msg.data.size);
+        if(len > SYNC_DATA_MAX) {
+            fail_message(s, "oversize data message");
+            goto fail;
+        }
+        if(readx(s, buffer, len))
+            goto fail;
+
+        if(fd < 0)
+            continue;
+        if(writex(fd, buffer, len)) {
+            adb_close(fd);
+            adb_unlink(path);
+            fd = -1;
+            if(fail_errno(s)) return -1;
+        }
+    }
+
+    if(fd >= 0) {
+        struct utimbuf u;
+        adb_close(fd);
+        u.actime = timestamp;
+        u.modtime = timestamp;
+        utime(path, &u);
+
+        msg.status.id = ID_OKAY;
+        msg.status.msglen = 0;
+        if(writex(s, &msg.status, sizeof(msg.status)))
+            return -1;
+    }
+    return 0;
+
+fail:
+    if(fd >= 0)
+        adb_close(fd);
+    adb_unlink(path);
+    return -1;
+}
+
+#ifdef HAVE_SYMLINKS
+static int handle_send_link(int s, char *path, char *buffer)
+{
+    syncmsg msg;
+    unsigned int len;
+    int ret;
+
+    if(readx(s, &msg.data, sizeof(msg.data)))
+        return -1;
+
+    if(msg.data.id != ID_DATA) {
+        fail_message(s, "invalid data message: expected ID_DATA");
+        return -1;
+    }
+
+    len = ltohl(msg.data.size);
+    if(len > SYNC_DATA_MAX) {
+        fail_message(s, "oversize data message");
+        return -1;
+    }
+    if(readx(s, buffer, len))
+        return -1;
+
+    ret = symlink(buffer, path);
+    if(ret && errno == ENOENT) {
+        mkdirs(path);
+        ret = symlink(buffer, path);
+    }
+    if(ret) {
+        fail_errno(s);
+        return -1;
+    }
+
+    if(readx(s, &msg.data, sizeof(msg.data)))
+        return -1;
+
+    if(msg.data.id == ID_DONE) {
+        msg.status.id = ID_OKAY;
+        msg.status.msglen = 0;
+        if(writex(s, &msg.status, sizeof(msg.status)))
+            return -1;
+    } else {
+        fail_message(s, "invalid data message: expected ID_DONE");
+        return -1;
+    }
+
+    return 0;
+}
+#endif /* HAVE_SYMLINKS */
+
+static int do_send(int s, char *path, char *buffer)
+{
+    char *tmp;
+    mode_t mode;
+    int is_link, ret;
+
+    tmp = strrchr(path,',');
+    if(tmp) {
+        *tmp = 0;
+        errno = 0;
+        mode = strtoul(tmp + 1, NULL, 0);
+#ifndef HAVE_SYMLINKS
+        is_link = 0;
+#else
+        is_link = S_ISLNK(mode);
+#endif
+        mode &= 0777;
+    }
+    if(!tmp || errno) {
+        mode = 0644;
+        is_link = 0;
+    }
+
+    adb_unlink(path);
+
+
+#ifdef HAVE_SYMLINKS
+    if(is_link)
+        ret = handle_send_link(s, path, buffer);
+    else {
+#else
+    {
+#endif
+        /* copy user permission bits to "group" and "other" permissions */
+        mode |= ((mode >> 3) & 0070);
+        mode |= ((mode >> 3) & 0007);
+
+        ret = handle_send_file(s, path, mode, buffer);
+    }
+
+    return ret;
+}
+
+static int do_recv(int s, const char *path, char *buffer)
+{
+    syncmsg msg;
+    int fd, r;
+
+    fd = adb_open(path, O_RDONLY);
+    if(fd < 0) {
+        if(fail_errno(s)) return -1;
+        return 0;
+    }
+
+    msg.data.id = ID_DATA;
+    for(;;) {
+        r = adb_read(fd, buffer, SYNC_DATA_MAX);
+        if(r <= 0) {
+            if(r == 0) break;
+            if(errno == EINTR) continue;
+            r = fail_errno(s);
+            adb_close(fd);
+            return r;
+        }
+        msg.data.size = htoll(r);
+        if(writex(s, &msg.data, sizeof(msg.data)) ||
+           writex(s, buffer, r)) {
+            adb_close(fd);
+            return -1;
+        }
+    }
+
+    adb_close(fd);
+
+    msg.data.id = ID_DONE;
+    msg.data.size = 0;
+    if(writex(s, &msg.data, sizeof(msg.data))) {
+        return -1;
+    }
+
+    return 0;
+}
+
+void file_sync_service(int fd, void *cookie)
+{
+    syncmsg msg;
+    char name[1025];
+    unsigned namelen;
+
+    char *buffer = malloc(SYNC_DATA_MAX);
+    if(buffer == 0) goto fail;
+
+    for(;;) {
+        D("sync: waiting for command\n");
+
+        if(readx(fd, &msg.req, sizeof(msg.req))) {
+            fail_message(fd, "command read failure");
+            break;
+        }
+        namelen = ltohl(msg.req.namelen);
+        if(namelen > 1024) {
+            fail_message(fd, "invalid namelen");
+            break;
+        }
+        if(readx(fd, name, namelen)) {
+            fail_message(fd, "filename read failure");
+            break;
+        }
+        name[namelen] = 0;
+
+        msg.req.namelen = 0;
+        D("sync: '%s' '%s'\n", (char*) &msg.req, name);
+
+        switch(msg.req.id) {
+        case ID_STAT:
+            if(do_stat(fd, name)) goto fail;
+            break;
+        case ID_LIST:
+            if(do_list(fd, name)) goto fail;
+            break;
+        case ID_SEND:
+            if(do_send(fd, name, buffer)) goto fail;
+            break;
+        case ID_RECV:
+            if(do_recv(fd, name, buffer)) goto fail;
+            break;
+        case ID_QUIT:
+            goto fail;
+        default:
+            fail_message(fd, "unknown command");
+            goto fail;
+        }
+    }
+
+fail:
+    if(buffer != 0) free(buffer);
+    D("sync: done\n");
+    adb_close(fd);
+}
diff --git a/adb/file_sync_service.h b/adb/file_sync_service.h
new file mode 100644 (file)
index 0000000..11ea06b
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _FILE_SYNC_SERVICE_H_
+#define _FILE_SYNC_SERVICE_H_
+
+#ifdef __ppc__
+static inline unsigned __swap_uint32(unsigned x) 
+{
+    return (((x) & 0xFF000000) >> 24)
+        | (((x) & 0x00FF0000) >> 8)
+        | (((x) & 0x0000FF00) << 8)
+        | (((x) & 0x000000FF) << 24);
+}
+#define htoll(x) __swap_uint32(x)
+#define ltohl(x) __swap_uint32(x)
+#define MKID(a,b,c,d) ((d) | ((c) << 8) | ((b) << 16) | ((a) << 24))
+#else
+#define htoll(x) (x)
+#define ltohl(x) (x)
+#define MKID(a,b,c,d) ((a) | ((b) << 8) | ((c) << 16) | ((d) << 24))
+#endif
+
+#define ID_STAT MKID('S','T','A','T')
+#define ID_LIST MKID('L','I','S','T')
+#define ID_ULNK MKID('U','L','N','K')
+#define ID_SEND MKID('S','E','N','D')
+#define ID_RECV MKID('R','E','C','V')
+#define ID_DENT MKID('D','E','N','T')
+#define ID_DONE MKID('D','O','N','E')
+#define ID_DATA MKID('D','A','T','A')
+#define ID_OKAY MKID('O','K','A','Y')
+#define ID_FAIL MKID('F','A','I','L')
+#define ID_QUIT MKID('Q','U','I','T')
+
+typedef union {
+    unsigned id;
+    struct {
+        unsigned id;
+        unsigned namelen;
+    } req;
+    struct {
+        unsigned id;
+        unsigned mode;
+        unsigned size;
+        unsigned time;
+    } stat;
+    struct {
+        unsigned id;
+        unsigned mode;
+        unsigned size;
+        unsigned time;
+        unsigned namelen;
+    } dent;
+    struct {
+        unsigned id;
+        unsigned size;
+    } data;
+    struct {
+        unsigned id;
+        unsigned msglen;
+    } status;    
+} syncmsg;
+
+
+void file_sync_service(int fd, void *cookie);
+int do_sync_ls(const char *path);
+int do_sync_push(const char *lpath, const char *rpath, int verifyApk);
+int do_sync_sync(const char *lpath, const char *rpath, int listonly);
+int do_sync_pull(const char *rpath, const char *lpath);
+
+#define SYNC_DATA_MAX (64*1024)
+
+#endif
diff --git a/adb/framebuffer_service.c b/adb/framebuffer_service.c
new file mode 100644 (file)
index 0000000..6d50f8e
--- /dev/null
@@ -0,0 +1,209 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#ifdef USE_SCREENCAP
+#include <errno.h>
+#include <hardware/hardware.h>
+
+#include "sysdeps.h"
+#endif
+#include "fdevent.h"
+#include "adb.h"
+
+#ifndef USE_SCREENCAP
+#include <linux/fb.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#endif
+
+/* TODO:
+** - sync with vsync to avoid tearing
+*/
+/* This version number defines the format of the fbinfo struct.
+   It must match versioning in ddms where this data is consumed. */
+#define DDMS_RAWIMAGE_VERSION 1
+struct fbinfo {
+    unsigned int version;
+    unsigned int bpp;
+    unsigned int size;
+    unsigned int width;
+    unsigned int height;
+    unsigned int red_offset;
+    unsigned int red_length;
+    unsigned int blue_offset;
+    unsigned int blue_length;
+    unsigned int green_offset;
+    unsigned int green_length;
+    unsigned int alpha_offset;
+    unsigned int alpha_length;
+} __attribute__((packed));
+
+#ifdef USE_SCREENCAP
+static int fill_format(struct fbinfo* info, int format)
+{
+    // bpp, red, green, blue, alpha
+
+    static const int format_map[][9] = {
+        {0, 0, 0, 0, 0, 0, 0, 0, 0},   // INVALID
+        {32, 0, 8, 8, 8, 16, 8, 24, 8}, // HAL_PIXEL_FORMAT_RGBA_8888
+        {32, 0, 8, 8, 8, 16, 8, 0, 0}, // HAL_PIXEL_FORMAT_RGBX_8888
+        {24, 16, 8, 8, 8, 0, 8, 0, 0},  // HAL_PIXEL_FORMAT_RGB_888
+        {16, 11, 5, 5, 6, 0, 5, 0, 0},  // HAL_PIXEL_FORMAT_RGB_565
+        {32, 16, 8, 8, 8, 0, 8, 24, 8}, // HAL_PIXEL_FORMAT_BGRA_8888
+        {16, 11, 5, 6, 5, 1, 5, 0, 1},  // HAL_PIXEL_FORMAT_RGBA_5551
+        {16, 12, 4, 8, 4, 4, 4, 0, 4}   // HAL_PIXEL_FORMAT_RGBA_4444
+    };
+    const int *p;
+
+    if (format == 0 || format > HAL_PIXEL_FORMAT_RGBA_4444)
+        return -ENOTSUP;
+
+    p = format_map[format];
+
+    info->bpp = *(p++);
+    info->red_offset = *(p++);
+    info->red_length = *(p++);
+    info->green_offset = *(p++);
+    info->green_length = *(p++);
+    info->blue_offset = *(p++);
+    info->blue_length = *(p++);
+    info->alpha_offset = *(p++);
+    info->alpha_length = *(p++);
+
+    return 0;
+}
+
+void framebuffer_service(int fd, void *cookie)
+{
+    char x[512];
+
+    struct fbinfo fbinfo;
+    int w, h, f;
+    int s[2];
+    int fb;
+    unsigned int i;
+    int rv;
+
+    if(adb_socketpair(s)) {
+        printf("cannot create service socket pair\n");
+        goto done;
+    }
+
+    pid_t pid = fork();
+    if (pid < 0) {
+        printf("- fork failed: %s -\n", strerror(errno));
+        goto done;
+    }
+
+    if (pid == 0) {
+        dup2(s[1], STDOUT_FILENO);
+        const char* cmd_path = "/system/bin/screencap";
+        const char* cmd = "screencap";
+        execl(cmd_path, cmd, NULL);
+        exit(1);
+    }
+
+    fb = s[0];
+
+    /* read w, h, f */
+    if (readx(fb, &w, 4)) goto done;
+    if (readx(fb, &h, 4)) goto done;
+    if (readx(fb, &f, 4)) goto done;
+
+    fbinfo.version = DDMS_RAWIMAGE_VERSION;
+    fbinfo.size = w * h * 4;
+    fbinfo.width = w;
+    fbinfo.height = h;
+    rv = fill_format(&fbinfo, f);
+    if (rv != 0) goto done;
+
+    if(writex(fd, &fbinfo, sizeof(fbinfo))) goto done;
+
+    for(i = 0; i < fbinfo.size; i += sizeof(x)) {
+        if(readx(fb, &x, sizeof(x))) goto done;
+        if(writex(fd, &x, sizeof(x))) goto done;
+    }
+
+    if(readx(fb, &x, fbinfo.size % sizeof(x))) goto done;
+    if(writex(fd, &x, fbinfo.size % sizeof(x))) goto done;
+
+done:
+    adb_close(s[0]);
+    adb_close(s[1]);
+    adb_close(fd);
+}
+#else
+void framebuffer_service(int fd, void *cookie)
+{
+    struct fb_var_screeninfo vinfo;
+    int fb, offset;
+    char x[256];
+
+    struct fbinfo fbinfo;
+    unsigned i, bytespp;
+
+    fb = open("/dev/graphics/fb0", O_RDONLY);
+    if(fb < 0) goto done;
+
+    if(ioctl(fb, FBIOGET_VSCREENINFO, &vinfo) < 0) goto done;
+    fcntl(fb, F_SETFD, FD_CLOEXEC);
+
+    bytespp = vinfo.bits_per_pixel / 8;
+
+    fbinfo.version = DDMS_RAWIMAGE_VERSION;
+    fbinfo.bpp = vinfo.bits_per_pixel;
+    fbinfo.size = vinfo.xres * vinfo.yres * bytespp;
+    fbinfo.width = vinfo.xres;
+    fbinfo.height = vinfo.yres;
+    fbinfo.red_offset = vinfo.red.offset;
+    fbinfo.red_length = vinfo.red.length;
+    fbinfo.green_offset = vinfo.green.offset;
+    fbinfo.green_length = vinfo.green.length;
+    fbinfo.blue_offset = vinfo.blue.offset;
+    fbinfo.blue_length = vinfo.blue.length;
+    fbinfo.alpha_offset = vinfo.transp.offset;
+    fbinfo.alpha_length = vinfo.transp.length;
+
+    /* HACK: for several of our 3d cores a specific alignment
+     * is required so the start of the fb may not be an integer number of lines
+     * from the base.  As a result we are storing the additional offset in
+     * xoffset. This is not the correct usage for xoffset, it should be added
+     * to each line, not just once at the beginning */
+    offset = vinfo.xoffset * bytespp;
+
+    offset += vinfo.xres * vinfo.yoffset * bytespp;
+
+    if(writex(fd, &fbinfo, sizeof(fbinfo))) goto done;
+
+    lseek(fb, offset, SEEK_SET);
+    for(i = 0; i < fbinfo.size; i += 256) {
+      if(readx(fb, &x, 256)) goto done;
+      if(writex(fd, &x, 256)) goto done;
+    }
+
+    if(readx(fb, &x, fbinfo.size % 256)) goto done;
+    if(writex(fd, &x, fbinfo.size % 256)) goto done;
+
+done:
+    if(fb >= 0) close(fb);
+    close(fd);
+}
+#endif
diff --git a/adb/get_my_path_darwin.c b/adb/get_my_path_darwin.c
new file mode 100644 (file)
index 0000000..5b95d15
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+#import <Carbon/Carbon.h>
+#include <unistd.h>
+
+void get_my_path(char *s, size_t maxLen)
+{
+    ProcessSerialNumber psn;
+    GetCurrentProcess(&psn);
+    CFDictionaryRef dict;
+    dict = ProcessInformationCopyDictionary(&psn, 0xffffffff);
+    CFStringRef value = (CFStringRef)CFDictionaryGetValue(dict,
+                CFSTR("CFBundleExecutable"));
+    CFStringGetCString(value, s, maxLen, kCFStringEncodingUTF8);
+}
+
diff --git a/adb/get_my_path_freebsd.c b/adb/get_my_path_freebsd.c
new file mode 100644 (file)
index 0000000..b06ec66
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2009 bsdroid project
+ *               Alexey Tarasov <tarasov@dodologics.com>
+ *
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <sys/types.h>
+#include <unistd.h>
+#include <limits.h>
+#include <stdio.h>
+
+void
+get_my_path(char *exe, size_t maxLen)
+{
+    char proc[64];
+
+    snprintf(proc, sizeof(proc), "/proc/%d/file", getpid());
+
+    int err = readlink(proc, exe, maxLen - 1);
+
+    exe[err > 0 ? err : 0] = '\0';
+}
+
diff --git a/adb/get_my_path_linux.c b/adb/get_my_path_linux.c
new file mode 100644 (file)
index 0000000..179c3dd
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <sys/types.h>
+#include <unistd.h>
+#include <limits.h>
+#include <stdio.h>
+
+void get_my_path(char *exe, size_t maxLen)
+{
+    char proc[64];
+    snprintf(proc, sizeof proc, "/proc/%d/exe", getpid());
+    int err = readlink(proc, exe, maxLen - 1);
+    if(err > 0) {
+        exe[err] = '\0';
+    } else {
+        exe[0] = '\0';
+    }
+}
+
diff --git a/adb/get_my_path_windows.c b/adb/get_my_path_windows.c
new file mode 100644 (file)
index 0000000..ddf2816
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <limits.h>
+#include <assert.h>
+#include <windows.h>
+
+void get_my_path(char *exe, size_t maxLen)
+{
+    char  *r;
+
+    /* XXX: should be GetModuleFileNameA */
+    if (GetModuleFileName(NULL, exe, maxLen) > 0) {
+        r = strrchr(exe, '\\');
+        if (r != NULL)
+            *r = '\0';
+    } else {
+        exe[0] = '\0';
+    }
+}
+
diff --git a/adb/jdwp_service.c b/adb/jdwp_service.c
new file mode 100644 (file)
index 0000000..0c26f7b
--- /dev/null
@@ -0,0 +1,709 @@
+/* implement the "debug-ports" and "track-debug-ports" device services */
+#include "sysdeps.h"
+#define  TRACE_TAG   TRACE_JDWP
+#include "adb.h"
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+/* here's how these things work.
+
+   when adbd starts, it creates a unix server socket
+   named @vm-debug-control (@ is a shortcut for "first byte is zero"
+   to use the private namespace instead of the file system)
+
+   when a new JDWP daemon thread starts in a new VM process, it creates
+   a connection to @vm-debug-control to announce its availability.
+
+
+     JDWP thread                             @vm-debug-control
+         |                                         |
+         |------------------------------->         |
+         | hello I'm in process <pid>              |
+         |                                         |
+         |                                         |
+
+    the connection is kept alive. it will be closed automatically if
+    the JDWP process terminates (this allows adbd to detect dead
+    processes).
+
+    adbd thus maintains a list of "active" JDWP processes. it can send
+    its content to clients through the "device:debug-ports" service,
+    or even updates through the "device:track-debug-ports" service.
+
+    when a debugger wants to connect, it simply runs the command
+    equivalent to  "adb forward tcp:<hostport> jdwp:<pid>"
+
+    "jdwp:<pid>" is a new forward destination format used to target
+    a given JDWP process on the device. when sutch a request arrives,
+    adbd does the following:
+
+      - first, it calls socketpair() to create a pair of equivalent
+        sockets.
+
+      - it attaches the first socket in the pair to a local socket
+        which is itself attached to the transport's remote socket:
+
+
+      - it sends the file descriptor of the second socket directly
+        to the JDWP process with the help of sendmsg()
+
+
+     JDWP thread                             @vm-debug-control
+         |                                         |
+         |                  <----------------------|
+         |           OK, try this file descriptor  |
+         |                                         |
+         |                                         |
+
+   then, the JDWP thread uses this new socket descriptor as its
+   pass-through connection to the debugger (and receives the
+   JDWP-Handshake message, answers to it, etc...)
+
+   this gives the following graphics:
+                    ____________________________________
+                   |                                    |
+                   |          ADB Server (host)         |
+                   |                                    |
+        Debugger <---> LocalSocket <----> RemoteSocket  |
+                   |                           ^^       |
+                   |___________________________||_______|
+                                               ||
+                                     Transport ||
+           (TCP for emulator - USB for device) ||
+                                               ||
+                    ___________________________||_______
+                   |                           ||       |
+                   |          ADBD  (device)   ||       |
+                   |                           VV       |
+         JDWP <======> LocalSocket <----> RemoteSocket  |
+                   |                                    |
+                   |____________________________________|
+
+    due to the way adb works, this doesn't need a special socket
+    type or fancy handling of socket termination if either the debugger
+    or the JDWP process closes the connection.
+
+    THIS IS THE SIMPLEST IMPLEMENTATION I COULD FIND, IF YOU HAPPEN
+    TO HAVE A BETTER IDEA, LET ME KNOW - Digit
+
+**********************************************************************/
+
+/** JDWP PID List Support Code
+ ** for each JDWP process, we record its pid and its connected socket
+ **/
+
+#define  MAX_OUT_FDS   4
+
+#if !ADB_HOST
+
+#include <sys/socket.h>
+#include <sys/un.h>
+
+typedef struct JdwpProcess  JdwpProcess;
+struct JdwpProcess {
+    JdwpProcess*  next;
+    JdwpProcess*  prev;
+    int           pid;
+    int           socket;
+    fdevent*      fde;
+
+    char          in_buff[4];  /* input character to read PID */
+    int           in_len;      /* number from JDWP process    */
+
+    int           out_fds[MAX_OUT_FDS]; /* output array of file descriptors */
+    int           out_count;            /* to send to the JDWP process      */
+};
+
+static JdwpProcess  _jdwp_list;
+
+static int
+jdwp_process_list( char*  buffer, int  bufferlen )
+{
+    char*         end  = buffer + bufferlen;
+    char*         p    = buffer;
+    JdwpProcess*  proc = _jdwp_list.next;
+
+    for ( ; proc != &_jdwp_list; proc = proc->next ) {
+        int  len;
+
+        /* skip transient connections */
+        if (proc->pid < 0)
+            continue;
+
+        len = snprintf(p, end-p, "%d\n", proc->pid);
+        if (p + len >= end)
+            break;
+        p += len;
+    }
+    p[0] = 0;
+    return (p - buffer);
+}
+
+
+static int
+jdwp_process_list_msg( char*  buffer, int  bufferlen )
+{
+    char  head[5];
+    int   len = jdwp_process_list( buffer+4, bufferlen-4 );
+    snprintf(head, sizeof head, "%04x", len);
+    memcpy(buffer, head, 4);
+    return len + 4;
+}
+
+
+static void  jdwp_process_list_updated(void);
+
+static void
+jdwp_process_free( JdwpProcess*  proc )
+{
+    if (proc) {
+        int  n;
+
+        proc->prev->next = proc->next;
+        proc->next->prev = proc->prev;
+
+        if (proc->socket >= 0) {
+            adb_shutdown(proc->socket);
+            adb_close(proc->socket);
+            proc->socket = -1;
+        }
+
+        if (proc->fde != NULL) {
+            fdevent_destroy(proc->fde);
+            proc->fde = NULL;
+        }
+        proc->pid = -1;
+
+        for (n = 0; n < proc->out_count; n++) {
+            adb_close(proc->out_fds[n]);
+        }
+        proc->out_count = 0;
+
+        free(proc);
+
+        jdwp_process_list_updated();
+    }
+}
+
+
+static void  jdwp_process_event(int, unsigned, void*);  /* forward */
+
+
+static JdwpProcess*
+jdwp_process_alloc( int  socket )
+{
+    JdwpProcess*  proc = calloc(1,sizeof(*proc));
+
+    if (proc == NULL) {
+        D("not enough memory to create new JDWP process\n");
+        return NULL;
+    }
+
+    proc->socket = socket;
+    proc->pid    = -1;
+    proc->next   = proc;
+    proc->prev   = proc;
+
+    proc->fde = fdevent_create( socket, jdwp_process_event, proc );
+    if (proc->fde == NULL) {
+        D("could not create fdevent for new JDWP process\n" );
+        free(proc);
+        return NULL;
+    }
+
+    proc->fde->state |= FDE_DONT_CLOSE;
+    proc->in_len      = 0;
+    proc->out_count   = 0;
+
+    /* append to list */
+    proc->next = &_jdwp_list;
+    proc->prev = proc->next->prev;
+
+    proc->prev->next = proc;
+    proc->next->prev = proc;
+
+    /* start by waiting for the PID */
+    fdevent_add(proc->fde, FDE_READ);
+
+    return proc;
+}
+
+
+static void
+jdwp_process_event( int  socket, unsigned  events, void*  _proc )
+{
+    JdwpProcess*  proc = _proc;
+
+    if (events & FDE_READ) {
+        if (proc->pid < 0) {
+            /* read the PID as a 4-hexchar string */
+            char*  p    = proc->in_buff + proc->in_len;
+            int    size = 4 - proc->in_len;
+            char   temp[5];
+            while (size > 0) {
+                int  len = recv( socket, p, size, 0 );
+                if (len < 0) {
+                    if (errno == EINTR)
+                        continue;
+                    if (errno == EAGAIN)
+                        return;
+                    /* this can fail here if the JDWP process crashes very fast */
+                    D("weird unknown JDWP process failure: %s\n",
+                      strerror(errno));
+
+                    goto CloseProcess;
+                }
+                if (len == 0) {  /* end of stream ? */
+                    D("weird end-of-stream from unknown JDWP process\n");
+                    goto CloseProcess;
+                }
+                p            += len;
+                proc->in_len += len;
+                size         -= len;
+            }
+            /* we have read 4 characters, now decode the pid */
+            memcpy(temp, proc->in_buff, 4);
+            temp[4] = 0;
+
+            if (sscanf( temp, "%04x", &proc->pid ) != 1) {
+                D("could not decode JDWP %p PID number: '%s'\n", proc, temp);
+                goto CloseProcess;
+            }
+
+            /* all is well, keep reading to detect connection closure */
+            D("Adding pid %d to jdwp process list\n", proc->pid);
+            jdwp_process_list_updated();
+        }
+        else
+        {
+            /* the pid was read, if we get there it's probably because the connection
+             * was closed (e.g. the JDWP process exited or crashed) */
+            char  buf[32];
+
+            for (;;) {
+                int  len = recv(socket, buf, sizeof(buf), 0);
+
+                if (len <= 0) {
+                    if (len < 0 && errno == EINTR)
+                        continue;
+                    if (len < 0 && errno == EAGAIN)
+                        return;
+                    else {
+                        D("terminating JDWP %d connection: %s\n", proc->pid,
+                          strerror(errno));
+                        break;
+                    }
+                }
+                else {
+                    D( "ignoring unexpected JDWP %d control socket activity (%d bytes)\n",
+                       proc->pid, len );
+                }
+            }
+
+        CloseProcess:
+            if (proc->pid >= 0)
+                D( "remove pid %d to jdwp process list\n", proc->pid );
+            jdwp_process_free(proc);
+            return;
+        }
+    }
+
+    if (events & FDE_WRITE) {
+        D("trying to write to JDWP pid controli (count=%d first=%d) %d\n",
+          proc->pid, proc->out_count, proc->out_fds[0]);
+        if (proc->out_count > 0) {
+            int  fd = proc->out_fds[0];
+            int  n, ret;
+            struct cmsghdr*  cmsg;
+            struct msghdr    msg;
+            struct iovec     iov;
+            char             dummy = '!';
+            char             buffer[sizeof(struct cmsghdr) + sizeof(int)];
+
+            iov.iov_base       = &dummy;
+            iov.iov_len        = 1;
+            msg.msg_name       = NULL;
+            msg.msg_namelen    = 0;
+            msg.msg_iov        = &iov;
+            msg.msg_iovlen     = 1;
+            msg.msg_flags      = 0;
+            msg.msg_control    = buffer;
+            msg.msg_controllen = sizeof(buffer);
+
+            cmsg = CMSG_FIRSTHDR(&msg);
+            cmsg->cmsg_len   = msg.msg_controllen;
+            cmsg->cmsg_level = SOL_SOCKET;
+            cmsg->cmsg_type  = SCM_RIGHTS;
+            ((int*)CMSG_DATA(cmsg))[0] = fd;
+
+            for (;;) {
+                ret = sendmsg(proc->socket, &msg, 0);
+                if (ret >= 0)
+                    break;
+                if (errno == EINTR)
+                    continue;
+                D("sending new file descriptor to JDWP %d failed: %s\n",
+                  proc->pid, strerror(errno));
+                goto CloseProcess;
+            }
+
+            D("sent file descriptor %d to JDWP process %d\n",
+              fd, proc->pid);
+
+            for (n = 1; n < proc->out_count; n++)
+                proc->out_fds[n-1] = proc->out_fds[n];
+
+            if (--proc->out_count == 0)
+                fdevent_del( proc->fde, FDE_WRITE );
+        }
+    }
+}
+
+
+int
+create_jdwp_connection_fd(int  pid)
+{
+    JdwpProcess*  proc = _jdwp_list.next;
+
+    D("looking for pid %d in JDWP process list\n", pid);
+    for ( ; proc != &_jdwp_list; proc = proc->next ) {
+        if (proc->pid == pid) {
+            goto FoundIt;
+        }
+    }
+    D("search failed !!\n");
+    return -1;
+
+FoundIt:
+    {
+        int  fds[2];
+
+        if (proc->out_count >= MAX_OUT_FDS) {
+            D("%s: too many pending JDWP connection for pid %d\n",
+              __FUNCTION__, pid);
+            return -1;
+        }
+
+        if (adb_socketpair(fds) < 0) {
+            D("%s: socket pair creation failed: %s\n",
+              __FUNCTION__, strerror(errno));
+            return -1;
+        }
+
+        proc->out_fds[ proc->out_count ] = fds[1];
+        if (++proc->out_count == 1)
+            fdevent_add( proc->fde, FDE_WRITE );
+
+        return fds[0];
+    }
+}
+
+/**  VM DEBUG CONTROL SOCKET
+ **
+ **  we do implement a custom asocket to receive the data
+ **/
+
+/* name of the debug control Unix socket */
+#define  JDWP_CONTROL_NAME      "\0jdwp-control"
+#define  JDWP_CONTROL_NAME_LEN  (sizeof(JDWP_CONTROL_NAME)-1)
+
+typedef struct {
+    int       listen_socket;
+    fdevent*  fde;
+
+} JdwpControl;
+
+
+static void
+jdwp_control_event(int  s, unsigned events, void*  user);
+
+
+static int
+jdwp_control_init( JdwpControl*  control,
+                   const char*   sockname,
+                   int           socknamelen )
+{
+    struct sockaddr_un   addr;
+    socklen_t            addrlen;
+    int                  s;
+    int                  maxpath = sizeof(addr.sun_path);
+    int                  pathlen = socknamelen;
+
+    if (pathlen >= maxpath) {
+        D( "vm debug control socket name too long (%d extra chars)\n",
+           pathlen+1-maxpath );
+        return -1;
+    }
+
+    memset(&addr, 0, sizeof(addr));
+    addr.sun_family = AF_UNIX;
+    memcpy(addr.sun_path, sockname, socknamelen);
+
+    s = socket( AF_UNIX, SOCK_STREAM, 0 );
+    if (s < 0) {
+        D( "could not create vm debug control socket. %d: %s\n",
+           errno, strerror(errno));
+        return -1;
+    }
+
+    addrlen = (pathlen + sizeof(addr.sun_family));
+
+    if (bind(s, (struct sockaddr*)&addr, addrlen) < 0) {
+        D( "could not bind vm debug control socket: %d: %s\n",
+           errno, strerror(errno) );
+        adb_close(s);
+        return -1;
+    }
+
+    if ( listen(s, 4) < 0 ) {
+        D("listen failed in jdwp control socket: %d: %s\n",
+          errno, strerror(errno));
+        adb_close(s);
+        return -1;
+    }
+
+    control->listen_socket = s;
+
+    control->fde = fdevent_create(s, jdwp_control_event, control);
+    if (control->fde == NULL) {
+        D( "could not create fdevent for jdwp control socket\n" );
+        adb_close(s);
+        return -1;
+    }
+
+    /* only wait for incoming connections */
+    fdevent_add(control->fde, FDE_READ);
+
+    D("jdwp control socket started (%d)\n", control->listen_socket);
+    return 0;
+}
+
+
+static void
+jdwp_control_event( int  s, unsigned  events, void*  _control )
+{
+    JdwpControl*  control = (JdwpControl*) _control;
+
+    if (events & FDE_READ) {
+        struct sockaddr   addr;
+        socklen_t         addrlen = sizeof(addr);
+        int               s = -1;
+        JdwpProcess*      proc;
+
+        do {
+            s = adb_socket_accept( control->listen_socket, &addr, &addrlen );
+            if (s < 0) {
+                if (errno == EINTR)
+                    continue;
+                if (errno == ECONNABORTED) {
+                    /* oops, the JDWP process died really quick */
+                    D("oops, the JDWP process died really quick\n");
+                    return;
+                }
+                /* the socket is probably closed ? */
+                D( "weird accept() failed on jdwp control socket: %s\n",
+                   strerror(errno) );
+                return;
+            }
+        }
+        while (s < 0);
+
+        proc = jdwp_process_alloc( s );
+        if (proc == NULL)
+            return;
+    }
+}
+
+
+static JdwpControl   _jdwp_control;
+
+/** "jdwp" local service implementation
+ ** this simply returns the list of known JDWP process pids
+ **/
+
+typedef struct {
+    asocket  socket;
+    int      pass;
+} JdwpSocket;
+
+static void
+jdwp_socket_close( asocket*  s )
+{
+    asocket*  peer = s->peer;
+
+    remove_socket(s);
+
+    if (peer) {
+        peer->peer = NULL;
+        peer->close(peer);
+    }
+    free(s);
+}
+
+static int
+jdwp_socket_enqueue( asocket*  s, apacket*  p )
+{
+    /* you can't write to this asocket */
+    put_apacket(p);
+    s->peer->close(s->peer);
+    return -1;
+}
+
+
+static void
+jdwp_socket_ready( asocket*  s )
+{
+    JdwpSocket*  jdwp = (JdwpSocket*)s;
+    asocket*     peer = jdwp->socket.peer;
+
+   /* on the first call, send the list of pids,
+    * on the second one, close the connection
+    */
+    if (jdwp->pass == 0) {
+        apacket*  p = get_apacket();
+        p->len = jdwp_process_list((char*)p->data, MAX_PAYLOAD);
+        peer->enqueue(peer, p);
+        jdwp->pass = 1;
+    }
+    else {
+        peer->close(peer);
+    }
+}
+
+asocket*
+create_jdwp_service_socket( void )
+{
+    JdwpSocket*  s = calloc(sizeof(*s),1);
+
+    if (s == NULL)
+        return NULL;
+
+    install_local_socket(&s->socket);
+
+    s->socket.ready   = jdwp_socket_ready;
+    s->socket.enqueue = jdwp_socket_enqueue;
+    s->socket.close   = jdwp_socket_close;
+    s->pass           = 0;
+
+    return &s->socket;
+}
+
+/** "track-jdwp" local service implementation
+ ** this periodically sends the list of known JDWP process pids
+ ** to the client...
+ **/
+
+typedef struct JdwpTracker  JdwpTracker;
+
+struct JdwpTracker {
+    asocket       socket;
+    JdwpTracker*  next;
+    JdwpTracker*  prev;
+    int           need_update;
+};
+
+static JdwpTracker   _jdwp_trackers_list;
+
+
+static void
+jdwp_process_list_updated(void)
+{
+    char             buffer[1024];
+    int              len;
+    JdwpTracker*  t = _jdwp_trackers_list.next;
+
+    len = jdwp_process_list_msg(buffer, sizeof(buffer));
+
+    for ( ; t != &_jdwp_trackers_list; t = t->next ) {
+        apacket*  p    = get_apacket();
+        asocket*  peer = t->socket.peer;
+        memcpy(p->data, buffer, len);
+        p->len = len;
+        peer->enqueue( peer, p );
+    }
+}
+
+static void
+jdwp_tracker_close( asocket*  s )
+{
+    JdwpTracker*  tracker = (JdwpTracker*) s;
+    asocket*      peer    = s->peer;
+
+    if (peer) {
+        peer->peer = NULL;
+        peer->close(peer);
+    }
+
+    remove_socket(s);
+
+    tracker->prev->next = tracker->next;
+    tracker->next->prev = tracker->prev;
+
+    free(s);
+}
+
+static void
+jdwp_tracker_ready( asocket*  s )
+{
+    JdwpTracker*  t = (JdwpTracker*) s;
+
+    if (t->need_update) {
+        apacket*  p = get_apacket();
+        t->need_update = 0;
+        p->len = jdwp_process_list_msg((char*)p->data, sizeof(p->data));
+        s->peer->enqueue(s->peer, p);
+    }
+}
+
+static int
+jdwp_tracker_enqueue( asocket*  s, apacket*  p )
+{
+    /* you can't write to this socket */
+    put_apacket(p);
+    s->peer->close(s->peer);
+    return -1;
+}
+
+
+asocket*
+create_jdwp_tracker_service_socket( void )
+{
+    JdwpTracker*  t = calloc(sizeof(*t),1);
+
+    if (t == NULL)
+        return NULL;
+
+    t->next = &_jdwp_trackers_list;
+    t->prev = t->next->prev;
+
+    t->next->prev = t;
+    t->prev->next = t;
+
+    install_local_socket(&t->socket);
+
+    t->socket.ready   = jdwp_tracker_ready;
+    t->socket.enqueue = jdwp_tracker_enqueue;
+    t->socket.close   = jdwp_tracker_close;
+    t->need_update    = 1;
+
+    return &t->socket;
+}
+
+
+int
+init_jdwp(void)
+{
+    _jdwp_list.next = &_jdwp_list;
+    _jdwp_list.prev = &_jdwp_list;
+
+    _jdwp_trackers_list.next = &_jdwp_trackers_list;
+    _jdwp_trackers_list.prev = &_jdwp_trackers_list;
+
+    return jdwp_control_init( &_jdwp_control,
+                              JDWP_CONTROL_NAME,
+                              JDWP_CONTROL_NAME_LEN );
+}
+
+#endif /* !ADB_HOST */
+
diff --git a/adb/log_service.c b/adb/log_service.c
new file mode 100644 (file)
index 0000000..6e9bdee
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include <cutils/logger.h>
+#include "sysdeps.h"
+#include "adb.h"
+
+#define LOG_FILE_DIR    "/dev/log/"
+
+void write_log_entry(int fd, struct logger_entry *buf);
+
+void log_service(int fd, void *cookie)
+{
+    /* get the name of the log filepath to read */
+    char * log_filepath = cookie;
+
+    /* open the log file. */
+    int logfd = unix_open(log_filepath, O_RDONLY);
+    if (logfd < 0) {
+        goto done;
+    }
+
+    // temp buffer to read the entries
+    unsigned char buf[LOGGER_ENTRY_MAX_LEN + 1] __attribute__((aligned(4)));
+    struct logger_entry *entry = (struct logger_entry *) buf;
+
+    while (1) {
+        int ret;
+
+        ret = unix_read(logfd, entry, LOGGER_ENTRY_MAX_LEN);
+        if (ret < 0) {
+            if (errno == EINTR || errno == EAGAIN)
+                continue;
+            // perror("logcat read");
+            goto done;
+        }
+        else if (!ret) {
+            // fprintf(stderr, "read: Unexpected EOF!\n");
+            goto done;
+        }
+
+        /* NOTE: driver guarantees we read exactly one full entry */
+
+        entry->msg[entry->len] = '\0';
+
+        write_log_entry(fd, entry);
+    }
+
+done:
+    unix_close(fd);
+    free(log_filepath);
+}
+
+/* returns the full path to the log file in a newly allocated string */
+char * get_log_file_path(const char * log_name) {
+    char *log_device = malloc(strlen(LOG_FILE_DIR) + strlen(log_name) + 1);
+
+    strcpy(log_device, LOG_FILE_DIR);
+    strcat(log_device, log_name);
+
+    return log_device;
+}
+
+
+/* prints one log entry into the file descriptor fd */
+void write_log_entry(int fd, struct logger_entry *buf)
+{
+    size_t size = sizeof(struct logger_entry) + buf->len;
+
+    writex(fd, buf, size);
+}
diff --git a/adb/mutex_list.h b/adb/mutex_list.h
new file mode 100644 (file)
index 0000000..eebe0df
--- /dev/null
@@ -0,0 +1,14 @@
+/* the list of mutexes used by addb */
+#ifndef ADB_MUTEX
+#error ADB_MUTEX not defined when including this file
+#endif
+
+ADB_MUTEX(dns_lock)
+ADB_MUTEX(socket_list_lock)
+ADB_MUTEX(transport_lock)
+#if ADB_HOST
+ADB_MUTEX(local_transports_lock)
+#endif
+ADB_MUTEX(usb_lock)
+
+#undef ADB_MUTEX
diff --git a/adb/protocol.txt b/adb/protocol.txt
new file mode 100644 (file)
index 0000000..398d042
--- /dev/null
@@ -0,0 +1,252 @@
+
+--- a replacement for aproto -------------------------------------------
+
+When it comes down to it, aproto's primary purpose is to forward
+various streams between the host computer and client device (in either
+direction).
+
+This replacement further simplifies the concept, reducing the protocol
+to an extremely straightforward model optimized to accomplish the
+forwarding of these streams and removing additional state or
+complexity.
+
+The host side becomes a simple comms bridge with no "UI", which will 
+be used by either commandline or interactive tools to communicate with 
+a device or emulator that is connected to the bridge.
+
+The protocol is designed to be straightforward and well-defined enough 
+that if it needs to be reimplemented in another environment (Java 
+perhaps), there should not problems ensuring perfect interoperability.
+
+The protocol discards the layering aproto has and should allow the 
+implementation to be much more robust.
+
+
+--- protocol overview and basics ---------------------------------------
+
+The transport layer deals in "messages", which consist of a 24 byte
+header followed (optionally) by a payload.  The header consists of 6
+32 bit words which are sent across the wire in little endian format.
+
+struct message {
+    unsigned command;       /* command identifier constant      */
+    unsigned arg0;          /* first argument                   */
+    unsigned arg1;          /* second argument                  */
+    unsigned data_length;   /* length of payload (0 is allowed) */
+    unsigned data_crc32;    /* crc32 of data payload            */
+    unsigned magic;         /* command ^ 0xffffffff             */
+};
+
+Receipt of an invalid message header, corrupt message payload, or an
+unrecognized command MUST result in the closing of the remote
+connection.  The protocol depends on shared state and any break in the
+message stream will result in state getting out of sync.
+
+The following sections describe the six defined message types in
+detail.  Their format is COMMAND(arg0, arg1, payload) where the payload
+is represented by a quoted string or an empty string if none should be
+sent.
+
+The identifiers "local-id" and "remote-id" are always relative to the
+*sender* of the message, so for a receiver, the meanings are effectively
+reversed.
+
+
+
+--- CONNECT(version, maxdata, "system-identity-string") ----------------
+
+The CONNECT message establishes the presence of a remote system.
+The version is used to ensure protocol compatibility and maxdata
+declares the maximum message body size that the remote system
+is willing to accept.
+
+Currently, version=0x01000000 and maxdata=4096
+
+Both sides send a CONNECT message when the connection between them is
+established.  Until a CONNECT message is received no other messages may
+be sent.  Any messages received before a CONNECT message MUST be ignored.
+
+If a CONNECT message is received with an unknown version or insufficiently
+large maxdata value, the connection with the other side must be closed.
+
+The system identity string should be "<systemtype>:<serialno>:<banner>"
+where systemtype is "bootloader", "device", or "host", serialno is some
+kind of unique ID (or empty), and banner is a human-readable version
+or identifier string (informational only).
+
+
+--- OPEN(local-id, 0, "destination") -----------------------------------
+
+The OPEN message informs the recipient that the sender has a stream
+identified by local-id that it wishes to connect to the named
+destination in the message payload.  The local-id may not be zero.
+
+The OPEN message MUST result in either a READY message indicating that
+the connection has been established (and identifying the other end) or
+a CLOSE message, indicating failure.  An OPEN message also implies
+a READY message sent at the same time.
+
+Common destination naming conventions include:
+
+* "tcp:<host>:<port>" - host may be omitted to indicate localhost
+* "udp:<host>:<port>" - host may be omitted to indicate localhost
+* "local-dgram:<identifier>"
+* "local-stream:<identifier>"
+* "shell" - local shell service
+* "upload" - service for pushing files across (like aproto's /sync)
+* "fs-bridge" - FUSE protocol filesystem bridge
+
+
+--- READY(local-id, remote-id, "") -------------------------------------
+
+The READY message informs the recipient that the sender's stream
+identified by local-id is ready for write messages and that it is
+connected to the recipient's stream identified by remote-id.
+
+Neither the local-id nor the remote-id may be zero. 
+
+A READY message containing a remote-id which does not map to an open
+stream on the recipient's side is ignored.  The stream may have been
+closed while this message was in-flight.
+
+The local-id is ignored on all but the first READY message (where it
+is used to establish the connection).  Nonetheless, the local-id MUST
+not change on later READY messages sent to the same stream.
+
+
+
+--- WRITE(0, remote-id, "data") ----------------------------------------
+
+The WRITE message sends data to the recipient's stream identified by
+remote-id.  The payload MUST be <= maxdata in length.
+
+A WRITE message containing a remote-id which does not map to an open
+stream on the recipient's side is ignored.  The stream may have been
+closed while this message was in-flight.
+
+A WRITE message may not be sent until a READY message is received.
+Once a WRITE message is sent, an additional WRITE message may not be
+sent until another READY message has been received.  Recipients of
+a WRITE message that is in violation of this requirement will CLOSE
+the connection.
+
+
+--- CLOSE(local-id, remote-id, "") -------------------------------------
+
+The CLOSE message informs recipient that the connection between the
+sender's stream (local-id) and the recipient's stream (remote-id) is
+broken.  The remote-id MUST not be zero, but the local-id MAY be zero
+if this CLOSE indicates a failed OPEN.
+
+A CLOSE message containing a remote-id which does not map to an open
+stream on the recipient's side is ignored.  The stream may have
+already been closed by the recipient while this message was in-flight.
+
+The recipient should not respond to a CLOSE message in any way.  The
+recipient should cancel pending WRITEs or CLOSEs, but this is not a
+requirement, since they will be ignored.
+
+
+--- SYNC(online, sequence, "") -----------------------------------------
+
+The SYNC message is used by the io pump to make sure that stale
+outbound messages are discarded when the connection to the remote side
+is broken.  It is only used internally to the bridge and never valid
+to send across the wire.  
+
+* when the connection to the remote side goes offline, the io pump 
+  sends a SYNC(0, 0) and starts discarding all messages
+* when the connection to the remote side is established, the io pump
+  sends a SYNC(1, token) and continues to discard messages
+* when the io pump receives a matching SYNC(1, token), it once again
+  starts accepting messages to forward to the remote side
+
+
+--- message command constants ------------------------------------------
+
+#define A_SYNC 0x434e5953
+#define A_CNXN 0x4e584e43
+#define A_OPEN 0x4e45504f
+#define A_OKAY 0x59414b4f
+#define A_CLSE 0x45534c43
+#define A_WRTE 0x45545257
+
+
+
+--- implementation details ---------------------------------------------
+
+The core of the bridge program will use three threads.  One thread
+will be a select/epoll loop to handle io between various inbound and
+outbound connections and the connection to the remote side.
+
+The remote side connection will be implemented as two threads (one for
+reading, one for writing) and a datagram socketpair to provide the
+channel between the main select/epoll thread and the remote connection
+threadpair.  The reason for this is that for usb connections, the
+kernel interface on linux and osx does not allow you to do meaningful
+nonblocking IO.
+
+The endian swapping for the message headers will happen (as needed) in
+the remote connection threadpair and that the rest of the program will
+always treat message header values as native-endian.
+
+The bridge program will be able to have a number of mini-servers
+compiled in.  They will be published under known names (examples
+"shell", "fs-bridge", etc) and upon receiving an OPEN() to such a
+service, the bridge program will create a stream socketpair and spawn
+a thread or subprocess to handle the io.
+
+
+--- simplified / embedded implementation -------------------------------
+
+For limited environments, like the bootloader, it is allowable to
+support a smaller, fixed number of channels using pre-assigned channel
+ID numbers such that only one stream may be connected to a bootloader
+endpoint at any given time.  The protocol remains unchanged, but the
+"embedded" version of it is less dynamic.
+
+The bootloader will support two streams.  A "bootloader:debug" stream,
+which may be opened to get debug messages from the bootloader and a 
+"bootloader:control", stream which will support the set of basic 
+bootloader commands.
+
+Example command stream dialogues:  
+  "flash_kernel,2515049,........\n" "okay\n" 
+  "flash_ramdisk,5038,........\n" "fail,flash write error\n" 
+  "bogus_command......" <CLOSE>
+
+
+--- future expansion ---------------------------------------------------
+
+I plan on providing either a message or a special control stream so that
+the client device could ask the host computer to setup inbound socket
+translations on the fly on behalf of the client device.
+
+
+The initial design does handshaking to provide flow control, with a
+message flow that looks like:
+
+  >OPEN <READY >WRITE <READY >WRITE <READY >WRITE <CLOSE
+
+The far side may choose to issue the READY message as soon as it receives
+a WRITE or it may defer the READY until the write to the local stream
+succeeds.  A future version may want to do some level of windowing where
+multiple WRITEs may be sent without requiring individual READY acks.
+
+------------------------------------------------------------------------
+
+--- smartsockets -------------------------------------------------------
+
+Port 5037 is used for smart sockets which allow a client on the host
+side to request access to a service in the host adb daemon or in the
+remote (device) daemon.  The service is requested by ascii name,
+preceeded by a 4 digit hex length.  Upon successful connection an
+"OKAY" response is sent, otherwise a "FAIL" message is returned.  Once
+connected the client is talking to that (remote or local) service.
+
+client: <hex4> <service-name>
+server: "OKAY"
+
+client: <hex4> <service-name>
+server: "FAIL" <hex4> <reason>
+
diff --git a/adb/remount_service.c b/adb/remount_service.c
new file mode 100644 (file)
index 0000000..4cb41e7
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/mount.h>
+#include <errno.h>
+
+#include "sysdeps.h"
+
+#define  TRACE_TAG  TRACE_ADB
+#include "adb.h"
+
+
+static int system_ro = 1;
+
+/* Returns the device used to mount a directory in /proc/mounts */
+static char *find_mount(const char *dir)
+{
+    int fd;
+    int res;
+    int size;
+    char *token = NULL;
+    const char delims[] = "\n";
+    char buf[4096];
+
+    fd = unix_open("/proc/mounts", O_RDONLY);
+    if (fd < 0)
+        return NULL;
+
+    buf[sizeof(buf) - 1] = '\0';
+    size = adb_read(fd, buf, sizeof(buf) - 1);
+    adb_close(fd);
+
+    token = strtok(buf, delims);
+
+    while (token) {
+        char mount_dev[256];
+        char mount_dir[256];
+        int mount_freq;
+        int mount_passno;
+
+        res = sscanf(token, "%255s %255s %*s %*s %d %d\n",
+                     mount_dev, mount_dir, &mount_freq, &mount_passno);
+        mount_dev[255] = 0;
+        mount_dir[255] = 0;
+        if (res == 4 && (strcmp(dir, mount_dir) == 0))
+            return strdup(mount_dev);
+
+        token = strtok(NULL, delims);
+    }
+    return NULL;
+}
+
+/* Init mounts /system as read only, remount to enable writes. */
+static int remount_system()
+{
+    char *dev;
+
+    if (system_ro == 0) {
+        return 0;
+    }
+
+    dev = find_mount("/system");
+
+    if (!dev)
+        return -1;
+
+    system_ro = mount(dev, "/system", "none", MS_REMOUNT, NULL);
+
+    free(dev);
+
+    return system_ro;
+}
+
+static void write_string(int fd, const char* str)
+{
+    writex(fd, str, strlen(str));
+}
+
+void remount_service(int fd, void *cookie)
+{
+    int ret = remount_system();
+
+    if (!ret)
+       write_string(fd, "remount succeeded\n");
+    else {
+        char    buffer[200];
+        snprintf(buffer, sizeof(buffer), "remount failed: %s\n", strerror(errno));
+        write_string(fd, buffer);
+    }
+
+    adb_close(fd);
+}
+
diff --git a/adb/services.c b/adb/services.c
new file mode 100644 (file)
index 0000000..a4edb44
--- /dev/null
@@ -0,0 +1,513 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+
+#include "sysdeps.h"
+
+#define  TRACE_TAG  TRACE_ADB
+#include "adb.h"
+#include "file_sync_service.h"
+
+#if ADB_HOST
+#  ifndef HAVE_WINSOCK
+#    include <netinet/in.h>
+#    include <netdb.h>
+#  endif
+#else
+#  include <sys/reboot.h>
+#endif
+
+typedef struct stinfo stinfo;
+
+struct stinfo {
+    void (*func)(int fd, void *cookie);
+    int fd;
+    void *cookie;
+};
+
+
+void *service_bootstrap_func(void *x)
+{
+    stinfo *sti = x;
+    sti->func(sti->fd, sti->cookie);
+    free(sti);
+    return 0;
+}
+
+#if ADB_HOST
+ADB_MUTEX_DEFINE( dns_lock );
+
+static void dns_service(int fd, void *cookie)
+{
+    char *hostname = cookie;
+    struct hostent *hp;
+    unsigned zero = 0;
+
+    adb_mutex_lock(&dns_lock);
+    hp = gethostbyname(hostname);
+    free(cookie);
+    if(hp == 0) {
+        writex(fd, &zero, 4);
+    } else {
+        writex(fd, hp->h_addr, 4);
+    }
+    adb_mutex_unlock(&dns_lock);
+    adb_close(fd);
+}
+#else
+extern int recovery_mode;
+
+static void recover_service(int s, void *cookie)
+{
+    unsigned char buf[4096];
+    unsigned count = (unsigned) cookie;
+    int fd;
+
+    fd = adb_creat("/tmp/update", 0644);
+    if(fd < 0) {
+        adb_close(s);
+        return;
+    }
+
+    while(count > 0) {
+        unsigned xfer = (count > 4096) ? 4096 : count;
+        if(readx(s, buf, xfer)) break;
+        if(writex(fd, buf, xfer)) break;
+        count -= xfer;
+    }
+
+    if(count == 0) {
+        writex(s, "OKAY", 4);
+    } else {
+        writex(s, "FAIL", 4);
+    }
+    adb_close(fd);
+    adb_close(s);
+
+    fd = adb_creat("/tmp/update.begin", 0644);
+    adb_close(fd);
+}
+
+void restart_root_service(int fd, void *cookie)
+{
+    char buf[100];
+    char value[PROPERTY_VALUE_MAX];
+
+    if (getuid() == 0) {
+        snprintf(buf, sizeof(buf), "adbd is already running as root\n");
+        writex(fd, buf, strlen(buf));
+        adb_close(fd);
+    } else {
+        property_get("ro.debuggable", value, "");
+        if (strcmp(value, "1") != 0) {
+            snprintf(buf, sizeof(buf), "adbd cannot run as root in production builds\n");
+            writex(fd, buf, strlen(buf));
+            adb_close(fd);
+            return;
+        }
+
+        property_set("service.adb.root", "1");
+        snprintf(buf, sizeof(buf), "restarting adbd as root\n");
+        writex(fd, buf, strlen(buf));
+        adb_close(fd);
+
+        // quit, and init will restart us as root
+        sleep(1);
+        exit(1);
+    }
+}
+
+void restart_tcp_service(int fd, void *cookie)
+{
+    char buf[100];
+    char value[PROPERTY_VALUE_MAX];
+    int port = (int)cookie;
+
+    if (port <= 0) {
+        snprintf(buf, sizeof(buf), "invalid port\n");
+        writex(fd, buf, strlen(buf));
+        adb_close(fd);
+        return;
+    }
+
+    snprintf(value, sizeof(value), "%d", port);
+    property_set("service.adb.tcp.port", value);
+    snprintf(buf, sizeof(buf), "restarting in TCP mode port: %d\n", port);
+    writex(fd, buf, strlen(buf));
+    adb_close(fd);
+
+    // quit, and init will restart us in TCP mode
+    sleep(1);
+    exit(1);
+}
+
+void restart_usb_service(int fd, void *cookie)
+{
+    char buf[100];
+
+    property_set("service.adb.tcp.port", "0");
+    snprintf(buf, sizeof(buf), "restarting in USB mode\n");
+    writex(fd, buf, strlen(buf));
+    adb_close(fd);
+
+    // quit, and init will restart us in USB mode
+    sleep(1);
+    exit(1);
+}
+
+void reboot_service(int fd, void *arg)
+{
+    char buf[100];
+    int pid, ret;
+
+#ifdef RECOVERY_PRE_COMMAND
+       if (!strncmp((char *)arg,"recovery",8))
+               system( RECOVERY_PRE_COMMAND );
+#endif
+
+    sync();
+
+    /* Attempt to unmount the SD card first.
+     * No need to bother checking for errors.
+     */
+    pid = fork();
+    if (pid == 0) {
+        /* ask vdc to unmount it */
+        execl("/system/bin/vdc", "/system/bin/vdc", "volume", "unmount",
+                getenv("EXTERNAL_STORAGE"), "force", NULL);
+    } else if (pid > 0) {
+        /* wait until vdc succeeds or fails */
+        waitpid(pid, &ret, 0);
+    }
+    ret = __reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
+                   LINUX_REBOOT_CMD_RESTART2, (char *)arg);
+    if (ret < 0) {
+        snprintf(buf, sizeof(buf), "reboot failed: %s\n", strerror(errno));
+        writex(fd, buf, strlen(buf));
+    }
+    free(arg);
+    adb_close(fd);
+}
+
+#endif
+
+#if 0
+static void echo_service(int fd, void *cookie)
+{
+    char buf[4096];
+    int r;
+    char *p;
+    int c;
+
+    for(;;) {
+        r = read(fd, buf, 4096);
+        if(r == 0) goto done;
+        if(r < 0) {
+            if(errno == EINTR) continue;
+            else goto done;
+        }
+
+        c = r;
+        p = buf;
+        while(c > 0) {
+            r = write(fd, p, c);
+            if(r > 0) {
+                c -= r;
+                p += r;
+                continue;
+            }
+            if((r < 0) && (errno == EINTR)) continue;
+            goto done;
+        }
+    }
+done:
+    close(fd);
+}
+#endif
+
+static int create_service_thread(void (*func)(int, void *), void *cookie)
+{
+    stinfo *sti;
+    adb_thread_t t;
+    int s[2];
+
+    if(adb_socketpair(s)) {
+        printf("cannot create service socket pair\n");
+        return -1;
+    }
+
+    sti = malloc(sizeof(stinfo));
+    if(sti == 0) fatal("cannot allocate stinfo");
+    sti->func = func;
+    sti->cookie = cookie;
+    sti->fd = s[1];
+
+    if(adb_thread_create( &t, service_bootstrap_func, sti)){
+        free(sti);
+        adb_close(s[0]);
+        adb_close(s[1]);
+        printf("cannot create service thread\n");
+        return -1;
+    }
+
+    D("service thread started, %d:%d\n",s[0], s[1]);
+    return s[0];
+}
+
+static int create_subprocess(const char *cmd, const char *arg0, const char *arg1)
+{
+#ifdef HAVE_WIN32_PROC
+       fprintf(stderr, "error: create_subprocess not implemented on Win32 (%s %s %s)\n", cmd, arg0, arg1);
+       return -1;
+#else /* !HAVE_WIN32_PROC */
+    char *devname;
+    int ptm;
+    pid_t pid;
+
+    ptm = unix_open("/dev/ptmx", O_RDWR); // | O_NOCTTY);
+    if(ptm < 0){
+        printf("[ cannot open /dev/ptmx - %s ]\n",strerror(errno));
+        return -1;
+    }
+    fcntl(ptm, F_SETFD, FD_CLOEXEC);
+
+    if(grantpt(ptm) || unlockpt(ptm) ||
+       ((devname = (char*) ptsname(ptm)) == 0)){
+        printf("[ trouble with /dev/ptmx - %s ]\n", strerror(errno));
+        return -1;
+    }
+
+    pid = fork();
+    if(pid < 0) {
+        printf("- fork failed: %s -\n", strerror(errno));
+        return -1;
+    }
+
+    if(pid == 0){
+        int pts;
+
+        setsid();
+
+        pts = unix_open(devname, O_RDWR);
+        if(pts < 0) exit(-1);
+
+        dup2(pts, 0);
+        dup2(pts, 1);
+        dup2(pts, 2);
+
+        adb_close(ptm);
+
+        execl(cmd, cmd, arg0, arg1, NULL);
+        fprintf(stderr, "- exec '%s' failed: %s (%d) -\n",
+                cmd, strerror(errno), errno);
+        exit(-1);
+    } else {
+#if !ADB_HOST
+        // set child's OOM adjustment to zero
+        char text[64];
+        snprintf(text, sizeof text, "/proc/%d/oom_adj", pid);
+        int fd = adb_open(text, O_WRONLY);
+        if (fd >= 0) {
+            adb_write(fd, "0", 1);
+            adb_close(fd);
+        } else {
+           D("adb: unable to open %s\n", text);
+        }
+#endif
+        return ptm;
+    }
+#endif /* !HAVE_WIN32_PROC */
+}
+
+#if ADB_HOST
+#define SHELL_COMMAND "/bin/sh"
+#define ALTERNATE_SHELL_COMMAND ""
+#else
+#define SHELL_COMMAND "/system/bin/sh"
+#define ALTERNATE_SHELL_COMMAND "/sbin/sh"
+#endif
+
+int service_to_fd(const char *name)
+{
+    int ret = -1;
+
+    if(!strncmp(name, "tcp:", 4)) {
+        int port = atoi(name + 4);
+        name = strchr(name + 4, ':');
+        if(name == 0) {
+            ret = socket_loopback_client(port, SOCK_STREAM);
+            if (ret >= 0)
+                disable_tcp_nagle(ret);
+        } else {
+#if ADB_HOST
+            adb_mutex_lock(&dns_lock);
+            ret = socket_network_client(name + 1, port, SOCK_STREAM);
+            adb_mutex_unlock(&dns_lock);
+#else
+            return -1;
+#endif
+        }
+#ifndef HAVE_WINSOCK   /* winsock doesn't implement unix domain sockets */
+    } else if(!strncmp(name, "local:", 6)) {
+        ret = socket_local_client(name + 6,
+                ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM);
+    } else if(!strncmp(name, "localreserved:", 14)) {
+        ret = socket_local_client(name + 14,
+                ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM);
+    } else if(!strncmp(name, "localabstract:", 14)) {
+        ret = socket_local_client(name + 14,
+                ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM);
+    } else if(!strncmp(name, "localfilesystem:", 16)) {
+        ret = socket_local_client(name + 16,
+                ANDROID_SOCKET_NAMESPACE_FILESYSTEM, SOCK_STREAM);
+#endif
+#if ADB_HOST
+    } else if(!strncmp("dns:", name, 4)){
+        char *n = strdup(name + 4);
+        if(n == 0) return -1;
+        ret = create_service_thread(dns_service, n);
+#else /* !ADB_HOST */
+    } else if(!strncmp("dev:", name, 4)) {
+        ret = unix_open(name + 4, O_RDWR);
+    } else if(!strncmp(name, "framebuffer:", 12)) {
+        ret = create_service_thread(framebuffer_service, 0);
+    } else if(recovery_mode && !strncmp(name, "recover:", 8)) {
+        ret = create_service_thread(recover_service, (void*) atoi(name + 8));
+    } else if (!strncmp(name, "jdwp:", 5)) {
+        ret = create_jdwp_connection_fd(atoi(name+5));
+    } else if (!strncmp(name, "log:", 4)) {
+        ret = create_service_thread(log_service, get_log_file_path(name + 4));
+#endif
+    } else if(!HOST && !strncmp(name, "shell:", 6)) {
+        if(name[6]) {
+            struct stat filecheck;
+            ret = -1;
+            if (stat(ALTERNATE_SHELL_COMMAND, &filecheck) == 0) {
+                ret = create_subprocess(ALTERNATE_SHELL_COMMAND, "-c", name + 6);
+            }
+            if (ret == -1) {
+                ret = create_subprocess(SHELL_COMMAND, "-c", name + 6);
+            }
+        } else {
+            struct stat filecheck;
+            ret = -1;
+            if (stat(ALTERNATE_SHELL_COMMAND, &filecheck) == 0) {
+                ret = create_subprocess(ALTERNATE_SHELL_COMMAND, "-", 0);
+            }
+            if (ret == -1) {
+                ret = create_subprocess(SHELL_COMMAND, "-", 0);
+            }
+        }
+#if !ADB_HOST
+    } else if(!strncmp(name, "sync:", 5)) {
+        ret = create_service_thread(file_sync_service, NULL);
+    } else if(!strncmp(name, "remount:", 8)) {
+        ret = create_service_thread(remount_service, NULL);
+    } else if(!strncmp(name, "reboot:", 7)) {
+        void* arg = strdup(name + 7);
+        if(arg == 0) return -1;
+        ret = create_service_thread(reboot_service, arg);
+    } else if(!strncmp(name, "root:", 5)) {
+        ret = create_service_thread(restart_root_service, NULL);
+    } else if(!strncmp(name, "tcpip:", 6)) {
+        int port;
+        if (sscanf(name + 6, "%d", &port) == 0) {
+            port = 0;
+        }
+        ret = create_service_thread(restart_tcp_service, (void *)port);
+    } else if(!strncmp(name, "usb:", 4)) {
+        ret = create_service_thread(restart_usb_service, NULL);
+#endif
+#if 0
+    } else if(!strncmp(name, "echo:", 5)){
+        ret = create_service_thread(echo_service, 0);
+#endif
+    }
+    if (ret >= 0) {
+        close_on_exec(ret);
+    }
+    return ret;
+}
+
+#if ADB_HOST
+struct state_info {
+    transport_type transport;
+    char* serial;
+    int state;
+};
+
+static void wait_for_state(int fd, void* cookie)
+{
+    struct state_info* sinfo = cookie;
+    char* err = "unknown error";
+
+    D("wait_for_state %d\n", sinfo->state);
+
+    atransport *t = acquire_one_transport(sinfo->state, sinfo->transport, sinfo->serial, &err);
+    if(t != 0) {
+        writex(fd, "OKAY", 4);
+    } else {
+        sendfailmsg(fd, err);
+    }
+
+    if (sinfo->serial)
+        free(sinfo->serial);
+    free(sinfo);
+    adb_close(fd);
+    D("wait_for_state is done\n");
+}
+#endif
+
+#if ADB_HOST
+asocket*  host_service_to_socket(const char*  name, const char *serial)
+{
+    if (!strcmp(name,"track-devices")) {
+        return create_device_tracker();
+    } else if (!strncmp(name, "wait-for-", strlen("wait-for-"))) {
+        struct state_info* sinfo = malloc(sizeof(struct state_info));
+
+        if (serial)
+            sinfo->serial = strdup(serial);
+        else
+            sinfo->serial = NULL;
+
+        name += strlen("wait-for-");
+
+        if (!strncmp(name, "local", strlen("local"))) {
+            sinfo->transport = kTransportLocal;
+            sinfo->state = CS_DEVICE;
+        } else if (!strncmp(name, "usb", strlen("usb"))) {
+            sinfo->transport = kTransportUsb;
+            sinfo->state = CS_DEVICE;
+        } else if (!strncmp(name, "any", strlen("any"))) {
+            sinfo->transport = kTransportAny;
+            sinfo->state = CS_DEVICE;
+        } else {
+            free(sinfo);
+            return NULL;
+        }
+
+        int fd = create_service_thread(wait_for_state, sinfo);
+        return create_local_socket(fd);
+    }
+    return NULL;
+}
+#endif /* ADB_HOST */
diff --git a/adb/sockets.c b/adb/sockets.c
new file mode 100644 (file)
index 0000000..68d640e
--- /dev/null
@@ -0,0 +1,790 @@
+/*
+ * 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 && !result; s = s->next) {
+        if(s->id == id) result = s;
+    }
+    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;
+
+        /* 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)
+{
+    if(s->peer) {
+        s->peer->peer = 0;
+        // tweak to avoid deadlock
+        if (s->peer->close == local_socket_close) {
+            local_socket_close_locked(s->peer);
+        } else {
+            s->peer->close(s->peer);
+        }
+        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);
+    insert_local_socket(s, &local_socket_closing_list);
+}
+
+static void local_socket_event_func(int fd, unsigned ev, void *_s)
+{
+    asocket *s = _s;
+
+    /* 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;
+                }
+                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) {
+            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);
+            if(r > 0) {
+                avail -= r;
+                x += r;
+                continue;
+            }
+            if(r < 0) {
+                if(errno == EAGAIN) break;
+                if(errno == EINTR) continue;
+            }
+
+                /* r = 0 or unhandled error */
+            is_eof = 1;
+            break;
+        }
+
+        if((avail == MAX_PAYLOAD) || (s->peer == 0)) {
+            put_apacket(p);
+        } else {
+            p->len = MAX_PAYLOAD - avail;
+
+            r = s->peer->enqueue(s->peer, p);
+
+            if(r < 0) {
+                    /* error return means they closed us as a side-effect
+                    ** and we must 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);
+            }
+        }
+
+        if(is_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);
+        return;
+    }
+}
+
+asocket *create_local_socket(int fd)
+{
+    asocket *s = calloc(1, sizeof(asocket));
+    if(s == 0) fatal("cannot allocate socket");
+    install_local_socket(s);
+    s->fd = fd;
+    s->enqueue = local_socket_enqueue;
+    s->ready = local_socket_ready;
+    s->close = local_socket_close;
+
+    fdevent_install(&s->fde, fd, local_socket_event_func, s);
+/*    fdevent_add(&s->fde, FDE_ERROR); */
+    //fprintf(stderr, "Created local socket in create_local_socket \n");
+    D("LS(%d): created (fd=%d)\n", s->id, s->fd);
+    return s;
+}
+
+asocket *create_local_service_socket(const char *name)
+{
+    asocket *s;
+    int fd;
+
+#if !ADB_HOST
+    if (!strcmp(name,"jdwp")) {
+        return create_jdwp_service_socket();
+    }
+    if (!strcmp(name,"track-jdwp")) {
+        return create_jdwp_tracker_service_socket();
+    }
+#endif
+    fd = service_to_fd(name);
+    if(fd < 0) return 0;
+
+    s = create_local_socket(fd);
+    D("LS(%d): bound to '%s'\n", s->id, name);
+    return s;
+}
+
+#if ADB_HOST
+static asocket *create_host_service_socket(const char *name, const char* serial)
+{
+    asocket *s;
+
+    s = host_service_to_socket(name, serial);
+
+    if (s != NULL) {
+        D("LS(%d) bound to '%s'\n", s->id, name);
+        return s;
+    }
+
+    return s;
+}
+#endif /* ADB_HOST */
+
+/* a Remote socket is used to send/receive data to/from a given transport object
+** it needs to be closed when the transport is forcibly destroyed by the user
+*/
+typedef struct aremotesocket {
+    asocket      socket;
+    adisconnect  disconnect;
+} aremotesocket;
+
+static int remote_socket_enqueue(asocket *s, apacket *p)
+{
+    D("Calling remote_socket_enqueue\n");
+    p->msg.command = A_WRTE;
+    p->msg.arg0 = s->peer->id;
+    p->msg.arg1 = s->id;
+    p->msg.data_length = p->len;
+    send_packet(p, s->transport);
+    return 1;
+}
+
+static void remote_socket_ready(asocket *s)
+{
+    D("Calling remote_socket_ready\n");
+    apacket *p = get_apacket();
+    p->msg.command = A_OKAY;
+    p->msg.arg0 = s->peer->id;
+    p->msg.arg1 = s->id;
+    send_packet(p, s->transport);
+}
+
+static void remote_socket_close(asocket *s)
+{
+    D("Calling remote_socket_close\n");
+    apacket *p = get_apacket();
+    p->msg.command = A_CLSE;
+    if(s->peer) {
+        p->msg.arg0 = s->peer->id;
+        s->peer->peer = 0;
+        s->peer->close(s->peer);
+    }
+    p->msg.arg1 = s->id;
+    send_packet(p, s->transport);
+    D("RS(%d): closed\n", s->id);
+    remove_transport_disconnect( s->transport, &((aremotesocket*)s)->disconnect );
+    free(s);
+}
+
+static void remote_socket_disconnect(void*  _s, atransport*  t)
+{
+    asocket*  s    = _s;
+    asocket*  peer = s->peer;
+
+    D("remote_socket_disconnect RS(%d)\n", s->id);
+    if (peer) {
+        peer->peer = NULL;
+        peer->close(peer);
+    }
+    remove_transport_disconnect( s->transport, &((aremotesocket*)s)->disconnect );
+    free(s);
+}
+
+asocket *create_remote_socket(unsigned id, atransport *t)
+{
+    asocket *s = calloc(1, sizeof(aremotesocket));
+    adisconnect*  dis = &((aremotesocket*)s)->disconnect;
+
+    if(s == 0) fatal("cannot allocate socket");
+    s->id = id;
+    s->enqueue = remote_socket_enqueue;
+    s->ready = remote_socket_ready;
+    s->close = remote_socket_close;
+    s->transport = t;
+
+    dis->func   = remote_socket_disconnect;
+    dis->opaque = s;
+    add_transport_disconnect( t, dis );
+    D("RS(%d): created\n", s->id);
+    return s;
+}
+
+void connect_to_remote(asocket *s, const char *destination)
+{
+    D("Connect_to_remote call \n");
+    apacket *p = get_apacket();
+    int len = strlen(destination) + 1;
+
+    if(len > (MAX_PAYLOAD-1)) {
+        fatal("destination oversized");
+    }
+
+    D("LS(%d): connect('%s')\n", s->id, destination);
+    p->msg.command = A_OPEN;
+    p->msg.arg0 = s->id;
+    p->msg.data_length = len;
+    strcpy((char*) p->data, destination);
+    send_packet(p, s->transport);
+}
+
+
+/* this is used by magic sockets to rig local sockets to
+   send the go-ahead message when they connect */
+static void local_socket_ready_notify(asocket *s)
+{
+    s->ready = local_socket_ready;
+    s->close = local_socket_close;
+    adb_write(s->fd, "OKAY", 4);
+    s->ready(s);
+}
+
+/* this is used by magic sockets to rig local sockets to
+   send the failure message if they are closed before
+   connected (to avoid closing them without a status message) */
+static void local_socket_close_notify(asocket *s)
+{
+    s->ready = local_socket_ready;
+    s->close = local_socket_close;
+    sendfailmsg(s->fd, "closed");
+    s->close(s);
+}
+
+unsigned unhex(unsigned char *s, int len)
+{
+    unsigned n = 0, c;
+
+    while(len-- > 0) {
+        switch((c = *s++)) {
+        case '0': case '1': case '2':
+        case '3': case '4': case '5':
+        case '6': case '7': case '8':
+        case '9':
+            c -= '0';
+            break;
+        case 'a': case 'b': case 'c':
+        case 'd': case 'e': case 'f':
+            c = c - 'a' + 10;
+            break;
+        case 'A': case 'B': case 'C':
+        case 'D': case 'E': case 'F':
+            c = c - 'A' + 10;
+            break;
+        default:
+            return 0xffffffff;
+        }
+
+        n = (n << 4) | c;
+    }
+
+    return n;
+}
+
+static int smart_socket_enqueue(asocket *s, apacket *p)
+{
+    unsigned len;
+#if ADB_HOST
+    char *service = NULL;
+    char* serial = NULL;
+    transport_type ttype = kTransportAny;
+#endif
+
+    D("SS(%d): enqueue %d\n", s->id, p->len);
+
+    if(s->pkt_first == 0) {
+        s->pkt_first = p;
+        s->pkt_last = p;
+    } else {
+        if((s->pkt_first->len + p->len) > MAX_PAYLOAD) {
+            D("SS(%d): overflow\n", s->id);
+            put_apacket(p);
+            goto fail;
+        }
+
+        memcpy(s->pkt_first->data + s->pkt_first->len,
+               p->data, p->len);
+        s->pkt_first->len += p->len;
+        put_apacket(p);
+
+        p = s->pkt_first;
+    }
+
+        /* don't bother if we can't decode the length */
+    if(p->len < 4) return 0;
+
+    len = unhex(p->data, 4);
+    if((len < 1) ||  (len > 1024)) {
+        D("SS(%d): bad size (%d)\n", s->id, len);
+        goto fail;
+    }
+
+    D("SS(%d): len is %d\n", s->id, len );
+        /* can't do anything until we have the full header */
+    if((len + 4) > p->len) {
+        D("SS(%d): waiting for %d more bytes\n", s->id, len+4 - p->len);
+        return 0;
+    }
+
+    p->data[len + 4] = 0;
+
+    D("SS(%d): '%s'\n", s->id, (char*) (p->data + 4));
+
+#if ADB_HOST
+    service = (char *)p->data + 4;
+    if(!strncmp(service, "host-serial:", strlen("host-serial:"))) {
+        char* serial_end;
+        service += strlen("host-serial:");
+
+        // serial number should follow "host:"
+        serial_end = strchr(service, ':');
+        if (serial_end) {
+            *serial_end = 0; // terminate string
+            serial = service;
+            service = serial_end + 1;
+        }
+    } else if (!strncmp(service, "host-usb:", strlen("host-usb:"))) {
+        ttype = kTransportUsb;
+        service += strlen("host-usb:");
+    } else if (!strncmp(service, "host-local:", strlen("host-local:"))) {
+        ttype = kTransportLocal;
+        service += strlen("host-local:");
+    } else if (!strncmp(service, "host:", strlen("host:"))) {
+        ttype = kTransportAny;
+        service += strlen("host:");
+    } else {
+        service = NULL;
+    }
+
+    if (service) {
+        asocket *s2;
+
+            /* some requests are handled immediately -- in that
+            ** case the handle_host_request() routine has sent
+            ** the OKAY or FAIL message and all we have to do
+            ** is clean up.
+            */
+        if(handle_host_request(service, ttype, serial, s->peer->fd, s) == 0) {
+                /* XXX fail message? */
+            D( "SS(%d): handled host service '%s'\n", s->id, service );
+            goto fail;
+        }
+        if (!strncmp(service, "transport", strlen("transport"))) {
+            D( "SS(%d): okay transport\n", s->id );
+            p->len = 0;
+            return 0;
+        }
+
+            /* try to find a local service with this name.
+            ** if no such service exists, we'll fail out
+            ** and tear down here.
+            */
+        s2 = create_host_service_socket(service, serial);
+        if(s2 == 0) {
+            D( "SS(%d): couldn't create host service '%s'\n", s->id, service );
+            sendfailmsg(s->peer->fd, "unknown host service");
+            goto fail;
+        }
+
+            /* we've connected to a local host service,
+            ** so we make our peer back into a regular
+            ** local socket and bind it to the new local
+            ** service socket, acknowledge the successful
+            ** connection, and close this smart socket now
+            ** that its work is done.
+            */
+        adb_write(s->peer->fd, "OKAY", 4);
+
+        s->peer->ready = local_socket_ready;
+        s->peer->close = local_socket_close;
+        s->peer->peer = s2;
+        s2->peer = s->peer;
+        s->peer = 0;
+        D( "SS(%d): okay\n", s->id );
+        s->close(s);
+
+            /* initial state is "ready" */
+        s2->ready(s2);
+        return 0;
+    }
+#else /* !ADB_HOST */
+    if (s->transport == NULL) {
+        char* error_string = "unknown failure";
+        s->transport = acquire_one_transport (CS_ANY,
+                kTransportAny, NULL, &error_string);
+
+        if (s->transport == NULL) {
+            sendfailmsg(s->peer->fd, error_string);
+            goto fail;
+        }
+    }
+#endif
+
+    if(!(s->transport) || (s->transport->connection_state == CS_OFFLINE)) {
+           /* if there's no remote we fail the connection
+            ** right here and terminate it
+            */
+        sendfailmsg(s->peer->fd, "device offline (x)");
+        goto fail;
+    }
+
+
+        /* instrument our peer to pass the success or fail
+        ** message back once it connects or closes, then
+        ** detach from it, request the connection, and
+        ** tear down
+        */
+    s->peer->ready = local_socket_ready_notify;
+    s->peer->close = local_socket_close_notify;
+    s->peer->peer = 0;
+        /* give him our transport and upref it */
+    s->peer->transport = s->transport;
+
+    connect_to_remote(s->peer, (char*) (p->data + 4));
+    s->peer = 0;
+    s->close(s);
+    return 1;
+
+fail:
+        /* we're going to close our peer as a side-effect, so
+        ** return -1 to signal that state to the local socket
+        ** who is enqueueing against us
+        */
+    s->close(s);
+    return -1;
+}
+
+static void smart_socket_ready(asocket *s)
+{
+    D("SS(%d): ready\n", s->id);
+}
+
+static void smart_socket_close(asocket *s)
+{
+    D("SS(%d): closed\n", s->id);
+    if(s->pkt_first){
+        put_apacket(s->pkt_first);
+    }
+    if(s->peer) {
+        s->peer->peer = 0;
+        s->peer->close(s->peer);
+        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 == 0) fatal("cannot allocate socket");
+    s->id = 0;
+    s->enqueue = smart_socket_enqueue;
+    s->ready = smart_socket_ready;
+    s->close = smart_socket_close;
+    s->extra = action_cb;
+
+    D("SS(%d): created %p\n", s->id, action_cb);
+    return s;
+}
+
+void smart_socket_action(asocket *s, const char *act)
+{
+
+}
+
+void connect_to_smartsocket(asocket *s)
+{
+    D("Connecting to smart socket \n");
+    asocket *ss = create_smart_socket(smart_socket_action);
+    s->peer = ss;
+    ss->peer = s;
+    s->ready(s);
+}
diff --git a/adb/sockets.dia b/adb/sockets.dia
new file mode 100644 (file)
index 0000000..c626f20
Binary files /dev/null and b/adb/sockets.dia differ
diff --git a/adb/sysdeps.h b/adb/sysdeps.h
new file mode 100644 (file)
index 0000000..6372649
--- /dev/null
@@ -0,0 +1,481 @@
+/*
+ * 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