summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaulK <contact@paulk.fr>2012-04-07 15:58:09 +0200
committerPaulK <contact@paulk.fr>2012-04-07 15:58:09 +0200
commit901efaa4660df6cd547a92f919a19c9e1578d7c5 (patch)
treedd31a34ab184affd1404c2afc8a72c65c1771db5
downloadadb-autotools-901efaa4660df6cd547a92f919a19c9e1578d7c5.tar.gz
adb-autotools-901efaa4660df6cd547a92f919a19c9e1578d7c5.tar.bz2
adb-autotools-901efaa4660df6cd547a92f919a19c9e1578d7c5.zip
Initial commit: added autotoolized adb (from Android 2.3.7 tree)
-rw-r--r--AUTHORS1
-rw-r--r--COPYING23
-rw-r--r--ChangeLog0
-rw-r--r--INSTALL302
-rw-r--r--Makefile.am10
-rw-r--r--NEWS0
-rw-r--r--README0
-rw-r--r--adb/Android.mk166
-rw-r--r--adb/MODULE_LICENSE_APACHE20
-rw-r--r--adb/Makefile.am34
-rw-r--r--adb/NOTICE191
-rw-r--r--adb/OVERVIEW.TXT139
-rw-r--r--adb/SERVICES.TXT236
-rw-r--r--adb/adb.c1282
-rw-r--r--adb/adb.h414
-rw-r--r--adb/adb_client.c324
-rw-r--r--adb/adb_client.h53
-rw-r--r--adb/commandline.c1272
-rw-r--r--adb/console.c45
-rw-r--r--adb/fdevent.c506
-rw-r--r--adb/fdevent.h81
-rw-r--r--adb/file_sync_client.c1023
-rw-r--r--adb/file_sync_service.c412
-rw-r--r--adb/file_sync_service.h87
-rw-r--r--adb/framebuffer_service.c209
-rw-r--r--adb/get_my_path_darwin.c30
-rw-r--r--adb/get_my_path_freebsd.c36
-rw-r--r--adb/get_my_path_linux.c33
-rw-r--r--adb/get_my_path_windows.c34
-rw-r--r--adb/jdwp_service.c709
-rw-r--r--adb/log_service.c92
-rw-r--r--adb/mutex_list.h14
-rw-r--r--adb/protocol.txt252
-rw-r--r--adb/remount_service.c111
-rw-r--r--adb/services.c513
-rw-r--r--adb/sockets.c790
-rw-r--r--adb/sockets.diabin0 -> 2333 bytes
-rw-r--r--adb/sysdeps.h481
-rw-r--r--adb/sysdeps_win32.c1967
-rw-r--r--adb/test_track_devices.c97
-rw-r--r--adb/test_track_jdwp.c97
-rw-r--r--adb/transport.c1042
-rw-r--r--adb/transport_local.c316
-rw-r--r--adb/transport_usb.c149
-rw-r--r--adb/usb_libusb.c657
-rw-r--r--adb/usb_linux.c689
-rw-r--r--adb/usb_linux_client.c156
-rw-r--r--adb/usb_osx.c537
-rw-r--r--adb/usb_vendors.c201
-rw-r--r--adb/usb_vendors.h25
-rw-r--r--adb/usb_windows.c513
-rw-r--r--adb/utils.c106
-rw-r--r--adb/utils.h68
-rwxr-xr-xautogen.sh16
-rw-r--r--bionic/dlmalloc.c5469
-rw-r--r--configure.ac32
-rw-r--r--include/android/log.h128
-rw-r--r--include/cutils/abort_socket.h103
-rw-r--r--include/cutils/array.h67
-rw-r--r--include/cutils/ashmem.h45
-rw-r--r--include/cutils/atomic-arm.h280
-rw-r--r--include/cutils/atomic-inline.h58
-rw-r--r--include/cutils/atomic-x86.h145
-rw-r--r--include/cutils/atomic.h133
-rw-r--r--include/cutils/compiler.h32
-rw-r--r--include/cutils/config_utils.h61
-rw-r--r--include/cutils/cpu_info.h34
-rw-r--r--include/cutils/dir_hash.h26
-rw-r--r--include/cutils/event_tag_map.h50
-rw-r--r--include/cutils/hashmap.h150
-rw-r--r--include/cutils/iosched_policy.h38
-rw-r--r--include/cutils/jstring.h43
-rw-r--r--include/cutils/log.h446
-rw-r--r--include/cutils/logd.h49
-rw-r--r--include/cutils/logger.h47
-rw-r--r--include/cutils/logprint.h163
-rw-r--r--include/cutils/memory.h42
-rw-r--r--include/cutils/misc.h48
-rw-r--r--include/cutils/mq.h124
-rw-r--r--include/cutils/mspace.h128
-rw-r--r--include/cutils/native_handle.h73
-rw-r--r--include/cutils/open_memstream.h36
-rw-r--r--include/cutils/process_name.h42
-rw-r--r--include/cutils/properties.h77
-rw-r--r--include/cutils/record_stream.h43
-rw-r--r--include/cutils/sched_policy.h36
-rw-r--r--include/cutils/selector.h130
-rw-r--r--include/cutils/sockets.h100
-rw-r--r--include/cutils/threads.h146
-rw-r--r--include/cutils/tztime.h55
-rw-r--r--include/cutils/uio.h48
-rw-r--r--include/cutils/zygote.h32
-rw-r--r--include/zipfile/zipfile.h58
-rw-r--r--libcutils/Android.mk149
-rw-r--r--libcutils/MODULE_LICENSE_APACHE20
-rw-r--r--libcutils/Makefile.am49
-rw-r--r--libcutils/NOTICE190
-rw-r--r--libcutils/abort_socket.c293
-rw-r--r--libcutils/arch-arm/memset32.S93
-rw-r--r--libcutils/arch-x86/android_memset16.S32
-rw-r--r--libcutils/arch-x86/android_memset32.S33
-rw-r--r--libcutils/arch-x86/cache_wrapper.S24
-rw-r--r--libcutils/arch-x86/sse2-memset16-atom.S722
-rw-r--r--libcutils/arch-x86/sse2-memset32-atom.S513
-rw-r--r--libcutils/array.c170
-rw-r--r--libcutils/ashmem-dev.c90
-rw-r--r--libcutils/ashmem-host.c114
-rw-r--r--libcutils/atomic-android-sh.c149
-rw-r--r--libcutils/atomic.c19
-rw-r--r--libcutils/buffer.c116
-rw-r--r--libcutils/buffer.h112
-rw-r--r--libcutils/config_utils.c317
-rw-r--r--libcutils/cpu_info.c83
-rw-r--r--libcutils/dir_hash.c334
-rw-r--r--libcutils/dlmalloc_stubs.c29
-rw-r--r--libcutils/hashmap.c350
-rw-r--r--libcutils/iosched_policy.c67
-rw-r--r--libcutils/load_file.c51
-rw-r--r--libcutils/loghack.h38
-rw-r--r--libcutils/memory.c91
-rw-r--r--libcutils/mq.c1357
-rw-r--r--libcutils/mspace.c286
-rw-r--r--libcutils/native_handle.c60
-rw-r--r--libcutils/open_memstream.c381
-rw-r--r--libcutils/private.h368
-rw-r--r--libcutils/process_name.c88
-rw-r--r--libcutils/properties.c392
-rw-r--r--libcutils/record_stream.c186
-rw-r--r--libcutils/sched_policy.c277
-rw-r--r--libcutils/selector.c263
-rw-r--r--libcutils/socket_inaddr_any_server.c70
-rw-r--r--libcutils/socket_local.h39
-rw-r--r--libcutils/socket_local_client.c167
-rw-r--r--libcutils/socket_local_server.c124
-rw-r--r--libcutils/socket_loopback_client.c59
-rw-r--r--libcutils/socket_loopback_server.c71
-rw-r--r--libcutils/socket_network_client.c65
-rw-r--r--libcutils/strdup16to8.c168
-rw-r--r--libcutils/strdup8to16.c214
-rw-r--r--libcutils/threads.c84
-rw-r--r--libcutils/tzfile.h180
-rw-r--r--libcutils/tzstrftime.c841
-rw-r--r--libcutils/tztime.c1950
-rw-r--r--libcutils/uio.c76
-rw-r--r--libcutils/zygote.c267
-rw-r--r--libzipfile/Android.mk48
-rw-r--r--libzipfile/MODULE_LICENSE_APACHE20
-rw-r--r--libzipfile/Makefile.am25
-rw-r--r--libzipfile/NOTICE190
-rw-r--r--libzipfile/centraldir.c253
-rw-r--r--libzipfile/private.h45
-rw-r--r--libzipfile/test_zipfile.c92
-rw-r--r--libzipfile/zipfile.c160
153 files changed, 38432 insertions, 0 deletions
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
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
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
index 0000000..e69de29
--- /dev/null
+++ b/ChangeLog
diff --git a/INSTALL b/INSTALL
new file mode 100644
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
index 0000000..4521681
--- /dev/null
+++ b/Makefile.am
@@ -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
index 0000000..e69de29
--- /dev/null
+++ b/NEWS
diff --git a/README b/README
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/README
diff --git a/adb/Android.mk b/adb/Android.mk
new file mode 100644
index 0000000..eafd36d
--- /dev/null
+++ b/adb/Android.mk
@@ -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
index 0000000..e69de29
--- /dev/null
+++ b/adb/MODULE_LICENSE_APACHE2
diff --git a/adb/Makefile.am b/adb/Makefile.am
new file mode 100644
index 0000000..a603860
--- /dev/null
+++ b/adb/Makefile.am
@@ -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
index 0000000..9ffcc08
--- /dev/null
+++ b/adb/NOTICE
@@ -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
index 0000000..c40695a
--- /dev/null
+++ b/adb/OVERVIEW.TXT
@@ -0,0 +1,139 @@
+Implementation notes regarding ADB.
+
+I. General Overview:
+
+The Android Debug Bridge (ADB) is used to:
+
+- keep track of all Android devices and emulators instances
+ connected to or running on a given host developer machine
+
+- implement various control commands (e.g. "adb shell", "adb pull", etc..)
+ for the benefit of clients (command-line users, or helper programs like
+ DDMS). These commands are what is called a 'service' in ADB.
+
+As a whole, everything works through the following components:
+
+ 1. The ADB server
+
+ This is a background process that runs on the host machine. Its purpose
+ if to sense the USB ports to know when devices are attached/removed,
+ as well as when emulator instances start/stop.
+
+ It thus maintains a list of "connected devices" and assigns a 'state'
+ to each one of them: OFFLINE, BOOTLOADER, RECOVERY or ONLINE (more on
+ this below).
+
+ The ADB server is really one giant multiplexing loop whose purpose is
+ to orchestrate the exchange of data (packets, really) between clients,
+ services and devices.
+
+
+ 2. The ADB daemon (adbd)
+
+ The 'adbd' program runs as a background process within an Android device
+ or emulated system. Its purpose is to connect to the ADB server
+ (through USB for devices, through TCP for emulators) and provide a
+ few services for clients that run on the host.
+
+ The ADB server considers that a device is ONLINE when it has 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
index 0000000..be4d50b
--- /dev/null
+++ b/adb/SERVICES.TXT
@@ -0,0 +1,236 @@
+This file tries to document all requests a client can make
+to the ADB server of an adbd daemon. See the OVERVIEW.TXT document
+to understand what's going on here.
+
+HOST SERVICES:
+
+host:version
+ Ask the ADB server for its internal version number.
+
+ As a special exception, the server will respond with a 4-byte
+ hex string corresponding to its internal version number, without
+ any OKAY or FAIL.
+
+host:kill
+ Ask the ADB server to quit immediately. This is used when the
+ ADB client detects that an obsolete server is running after an
+ upgrade.
+
+host:devices
+ Ask to return the list of available Android devices and their
+ state. After the OKAY, this is followed by a 4-byte hex len,
+ and a string that will be dumped as-is by the client, then
+ the connection is closed
+
+host:track-devices
+ This is a variant of host:devices which doesn't close the
+ connection. Instead, a new device list description is sent
+ each time a device is added/removed or the state of a given
+ device changes (hex4 + content). This allows tools like DDMS
+ to track the state of connected devices in real-time without
+ polling the server repeatedly.
+
+host:emulator:<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
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
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
index 0000000..882810a
--- /dev/null
+++ b/adb/adb_client.c
@@ -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
index 0000000..40ab189
--- /dev/null
+++ b/adb/adb_client.h
@@ -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
index 0000000..dcba83b
--- /dev/null
+++ b/adb/commandline.c
@@ -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
index 0000000..b813d33
--- /dev/null
+++ b/adb/console.c
@@ -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
index 0000000..c179b20
--- /dev/null
+++ b/adb/fdevent.c
@@ -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
index 0000000..6b7e7ec
--- /dev/null
+++ b/adb/fdevent.h
@@ -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
index 0000000..da25ae8
--- /dev/null
+++ b/adb/file_sync_client.c
@@ -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
index 0000000..a231e93
--- /dev/null
+++ b/adb/file_sync_service.c
@@ -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
index 0000000..11ea06b
--- /dev/null
+++ b/adb/file_sync_service.h
@@ -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
index 0000000..6d50f8e
--- /dev/null
+++ b/adb/framebuffer_service.c
@@ -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
index 0000000..5b95d15
--- /dev/null
+++ b/adb/get_my_path_darwin.c
@@ -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
index 0000000..b06ec66
--- /dev/null
+++ b/adb/get_my_path_freebsd.c
@@ -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
index 0000000..179c3dd
--- /dev/null
+++ b/adb/get_my_path_linux.c
@@ -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
index 0000000..ddf2816
--- /dev/null
+++ b/adb/get_my_path_windows.c
@@ -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
index 0000000..0c26f7b
--- /dev/null
+++ b/adb/jdwp_service.c
@@ -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
index 0000000..6e9bdee
--- /dev/null
+++ b/adb/log_service.c
@@ -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
index 0000000..eebe0df
--- /dev/null
+++ b/adb/mutex_list.h
@@ -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
index 0000000..398d042
--- /dev/null
+++ b/adb/protocol.txt
@@ -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
index 0000000..4cb41e7
--- /dev/null
+++ b/adb/remount_service.c
@@ -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
index 0000000..a4edb44
--- /dev/null
+++ b/adb/services.c
@@ -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
index 0000000..68d640e
--- /dev/null
+++ b/adb/sockets.c
@@ -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
index 0000000..c626f20
--- /dev/null
+++ b/adb/sockets.dia
Binary files differ
diff --git a/adb/sysdeps.h b/adb/sysdeps.h
new file mode 100644
index 0000000..6372649
--- /dev/null
+++ b/adb/sysdeps.h
@@ -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
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* this file contains system-dependent definitions used by ADB
+ * they're related to threads, sockets and file descriptors
+ */
+#ifndef _ADB_SYSDEPS_H
+#define _ADB_SYSDEPS_H
+
+#ifdef __CYGWIN__
+# undef _WIN32
+#endif
+
+#ifdef _WIN32
+
+#include <windows.h>
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#include <process.h>
+#include <fcntl.h>
+#include <io.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <ctype.h>
+
+#define OS_PATH_SEPARATOR '\\'
+#define OS_PATH_SEPARATOR_STR "\\"
+
+typedef CRITICAL_SECTION adb_mutex_t;
+
+#define ADB_MUTEX_DEFINE(x) adb_mutex_t x
+
+/* declare all mutexes */
+#define ADB_MUTEX(x) extern adb_mutex_t x;
+#include "mutex_list.h"
+
+extern void adb_sysdeps_init(void);
+
+static __inline__ void adb_mutex_lock( adb_mutex_t* lock )
+{
+ EnterCriticalSection( lock );
+}
+
+static __inline__ void adb_mutex_unlock( adb_mutex_t* lock )
+{
+ LeaveCriticalSection( lock );
+}
+
+typedef struct { unsigned tid; } adb_thread_t;
+
+typedef void* (*adb_thread_func_t)(void* arg);
+
+typedef void (*win_thread_func_t)(void* arg);
+
+static __inline__ int adb_thread_create( adb_thread_t *thread, adb_thread_func_t func, void* arg)
+{
+ thread->tid = _beginthread( (win_thread_func_t)func, 0, arg );
+ if (thread->tid == (unsigned)-1L) {
+ return -1;
+ }
+ return 0;
+}
+
+static __inline__ void close_on_exec(int fd)
+{
+ /* nothing really */
+}
+
+extern void disable_tcp_nagle(int fd);
+
+#define lstat stat /* no symlinks on Win32 */
+
+#define S_ISLNK(m) 0 /* no symlinks on Win32 */
+
+static __inline__ int adb_unlink(const char* path)
+{
+ int rc = unlink(path);
+
+ if (rc == -1 && errno == EACCES) {
+ /* unlink returns EACCES when the file is read-only, so we first */
+ /* try to make it writable, then unlink again... */
+ rc = chmod(path, _S_IREAD|_S_IWRITE );
+ if (rc == 0)
+ rc = unlink(path);
+ }
+ return rc;
+}
+#undef unlink
+#define unlink ___xxx_unlink
+
+static __inline__ int adb_mkdir(const char* path, int mode)
+{
+ return _mkdir(path);
+}
+#undef mkdir
+#define mkdir ___xxx_mkdir
+
+extern int adb_open(const char* path, int options);
+extern int adb_creat(const char* path, int mode);
+extern int adb_read(int fd, void* buf, int len);
+extern int adb_write(int fd, const void* buf, int len);
+extern int adb_lseek(int fd, int pos, int where);
+extern int adb_shutdown(int fd);
+extern int adb_close(int fd);
+
+static __inline__ int unix_close(int fd)
+{
+ return close(fd);
+}
+#undef close
+#define close ____xxx_close
+
+static __inline__ int unix_read(int fd, void* buf, size_t len)
+{
+ return read(fd, buf, len);
+}
+#undef read
+#define read ___xxx_read
+
+static __inline__ int unix_write(int fd, const void* buf, size_t len)
+{
+ return write(fd, buf, len);
+}
+#undef write
+#define write ___xxx_write
+
+static __inline__ int adb_open_mode(const char* path, int options, int mode)
+{
+ return adb_open(path, options);
+}
+
+static __inline__ int unix_open(const char* path, int options,...)
+{
+ if ((options & O_CREAT) == 0)
+ {
+ return open(path, options);
+ }
+ else
+ {
+ int mode;
+ va_list args;
+ va_start( args, options );
+ mode = va_arg( args, int );
+ va_end( args );
+ return open(path, options, mode);
+ }
+}
+#define open ___xxx_unix_open
+
+
+/* normally provided by <cutils/misc.h> */
+extern void* load_file(const char* pathname, unsigned* psize);
+
+/* normally provided by <cutils/sockets.h> */
+extern int socket_loopback_client(int port, int type);
+extern int socket_network_client(const char *host, int port, int type);
+extern int socket_loopback_server(int port, int type);
+extern int socket_inaddr_any_server(int port, int type);
+
+/* normally provided by "fdevent.h" */
+
+#define FDE_READ 0x0001
+#define FDE_WRITE 0x0002
+#define FDE_ERROR 0x0004
+#define FDE_DONT_CLOSE 0x0080
+
+typedef struct fdevent fdevent;
+
+typedef void (*fd_func)(int fd, unsigned events, void *userdata);
+
+fdevent *fdevent_create(int fd, fd_func func, void *arg);
+void fdevent_destroy(fdevent *fde);
+void fdevent_install(fdevent *fde, int fd, fd_func func, void *arg);
+void fdevent_remove(fdevent *item);
+void fdevent_set(fdevent *fde, unsigned events);
+void fdevent_add(fdevent *fde, unsigned events);
+void fdevent_del(fdevent *fde, unsigned events);
+void fdevent_loop();
+
+struct fdevent {
+ fdevent *next;
+ fdevent *prev;
+
+ int fd;
+ unsigned short state;
+ unsigned short events;
+
+ fd_func func;
+ void *arg;
+};
+
+static __inline__ void adb_sleep_ms( int mseconds )
+{
+ Sleep( mseconds );
+}
+
+extern int adb_socket_accept(int serverfd, struct sockaddr* addr, socklen_t *addrlen);
+
+#undef accept
+#define accept ___xxx_accept
+
+static __inline__ int adb_socket_setbufsize( int fd, int bufsize )
+{
+ int opt = bufsize;
+ return setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (const char*)&opt, sizeof(opt));
+}
+
+extern int adb_socketpair( int sv[2] );
+
+static __inline__ char* adb_dirstart( const char* path )
+{
+ char* p = strchr(path, '/');
+ char* p2 = strchr(path, '\\');
+
+ if ( !p )
+ p = p2;
+ else if ( p2 && p2 > p )
+ p = p2;
+
+ return p;
+}
+
+static __inline__ char* adb_dirstop( const char* path )
+{
+ char* p = strrchr(path, '/');
+ char* p2 = strrchr(path, '\\');
+
+ if ( !p )
+ p = p2;
+ else if ( p2 && p2 > p )
+ p = p2;
+
+ return p;
+}
+
+static __inline__ int adb_is_absolute_host_path( const char* path )
+{
+ return isalpha(path[0]) && path[1] == ':' && path[2] == '\\';
+}
+
+#else /* !_WIN32 a.k.a. Unix */
+
+#include "fdevent.h"
+#include <cutils/sockets.h>
+#include <cutils/properties.h>
+#include <cutils/misc.h>
+#include <signal.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <pthread.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <string.h>
+
+#define OS_PATH_SEPARATOR '/'
+#define OS_PATH_SEPARATOR_STR "/"
+
+typedef pthread_mutex_t adb_mutex_t;
+#define ADB_MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER
+#define adb_mutex_init pthread_mutex_init
+#define adb_mutex_lock pthread_mutex_lock
+#define adb_mutex_unlock pthread_mutex_unlock
+#define adb_mutex_destroy pthread_mutex_destroy
+
+#define ADB_MUTEX_DEFINE(m) static adb_mutex_t m = PTHREAD_MUTEX_INITIALIZER
+
+#define adb_cond_t pthread_cond_t
+#define adb_cond_init pthread_cond_init
+#define adb_cond_wait pthread_cond_wait
+#define adb_cond_broadcast pthread_cond_broadcast
+#define adb_cond_signal pthread_cond_signal
+#define adb_cond_destroy pthread_cond_destroy
+
+static __inline__ void close_on_exec(int fd)
+{
+ fcntl( fd, F_SETFD, FD_CLOEXEC );
+}
+
+static __inline__ int unix_open(const char* path, int options,...)
+{
+ if ((options & O_CREAT) == 0)
+ {
+ return open(path, options);
+ }
+ else
+ {
+ int mode;
+ va_list args;
+ va_start( args, options );
+ mode = va_arg( args, int );
+ va_end( args );
+ return open(path, options, mode);
+ }
+}
+
+static __inline__ int adb_open_mode( const char* pathname, int options, int mode )
+{
+ return open( pathname, options, mode );
+}
+
+
+static __inline__ int adb_open( const char* pathname, int options )
+{
+ int fd = open( pathname, options );
+ if (fd < 0)
+ return -1;
+ close_on_exec( fd );
+ return fd;
+}
+#undef open
+#define open ___xxx_open
+
+static __inline__ int adb_shutdown(int fd)
+{
+ return shutdown(fd, SHUT_RDWR);
+}
+#undef shutdown
+#define shutdown ____xxx_shutdown
+
+static __inline__ int adb_close(int fd)
+{
+ return close(fd);
+}
+#undef close
+#define close ____xxx_close
+
+
+static __inline__ int adb_read(int fd, void* buf, size_t len)
+{
+ return read(fd, buf, len);
+}
+
+#undef read
+#define read ___xxx_read
+
+static __inline__ int adb_write(int fd, const void* buf, size_t len)
+{
+ return write(fd, buf, len);
+}
+#undef write
+#define write ___xxx_write
+
+static __inline__ int adb_lseek(int fd, int pos, int where)
+{
+ return lseek(fd, pos, where);
+}
+#undef lseek
+#define lseek ___xxx_lseek
+
+static __inline__ int adb_unlink(const char* path)
+{
+ return unlink(path);
+}
+#undef unlink
+#define unlink ___xxx_unlink
+
+static __inline__ int adb_creat(const char* path, int mode)
+{
+ int fd = creat(path, mode);
+
+ if ( fd < 0 )
+ return -1;
+
+ close_on_exec(fd);
+ return fd;
+}
+#undef creat
+#define creat ___xxx_creat
+
+static __inline__ int adb_socket_accept(int serverfd, struct sockaddr* addr, socklen_t *addrlen)
+{
+ return accept( serverfd, addr, addrlen );
+}
+
+#undef accept
+#define accept ___xxx_accept
+
+#define unix_read adb_read
+#define unix_write adb_write
+#define unix_close adb_close
+
+typedef pthread_t adb_thread_t;
+
+typedef void* (*adb_thread_func_t)( void* arg );
+
+static __inline__ int adb_thread_create( adb_thread_t *pthread, adb_thread_func_t start, void* arg )
+{
+ pthread_attr_t attr;
+
+ pthread_attr_init (&attr);
+ pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
+
+ return pthread_create( pthread, &attr, start, arg );
+}
+
+static __inline__ int adb_socket_setbufsize( int fd, int bufsize )
+{
+ int opt = bufsize;
+ return setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt));
+}
+
+static __inline__ void disable_tcp_nagle(int fd)
+{
+ int on = 1;
+ setsockopt( fd, IPPROTO_TCP, TCP_NODELAY, (void*)&on, sizeof(on) );
+}
+
+
+static __inline__ int unix_socketpair( int d, int type, int protocol, int sv[2] )
+{
+ return socketpair( d, type, protocol, sv );
+}
+
+static __inline__ int adb_socketpair( int sv[2] )
+{
+ int rc;
+
+ rc = unix_socketpair( AF_UNIX, SOCK_STREAM, 0, sv );
+ if (rc < 0)
+ return -1;
+
+ close_on_exec( sv[0] );
+ close_on_exec( sv[1] );
+ return 0;
+}
+
+#undef socketpair
+#define socketpair ___xxx_socketpair
+
+static __inline__ void adb_sleep_ms( int mseconds )
+{
+ usleep( mseconds*1000 );
+}
+
+static __inline__ int adb_mkdir(const char* path, int mode)
+{
+ return mkdir(path, mode);
+}
+#undef mkdir
+#define mkdir ___xxx_mkdir
+
+static __inline__ void adb_sysdeps_init(void)
+{
+}
+
+static __inline__ char* adb_dirstart(const char* path)
+{
+ return strchr(path, '/');
+}
+
+static __inline__ char* adb_dirstop(const char* path)
+{
+ return strrchr(path, '/');
+}
+
+static __inline__ int adb_is_absolute_host_path( const char* path )
+{
+ return path[0] == '/';
+}
+
+#endif /* !_WIN32 */
+
+#endif /* _ADB_SYSDEPS_H */
diff --git a/adb/sysdeps_win32.c b/adb/sysdeps_win32.c
new file mode 100644
index 0000000..ced91e8
--- /dev/null
+++ b/adb/sysdeps_win32.c
@@ -0,0 +1,1967 @@
+#include "sysdeps.h"
+#include <windows.h>
+#include <winsock2.h>
+#include <stdio.h>
+#include <errno.h>
+#define TRACE_TAG TRACE_SYSDEPS
+#include "adb.h"
+
+extern void fatal(const char *fmt, ...);
+
+#define assert(cond) do { if (!(cond)) fatal( "assertion failed '%s' on %s:%ld\n", #cond, __FILE__, __LINE__ ); } while (0)
+
+/**************************************************************************/
+/**************************************************************************/
+/***** *****/
+/***** replaces libs/cutils/load_file.c *****/
+/***** *****/
+/**************************************************************************/
+/**************************************************************************/
+
+void *load_file(const char *fn, unsigned *_sz)
+{
+ HANDLE file;
+ char *data;
+ DWORD file_size;
+
+ file = CreateFile( fn,
+ GENERIC_READ,
+ FILE_SHARE_READ,
+ NULL,
+ OPEN_EXISTING,
+ 0,
+ NULL );
+
+ if (file == INVALID_HANDLE_VALUE)
+ return NULL;
+
+ file_size = GetFileSize( file, NULL );
+ data = NULL;
+
+ if (file_size > 0) {
+ data = (char*) malloc( file_size + 1 );
+ if (data == NULL) {
+ D("load_file: could not allocate %ld bytes\n", file_size );
+ file_size = 0;
+ } else {
+ DWORD out_bytes;
+
+ if ( !ReadFile( file, data, file_size, &out_bytes, NULL ) ||
+ out_bytes != file_size )
+ {
+ D("load_file: could not read %ld bytes from '%s'\n", file_size, fn);
+ free(data);
+ data = NULL;
+ file_size = 0;
+ }
+ }
+ }
+ CloseHandle( file );
+
+ *_sz = (unsigned) file_size;
+ return data;
+}
+
+/**************************************************************************/
+/**************************************************************************/
+/***** *****/
+/***** common file descriptor handling *****/
+/***** *****/
+/**************************************************************************/
+/**************************************************************************/
+
+typedef const struct FHClassRec_* FHClass;
+
+typedef struct FHRec_* FH;
+
+typedef struct EventHookRec_* EventHook;
+
+typedef struct FHClassRec_
+{
+ void (*_fh_init) ( FH f );
+ int (*_fh_close)( FH f );
+ int (*_fh_lseek)( FH f, int pos, int origin );
+ int (*_fh_read) ( FH f, void* buf, int len );
+ int (*_fh_write)( FH f, const void* buf, int len );
+ void (*_fh_hook) ( FH f, int events, EventHook hook );
+
+} FHClassRec;
+
+/* used to emulate unix-domain socket pairs */
+typedef struct SocketPairRec_* SocketPair;
+
+typedef struct FHRec_
+{
+ FHClass clazz;
+ int used;
+ int eof;
+ union {
+ HANDLE handle;
+ SOCKET socket;
+ SocketPair pair;
+ } u;
+
+ HANDLE event;
+ int mask;
+
+ char name[32];
+
+} FHRec;
+
+#define fh_handle u.handle
+#define fh_socket u.socket
+#define fh_pair u.pair
+
+#define WIN32_FH_BASE 100
+
+#define WIN32_MAX_FHS 128
+
+static adb_mutex_t _win32_lock;
+static FHRec _win32_fhs[ WIN32_MAX_FHS ];
+static int _win32_fh_count;
+
+static FH
+_fh_from_int( int fd )
+{
+ FH f;
+
+ fd -= WIN32_FH_BASE;
+
+ if (fd < 0 || fd >= _win32_fh_count) {
+ D( "_fh_from_int: invalid fd %d\n", fd + WIN32_FH_BASE );
+ errno = EBADF;
+ return NULL;
+ }
+
+ f = &_win32_fhs[fd];
+
+ if (f->used == 0) {
+ D( "_fh_from_int: invalid fd %d\n", fd + WIN32_FH_BASE );
+ errno = EBADF;
+ return NULL;
+ }
+
+ return f;
+}
+
+
+static int
+_fh_to_int( FH f )
+{
+ if (f && f->used && f >= _win32_fhs && f < _win32_fhs + WIN32_MAX_FHS)
+ return (int)(f - _win32_fhs) + WIN32_FH_BASE;
+
+ return -1;
+}
+
+static FH
+_fh_alloc( FHClass clazz )
+{
+ int nn;
+ FH f = NULL;
+
+ adb_mutex_lock( &_win32_lock );
+
+ if (_win32_fh_count < WIN32_MAX_FHS) {
+ f = &_win32_fhs[ _win32_fh_count++ ];
+ goto Exit;
+ }
+
+ for (nn = 0; nn < WIN32_MAX_FHS; nn++) {
+ if ( _win32_fhs[nn].clazz == NULL) {
+ f = &_win32_fhs[nn];
+ goto Exit;
+ }
+ }
+ D( "_fh_alloc: no more free file descriptors\n" );
+Exit:
+ if (f) {
+ f->clazz = clazz;
+ f->used = 1;
+ f->eof = 0;
+ clazz->_fh_init(f);
+ }
+ adb_mutex_unlock( &_win32_lock );
+ return f;
+}
+
+
+static int
+_fh_close( FH f )
+{
+ if ( f->used ) {
+ f->clazz->_fh_close( f );
+ f->used = 0;
+ f->eof = 0;
+ f->clazz = NULL;
+ }
+ return 0;
+}
+
+/* forward definitions */
+static const FHClassRec _fh_file_class;
+static const FHClassRec _fh_socket_class;
+
+/**************************************************************************/
+/**************************************************************************/
+/***** *****/
+/***** file-based descriptor handling *****/
+/***** *****/
+/**************************************************************************/
+/**************************************************************************/
+
+static void
+_fh_file_init( FH f )
+{
+ f->fh_handle = INVALID_HANDLE_VALUE;
+}
+
+static int
+_fh_file_close( FH f )
+{
+ CloseHandle( f->fh_handle );
+ f->fh_handle = INVALID_HANDLE_VALUE;
+ return 0;
+}
+
+static int
+_fh_file_read( FH f, void* buf, int len )
+{
+ DWORD read_bytes;
+
+ if ( !ReadFile( f->fh_handle, buf, (DWORD)len, &read_bytes, NULL ) ) {
+ D( "adb_read: could not read %d bytes from %s\n", len, f->name );
+ errno = EIO;
+ return -1;
+ } else if (read_bytes < (DWORD)len) {
+ f->eof = 1;
+ }
+ return (int)read_bytes;
+}
+
+static int
+_fh_file_write( FH f, const void* buf, int len )
+{
+ DWORD wrote_bytes;
+
+ if ( !WriteFile( f->fh_handle, buf, (DWORD)len, &wrote_bytes, NULL ) ) {
+ D( "adb_file_write: could not write %d bytes from %s\n", len, f->name );
+ errno = EIO;
+ return -1;
+ } else if (wrote_bytes < (DWORD)len) {
+ f->eof = 1;
+ }
+ return (int)wrote_bytes;
+}
+
+static int
+_fh_file_lseek( FH f, int pos, int origin )
+{
+ DWORD method;
+ DWORD result;
+
+ switch (origin)
+ {
+ case SEEK_SET: method = FILE_BEGIN; break;
+ case SEEK_CUR: method = FILE_CURRENT; break;
+ case SEEK_END: method = FILE_END; break;
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+
+ result = SetFilePointer( f->fh_handle, pos, NULL, method );
+ if (result == INVALID_SET_FILE_POINTER) {
+ errno = EIO;
+ return -1;
+ } else {
+ f->eof = 0;
+ }
+ return (int)result;
+}
+
+static void _fh_file_hook( FH f, int event, EventHook eventhook ); /* forward */
+
+static const FHClassRec _fh_file_class =
+{
+ _fh_file_init,
+ _fh_file_close,
+ _fh_file_lseek,
+ _fh_file_read,
+ _fh_file_write,
+ _fh_file_hook
+};
+
+/**************************************************************************/
+/**************************************************************************/
+/***** *****/
+/***** file-based descriptor handling *****/
+/***** *****/
+/**************************************************************************/
+/**************************************************************************/
+
+int adb_open(const char* path, int options)
+{
+ FH f;
+
+ DWORD desiredAccess = 0;
+ DWORD shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
+
+ switch (options) {
+ case O_RDONLY:
+ desiredAccess = GENERIC_READ;
+ break;
+ case O_WRONLY:
+ desiredAccess = GENERIC_WRITE;
+ break;
+ case O_RDWR:
+ desiredAccess = GENERIC_READ | GENERIC_WRITE;
+ break;
+ default:
+ D("adb_open: invalid options (0x%0x)\n", options);
+ errno = EINVAL;
+ return -1;
+ }
+
+ f = _fh_alloc( &_fh_file_class );
+ if ( !f ) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ f->fh_handle = CreateFile( path, desiredAccess, shareMode, NULL, OPEN_EXISTING,
+ 0, NULL );
+
+ if ( f->fh_handle == INVALID_HANDLE_VALUE ) {
+ _fh_close(f);
+ D( "adb_open: could not open '%s':", path );
+ switch (GetLastError()) {
+ case ERROR_FILE_NOT_FOUND:
+ D( "file not found\n" );
+ errno = ENOENT;
+ return -1;
+
+ case ERROR_PATH_NOT_FOUND:
+ D( "path not found\n" );
+ errno = ENOTDIR;
+ return -1;
+
+ default:
+ D( "unknown error\n" );
+ errno = ENOENT;
+ return -1;
+ }
+ }
+
+ snprintf( f->name, sizeof(f->name), "%d(%s)", _fh_to_int(f), path );
+ D( "adb_open: '%s' => fd %d\n", path, _fh_to_int(f) );
+ return _fh_to_int(f);
+}
+
+/* ignore mode on Win32 */
+int adb_creat(const char* path, int mode)
+{
+ FH f;
+
+ f = _fh_alloc( &_fh_file_class );
+ if ( !f ) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ f->fh_handle = CreateFile( path, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,
+ NULL );
+
+ if ( f->fh_handle == INVALID_HANDLE_VALUE ) {
+ _fh_close(f);
+ D( "adb_creat: could not open '%s':", path );
+ switch (GetLastError()) {
+ case ERROR_FILE_NOT_FOUND:
+ D( "file not found\n" );
+ errno = ENOENT;
+ return -1;
+
+ case ERROR_PATH_NOT_FOUND:
+ D( "path not found\n" );
+ errno = ENOTDIR;
+ return -1;
+
+ default:
+ D( "unknown error\n" );
+ errno = ENOENT;
+ return -1;
+ }
+ }
+ snprintf( f->name, sizeof(f->name), "%d(%s)", _fh_to_int(f), path );
+ D( "adb_creat: '%s' => fd %d\n", path, _fh_to_int(f) );
+ return _fh_to_int(f);
+}
+
+
+int adb_read(int fd, void* buf, int len)
+{
+ FH f = _fh_from_int(fd);
+
+ if (f == NULL) {
+ return -1;
+ }
+
+ return f->clazz->_fh_read( f, buf, len );
+}
+
+
+int adb_write(int fd, const void* buf, int len)
+{
+ FH f = _fh_from_int(fd);
+
+ if (f == NULL) {
+ return -1;
+ }
+
+ return f->clazz->_fh_write(f, buf, len);
+}
+
+
+int adb_lseek(int fd, int pos, int where)
+{
+ FH f = _fh_from_int(fd);
+
+ if (!f) {
+ return -1;
+ }
+
+ return f->clazz->_fh_lseek(f, pos, where);
+}
+
+
+int adb_shutdown(int fd)
+{
+ FH f = _fh_from_int(fd);
+
+ if (!f) {
+ return -1;
+ }
+
+ D( "adb_shutdown: %s\n", f->name);
+ shutdown( f->fh_socket, SD_BOTH );
+ return 0;
+}
+
+
+int adb_close(int fd)
+{
+ FH f = _fh_from_int(fd);
+
+ if (!f) {
+ return -1;
+ }
+
+ D( "adb_close: %s\n", f->name);
+ _fh_close(f);
+ return 0;
+}
+
+/**************************************************************************/
+/**************************************************************************/
+/***** *****/
+/***** socket-based file descriptors *****/
+/***** *****/
+/**************************************************************************/
+/**************************************************************************/
+
+static void
+_socket_set_errno( void )
+{
+ switch (WSAGetLastError()) {
+ case 0: errno = 0; break;
+ case WSAEWOULDBLOCK: errno = EAGAIN; break;
+ case WSAEINTR: errno = EINTR; break;
+ default:
+ D( "_socket_set_errno: unhandled value %d\n", WSAGetLastError() );
+ errno = EINVAL;
+ }
+}
+
+static void
+_fh_socket_init( FH f )
+{
+ f->fh_socket = INVALID_SOCKET;
+ f->event = WSACreateEvent();
+ f->mask = 0;
+}
+
+static int
+_fh_socket_close( FH f )
+{
+ /* gently tell any peer that we're closing the socket */
+ shutdown( f->fh_socket, SD_BOTH );
+ closesocket( f->fh_socket );
+ f->fh_socket = INVALID_SOCKET;
+ CloseHandle( f->event );
+ f->mask = 0;
+ return 0;
+}
+
+static int
+_fh_socket_lseek( FH f, int pos, int origin )
+{
+ errno = EPIPE;
+ return -1;
+}
+
+static int
+_fh_socket_read( FH f, void* buf, int len )
+{
+ int result = recv( f->fh_socket, buf, len, 0 );
+ if (result == SOCKET_ERROR) {
+ _socket_set_errno();
+ result = -1;
+ }
+ return result;
+}
+
+static int
+_fh_socket_write( FH f, const void* buf, int len )
+{
+ int result = send( f->fh_socket, buf, len, 0 );
+ if (result == SOCKET_ERROR) {
+ _socket_set_errno();
+ result = -1;
+ }
+ return result;
+}
+
+static void _fh_socket_hook( FH f, int event, EventHook hook ); /* forward */
+
+static const FHClassRec _fh_socket_class =
+{
+ _fh_socket_init,
+ _fh_socket_close,
+ _fh_socket_lseek,
+ _fh_socket_read,
+ _fh_socket_write,
+ _fh_socket_hook
+};
+
+/**************************************************************************/
+/**************************************************************************/
+/***** *****/
+/***** replacement for libs/cutils/socket_xxxx.c *****/
+/***** *****/
+/**************************************************************************/
+/**************************************************************************/
+
+#include <winsock2.h>
+
+static int _winsock_init;
+
+static void
+_cleanup_winsock( void )
+{
+ WSACleanup();
+}
+
+static void
+_init_winsock( void )
+{
+ if (!_winsock_init) {
+ WSADATA wsaData;
+ int rc = WSAStartup( MAKEWORD(2,2), &wsaData);
+ if (rc != 0) {
+ fatal( "adb: could not initialize Winsock\n" );
+ }
+ atexit( _cleanup_winsock );
+ _winsock_init = 1;
+ }
+}
+
+int socket_loopback_client(int port, int type)
+{
+ FH f = _fh_alloc( &_fh_socket_class );
+ struct sockaddr_in addr;
+ SOCKET s;
+
+ if (!f)
+ return -1;
+
+ if (!_winsock_init)
+ _init_winsock();
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(port);
+ addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+ s = socket(AF_INET, type, 0);
+ if(s == INVALID_SOCKET) {
+ D("socket_loopback_client: could not create socket\n" );
+ _fh_close(f);
+ return -1;
+ }
+
+ f->fh_socket = s;
+ if(connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ D("socket_loopback_client: could not connect to %s:%d\n", type != SOCK_STREAM ? "udp" : "tcp", port );
+ _fh_close(f);
+ return -1;
+ }
+ snprintf( f->name, sizeof(f->name), "%d(lo-client:%s%d)", _fh_to_int(f), type != SOCK_STREAM ? "udp:" : "", port );
+ D( "socket_loopback_client: port %d type %s => fd %d\n", port, type != SOCK_STREAM ? "udp" : "tcp", _fh_to_int(f) );
+ return _fh_to_int(f);
+}
+
+#define LISTEN_BACKLOG 4
+
+int socket_loopback_server(int port, int type)
+{
+ FH f = _fh_alloc( &_fh_socket_class );
+ struct sockaddr_in addr;
+ SOCKET s;
+ int n;
+
+ if (!f) {
+ return -1;
+ }
+
+ if (!_winsock_init)
+ _init_winsock();
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(port);
+ addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+ s = socket(AF_INET, type, 0);
+ if(s == INVALID_SOCKET) return -1;
+
+ f->fh_socket = s;
+
+ n = 1;
+ setsockopt(s, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (const char*)&n, sizeof(n));
+
+ if(bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ _fh_close(f);
+ return -1;
+ }
+ if (type == SOCK_STREAM) {
+ int ret;
+
+ ret = listen(s, LISTEN_BACKLOG);
+ if (ret < 0) {
+ _fh_close(f);
+ return -1;
+ }
+ }
+ snprintf( f->name, sizeof(f->name), "%d(lo-server:%s%d)", _fh_to_int(f), type != SOCK_STREAM ? "udp:" : "", port );
+ D( "socket_loopback_server: port %d type %s => fd %d\n", port, type != SOCK_STREAM ? "udp" : "tcp", _fh_to_int(f) );
+ return _fh_to_int(f);
+}
+
+
+int socket_network_client(const char *host, int port, int type)
+{
+ FH f = _fh_alloc( &_fh_socket_class );
+ struct hostent *hp;
+ struct sockaddr_in addr;
+ SOCKET s;
+
+ if (!f)
+ return -1;
+
+ if (!_winsock_init)
+ _init_winsock();
+
+ hp = gethostbyname(host);
+ if(hp == 0) {
+ _fh_close(f);
+ return -1;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = hp->h_addrtype;
+ addr.sin_port = htons(port);
+ memcpy(&addr.sin_addr, hp->h_addr, hp->h_length);
+
+ s = socket(hp->h_addrtype, type, 0);
+ if(s == INVALID_SOCKET) {
+ _fh_close(f);
+ return -1;
+ }
+ f->fh_socket = s;
+
+ if(connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ _fh_close(f);
+ return -1;
+ }
+
+ snprintf( f->name, sizeof(f->name), "%d(net-client:%s%d)", _fh_to_int(f), type != SOCK_STREAM ? "udp:" : "", port );
+ D( "socket_network_client: host '%s' port %d type %s => fd %d\n", host, port, type != SOCK_STREAM ? "udp" : "tcp", _fh_to_int(f) );
+ return _fh_to_int(f);
+}
+
+
+int socket_inaddr_any_server(int port, int type)
+{
+ FH f = _fh_alloc( &_fh_socket_class );
+ struct sockaddr_in addr;
+ SOCKET s;
+ int n;
+
+ if (!f)
+ return -1;
+
+ if (!_winsock_init)
+ _init_winsock();
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(port);
+ addr.sin_addr.s_addr = htonl(INADDR_ANY);
+
+ s = socket(AF_INET, type, 0);
+ if(s == INVALID_SOCKET) {
+ _fh_close(f);
+ return -1;
+ }
+
+ f->fh_socket = s;
+ n = 1;
+ setsockopt(s, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (const char*)&n, sizeof(n));
+
+ if(bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ _fh_close(f);
+ return -1;
+ }
+
+ if (type == SOCK_STREAM) {
+ int ret;
+
+ ret = listen(s, LISTEN_BACKLOG);
+ if (ret < 0) {
+ _fh_close(f);
+ return -1;
+ }
+ }
+ snprintf( f->name, sizeof(f->name), "%d(any-server:%s%d)", _fh_to_int(f), type != SOCK_STREAM ? "udp:" : "", port );
+ D( "socket_inaddr_server: port %d type %s => fd %d\n", port, type != SOCK_STREAM ? "udp" : "tcp", _fh_to_int(f) );
+ return _fh_to_int(f);
+}
+
+#undef accept
+int adb_socket_accept(int serverfd, struct sockaddr* addr, socklen_t *addrlen)
+{
+ FH serverfh = _fh_from_int(serverfd);
+ FH fh;
+
+ if ( !serverfh || serverfh->clazz != &_fh_socket_class ) {
+ D( "adb_socket_accept: invalid fd %d\n", serverfd );
+ return -1;
+ }
+
+ fh = _fh_alloc( &_fh_socket_class );
+ if (!fh) {
+ D( "adb_socket_accept: not enough memory to allocate accepted socket descriptor\n" );
+ return -1;
+ }
+
+ fh->fh_socket = accept( serverfh->fh_socket, addr, addrlen );
+ if (fh->fh_socket == INVALID_SOCKET) {
+ _fh_close( fh );
+ D( "adb_socket_accept: accept on fd %d return error %ld\n", serverfd, GetLastError() );
+ return -1;
+ }
+
+ snprintf( fh->name, sizeof(fh->name), "%d(accept:%s)", _fh_to_int(fh), serverfh->name );
+ D( "adb_socket_accept on fd %d returns fd %d\n", serverfd, _fh_to_int(fh) );
+ return _fh_to_int(fh);
+}
+
+
+void disable_tcp_nagle(int fd)
+{
+ FH fh = _fh_from_int(fd);
+ int on;
+
+ if ( !fh || fh->clazz != &_fh_socket_class )
+ return;
+
+ setsockopt( fh->fh_socket, IPPROTO_TCP, TCP_NODELAY, (const char*)&on, sizeof(on) );
+}
+
+/**************************************************************************/
+/**************************************************************************/
+/***** *****/
+/***** emulated socketpairs *****/
+/***** *****/
+/**************************************************************************/
+/**************************************************************************/
+
+/* we implement socketpairs directly in use space for the following reasons:
+ * - it avoids copying data from/to the Nt kernel
+ * - it allows us to implement fdevent hooks easily and cheaply, something
+ * that is not possible with standard Win32 pipes !!
+ *
+ * basically, we use two circular buffers, each one corresponding to a given
+ * direction.
+ *
+ * each buffer is implemented as two regions:
+ *
+ * region A which is (a_start,a_end)
+ * region B which is (0, b_end) with b_end <= a_start
+ *
+ * an empty buffer has: a_start = a_end = b_end = 0
+ *
+ * a_start is the pointer where we start reading data
+ * a_end is the pointer where we start writing data, unless it is BUFFER_SIZE,
+ * then you start writing at b_end
+ *
+ * the buffer is full when b_end == a_start && a_end == BUFFER_SIZE
+ *
+ * there is room when b_end < a_start || a_end < BUFER_SIZE
+ *
+ * when reading, a_start is incremented, it a_start meets a_end, then
+ * we do: a_start = 0, a_end = b_end, b_end = 0, and keep going on..
+ */
+
+#define BIP_BUFFER_SIZE 4096
+
+#if 0
+#include <stdio.h>
+# define BIPD(x) D x
+# define BIPDUMP bip_dump_hex
+
+static void bip_dump_hex( const unsigned char* ptr, size_t len )
+{
+ int nn, len2 = len;
+
+ if (len2 > 8) len2 = 8;
+
+ for (nn = 0; nn < len2; nn++)
+ printf("%02x", ptr[nn]);
+ printf(" ");
+
+ for (nn = 0; nn < len2; nn++) {
+ int c = ptr[nn];
+ if (c < 32 || c > 127)
+ c = '.';
+ printf("%c", c);
+ }
+ printf("\n");
+ fflush(stdout);
+}
+
+#else
+# define BIPD(x) do {} while (0)
+# define BIPDUMP(p,l) BIPD(p)
+#endif
+
+typedef struct BipBufferRec_
+{
+ int a_start;
+ int a_end;
+ int b_end;
+ int fdin;
+ int fdout;
+ int closed;
+ int can_write; /* boolean */
+ HANDLE evt_write; /* event signaled when one can write to a buffer */
+ int can_read; /* boolean */
+ HANDLE evt_read; /* event signaled when one can read from a buffer */
+ CRITICAL_SECTION lock;
+ unsigned char buff[ BIP_BUFFER_SIZE ];
+
+} BipBufferRec, *BipBuffer;
+
+static void
+bip_buffer_init( BipBuffer buffer )
+{
+ D( "bit_buffer_init %p\n", buffer );
+ buffer->a_start = 0;
+ buffer->a_end = 0;
+ buffer->b_end = 0;
+ buffer->can_write = 1;
+ buffer->can_read = 0;
+ buffer->fdin = 0;
+ buffer->fdout = 0;
+ buffer->closed = 0;
+ buffer->evt_write = CreateEvent( NULL, TRUE, TRUE, NULL );
+ buffer->evt_read = CreateEvent( NULL, TRUE, FALSE, NULL );
+ InitializeCriticalSection( &buffer->lock );
+}
+
+static void
+bip_buffer_close( BipBuffer bip )
+{
+ bip->closed = 1;
+
+ if (!bip->can_read) {
+ SetEvent( bip->evt_read );
+ }
+ if (!bip->can_write) {
+ SetEvent( bip->evt_write );
+ }
+}
+
+static void
+bip_buffer_done( BipBuffer bip )
+{
+ BIPD(( "bip_buffer_done: %d->%d\n", bip->fdin, bip->fdout ));
+ CloseHandle( bip->evt_read );
+ CloseHandle( bip->evt_write );
+ DeleteCriticalSection( &bip->lock );
+}
+
+static int
+bip_buffer_write( BipBuffer bip, const void* src, int len )
+{
+ int avail, count = 0;
+
+ if (len <= 0)
+ return 0;
+
+ BIPD(( "bip_buffer_write: enter %d->%d len %d\n", bip->fdin, bip->fdout, len ));
+ BIPDUMP( src, len );
+
+ EnterCriticalSection( &bip->lock );
+
+ while (!bip->can_write) {
+ int ret;
+ LeaveCriticalSection( &bip->lock );
+
+ if (bip->closed) {
+ errno = EPIPE;
+ return -1;
+ }
+ /* spinlocking here is probably unfair, but let's live with it */
+ ret = WaitForSingleObject( bip->evt_write, INFINITE );
+ if (ret != WAIT_OBJECT_0) { /* buffer probably closed */
+ D( "bip_buffer_write: error %d->%d WaitForSingleObject returned %d, error %ld\n", bip->fdin, bip->fdout, ret, GetLastError() );
+ return 0;
+ }
+ if (bip->closed) {
+ errno = EPIPE;
+ return -1;
+ }
+ EnterCriticalSection( &bip->lock );
+ }
+
+ BIPD(( "bip_buffer_write: exec %d->%d len %d\n", bip->fdin, bip->fdout, len ));
+
+ avail = BIP_BUFFER_SIZE - bip->a_end;
+ if (avail > 0)
+ {
+ /* we can append to region A */
+ if (avail > len)
+ avail = len;
+
+ memcpy( bip->buff + bip->a_end, src, avail );
+ src += avail;
+ count += avail;
+ len -= avail;
+
+ bip->a_end += avail;
+ if (bip->a_end == BIP_BUFFER_SIZE && bip->a_start == 0) {
+ bip->can_write = 0;
+ ResetEvent( bip->evt_write );
+ goto Exit;
+ }
+ }
+
+ if (len == 0)
+ goto Exit;
+
+ avail = bip->a_start - bip->b_end;
+ assert( avail > 0 ); /* since can_write is TRUE */
+
+ if (avail > len)
+ avail = len;
+
+ memcpy( bip->buff + bip->b_end, src, avail );
+ count += avail;
+ bip->b_end += avail;
+
+ if (bip->b_end == bip->a_start) {
+ bip->can_write = 0;
+ ResetEvent( bip->evt_write );
+ }
+
+Exit:
+ assert( count > 0 );
+
+ if ( !bip->can_read ) {
+ bip->can_read = 1;
+ SetEvent( bip->evt_read );
+ }
+
+ BIPD(( "bip_buffer_write: exit %d->%d count %d (as=%d ae=%d be=%d cw=%d cr=%d\n",
+ bip->fdin, bip->fdout, count, bip->a_start, bip->a_end, bip->b_end, bip->can_write, bip->can_read ));
+ LeaveCriticalSection( &bip->lock );
+
+ return count;
+ }
+
+static int
+bip_buffer_read( BipBuffer bip, void* dst, int len )
+{
+ int avail, count = 0;
+
+ if (len <= 0)
+ return 0;
+
+ BIPD(( "bip_buffer_read: enter %d->%d len %d\n", bip->fdin, bip->fdout, len ));
+
+ EnterCriticalSection( &bip->lock );
+ while ( !bip->can_read )
+ {
+#if 0
+ LeaveCriticalSection( &bip->lock );
+ errno = EAGAIN;
+ return -1;
+#else
+ int ret;
+ LeaveCriticalSection( &bip->lock );
+
+ if (bip->closed) {
+ errno = EPIPE;
+ return -1;
+ }
+
+ ret = WaitForSingleObject( bip->evt_read, INFINITE );
+ if (ret != WAIT_OBJECT_0) { /* probably closed buffer */
+ D( "bip_buffer_read: error %d->%d WaitForSingleObject returned %d, error %ld\n", bip->fdin, bip->fdout, ret, GetLastError());
+ return 0;
+ }
+ if (bip->closed) {
+ errno = EPIPE;
+ return -1;
+ }
+ EnterCriticalSection( &bip->lock );
+#endif
+ }
+
+ BIPD(( "bip_buffer_read: exec %d->%d len %d\n", bip->fdin, bip->fdout, len ));
+
+ avail = bip->a_end - bip->a_start;
+ assert( avail > 0 ); /* since can_read is TRUE */
+
+ if (avail > len)
+ avail = len;
+
+ memcpy( dst, bip->buff + bip->a_start, avail );
+ dst += avail;
+ count += avail;
+ len -= avail;
+
+ bip->a_start += avail;
+ if (bip->a_start < bip->a_end)
+ goto Exit;
+
+ bip->a_start = 0;
+ bip->a_end = bip->b_end;
+ bip->b_end = 0;
+
+ avail = bip->a_end;
+ if (avail > 0) {
+ if (avail > len)
+ avail = len;
+ memcpy( dst, bip->buff, avail );
+ count += avail;
+ bip->a_start += avail;
+
+ if ( bip->a_start < bip->a_end )
+ goto Exit;
+
+ bip->a_start = bip->a_end = 0;
+ }
+
+ bip->can_read = 0;
+ ResetEvent( bip->evt_read );
+
+Exit:
+ assert( count > 0 );
+
+ if (!bip->can_write ) {
+ bip->can_write = 1;
+ SetEvent( bip->evt_write );
+ }
+
+ BIPDUMP( (const unsigned char*)dst - count, count );
+ BIPD(( "bip_buffer_read: exit %d->%d count %d (as=%d ae=%d be=%d cw=%d cr=%d\n",
+ bip->fdin, bip->fdout, count, bip->a_start, bip->a_end, bip->b_end, bip->can_write, bip->can_read ));
+ LeaveCriticalSection( &bip->lock );
+
+ return count;
+}
+
+typedef struct SocketPairRec_
+{
+ BipBufferRec a2b_bip;
+ BipBufferRec b2a_bip;
+ FH a_fd;
+ int used;
+
+} SocketPairRec;
+
+void _fh_socketpair_init( FH f )
+{
+ f->fh_pair = NULL;
+}
+
+static int
+_fh_socketpair_close( FH f )
+{
+ if ( f->fh_pair ) {
+ SocketPair pair = f->fh_pair;
+
+ if ( f == pair->a_fd ) {
+ pair->a_fd = NULL;
+ }
+
+ bip_buffer_close( &pair->b2a_bip );
+ bip_buffer_close( &pair->a2b_bip );
+
+ if ( --pair->used == 0 ) {
+ bip_buffer_done( &pair->b2a_bip );
+ bip_buffer_done( &pair->a2b_bip );
+ free( pair );
+ }
+ f->fh_pair = NULL;
+ }
+ return 0;
+}
+
+static int
+_fh_socketpair_lseek( FH f, int pos, int origin )
+{
+ errno = ESPIPE;
+ return -1;
+}
+
+static int
+_fh_socketpair_read( FH f, void* buf, int len )
+{
+ SocketPair pair = f->fh_pair;
+ BipBuffer bip;
+
+ if (!pair)
+ return -1;
+
+ if ( f == pair->a_fd )
+ bip = &pair->b2a_bip;
+ else
+ bip = &pair->a2b_bip;
+
+ return bip_buffer_read( bip, buf, len );
+}
+
+static int
+_fh_socketpair_write( FH f, const void* buf, int len )
+{
+ SocketPair pair = f->fh_pair;
+ BipBuffer bip;
+
+ if (!pair)
+ return -1;
+
+ if ( f == pair->a_fd )
+ bip = &pair->a2b_bip;
+ else
+ bip = &pair->b2a_bip;
+
+ return bip_buffer_write( bip, buf, len );
+}
+
+
+static void _fh_socketpair_hook( FH f, int event, EventHook hook ); /* forward */
+
+static const FHClassRec _fh_socketpair_class =
+{
+ _fh_socketpair_init,
+ _fh_socketpair_close,
+ _fh_socketpair_lseek,
+ _fh_socketpair_read,
+ _fh_socketpair_write,
+ _fh_socketpair_hook
+};
+
+
+int adb_socketpair( int sv[2] )
+{
+ FH fa, fb;
+ SocketPair pair;
+
+ fa = _fh_alloc( &_fh_socketpair_class );
+ fb = _fh_alloc( &_fh_socketpair_class );
+
+ if (!fa || !fb)
+ goto Fail;
+
+ pair = malloc( sizeof(*pair) );
+ if (pair == NULL) {
+ D("adb_socketpair: not enough memory to allocate pipes\n" );
+ goto Fail;
+ }
+
+ bip_buffer_init( &pair->a2b_bip );
+ bip_buffer_init( &pair->b2a_bip );
+
+ fa->fh_pair = pair;
+ fb->fh_pair = pair;
+ pair->used = 2;
+ pair->a_fd = fa;
+
+ sv[0] = _fh_to_int(fa);
+ sv[1] = _fh_to_int(fb);
+
+ pair->a2b_bip.fdin = sv[0];
+ pair->a2b_bip.fdout = sv[1];
+ pair->b2a_bip.fdin = sv[1];
+ pair->b2a_bip.fdout = sv[0];
+
+ snprintf( fa->name, sizeof(fa->name), "%d(pair:%d)", sv[0], sv[1] );
+ snprintf( fb->name, sizeof(fb->name), "%d(pair:%d)", sv[1], sv[0] );
+ D( "adb_socketpair: returns (%d, %d)\n", sv[0], sv[1] );
+ return 0;
+
+Fail:
+ _fh_close(fb);
+ _fh_close(fa);
+ return -1;
+}
+
+/**************************************************************************/
+/**************************************************************************/
+/***** *****/
+/***** fdevents emulation *****/
+/***** *****/
+/***** this is a very simple implementation, we rely on the fact *****/
+/***** that ADB doesn't use FDE_ERROR. *****/
+/***** *****/
+/**************************************************************************/
+/**************************************************************************/
+
+#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;
+
+typedef struct EventLooperRec_* EventLooper;
+
+typedef struct EventHookRec_
+{
+ EventHook next;
+ FH fh;
+ HANDLE h;
+ int wanted; /* wanted event flags */
+ int ready; /* ready event flags */
+ void* aux;
+ void (*prepare)( EventHook hook );
+ int (*start) ( EventHook hook );
+ void (*stop) ( EventHook hook );
+ int (*check) ( EventHook hook );
+ int (*peek) ( EventHook hook );
+} EventHookRec;
+
+static EventHook _free_hooks;
+
+static EventHook
+event_hook_alloc( FH fh )
+{
+ EventHook hook = _free_hooks;
+ if (hook != NULL)
+ _free_hooks = hook->next;
+ else {
+ hook = malloc( sizeof(*hook) );
+ if (hook == NULL)
+ fatal( "could not allocate event hook\n" );
+ }
+ hook->next = NULL;
+ hook->fh = fh;
+ hook->wanted = 0;
+ hook->ready = 0;
+ hook->h = INVALID_HANDLE_VALUE;
+ hook->aux = NULL;
+
+ hook->prepare = NULL;
+ hook->start = NULL;
+ hook->stop = NULL;
+ hook->check = NULL;
+ hook->peek = NULL;
+
+ return hook;
+}
+
+static void
+event_hook_free( EventHook hook )
+{
+ hook->fh = NULL;
+ hook->wanted = 0;
+ hook->ready = 0;
+ hook->next = _free_hooks;
+ _free_hooks = hook;
+}
+
+
+static void
+event_hook_signal( EventHook hook )
+{
+ FH f = hook->fh;
+ int fd = _fh_to_int(f);
+ fdevent* fde = fd_table[ fd - WIN32_FH_BASE ];
+
+ if (fde != NULL && fde->fd == fd) {
+ if ((fde->state & FDE_PENDING) == 0) {
+ fde->state |= FDE_PENDING;
+ fdevent_plist_enqueue( fde );
+ }
+ fde->events |= hook->wanted;
+ }
+}
+
+
+#define MAX_LOOPER_HANDLES WIN32_MAX_FHS
+
+typedef struct EventLooperRec_
+{
+ EventHook hooks;
+ HANDLE htab[ MAX_LOOPER_HANDLES ];
+ int htab_count;
+
+} EventLooperRec;
+
+static EventHook*
+event_looper_find_p( EventLooper looper, FH fh )
+{
+ EventHook *pnode = &looper->hooks;
+ EventHook node = *pnode;
+ for (;;) {
+ if ( node == NULL || node->fh == fh )
+ break;
+ pnode = &node->next;
+ node = *pnode;
+ }
+ return pnode;
+}
+
+static void
+event_looper_hook( EventLooper looper, int fd, int events )
+{
+ FH f = _fh_from_int(fd);
+ EventHook *pnode;
+ EventHook node;
+
+ if (f == NULL) /* invalid arg */ {
+ D("event_looper_hook: invalid fd=%d\n", fd);
+ return;
+ }
+
+ pnode = event_looper_find_p( looper, f );
+ node = *pnode;
+ if ( node == NULL ) {
+ node = event_hook_alloc( f );
+ node->next = *pnode;
+ *pnode = node;
+ }
+
+ if ( (node->wanted & events) != events ) {
+ /* this should update start/stop/check/peek */
+ D("event_looper_hook: call hook for %d (new=%x, old=%x)\n",
+ fd, node->wanted, events);
+ f->clazz->_fh_hook( f, events & ~node->wanted, node );
+ node->wanted |= events;
+ } else {
+ D("event_looper_hook: ignoring events %x for %d wanted=%x)\n",
+ events, fd, node->wanted);
+ }
+}
+
+static void
+event_looper_unhook( EventLooper looper, int fd, int events )
+{
+ FH fh = _fh_from_int(fd);
+ EventHook *pnode = event_looper_find_p( looper, fh );
+ EventHook node = *pnode;
+
+ if (node != NULL) {
+ int events2 = events & node->wanted;
+ if ( events2 == 0 ) {
+ D( "event_looper_unhook: events %x not registered for fd %d\n", events, fd );
+ return;
+ }
+ node->wanted &= ~events2;
+ if (!node->wanted) {
+ *pnode = node->next;
+ event_hook_free( node );
+ }
+ }
+}
+
+static EventLooperRec win32_looper;
+
+static void fdevent_init(void)
+{
+ win32_looper.htab_count = 0;
+ win32_looper.hooks = NULL;
+}
+
+static void fdevent_connect(fdevent *fde)
+{
+ EventLooper looper = &win32_looper;
+ int events = fde->state & FDE_EVENTMASK;
+
+ if (events != 0)
+ event_looper_hook( looper, fde->fd, events );
+}
+
+static void fdevent_disconnect(fdevent *fde)
+{
+ EventLooper looper = &win32_looper;
+ int events = fde->state & FDE_EVENTMASK;
+
+ if (events != 0)
+ event_looper_unhook( looper, fde->fd, events );
+}
+
+static void fdevent_update(fdevent *fde, unsigned events)
+{
+ EventLooper looper = &win32_looper;
+ unsigned events0 = fde->state & FDE_EVENTMASK;
+
+ if (events != events0) {
+ int removes = events0 & ~events;
+ int adds = events & ~events0;
+ if (removes) {
+ D("fdevent_update: remove %x from %d\n", removes, fde->fd);
+ event_looper_unhook( looper, fde->fd, removes );
+ }
+ if (adds) {
+ D("fdevent_update: add %x to %d\n", adds, fde->fd);
+ event_looper_hook ( looper, fde->fd, adds );
+ }
+ }
+}
+
+static void fdevent_process()
+{
+ EventLooper looper = &win32_looper;
+ EventHook hook;
+ int gotone = 0;
+
+ /* if we have at least one ready hook, execute it/them */
+ for (hook = looper->hooks; hook; hook = hook->next) {
+ hook->ready = 0;
+ if (hook->prepare) {
+ hook->prepare(hook);
+ if (hook->ready != 0) {
+ event_hook_signal( hook );
+ gotone = 1;
+ }
+ }
+ }
+
+ /* nothing's ready yet, so wait for something to happen */
+ if (!gotone)
+ {
+ looper->htab_count = 0;
+
+ for (hook = looper->hooks; hook; hook = hook->next)
+ {
+ if (hook->start && !hook->start(hook)) {
+ D( "fdevent_process: error when starting a hook\n" );
+ return;
+ }
+ if (hook->h != INVALID_HANDLE_VALUE) {
+ int nn;
+
+ for (nn = 0; nn < looper->htab_count; nn++)
+ {
+ if ( looper->htab[nn] == hook->h )
+ goto DontAdd;
+ }
+ looper->htab[ looper->htab_count++ ] = hook->h;
+ DontAdd:
+ ;
+ }
+ }
+
+ if (looper->htab_count == 0) {
+ D( "fdevent_process: nothing to wait for !!\n" );
+ return;
+ }
+
+ do
+ {
+ int wait_ret;
+
+ D( "adb_win32: waiting for %d events\n", looper->htab_count );
+ if (looper->htab_count > MAXIMUM_WAIT_OBJECTS) {
+ D("handle count %d exceeds MAXIMUM_WAIT_OBJECTS, aborting!\n", looper->htab_count);
+ abort();
+ }
+ wait_ret = WaitForMultipleObjects( looper->htab_count, looper->htab, FALSE, INFINITE );
+ if (wait_ret == (int)WAIT_FAILED) {
+ D( "adb_win32: wait failed, error %ld\n", GetLastError() );
+ } else {
+ D( "adb_win32: got one (index %d)\n", wait_ret );
+
+ /* according to Cygwin, some objects like consoles wake up on "inappropriate" events
+ * like mouse movements. we need to filter these with the "check" function
+ */
+ if ((unsigned)wait_ret < (unsigned)looper->htab_count)
+ {
+ for (hook = looper->hooks; hook; hook = hook->next)
+ {
+ if ( looper->htab[wait_ret] == hook->h &&
+ (!hook->check || hook->check(hook)) )
+ {
+ D( "adb_win32: signaling %s for %x\n", hook->fh->name, hook->ready );
+ event_hook_signal( hook );
+ gotone = 1;
+ break;
+ }
+ }
+ }
+ }
+ }
+ while (!gotone);
+
+ for (hook = looper->hooks; hook; hook = hook->next) {
+ if (hook->stop)
+ hook->stop( hook );
+ }
+ }
+
+ for (hook = looper->hooks; hook; hook = hook->next) {
+ if (hook->peek && hook->peek(hook))
+ event_hook_signal( hook );
+ }
+}
+
+
+static void fdevent_register(fdevent *fde)
+{
+ int fd = fde->fd - WIN32_FH_BASE;
+
+ if(fd < 0) {
+ FATAL("bogus negative fd (%d)\n", fde->fd);
+ }
+
+ if(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 <= 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[fd] = fde;
+}
+
+static void fdevent_unregister(fdevent *fde)
+{
+ int fd = fde->fd - WIN32_FH_BASE;
+
+ if((fd < 0) || (fd >= fd_table_max)) {
+ FATAL("fd out of range (%d)\n", fde->fd);
+ }
+
+ if(fd_table[fd] != fde) {
+ FATAL("fd_table out of sync");
+ }
+
+ fd_table[fd] = 0;
+
+ if(!(fde->state & FDE_DONT_CLOSE)) {
+ dump_fde(fde, "close");
+ adb_close(fde->fd);
+ }
+}
+
+static void fdevent_plist_enqueue(fdevent *node)
+{
+ fdevent *list = &list_pending;
+
+ node->next = list;
+ node->prev = list->prev;
+ node->prev->next = node;
+ list->prev = node;
+}
+
+static void fdevent_plist_remove(fdevent *node)
+{
+ node->prev->next = node->next;
+ node->next->prev = node->prev;
+ node->next = 0;
+ node->prev = 0;
+}
+
+static fdevent *fdevent_plist_dequeue(void)
+{
+ fdevent *list = &list_pending;
+ fdevent *node = list->next;
+
+ if(node == list) return 0;
+
+ list->next = node->next;
+ list->next->prev = list;
+ node->next = 0;
+ node->prev = 0;
+
+ return node;
+}
+
+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;
+
+ 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) == (int)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);
+ }
+ }
+}
+
+/** FILE EVENT HOOKS
+ **/
+
+static void _event_file_prepare( EventHook hook )
+{
+ if (hook->wanted & (FDE_READ|FDE_WRITE)) {
+ /* we can always read/write */
+ hook->ready |= hook->wanted & (FDE_READ|FDE_WRITE);
+ }
+}
+
+static int _event_file_peek( EventHook hook )
+{
+ return (hook->wanted & (FDE_READ|FDE_WRITE));
+}
+
+static void _fh_file_hook( FH f, int events, EventHook hook )
+{
+ hook->h = f->fh_handle;
+ hook->prepare = _event_file_prepare;
+ hook->peek = _event_file_peek;
+}
+
+/** SOCKET EVENT HOOKS
+ **/
+
+static void _event_socket_verify( EventHook hook, WSANETWORKEVENTS* evts )
+{
+ if ( evts->lNetworkEvents & (FD_READ|FD_ACCEPT|FD_CLOSE) ) {
+ if (hook->wanted & FDE_READ)
+ hook->ready |= FDE_READ;
+ if ((evts->iErrorCode[FD_READ] != 0) && hook->wanted & FDE_ERROR)
+ hook->ready |= FDE_ERROR;
+ }
+ if ( evts->lNetworkEvents & (FD_WRITE|FD_CONNECT|FD_CLOSE) ) {
+ if (hook->wanted & FDE_WRITE)
+ hook->ready |= FDE_WRITE;
+ if ((evts->iErrorCode[FD_WRITE] != 0) && hook->wanted & FDE_ERROR)
+ hook->ready |= FDE_ERROR;
+ }
+ if ( evts->lNetworkEvents & FD_OOB ) {
+ if (hook->wanted & FDE_ERROR)
+ hook->ready |= FDE_ERROR;
+ }
+}
+
+static void _event_socket_prepare( EventHook hook )
+{
+ WSANETWORKEVENTS evts;
+
+ /* look if some of the events we want already happened ? */
+ if (!WSAEnumNetworkEvents( hook->fh->fh_socket, NULL, &evts ))
+ _event_socket_verify( hook, &evts );
+}
+
+static int _socket_wanted_to_flags( int wanted )
+{
+ int flags = 0;
+ if (wanted & FDE_READ)
+ flags |= FD_READ | FD_ACCEPT | FD_CLOSE;
+
+ if (wanted & FDE_WRITE)
+ flags |= FD_WRITE | FD_CONNECT | FD_CLOSE;
+
+ if (wanted & FDE_ERROR)
+ flags |= FD_OOB;
+
+ return flags;
+}
+
+static int _event_socket_start( EventHook hook )
+{
+ /* create an event which we're going to wait for */
+ FH fh = hook->fh;
+ long flags = _socket_wanted_to_flags( hook->wanted );
+
+ hook->h = fh->event;
+ if (hook->h == INVALID_HANDLE_VALUE) {
+ D( "_event_socket_start: no event for %s\n", fh->name );
+ return 0;
+ }
+
+ if ( flags != fh->mask ) {
+ D( "_event_socket_start: hooking %s for %x (flags %ld)\n", hook->fh->name, hook->wanted, flags );
+ if ( WSAEventSelect( fh->fh_socket, hook->h, flags ) ) {
+ D( "_event_socket_start: WSAEventSelect() for %s failed, error %d\n", hook->fh->name, WSAGetLastError() );
+ CloseHandle( hook->h );
+ hook->h = INVALID_HANDLE_VALUE;
+ exit(1);
+ return 0;
+ }
+ fh->mask = flags;
+ }
+ return 1;
+}
+
+static void _event_socket_stop( EventHook hook )
+{
+ hook->h = INVALID_HANDLE_VALUE;
+}
+
+static int _event_socket_check( EventHook hook )
+{
+ int result = 0;
+ FH fh = hook->fh;
+ WSANETWORKEVENTS evts;
+
+ if (!WSAEnumNetworkEvents( fh->fh_socket, hook->h, &evts ) ) {
+ _event_socket_verify( hook, &evts );
+ result = (hook->ready != 0);
+ if (result) {
+ ResetEvent( hook->h );
+ }
+ }
+ D( "_event_socket_check %s returns %d\n", fh->name, result );
+ return result;
+}
+
+static int _event_socket_peek( EventHook hook )
+{
+ WSANETWORKEVENTS evts;
+ FH fh = hook->fh;
+
+ /* look if some of the events we want already happened ? */
+ if (!WSAEnumNetworkEvents( fh->fh_socket, NULL, &evts )) {
+ _event_socket_verify( hook, &evts );
+ if (hook->ready)
+ ResetEvent( hook->h );
+ }
+
+ return hook->ready != 0;
+}
+
+
+
+static void _fh_socket_hook( FH f, int events, EventHook hook )
+{
+ hook->prepare = _event_socket_prepare;
+ hook->start = _event_socket_start;
+ hook->stop = _event_socket_stop;
+ hook->check = _event_socket_check;
+ hook->peek = _event_socket_peek;
+
+ _event_socket_start( hook );
+}
+
+/** SOCKETPAIR EVENT HOOKS
+ **/
+
+static void _event_socketpair_prepare( EventHook hook )
+{
+ FH fh = hook->fh;
+ SocketPair pair = fh->fh_pair;
+ BipBuffer rbip = (pair->a_fd == fh) ? &pair->b2a_bip : &pair->a2b_bip;
+ BipBuffer wbip = (pair->a_fd == fh) ? &pair->a2b_bip : &pair->b2a_bip;
+
+ if (hook->wanted & FDE_READ && rbip->can_read)
+ hook->ready |= FDE_READ;
+
+ if (hook->wanted & FDE_WRITE && wbip->can_write)
+ hook->ready |= FDE_WRITE;
+ }
+
+ static int _event_socketpair_start( EventHook hook )
+ {
+ FH fh = hook->fh;
+ SocketPair pair = fh->fh_pair;
+ BipBuffer rbip = (pair->a_fd == fh) ? &pair->b2a_bip : &pair->a2b_bip;
+ BipBuffer wbip = (pair->a_fd == fh) ? &pair->a2b_bip : &pair->b2a_bip;
+
+ if (hook->wanted == FDE_READ)
+ hook->h = rbip->evt_read;
+
+ else if (hook->wanted == FDE_WRITE)
+ hook->h = wbip->evt_write;
+
+ else {
+ D("_event_socketpair_start: can't handle FDE_READ+FDE_WRITE\n" );
+ return 0;
+ }
+ D( "_event_socketpair_start: hook %s for %x wanted=%x\n",
+ hook->fh->name, _fh_to_int(fh), hook->wanted);
+ return 1;
+}
+
+static int _event_socketpair_peek( EventHook hook )
+{
+ _event_socketpair_prepare( hook );
+ return hook->ready != 0;
+}
+
+static void _fh_socketpair_hook( FH fh, int events, EventHook hook )
+{
+ hook->prepare = _event_socketpair_prepare;
+ hook->start = _event_socketpair_start;
+ hook->peek = _event_socketpair_peek;
+}
+
+
+void
+adb_sysdeps_init( void )
+{
+#define ADB_MUTEX(x) InitializeCriticalSection( & x );
+#include "mutex_list.h"
+ InitializeCriticalSection( &_win32_lock );
+}
+
diff --git a/adb/test_track_devices.c b/adb/test_track_devices.c
new file mode 100644
index 0000000..77b3ad9
--- /dev/null
+++ b/adb/test_track_devices.c
@@ -0,0 +1,97 @@
+/* a simple test program, connects to ADB server, and opens a track-devices session */
+#include <netdb.h>
+#include <sys/socket.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <memory.h>
+
+static void
+panic( const char* msg )
+{
+ fprintf(stderr, "PANIC: %s: %s\n", msg, strerror(errno));
+ exit(1);
+}
+
+static int
+unix_write( int fd, const char* buf, int len )
+{
+ int result = 0;
+ while (len > 0) {
+ int len2 = write(fd, buf, len);
+ if (len2 < 0) {
+ if (errno == EINTR || errno == EAGAIN)
+ continue;
+ return -1;
+ }
+ result += len2;
+ len -= len2;
+ buf += len2;
+ }
+ return result;
+}
+
+static int
+unix_read( int fd, char* buf, int len )
+{
+ int result = 0;
+ while (len > 0) {
+ int len2 = read(fd, buf, len);
+ if (len2 < 0) {
+ if (errno == EINTR || errno == EAGAIN)
+ continue;
+ return -1;
+ }
+ result += len2;
+ len -= len2;
+ buf += len2;
+ }
+ return result;
+}
+
+
+int main( void )
+{
+ int ret, s;
+ struct sockaddr_in server;
+ char buffer[1024];
+ const char* request = "host:track-devices";
+ int len;
+
+ memset( &server, 0, sizeof(server) );
+ server.sin_family = AF_INET;
+ server.sin_port = htons(5037);
+ server.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+ s = socket( PF_INET, SOCK_STREAM, 0 );
+ ret = connect( s, (struct sockaddr*) &server, sizeof(server) );
+ if (ret < 0) panic( "could not connect to server" );
+
+ /* send the request */
+ len = snprintf( buffer, sizeof buffer, "%04x%s", strlen(request), request );
+ if (unix_write(s, buffer, len) < 0)
+ panic( "could not send request" );
+
+ /* read the OKAY answer */
+ if (unix_read(s, buffer, 4) != 4)
+ panic( "could not read request" );
+
+ printf( "server answer: %.*s\n", 4, buffer );
+
+ /* now loop */
+ for (;;) {
+ char head[5] = "0000";
+
+ if (unix_read(s, head, 4) < 0)
+ panic("could not read length");
+
+ if ( sscanf( head, "%04x", &len ) != 1 )
+ panic("could not decode length");
+
+ if (unix_read(s, buffer, len) != len)
+ panic("could not read data");
+
+ printf( "received header %.*s (%d bytes):\n%.*s", 4, head, len, len, buffer );
+ }
+ close(s);
+}
diff --git a/adb/test_track_jdwp.c b/adb/test_track_jdwp.c
new file mode 100644
index 0000000..8ecc6b8
--- /dev/null
+++ b/adb/test_track_jdwp.c
@@ -0,0 +1,97 @@
+/* a simple test program, connects to ADB server, and opens a track-devices session */
+#include <netdb.h>
+#include <sys/socket.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <memory.h>
+
+static void
+panic( const char* msg )
+{
+ fprintf(stderr, "PANIC: %s: %s\n", msg, strerror(errno));
+ exit(1);
+}
+
+static int
+unix_write( int fd, const char* buf, int len )
+{
+ int result = 0;
+ while (len > 0) {
+ int len2 = write(fd, buf, len);
+ if (len2 < 0) {
+ if (errno == EINTR || errno == EAGAIN)
+ continue;
+ return -1;
+ }
+ result += len2;
+ len -= len2;
+ buf += len2;
+ }
+ return result;
+}
+
+static int
+unix_read( int fd, char* buf, int len )
+{
+ int result = 0;
+ while (len > 0) {
+ int len2 = read(fd, buf, len);
+ if (len2 < 0) {
+ if (errno == EINTR || errno == EAGAIN)
+ continue;
+ return -1;
+ }
+ result += len2;
+ len -= len2;
+ buf += len2;
+ }
+ return result;
+}
+
+
+int main( void )
+{
+ int ret, s;
+ struct sockaddr_in server;
+ char buffer[1024];
+ const char* request = "track-jdwp";
+ int len;
+
+ memset( &server, 0, sizeof(server) );
+ server.sin_family = AF_INET;
+ server.sin_port = htons(5037);
+ server.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+ s = socket( PF_INET, SOCK_STREAM, 0 );
+ ret = connect( s, (struct sockaddr*) &server, sizeof(server) );
+ if (ret < 0) panic( "could not connect to server" );
+
+ /* send the request */
+ len = snprintf( buffer, sizeof buffer, "%04x%s", strlen(request), request );
+ if (unix_write(s, buffer, len) < 0)
+ panic( "could not send request" );
+
+ /* read the OKAY answer */
+ if (unix_read(s, buffer, 4) != 4)
+ panic( "could not read request" );
+
+ printf( "server answer: %.*s\n", 4, buffer );
+
+ /* now loop */
+ for (;;) {
+ char head[5] = "0000";
+
+ if (unix_read(s, head, 4) < 0)
+ panic("could not read length");
+
+ if ( sscanf( head, "%04x", &len ) != 1 )
+ panic("could not decode length");
+
+ if (unix_read(s, buffer, len) != len)
+ panic("could not read data");
+
+ printf( "received header %.*s (%d bytes):\n%.*s", 4, head, len, len, buffer );
+ }
+ close(s);
+}
diff --git a/adb/transport.c b/adb/transport.c
new file mode 100644
index 0000000..62bdfdb
--- /dev/null
+++ b/adb/transport.c
@@ -0,0 +1,1042 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+
+#include "sysdeps.h"
+
+#define TRACE_TAG TRACE_TRANSPORT
+#include "adb.h"
+
+static void transport_unref(atransport *t);
+
+static atransport transport_list = {
+ .next = &transport_list,
+ .prev = &transport_list,
+};
+
+ADB_MUTEX_DEFINE( transport_lock );
+
+#if ADB_TRACE
+static void dump_hex( const unsigned char* ptr, size_t len )
+{
+ int nn, len2 = len;
+
+ if (len2 > 16) len2 = 16;
+
+ for (nn = 0; nn < len2; nn++)
+ D("%02x", ptr[nn]);
+ D(" ");
+
+ for (nn = 0; nn < len2; nn++) {
+ int c = ptr[nn];
+ if (c < 32 || c > 127)
+ c = '.';
+ D("%c", c);
+ }
+ D("\n");
+ fflush(stdout);
+}
+#endif
+
+void
+kick_transport(atransport* t)
+{
+ if (t && !t->kicked)
+ {
+ int kicked;
+
+ adb_mutex_lock(&transport_lock);
+ kicked = t->kicked;
+ if (!kicked)
+ t->kicked = 1;
+ adb_mutex_unlock(&transport_lock);
+
+ if (!kicked)
+ t->kick(t);
+ }
+}
+
+void
+run_transport_disconnects(atransport* t)
+{
+ adisconnect* dis = t->disconnects.next;
+
+ D("run_transport_disconnects: %p (%s)\n", t, t->serial ? t->serial : "unknown" );
+ while (dis != &t->disconnects) {
+ adisconnect* next = dis->next;
+ dis->func( dis->opaque, t );
+ dis = next;
+ }
+}
+
+static int
+read_packet(int fd, apacket** ppacket)
+{
+ char *p = (char*)ppacket; /* really read a packet address */
+ int r;
+ int len = sizeof(*ppacket);
+ while(len > 0) {
+ r = adb_read(fd, p, len);
+ if(r > 0) {
+ len -= r;
+ p += r;
+ } else {
+ D("read_packet: %d error %d %d\n", fd, r, errno);
+ if((r < 0) && (errno == EINTR)) continue;
+ return -1;
+ }
+ }
+
+#if ADB_TRACE
+ if (ADB_TRACING)
+ {
+ unsigned command = (*ppacket)->msg.command;
+ int len = (*ppacket)->msg.data_length;
+ char cmd[5];
+ int n;
+
+ for (n = 0; n < 4; n++) {
+ int b = (command >> (n*8)) & 255;
+ if (b >= 32 && b < 127)
+ cmd[n] = (char)b;
+ else
+ cmd[n] = '.';
+ }
+ cmd[4] = 0;
+
+ D("read_packet: %d ok: [%08x %s] %08x %08x (%d) ",
+ fd, command, cmd, (*ppacket)->msg.arg0, (*ppacket)->msg.arg1, len);
+ dump_hex((*ppacket)->data, len);
+ }
+#endif
+ return 0;
+}
+
+static int
+write_packet(int fd, apacket** ppacket)
+{
+ char *p = (char*) ppacket; /* we really write the packet address */
+ int r, len = sizeof(ppacket);
+
+#if ADB_TRACE
+ if (ADB_TRACING)
+ {
+ unsigned command = (*ppacket)->msg.command;
+ int len = (*ppacket)->msg.data_length;
+ char cmd[5];
+ int n;
+
+ for (n = 0; n < 4; n++) {
+ int b = (command >> (n*8)) & 255;
+ if (b >= 32 && b < 127)
+ cmd[n] = (char)b;
+ else
+ cmd[n] = '.';
+ }
+ cmd[4] = 0;
+
+ D("write_packet: %d [%08x %s] %08x %08x (%d) ",
+ fd, command, cmd, (*ppacket)->msg.arg0, (*ppacket)->msg.arg1, len);
+ dump_hex((*ppacket)->data, len);
+ }
+#endif
+ len = sizeof(ppacket);
+ while(len > 0) {
+ r = adb_write(fd, p, len);
+ if(r > 0) {
+ len -= r;
+ p += r;
+ } else {
+ D("write_packet: %d error %d %d\n", fd, r, errno);
+ if((r < 0) && (errno == EINTR)) continue;
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static void transport_socket_events(int fd, unsigned events, void *_t)
+{
+ if(events & FDE_READ){
+ apacket *p = 0;
+ if(read_packet(fd, &p)){
+ D("failed to read packet from transport socket on fd %d\n", fd);
+ } else {
+ handle_packet(p, (atransport *) _t);
+ }
+ }
+}
+
+void send_packet(apacket *p, atransport *t)
+{
+ unsigned char *x;
+ unsigned sum;
+ unsigned count;
+
+ p->msg.magic = p->msg.command ^ 0xffffffff;
+
+ count = p->msg.data_length;
+ x = (unsigned char *) p->data;
+ sum = 0;
+ while(count-- > 0){
+ sum += *x++;
+ }
+ p->msg.data_check = sum;
+
+ print_packet("send", p);
+
+ if (t == NULL) {
+ fatal_errno("Transport is null");
+ D("Transport is null \n");
+ }
+
+ if(write_packet(t->transport_socket, &p)){
+ fatal_errno("cannot enqueue packet on transport socket");
+ }
+}
+
+/* The transport is opened by transport_register_func before
+** the input and output threads are started.
+**
+** The output thread issues a SYNC(1, token) message to let
+** the input thread know to start things up. In the event
+** of transport IO failure, the output thread will post a
+** SYNC(0,0) message to ensure shutdown.
+**
+** The transport will not actually be closed until both
+** threads exit, but the input thread will kick the transport
+** on its way out to disconnect the underlying device.
+*/
+
+static void *output_thread(void *_t)
+{
+ atransport *t = _t;
+ apacket *p;
+
+ D("from_remote: starting thread for transport %p, on fd %d\n", t, t->fd );
+
+ D("from_remote: transport %p SYNC online (%d)\n", t, t->sync_token + 1);
+ p = get_apacket();
+ p->msg.command = A_SYNC;
+ p->msg.arg0 = 1;
+ p->msg.arg1 = ++(t->sync_token);
+ p->msg.magic = A_SYNC ^ 0xffffffff;
+ if(write_packet(t->fd, &p)) {
+ put_apacket(p);
+ D("from_remote: failed to write SYNC apacket to transport %p", t);
+ goto oops;
+ }
+
+ D("from_remote: data pump for transport %p\n", t);
+ for(;;) {
+ p = get_apacket();
+
+ if(t->read_from_remote(p, t) == 0){
+ D("from_remote: received remote packet, sending to transport %p\n",
+ t);
+ if(write_packet(t->fd, &p)){
+ put_apacket(p);
+ D("from_remote: failed to write apacket to transport %p", t);
+ goto oops;
+ }
+ } else {
+ D("from_remote: remote read failed for transport %p\n", p);
+ put_apacket(p);
+ break;
+ }
+ }
+
+ D("from_remote: SYNC offline for transport %p\n", t);
+ p = get_apacket();
+ p->msg.command = A_SYNC;
+ p->msg.arg0 = 0;
+ p->msg.arg1 = 0;
+ p->msg.magic = A_SYNC ^ 0xffffffff;
+ if(write_packet(t->fd, &p)) {
+ put_apacket(p);
+ D("from_remote: failed to write SYNC apacket to transport %p", t);
+ }
+
+oops:
+ D("from_remote: thread is exiting for transport %p\n", t);
+ kick_transport(t);
+ transport_unref(t);
+ return 0;
+}
+
+static void *input_thread(void *_t)
+{
+ atransport *t = _t;
+ apacket *p;
+ int active = 0;
+
+ D("to_remote: starting input_thread for %p, reading from fd %d\n",
+ t, t->fd);
+
+ for(;;){
+ if(read_packet(t->fd, &p)) {
+ D("to_remote: failed to read apacket from transport %p on fd %d\n",
+ t, t->fd );
+ break;
+ }
+ if(p->msg.command == A_SYNC){
+ if(p->msg.arg0 == 0) {
+ D("to_remote: transport %p SYNC offline\n", t);
+ put_apacket(p);
+ break;
+ } else {
+ if(p->msg.arg1 == t->sync_token) {
+ D("to_remote: transport %p SYNC online\n", t);
+ active = 1;
+ } else {
+ D("to_remote: trandport %p ignoring SYNC %d != %d\n",
+ t, p->msg.arg1, t->sync_token);
+ }
+ }
+ } else {
+ if(active) {
+ D("to_remote: transport %p got packet, sending to remote\n", t);
+ t->write_to_remote(p, t);
+ } else {
+ D("to_remote: transport %p ignoring packet while offline\n", t);
+ }
+ }
+
+ put_apacket(p);
+ }
+
+ // this is necessary to avoid a race condition that occured when a transport closes
+ // while a client socket is still active.
+ close_all_sockets(t);
+
+ D("to_remote: thread is exiting for transport %p, fd %d\n", t, t->fd);
+ kick_transport(t);
+ transport_unref(t);
+ return 0;
+}
+
+
+static int transport_registration_send = -1;
+static int transport_registration_recv = -1;
+static fdevent transport_registration_fde;
+
+
+#if ADB_HOST
+static int list_transports_msg(char* buffer, size_t bufferlen)
+{
+ char head[5];
+ int len;
+
+ len = list_transports(buffer+4, bufferlen-4);
+ snprintf(head, sizeof(head), "%04x", len);
+ memcpy(buffer, head, 4);
+ len += 4;
+ return len;
+}
+
+/* this adds support required by the 'track-devices' service.
+ * this is used to send the content of "list_transport" to any
+ * number of client connections that want it through a single
+ * live TCP connection
+ */
+typedef struct device_tracker device_tracker;
+struct device_tracker {
+ asocket socket;
+ int update_needed;
+ device_tracker* next;
+};
+
+/* linked list of all device trackers */
+static device_tracker* device_tracker_list;
+
+static void
+device_tracker_remove( device_tracker* tracker )
+{
+ device_tracker** pnode = &device_tracker_list;
+ device_tracker* node = *pnode;
+
+ adb_mutex_lock( &transport_lock );
+ while (node) {
+ if (node == tracker) {
+ *pnode = node->next;
+ break;
+ }
+ pnode = &node->next;
+ node = *pnode;
+ }
+ adb_mutex_unlock( &transport_lock );
+}
+
+static void
+device_tracker_close( asocket* socket )
+{
+ device_tracker* tracker = (device_tracker*) socket;
+ asocket* peer = socket->peer;
+
+ D( "device tracker %p removed\n", tracker);
+ if (peer) {
+ peer->peer = NULL;
+ peer->close(peer);
+ }
+ device_tracker_remove(tracker);
+ free(tracker);
+}
+
+static int
+device_tracker_enqueue( asocket* socket, apacket* p )
+{
+ /* you can't read from a device tracker, close immediately */
+ put_apacket(p);
+ device_tracker_close(socket);
+ return -1;
+}
+
+static int
+device_tracker_send( device_tracker* tracker,
+ const char* buffer,
+ int len )
+{
+ apacket* p = get_apacket();
+ asocket* peer = tracker->socket.peer;
+
+ memcpy(p->data, buffer, len);
+ p->len = len;
+ return peer->enqueue( peer, p );
+}
+
+
+static void
+device_tracker_ready( asocket* socket )
+{
+ device_tracker* tracker = (device_tracker*) socket;
+
+ /* we want to send the device list when the tracker connects
+ * for the first time, even if no update occured */
+ if (tracker->update_needed > 0) {
+ char buffer[1024];
+ int len;
+
+ tracker->update_needed = 0;
+
+ len = list_transports_msg(buffer, sizeof(buffer));
+ device_tracker_send(tracker, buffer, len);
+ }
+}
+
+
+asocket*
+create_device_tracker(void)
+{
+ device_tracker* tracker = calloc(1,sizeof(*tracker));
+
+ if(tracker == 0) fatal("cannot allocate device tracker");
+
+ D( "device tracker %p created\n", tracker);
+
+ tracker->socket.enqueue = device_tracker_enqueue;
+ tracker->socket.ready = device_tracker_ready;
+ tracker->socket.close = device_tracker_close;
+ tracker->update_needed = 1;
+
+ tracker->next = device_tracker_list;
+ device_tracker_list = tracker;
+
+ return &tracker->socket;
+}
+
+
+/* call this function each time the transport list has changed */
+void update_transports(void)
+{
+ char buffer[1024];
+ int len;
+ device_tracker* tracker;
+
+ len = list_transports_msg(buffer, sizeof(buffer));
+
+ tracker = device_tracker_list;
+ while (tracker != NULL) {
+ device_tracker* next = tracker->next;
+ /* note: this may destroy the tracker if the connection is closed */
+ device_tracker_send(tracker, buffer, len);
+ tracker = next;
+ }
+}
+#else
+void update_transports(void)
+{
+ // nothing to do on the device side
+}
+#endif // ADB_HOST
+
+typedef struct tmsg tmsg;
+struct tmsg
+{
+ atransport *transport;
+ int action;
+};
+
+static int
+transport_read_action(int fd, struct tmsg* m)
+{
+ char *p = (char*)m;
+ int len = sizeof(*m);
+ int r;
+
+ while(len > 0) {
+ r = adb_read(fd, p, len);
+ if(r > 0) {
+ len -= r;
+ p += r;
+ } else {
+ if((r < 0) && (errno == EINTR)) continue;
+ D("transport_read_action: on fd %d, error %d: %s\n",
+ fd, errno, strerror(errno));
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static int
+transport_write_action(int fd, struct tmsg* m)
+{
+ char *p = (char*)m;
+ int len = sizeof(*m);
+ int r;
+
+ while(len > 0) {
+ r = adb_write(fd, p, len);
+ if(r > 0) {
+ len -= r;
+ p += r;
+ } else {
+ if((r < 0) && (errno == EINTR)) continue;
+ D("transport_write_action: on fd %d, error %d: %s\n",
+ fd, errno, strerror(errno));
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static void transport_registration_func(int _fd, unsigned ev, void *data)
+{
+ tmsg m;
+ adb_thread_t output_thread_ptr;
+ adb_thread_t input_thread_ptr;
+ int s[2];
+ atransport *t;
+
+ if(!(ev & FDE_READ)) {
+ return;
+ }
+
+ if(transport_read_action(_fd, &m)) {
+ fatal_errno("cannot read transport registration socket");
+ }
+
+ t = m.transport;
+
+ if(m.action == 0){
+ D("transport: %p removing and free'ing %d\n", t, t->transport_socket);
+
+ /* IMPORTANT: the remove closes one half of the
+ ** socket pair. The close closes the other half.
+ */
+ fdevent_remove(&(t->transport_fde));
+ adb_close(t->fd);
+
+ adb_mutex_lock(&transport_lock);
+ t->next->prev = t->prev;
+ t->prev->next = t->next;
+ adb_mutex_unlock(&transport_lock);
+
+ run_transport_disconnects(t);
+
+ if (t->product)
+ free(t->product);
+ if (t->serial)
+ free(t->serial);
+
+ memset(t,0xee,sizeof(atransport));
+ free(t);
+
+ update_transports();
+ return;
+ }
+
+ /* don't create transport threads for inaccessible devices */
+ if (t->connection_state != CS_NOPERM) {
+ /* initial references are the two threads */
+ t->ref_count = 2;
+
+ if(adb_socketpair(s)) {
+ fatal_errno("cannot open transport socketpair");
+ }
+
+ D("transport: %p (%d,%d) starting\n", t, s[0], s[1]);
+
+ t->transport_socket = s[0];
+ t->fd = s[1];
+
+ D("transport: %p install %d\n", t, t->transport_socket );
+ fdevent_install(&(t->transport_fde),
+ t->transport_socket,
+ transport_socket_events,
+ t);
+
+ fdevent_set(&(t->transport_fde), FDE_READ);
+
+ if(adb_thread_create(&input_thread_ptr, input_thread, t)){
+ fatal_errno("cannot create input thread");
+ }
+
+ if(adb_thread_create(&output_thread_ptr, output_thread, t)){
+ fatal_errno("cannot create output thread");
+ }
+ }
+
+ /* put us on the master device list */
+ adb_mutex_lock(&transport_lock);
+ t->next = &transport_list;
+ t->prev = transport_list.prev;
+ t->next->prev = t;
+ t->prev->next = t;
+ adb_mutex_unlock(&transport_lock);
+
+ t->disconnects.next = t->disconnects.prev = &t->disconnects;
+
+ update_transports();
+}
+
+void init_transport_registration(void)
+{
+ int s[2];
+
+ if(adb_socketpair(s)){
+ fatal_errno("cannot open transport registration socketpair");
+ }
+
+ transport_registration_send = s[0];
+ transport_registration_recv = s[1];
+
+ fdevent_install(&transport_registration_fde,
+ transport_registration_recv,
+ transport_registration_func,
+ 0);
+
+ fdevent_set(&transport_registration_fde, FDE_READ);
+}
+
+/* the fdevent select pump is single threaded */
+static void register_transport(atransport *transport)
+{
+ tmsg m;
+ m.transport = transport;
+ m.action = 1;
+ D("transport: %p registered\n", transport);
+ if(transport_write_action(transport_registration_send, &m)) {
+ fatal_errno("cannot write transport registration socket\n");
+ }
+}
+
+static void remove_transport(atransport *transport)
+{
+ tmsg m;
+ m.transport = transport;
+ m.action = 0;
+ D("transport: %p removed\n", transport);
+ if(transport_write_action(transport_registration_send, &m)) {
+ fatal_errno("cannot write transport registration socket\n");
+ }
+}
+
+
+static void transport_unref_locked(atransport *t)
+{
+ t->ref_count--;
+ D("transport: %p R- (ref=%d)\n", t, t->ref_count);
+ if (t->ref_count == 0) {
+ D("transport: %p kicking and closing\n", t);
+ if (!t->kicked) {
+ t->kicked = 1;
+ t->kick(t);
+ }
+ t->close(t);
+ remove_transport(t);
+ }
+}
+
+static void transport_unref(atransport *t)
+{
+ if (t) {
+ adb_mutex_lock(&transport_lock);
+ transport_unref_locked(t);
+ adb_mutex_unlock(&transport_lock);
+ }
+}
+
+void add_transport_disconnect(atransport* t, adisconnect* dis)
+{
+ adb_mutex_lock(&transport_lock);
+ dis->next = &t->disconnects;
+ dis->prev = dis->next->prev;
+ dis->prev->next = dis;
+ dis->next->prev = dis;
+ adb_mutex_unlock(&transport_lock);
+}
+
+void remove_transport_disconnect(atransport* t, adisconnect* dis)
+{
+ dis->prev->next = dis->next;
+ dis->next->prev = dis->prev;
+ dis->next = dis->prev = dis;
+}
+
+
+atransport *acquire_one_transport(int state, transport_type ttype, const char* serial, char** error_out)
+{
+ atransport *t;
+ atransport *result = NULL;
+ int ambiguous = 0;
+
+retry:
+ if (error_out)
+ *error_out = "device not found";
+
+ adb_mutex_lock(&transport_lock);
+ for (t = transport_list.next; t != &transport_list; t = t->next) {
+ if (t->connection_state == CS_NOPERM) {
+ if (error_out)
+ *error_out = "insufficient permissions for device";
+ continue;
+ }
+
+ /* check for matching serial number */
+ if (serial) {
+ if (t->serial && !strcmp(serial, t->serial)) {
+ result = t;
+ break;
+ }
+ } else {
+ if (ttype == kTransportUsb && t->type == kTransportUsb) {
+ if (result) {
+ if (error_out)
+ *error_out = "more than one device";
+ ambiguous = 1;
+ result = NULL;
+ break;
+ }
+ result = t;
+ } else if (ttype == kTransportLocal && t->type == kTransportLocal) {
+ if (result) {
+ if (error_out)
+ *error_out = "more than one emulator";
+ ambiguous = 1;
+ result = NULL;
+ break;
+ }
+ result = t;
+ } else if (ttype == kTransportAny) {
+ if (result) {
+ if (error_out)
+ *error_out = "more than one device and emulator";
+ ambiguous = 1;
+ result = NULL;
+ break;
+ }
+ result = t;
+ }
+ }
+ }
+ adb_mutex_unlock(&transport_lock);
+
+ if (result) {
+ /* offline devices are ignored -- they are either being born or dying */
+ if (result && result->connection_state == CS_OFFLINE) {
+ if (error_out)
+ *error_out = "device offline";
+ result = NULL;
+ }
+ /* check for required connection state */
+ if (result && state != CS_ANY && result->connection_state != state) {
+ if (error_out)
+ *error_out = "invalid device state";
+ result = NULL;
+ }
+ }
+
+ if (result) {
+ /* found one that we can take */
+ if (error_out)
+ *error_out = NULL;
+ } else if (state != CS_ANY && (serial || !ambiguous)) {
+ adb_sleep_ms(1000);
+ goto retry;
+ }
+
+ return result;
+}
+
+#if ADB_HOST
+static const char *statename(atransport *t)
+{
+ switch(t->connection_state){
+ case CS_OFFLINE: return "offline";
+ case CS_BOOTLOADER: return "bootloader";
+ case CS_DEVICE: return "device";
+ case CS_HOST: return "host";
+ case CS_RECOVERY: return "recovery";
+ case CS_NOPERM: return "no permissions";
+ default: return "unknown";
+ }
+}
+
+int list_transports(char *buf, size_t bufsize)
+{
+ char* p = buf;
+ char* end = buf + bufsize;
+ int len;
+ atransport *t;
+
+ /* XXX OVERRUN PROBLEMS XXX */
+ adb_mutex_lock(&transport_lock);
+ for(t = transport_list.next; t != &transport_list; t = t->next) {
+ const char* serial = t->serial;
+ if (!serial || !serial[0])
+ serial = "????????????";
+ len = snprintf(p, end - p, "%s\t%s\n", serial, statename(t));
+
+ if (p + len >= end) {
+ /* discard last line if buffer is too short */
+ break;
+ }
+ p += len;
+ }
+ p[0] = 0;
+ adb_mutex_unlock(&transport_lock);
+ return p - buf;
+}
+
+
+/* hack for osx */
+void close_usb_devices()
+{
+ atransport *t;
+
+ adb_mutex_lock(&transport_lock);
+ for(t = transport_list.next; t != &transport_list; t = t->next) {
+ if ( !t->kicked ) {
+ t->kicked = 1;
+ t->kick(t);
+ }
+ }
+ adb_mutex_unlock(&transport_lock);
+}
+#endif // ADB_HOST
+
+void register_socket_transport(int s, const char *serial, int port, int local)
+{
+ atransport *t = calloc(1, sizeof(atransport));
+ D("transport: %p init'ing for socket %d, on port %d\n", t, s, port);
+ if ( init_socket_transport(t, s, port, local) < 0 ) {
+ adb_close(s);
+ free(t);
+ return;
+ }
+ if(serial) {
+ t->serial = strdup(serial);
+ }
+ register_transport(t);
+}
+
+#if ADB_HOST
+atransport *find_transport(const char *serial)
+{
+ atransport *t;
+
+ adb_mutex_lock(&transport_lock);
+ for(t = transport_list.next; t != &transport_list; t = t->next) {
+ if (t->serial && !strcmp(serial, t->serial)) {
+ break;
+ }
+ }
+ adb_mutex_unlock(&transport_lock);
+
+ if (t != &transport_list)
+ return t;
+ else
+ return 0;
+}
+
+void unregister_transport(atransport *t)
+{
+ adb_mutex_lock(&transport_lock);
+ t->next->prev = t->prev;
+ t->prev->next = t->next;
+ adb_mutex_unlock(&transport_lock);
+
+ kick_transport(t);
+ transport_unref(t);
+}
+
+// unregisters all non-emulator TCP transports
+void unregister_all_tcp_transports()
+{
+ atransport *t, *next;
+ adb_mutex_lock(&transport_lock);
+ for (t = transport_list.next; t != &transport_list; t = next) {
+ next = t->next;
+ if (t->type == kTransportLocal && t->adb_port == 0) {
+ t->next->prev = t->prev;
+ t->prev->next = next;
+ // we cannot call kick_transport when holding transport_lock
+ if (!t->kicked)
+ {
+ t->kicked = 1;
+ t->kick(t);
+ }
+ transport_unref_locked(t);
+ }
+ }
+
+ adb_mutex_unlock(&transport_lock);
+}
+
+#endif
+
+void register_usb_transport(usb_handle *usb, const char *serial, unsigned writeable)
+{
+ atransport *t = calloc(1, sizeof(atransport));
+ D("transport: %p init'ing for usb_handle %p (sn='%s')\n", t, usb,
+ serial ? serial : "");
+ init_usb_transport(t, usb, (writeable ? CS_OFFLINE : CS_NOPERM));
+ if(serial) {
+ t->serial = strdup(serial);
+ }
+ register_transport(t);
+}
+
+/* this should only be used for transports with connection_state == CS_NOPERM */
+void unregister_usb_transport(usb_handle *usb)
+{
+ atransport *t;
+ adb_mutex_lock(&transport_lock);
+ for(t = transport_list.next; t != &transport_list; t = t->next) {
+ if (t->usb == usb && t->connection_state == CS_NOPERM) {
+ t->next->prev = t->prev;
+ t->prev->next = t->next;
+ break;
+ }
+ }
+ adb_mutex_unlock(&transport_lock);
+}
+
+#undef TRACE_TAG
+#define TRACE_TAG TRACE_RWX
+
+int readx(int fd, void *ptr, size_t len)
+{
+ char *p = ptr;
+ int r;
+#if ADB_TRACE
+ int len0 = len;
+#endif
+ D("readx: %d %p %d\n", fd, ptr, (int)len);
+ while(len > 0) {
+ r = adb_read(fd, p, len);
+ if(r > 0) {
+ len -= r;
+ p += r;
+ } else {
+ D("readx: %d %d %s\n", fd, r, strerror(errno));
+ if((r < 0) && (errno == EINTR)) continue;
+ return -1;
+ }
+ }
+
+#if ADB_TRACE
+ D("readx: %d ok: ", fd);
+ dump_hex( ptr, len0 );
+#endif
+ return 0;
+}
+
+int writex(int fd, const void *ptr, size_t len)
+{
+ char *p = (char*) ptr;
+ int r;
+
+#if ADB_TRACE
+ D("writex: %d %p %d: ", fd, ptr, (int)len);
+ dump_hex( ptr, len );
+#endif
+ while(len > 0) {
+ r = adb_write(fd, p, len);
+ if(r > 0) {
+ len -= r;
+ p += r;
+ } else {
+ D("writex: %d %d %s\n", fd, r, strerror(errno));
+ if((r < 0) && (errno == EINTR)) continue;
+ return -1;
+ }
+ }
+
+ D("writex: %d ok\n", fd);
+ return 0;
+}
+
+int check_header(apacket *p)
+{
+ if(p->msg.magic != (p->msg.command ^ 0xffffffff)) {
+ D("check_header(): invalid magic\n");
+ return -1;
+ }
+
+ if(p->msg.data_length > MAX_PAYLOAD) {
+ D("check_header(): %d > MAX_PAYLOAD\n", p->msg.data_length);
+ return -1;
+ }
+
+ return 0;
+}
+
+int check_data(apacket *p)
+{
+ unsigned count, sum;
+ unsigned char *x;
+
+ count = p->msg.data_length;
+ x = p->data;
+ sum = 0;
+ while(count-- > 0) {
+ sum += *x++;
+ }
+
+ if(sum != p->msg.data_check) {
+ return -1;
+ } else {
+ return 0;
+ }
+}
+
diff --git a/adb/transport_local.c b/adb/transport_local.c
new file mode 100644
index 0000000..8dfc98d
--- /dev/null
+++ b/adb/transport_local.c
@@ -0,0 +1,316 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include "sysdeps.h"
+#include <sys/types.h>
+
+#define TRACE_TAG TRACE_TRANSPORT
+#include "adb.h"
+
+#ifdef __ppc__
+#define H4(x) (((x) & 0xFF000000) >> 24) | (((x) & 0x00FF0000) >> 8) | (((x) & 0x0000FF00) << 8) | (((x) & 0x000000FF) << 24)
+static inline void fix_endians(apacket *p)
+{
+ p->msg.command = H4(p->msg.command);
+ p->msg.arg0 = H4(p->msg.arg0);
+ p->msg.arg1 = H4(p->msg.arg1);
+ p->msg.data_length = H4(p->msg.data_length);
+ p->msg.data_check = H4(p->msg.data_check);
+ p->msg.magic = H4(p->msg.magic);
+}
+#else
+#define fix_endians(p) do {} while (0)
+#endif
+
+#if ADB_HOST
+/* we keep a list of opened transports. The atransport struct knows to which
+ * local transport it is connected. The list is used to detect when we're
+ * trying to connect twice to a given local transport.
+ */
+#define ADB_LOCAL_TRANSPORT_MAX 16
+
+ADB_MUTEX_DEFINE( local_transports_lock );
+
+static atransport* local_transports[ ADB_LOCAL_TRANSPORT_MAX ];
+#endif /* ADB_HOST */
+
+static int remote_read(apacket *p, atransport *t)
+{
+ if(readx(t->sfd, &p->msg, sizeof(amessage))){
+ D("remote local: read terminated (message)\n");
+ return -1;
+ }
+
+ fix_endians(p);
+
+#if 0 && defined __ppc__
+ D("read remote packet: %04x arg0=%0x arg1=%0x data_length=%0x data_check=%0x magic=%0x\n",
+ p->msg.command, p->msg.arg0, p->msg.arg1, p->msg.data_length, p->msg.data_check, p->msg.magic);
+#endif
+ if(check_header(p)) {
+ D("bad header: terminated (data)\n");
+ return -1;
+ }
+
+ if(readx(t->sfd, p->data, p->msg.data_length)){
+ D("remote local: terminated (data)\n");
+ return -1;
+ }
+
+ if(check_data(p)) {
+ D("bad data: terminated (data)\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int remote_write(apacket *p, atransport *t)
+{
+ int length = p->msg.data_length;
+
+ fix_endians(p);
+
+#if 0 && defined __ppc__
+ D("write remote packet: %04x arg0=%0x arg1=%0x data_length=%0x data_check=%0x magic=%0x\n",
+ p->msg.command, p->msg.arg0, p->msg.arg1, p->msg.data_length, p->msg.data_check, p->msg.magic);
+#endif
+ if(writex(t->sfd, &p->msg, sizeof(amessage) + length)) {
+ D("remote local: write terminated\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int local_connect(int port) {
+ return local_connect_arbitrary_ports(port-1, port);
+}
+
+int local_connect_arbitrary_ports(int console_port, int adb_port)
+{
+ char buf[64];
+ int fd = -1;
+
+#if ADB_HOST
+ const char *host = getenv("ADBHOST");
+ if (host) {
+ fd = socket_network_client(host, adb_port, SOCK_STREAM);
+ }
+#endif
+ if (fd < 0) {
+ fd = socket_loopback_client(adb_port, SOCK_STREAM);
+ }
+
+ if (fd >= 0) {
+ D("client: connected on remote on fd %d\n", fd);
+ close_on_exec(fd);
+ disable_tcp_nagle(fd);
+ snprintf(buf, sizeof buf, "%s%d", LOCAL_CLIENT_PREFIX, console_port);
+ register_socket_transport(fd, buf, adb_port, 1);
+ return 0;
+ }
+ return -1;
+}
+
+
+static void *client_socket_thread(void *x)
+{
+#if ADB_HOST
+ int port = DEFAULT_ADB_LOCAL_TRANSPORT_PORT;
+ int count = ADB_LOCAL_TRANSPORT_MAX;
+
+ D("transport: client_socket_thread() starting\n");
+
+ /* try to connect to any number of running emulator instances */
+ /* this is only done when ADB starts up. later, each new emulator */
+ /* will send a message to ADB to indicate that is is starting up */
+ for ( ; count > 0; count--, port += 2 ) {
+ (void) local_connect(port);
+ }
+#endif
+ return 0;
+}
+
+static void *server_socket_thread(void * arg)
+{
+ int serverfd, fd;
+ struct sockaddr addr;
+ socklen_t alen;
+ int port = (int)arg;
+
+ D("transport: server_socket_thread() starting\n");
+ serverfd = -1;
+ for(;;) {
+ if(serverfd == -1) {
+ serverfd = socket_inaddr_any_server(port, SOCK_STREAM);
+ if(serverfd < 0) {
+ D("server: cannot bind socket yet\n");
+ adb_sleep_ms(1000);
+ continue;
+ }
+ close_on_exec(serverfd);
+ }
+
+ alen = sizeof(addr);
+ D("server: trying to get new connection from %d\n", port);
+ fd = adb_socket_accept(serverfd, &addr, &alen);
+ if(fd >= 0) {
+ D("server: new connection on fd %d\n", fd);
+ close_on_exec(fd);
+ disable_tcp_nagle(fd);
+ register_socket_transport(fd, "host", port, 1);
+ }
+ }
+ D("transport: server_socket_thread() exiting\n");
+ return 0;
+}
+
+void local_init(int port)
+{
+ adb_thread_t thr;
+ void* (*func)(void *);
+
+ if(HOST) {
+ func = client_socket_thread;
+ } else {
+ func = server_socket_thread;
+ }
+
+ D("transport: local %s init\n", HOST ? "client" : "server");
+
+ if(adb_thread_create(&thr, func, (void *)port)) {
+ fatal_errno("cannot create local socket %s thread",
+ HOST ? "client" : "server");
+ }
+}
+
+static void remote_kick(atransport *t)
+{
+ int fd = t->sfd;
+ t->sfd = -1;
+ adb_shutdown(fd);
+ adb_close(fd);
+
+#if ADB_HOST
+ if(HOST) {
+ int nn;
+ adb_mutex_lock( &local_transports_lock );
+ for (nn = 0; nn < ADB_LOCAL_TRANSPORT_MAX; nn++) {
+ if (local_transports[nn] == t) {
+ local_transports[nn] = NULL;
+ break;
+ }
+ }
+ adb_mutex_unlock( &local_transports_lock );
+ }
+#endif
+}
+
+static void remote_close(atransport *t)
+{
+ adb_close(t->fd);
+}
+
+
+#if ADB_HOST
+/* Only call this function if you already hold local_transports_lock. */
+atransport* find_emulator_transport_by_adb_port_locked(int adb_port)
+{
+ int i;
+ for (i = 0; i < ADB_LOCAL_TRANSPORT_MAX; i++) {
+ if (local_transports[i] && local_transports[i]->adb_port == adb_port) {
+ return local_transports[i];
+ }
+ }
+ return NULL;
+}
+
+atransport* find_emulator_transport_by_adb_port(int adb_port)
+{
+ adb_mutex_lock( &local_transports_lock );
+ atransport* result = find_emulator_transport_by_adb_port_locked(adb_port);
+ adb_mutex_unlock( &local_transports_lock );
+ return result;
+}
+
+/* Only call this function if you already hold local_transports_lock. */
+int get_available_local_transport_index_locked()
+{
+ int i;
+ for (i = 0; i < ADB_LOCAL_TRANSPORT_MAX; i++) {
+ if (local_transports[i] == NULL) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+int get_available_local_transport_index()
+{
+ adb_mutex_lock( &local_transports_lock );
+ int result = get_available_local_transport_index_locked();
+ adb_mutex_unlock( &local_transports_lock );
+ return result;
+}
+#endif
+
+int init_socket_transport(atransport *t, int s, int adb_port, int local)
+{
+ int fail = 0;
+
+ t->kick = remote_kick;
+ t->close = remote_close;
+ t->read_from_remote = remote_read;
+ t->write_to_remote = remote_write;
+ t->sfd = s;
+ t->sync_token = 1;
+ t->connection_state = CS_OFFLINE;
+ t->type = kTransportLocal;
+ t->adb_port = 0;
+
+#if ADB_HOST
+ if (HOST && local) {
+ adb_mutex_lock( &local_transports_lock );
+ {
+ t->adb_port = adb_port;
+ atransport* existing_transport =
+ find_emulator_transport_by_adb_port_locked(adb_port);
+ int index = get_available_local_transport_index_locked();
+ if (existing_transport != NULL) {
+ D("local transport for port %d already registered (%p)?\n",
+ adb_port, existing_transport);
+ fail = -1;
+ } else if (index < 0) {
+ // Too many emulators.
+ D("cannot register more emulators. Maximum is %d\n",
+ ADB_LOCAL_TRANSPORT_MAX);
+ fail = -1;
+ } else {
+ local_transports[index] = t;
+ }
+ }
+ adb_mutex_unlock( &local_transports_lock );
+ }
+#endif
+ return fail;
+}
diff --git a/adb/transport_usb.c b/adb/transport_usb.c
new file mode 100644
index 0000000..2584163
--- /dev/null
+++ b/adb/transport_usb.c
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <sysdeps.h>
+
+#define TRACE_TAG TRACE_TRANSPORT
+#include "adb.h"
+
+#if ADB_HOST
+#include "usb_vendors.h"
+#endif
+
+/* XXX better define? */
+#ifdef __ppc__
+#define H4(x) (((x) & 0xFF000000) >> 24) | (((x) & 0x00FF0000) >> 8) | (((x) & 0x0000FF00) << 8) | (((x) & 0x000000FF) << 24)
+static inline void fix_endians(apacket *p)
+{
+ p->msg.command = H4(p->msg.command);
+ p->msg.arg0 = H4(p->msg.arg0);
+ p->msg.arg1 = H4(p->msg.arg1);
+ p->msg.data_length = H4(p->msg.data_length);
+ p->msg.data_check = H4(p->msg.data_check);
+ p->msg.magic = H4(p->msg.magic);
+}
+unsigned host_to_le32(unsigned n)
+{
+ return H4(n);
+}
+#else
+#define fix_endians(p) do {} while (0)
+unsigned host_to_le32(unsigned n)
+{
+ return n;
+}
+#endif
+
+static int remote_read(apacket *p, atransport *t)
+{
+ if(usb_read(t->usb, &p->msg, sizeof(amessage))){
+ D("remote usb: read terminated (message)\n");
+ return -1;
+ }
+
+ fix_endians(p);
+
+ if(check_header(p)) {
+ D("remote usb: check_header failed\n");
+ return -1;
+ }
+
+ if(p->msg.data_length) {
+ if(usb_read(t->usb, p->data, p->msg.data_length)){
+ D("remote usb: terminated (data)\n");
+ return -1;
+ }
+ }
+
+ if(check_data(p)) {
+ D("remote usb: check_data failed\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int remote_write(apacket *p, atransport *t)
+{
+ unsigned size = p->msg.data_length;
+
+ fix_endians(p);
+
+ if(usb_write(t->usb, &p->msg, sizeof(amessage))) {
+ D("remote usb: 1 - write terminated\n");
+ return -1;
+ }
+ if(p->msg.data_length == 0) return 0;
+ if(usb_write(t->usb, &p->data, size)) {
+ D("remote usb: 2 - write terminated\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static void remote_close(atransport *t)
+{
+ usb_close(t->usb);
+ t->usb = 0;
+}
+
+static void remote_kick(atransport *t)
+{
+ usb_kick(t->usb);
+}
+
+void init_usb_transport(atransport *t, usb_handle *h, int state)
+{
+ D("transport: usb\n");
+ t->close = remote_close;
+ t->kick = remote_kick;
+ t->read_from_remote = remote_read;
+ t->write_to_remote = remote_write;
+ t->sync_token = 1;
+ t->connection_state = state;
+ t->type = kTransportUsb;
+ t->usb = h;
+
+#if ADB_HOST
+ HOST = 1;
+#else
+ HOST = 0;
+#endif
+}
+
+#if ADB_HOST
+int is_adb_interface(int vid, int pid, int usb_class, int usb_subclass, int usb_protocol)
+{
+ unsigned i;
+ for (i = 0; i < vendorIdCount; i++) {
+ if (vid == vendorIds[i]) {
+ if (usb_class == ADB_CLASS && usb_subclass == ADB_SUBCLASS &&
+ usb_protocol == ADB_PROTOCOL) {
+ return 1;
+ }
+
+ return 0;
+ }
+ }
+
+ return 0;
+}
+#endif
diff --git a/adb/usb_libusb.c b/adb/usb_libusb.c
new file mode 100644
index 0000000..8c75266
--- /dev/null
+++ b/adb/usb_libusb.c
@@ -0,0 +1,657 @@
+/*
+ * 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/endian.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+
+#include <err.h>
+#include <errno.h>
+#include <poll.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+#include <libusb.h>
+#include "sysdeps.h"
+
+#define TRACE_TAG TRACE_USB
+#include "adb.h"
+
+static adb_mutex_t usb_lock = ADB_MUTEX_INITIALIZER;
+static libusb_context *ctx = NULL;
+
+struct usb_handle
+{
+ usb_handle *prev;
+ usb_handle *next;
+
+ libusb_device *dev;
+ libusb_device_handle *devh;
+ int interface;
+ uint8_t dev_bus;
+ uint8_t dev_addr;
+
+ int zero_mask;
+ unsigned char end_point_address[2];
+ char serial[128];
+
+ adb_cond_t notify;
+ adb_mutex_t lock;
+};
+
+static struct usb_handle handle_list = {
+ .prev = &handle_list,
+ .next = &handle_list,
+};
+
+void
+usb_cleanup()
+{
+ libusb_exit(ctx);
+}
+
+void
+report_bulk_libusb_error(int r)
+{
+ switch (r) {
+ case LIBUSB_ERROR_TIMEOUT:
+ D("Transfer timeout\n");
+ break;
+
+ case LIBUSB_ERROR_PIPE:
+ D("Control request is not supported\n");
+ break;
+
+ case LIBUSB_ERROR_OVERFLOW:
+ D("Device offered more data\n");
+ break;
+
+ case LIBUSB_ERROR_NO_DEVICE :
+ D("Device was disconnected\n");
+ break;
+
+ default:
+ D("Error %d during transfer\n", r);
+ break;
+ };
+}
+
+static int
+usb_bulk_write(usb_handle *uh, const void *data, int len)
+{
+ int r = 0;
+ int transferred = 0;
+
+ r = libusb_bulk_transfer(uh->devh, uh->end_point_address[1], (void *)data, len,
+ &transferred, 0);
+
+ if (r != 0) {
+ D("usb_bulk_write(): ");
+ report_bulk_libusb_error(r);
+ return r;
+ }
+
+ return (transferred);
+}
+
+static int
+usb_bulk_read(usb_handle *uh, void *data, int len)
+{
+ int r = 0;
+ int transferred = 0;
+
+ r = libusb_bulk_transfer(uh->devh, uh->end_point_address[0], data, len,
+ &transferred, 0);
+
+ if (r != 0) {
+ D("usb_bulk_read(): ");
+ report_bulk_libusb_error(r);
+ return r;
+ }
+
+ return (transferred);
+}
+
+int
+usb_write(struct usb_handle *uh, const void *_data, int len)
+{
+ unsigned char *data = (unsigned char*) _data;
+ int n;
+ int need_zero = 0;
+
+ if (uh->zero_mask == 1) {
+ if (!(len & uh->zero_mask)) {
+ need_zero = 1;
+ }
+ }
+
+ D("usb_write(): %p:%d -> transport %p\n", _data, len, uh);
+
+ while (len > 0) {
+ int xfer = (len > 4096) ? 4096 : len;
+
+ n = usb_bulk_write(uh, data, xfer);
+
+ if (n != xfer) {
+ D("usb_write(): failed for transport %p (%d bytes left)\n", uh, len);
+ return -1;
+ }
+
+ len -= xfer;
+ data += xfer;
+ }
+
+ if (need_zero){
+ n = usb_bulk_write(uh, _data, 0);
+
+ if (n < 0) {
+ D("usb_write(): failed to finish operation for transport %p\n", uh);
+ }
+ return n;
+ }
+
+ return 0;
+}
+
+int
+usb_read(struct usb_handle *uh, void *_data, int len)
+{
+ unsigned char *data = (unsigned char*) _data;
+ int n;
+
+ D("usb_read(): %p:%d <- transport %p\n", _data, len, uh);
+
+ while (len > 0) {
+ int xfer = (len > 4096) ? 4096 : len;
+
+ n = usb_bulk_read(uh, data, xfer);
+
+ if (n != xfer) {
+ if (n > 0) {
+ data += n;
+ len -= n;
+ continue;
+ }
+
+ D("usb_read(): failed for transport %p (%d bytes left)\n", uh, len);
+ return -1;
+ }
+
+ len -= xfer;
+ data += xfer;
+ }
+
+ return 0;
+ }
+
+int
+usb_close(struct usb_handle *h)
+{
+ D("usb_close(): closing transport %p\n", h);
+ adb_mutex_lock(&usb_lock);
+
+ h->next->prev = h->prev;
+ h->prev->next = h->next;
+ h->prev = NULL;
+ h->next = NULL;
+
+ libusb_release_interface(h->devh, h->interface);
+ libusb_close(h->devh);
+ libusb_unref_device(h->dev);
+
+ adb_mutex_unlock(&usb_lock);
+
+ free(h);
+
+ return (0);
+}
+
+void usb_kick(struct usb_handle *h)
+{
+ D("usb_cick(): kicking transport %p\n", h);
+
+ adb_mutex_lock(&h->lock);
+ unregister_usb_transport(h);
+ adb_mutex_unlock(&h->lock);
+
+ h->next->prev = h->prev;
+ h->prev->next = h->next;
+ h->prev = NULL;
+ h->next = NULL;
+
+ libusb_release_interface(h->devh, h->interface);
+ libusb_close(h->devh);
+ libusb_unref_device(h->dev);
+ free(h);
+}
+
+int
+check_usb_interface(libusb_interface *interface,
+ libusb_device_descriptor *desc,
+ struct usb_handle *uh)
+{
+ int e;
+
+ if (interface->num_altsetting == 0) {
+ D("check_usb_interface(): No interface settings\n");
+ return -1;
+ }
+
+ libusb_interface_descriptor *idesc = &interface->altsetting[0];
+
+ if (idesc->bNumEndpoints != 2) {
+ D("check_usb_interface(): Interface have not 2 endpoints, ignoring\n");
+ return -1;
+ }
+
+ for (e = 0; e < idesc->bNumEndpoints; e++) {
+ libusb_endpoint_descriptor *edesc = &idesc->endpoint[e];
+
+ if (edesc->bmAttributes != LIBUSB_TRANSFER_TYPE_BULK) {
+ D("check_usb_interface(): Endpoint (%u) is not bulk (%u), ignoring\n",
+ edesc->bmAttributes, LIBUSB_TRANSFER_TYPE_BULK);
+ return -1;
+ }
+
+ if (edesc->bEndpointAddress & LIBUSB_ENDPOINT_IN)
+ uh->end_point_address[0] = edesc->bEndpointAddress;
+ else
+ uh->end_point_address[1] = edesc->bEndpointAddress;
+
+ /* aproto 01 needs 0 termination */
+ if (idesc->bInterfaceProtocol == 0x01) {
+ uh->zero_mask = edesc->wMaxPacketSize - 1;
+ D("check_usb_interface(): Forced Android interface protocol v.1\n");
+ }
+ }
+
+ D("check_usb_interface(): Device: %04x:%04x "
+ "iclass: %x, isclass: %x, iproto: %x ep: %x/%x-> ",
+ desc->idVendor, desc->idProduct, idesc->bInterfaceClass,
+ idesc->bInterfaceSubClass, idesc->bInterfaceProtocol,
+ uh->end_point_address[0], uh->end_point_address[1]);
+
+ if (!is_adb_interface(desc->idVendor, desc->idProduct,
+ idesc->bInterfaceClass, idesc->bInterfaceSubClass,
+ idesc->bInterfaceProtocol))
+ {
+ D("not matches\n");
+ return -1;
+ }
+
+ D("matches\n");
+ return 1;
+}
+
+int
+check_usb_interfaces(libusb_config_descriptor *config,
+ libusb_device_descriptor *desc, struct usb_handle *uh)
+{
+ int i;
+
+ for (i = 0; i < config->bNumInterfaces; ++i) {
+ if (check_usb_interface(&config->interface[i], desc, uh) != -1) {
+ /* found some interface and saved information about it */
+ D("check_usb_interfaces(): Interface %d of %04x:%04x "
+ "matches Android device\n", i, desc->idVendor,
+ desc->idProduct);
+
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+int
+register_device(struct usb_handle *uh, const char *serial)
+{
+ D("register_device(): Registering %p [%s] as USB transport\n",
+ uh, serial);
+
+ struct usb_handle *usb= NULL;
+
+ usb = calloc(1, sizeof(struct usb_handle));
+ memcpy(usb, uh, sizeof(struct usb_handle));
+ strcpy(usb->serial, uh->serial);
+
+ adb_cond_init(&usb->notify, 0);
+ adb_mutex_init(&usb->lock, 0);
+
+ adb_mutex_lock(&usb_lock);
+
+ usb->next = &handle_list;
+ usb->prev = handle_list.prev;
+ usb->prev->next = usb;
+ usb->next->prev = usb;
+
+ adb_mutex_unlock(&usb_lock);
+
+ register_usb_transport(usb, serial, 1);
+
+ return (1);
+}
+
+int
+already_registered(usb_handle *uh)
+{
+ struct usb_handle *usb= NULL;
+ int exists = 0;
+
+ adb_mutex_lock(&usb_lock);
+
+ for (usb = handle_list.next; usb != &handle_list; usb = usb->next) {
+ if ((usb->dev_bus == uh->dev_bus) &&
+ (usb->dev_addr == uh->dev_addr))
+ {
+ exists = 1;
+ break;
+ }
+ }
+
+ adb_mutex_unlock(&usb_lock);
+
+ return exists;
+}
+
+void
+check_device(libusb_device *dev)
+{
+ struct usb_handle uh;
+ int i = 0;
+ int found = -1;
+ char serial[256] = {0};
+
+ libusb_device_descriptor desc;
+ libusb_config_descriptor *config = NULL;
+
+ int r = libusb_get_device_descriptor(dev, &desc);
+
+ if (r != LIBUSB_SUCCESS) {
+ D("check_device(): Failed to get device descriptor\n");
+ return;
+ }
+
+ if ((desc.idVendor == 0) && (desc.idProduct == 0))
+ return;
+
+ D("check_device(): Probing usb device %04x:%04x\n",
+ desc.idVendor, desc.idProduct);
+
+ if (!is_adb_interface (desc.idVendor, desc.idProduct,
+ ADB_CLASS, ADB_SUBCLASS, ADB_PROTOCOL))
+ {
+ D("check_device(): Ignored due unknown vendor id\n");
+ return;
+ }
+
+ uh.dev_bus = libusb_get_bus_number(dev);
+ uh.dev_addr = libusb_get_device_address(dev);
+
+ if (already_registered(&uh)) {
+ D("check_device(): Device (bus: %d, address: %d) "
+ "is already registered\n", uh.dev_bus, uh.dev_addr);
+ return;
+ }
+
+ D("check_device(): Device bus: %d, address: %d\n",
+ uh.dev_bus, uh.dev_addr);
+
+ r = libusb_get_active_config_descriptor(dev, &config);
+
+ if (r != 0) {
+ if (r == LIBUSB_ERROR_NOT_FOUND) {
+ D("check_device(): Device %4x:%4x is unconfigured\n",
+ desc.idVendor, desc.idProduct);
+ return;
+ }
+
+ D("check_device(): Failed to get configuration for %4x:%4x\n",
+ desc.idVendor, desc.idProduct);
+ return;
+ }
+
+ if (config == NULL) {
+ D("check_device(): Sanity check failed after "
+ "getting active config\n");
+ return;
+ }
+
+ if (config->interface != NULL) {
+ found = check_usb_interfaces(config, &desc, &uh);
+ }
+
+ /* not needed anymore */
+ libusb_free_config_descriptor(config);
+
+ r = libusb_open(dev, &uh.devh);
+ uh.dev = dev;
+
+ if (r != 0) {
+ switch (r) {
+ case LIBUSB_ERROR_NO_MEM:
+ D("check_device(): Memory allocation problem\n");
+ break;
+
+ case LIBUSB_ERROR_ACCESS:
+ D("check_device(): Permissions problem, "
+ "current user priveleges are messed up?\n");
+ break;
+
+ case LIBUSB_ERROR_NO_DEVICE:
+ D("check_device(): Device disconected, bad cable?\n");
+ break;
+
+ default:
+ D("check_device(): libusb triggered error %d\n", r);
+ }
+ // skip rest
+ found = -1;
+ }
+
+ if (found >= 0) {
+ D("check_device(): Device matches Android interface\n");
+ // read the device's serial number
+ memset(serial, 0, sizeof(serial));
+ uh.interface = found;
+
+ r = libusb_claim_interface(uh.devh, uh.interface);
+
+ if (r < 0) {
+ D("check_device(): Failed to claim interface %d\n",
+ uh.interface);
+
+ goto fail;
+ }
+
+ if (desc.iSerialNumber) {
+ // reading serial
+ uint16_t buffer[128] = {0};
+ uint16_t languages[128] = {0};
+ int languageCount = 0;
+
+ memset(languages, 0, sizeof(languages));
+ r = libusb_control_transfer(uh.devh,
+ LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_STANDARD | LIBUSB_RECIPIENT_DEVICE,
+ LIBUSB_REQUEST_GET_DESCRIPTOR, LIBUSB_DT_STRING << 8,
+ 0, (uint8_t *)languages, sizeof(languages), 0);
+
+ if (r <= 0) {
+ D("check_device(): Failed to get languages count\n");
+ goto fail;
+ }
+
+ languageCount = (r - 2) / 2;
+
+ for (i = 1; i <= languageCount; ++i) {
+ memset(buffer, 0, sizeof(buffer));
+
+ r = libusb_control_transfer(uh.devh,
+ LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_STANDARD | LIBUSB_RECIPIENT_DEVICE,
+ LIBUSB_REQUEST_GET_DESCRIPTOR, (LIBUSB_DT_STRING << 8) | desc.iSerialNumber,
+ languages[i], (uint8_t *)buffer, sizeof(buffer), 0);
+
+ if (r > 0) { /* converting serial */
+ int j = 0;
+ r /= 2;
+
+ for (j = 1; j < r; ++j)
+ serial[j - 1] = buffer[j];
+
+ serial[j - 1] = '\0';
+ break; /* languagesCount cycle */
+ }
+ }
+
+ if (register_device(&uh, serial) == 0) {
+ D("check_device(): Failed to register device\n");
+ goto fail_interface;
+ }
+
+ libusb_ref_device(dev);
+ }
+ }
+
+ return;
+
+fail_interface:
+ libusb_release_interface(uh.devh, uh.interface);
+
+fail:
+ libusb_close(uh.devh);
+ uh.devh = NULL;
+}
+
+int
+check_device_connected(struct usb_handle *uh)
+{
+ int r = libusb_kernel_driver_active(uh->devh, uh->interface);
+
+ if (r == LIBUSB_ERROR_NO_DEVICE)
+ return 0;
+
+ if (r < 0)
+ return -1;
+
+ return 1;
+}
+
+void
+kick_disconnected()
+{
+ struct usb_handle *usb= NULL;
+
+ adb_mutex_lock(&usb_lock);
+
+ for (usb = handle_list.next; usb != &handle_list; usb = usb->next) {
+
+ if (check_device_connected(usb) == 0) {
+ D("kick_disconnected(): Transport %p is not online anymore\n",
+ usb);
+
+ usb_kick(usb);
+ }
+ }
+
+ adb_mutex_unlock(&usb_lock);
+}
+
+void
+scan_usb_devices()
+{
+ D("scan_usb_devices(): started\n");
+
+ libusb_device **devs= NULL;
+ libusb_device *dev= NULL;
+ ssize_t cnt = libusb_get_device_list(ctx, &devs);
+
+ if (cnt < 0) {
+ D("scan_usb_devices(): Failed to get device list (error: %d)\n",
+ cnt);
+
+ return;
+ }
+
+ int i = 0;
+
+ while ((dev = devs[i++]) != NULL) {
+ check_device(dev);
+ }
+
+ libusb_free_device_list(devs, 1);
+}
+
+void *
+device_poll_thread(void* unused)
+{
+ D("device_poll_thread(): Created USB scan thread\n");
+
+ for (;;) {
+ sleep(5);
+ kick_disconnected();
+ scan_usb_devices();
+ }
+
+ /* never reaching this point */
+ return (NULL);
+}
+
+static void
+sigalrm_handler(int signo)
+{
+ /* nothing */
+}
+
+void
+usb_init()
+{
+ D("usb_init(): started\n");
+ adb_thread_t tid;
+ struct sigaction actions;
+
+ int r = libusb_init(&ctx);
+
+ if (r != LIBUSB_SUCCESS) {
+ err(EX_IOERR, "Failed to init libusb\n");
+ }
+
+ memset(&actions, 0, sizeof(actions));
+
+ sigemptyset(&actions.sa_mask);
+
+ actions.sa_flags = 0;
+ actions.sa_handler = sigalrm_handler;
+
+ sigaction(SIGALRM, &actions, NULL);
+
+ /* initial device scan */
+ scan_usb_devices();
+
+ /* starting USB event polling thread */
+ if (adb_thread_create(&tid, device_poll_thread, NULL)) {
+ err(EX_IOERR, "cannot create USB scan thread\n");
+ }
+
+ D("usb_init(): finished\n");
+}
+
diff --git a/adb/usb_linux.c b/adb/usb_linux.c
new file mode 100644
index 0000000..bb86813
--- /dev/null
+++ b/adb/usb_linux.c
@@ -0,0 +1,689 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <ctype.h>
+
+#include <linux/usbdevice_fs.h>
+#include <linux/version.h>
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 20)
+#include <linux/usb/ch9.h>
+#else
+#include <linux/usb_ch9.h>
+#endif
+#include <asm/byteorder.h>
+
+#include "sysdeps.h"
+
+#define TRACE_TAG TRACE_USB
+#include "adb.h"
+
+
+/* usb scan debugging is waaaay too verbose */
+#define DBGX(x...)
+
+static adb_mutex_t usb_lock = ADB_MUTEX_INITIALIZER;
+
+struct usb_handle
+{
+ usb_handle *prev;
+ usb_handle *next;
+
+ char fname[64];
+ int desc;
+ unsigned char ep_in;
+ unsigned char ep_out;
+
+ unsigned zero_mask;
+ unsigned writeable;
+
+ struct usbdevfs_urb urb_in;
+ struct usbdevfs_urb urb_out;
+
+ int urb_in_busy;
+ int urb_out_busy;
+ int dead;
+
+ adb_cond_t notify;
+ adb_mutex_t lock;
+
+ // for garbage collecting disconnected devices
+ int mark;
+
+ // ID of thread currently in REAPURB
+ pthread_t reaper_thread;
+};
+
+static usb_handle handle_list = {
+ .prev = &handle_list,
+ .next = &handle_list,
+};
+
+static int known_device(const char *dev_name)
+{
+ usb_handle *usb;
+
+ adb_mutex_lock(&usb_lock);
+ for(usb = handle_list.next; usb != &handle_list; usb = usb->next){
+ if(!strcmp(usb->fname, dev_name)) {
+ // set mark flag to indicate this device is still alive
+ usb->mark = 1;
+ adb_mutex_unlock(&usb_lock);
+ return 1;
+ }
+ }
+ adb_mutex_unlock(&usb_lock);
+ return 0;
+}
+
+static void kick_disconnected_devices()
+{
+ usb_handle *usb;
+
+ adb_mutex_lock(&usb_lock);
+ // kick any devices in the device list that were not found in the device scan
+ for(usb = handle_list.next; usb != &handle_list; usb = usb->next){
+ if (usb->mark == 0) {
+ usb_kick(usb);
+ } else {
+ usb->mark = 0;
+ }
+ }
+ adb_mutex_unlock(&usb_lock);
+
+}
+
+static void register_device(const char *dev_name, unsigned char ep_in, unsigned char ep_out,
+ int ifc, int serial_index, unsigned zero_mask);
+
+static inline int badname(const char *name)
+{
+ while(*name) {
+ if(!isdigit(*name++)) return 1;
+ }
+ return 0;
+}
+
+static void find_usb_device(const char *base,
+ void (*register_device_callback)
+ (const char *, unsigned char, unsigned char, int, int, unsigned))
+{
+ char busname[32], devname[32];
+ unsigned char local_ep_in, local_ep_out;
+ DIR *busdir , *devdir ;
+ struct dirent *de;
+ int fd ;
+
+ busdir = opendir(base);
+ if(busdir == 0) return;
+
+ while((de = readdir(busdir)) != 0) {
+ if(badname(de->d_name)) continue;
+
+ snprintf(busname, sizeof busname, "%s/%s", base, de->d_name);
+ devdir = opendir(busname);
+ if(devdir == 0) continue;
+
+// DBGX("[ scanning %s ]\n", busname);
+ while((de = readdir(devdir))) {
+ unsigned char devdesc[256];
+ unsigned char* bufptr = devdesc;
+ unsigned char* bufend;
+ struct usb_device_descriptor* device;
+ struct usb_config_descriptor* config;
+ struct usb_interface_descriptor* interface;
+ struct usb_endpoint_descriptor *ep1, *ep2;
+ unsigned zero_mask = 0;
+ unsigned vid, pid;
+ size_t desclength;
+
+ if(badname(de->d_name)) continue;
+ snprintf(devname, sizeof devname, "%s/%s", busname, de->d_name);
+
+ if(known_device(devname)) {
+ DBGX("skipping %s\n", devname);
+ continue;
+ }
+
+// DBGX("[ scanning %s ]\n", devname);
+ if((fd = unix_open(devname, O_RDONLY)) < 0) {
+ continue;
+ }
+
+ desclength = adb_read(fd, devdesc, sizeof(devdesc));
+ bufend = bufptr + desclength;
+
+ // should have device and configuration descriptors, and atleast two endpoints
+ if (desclength < USB_DT_DEVICE_SIZE + USB_DT_CONFIG_SIZE) {
+ D("desclength %d is too small\n", desclength);
+ adb_close(fd);
+ continue;
+ }
+
+ device = (struct usb_device_descriptor*)bufptr;
+ bufptr += USB_DT_DEVICE_SIZE;
+
+ if((device->bLength != USB_DT_DEVICE_SIZE) || (device->bDescriptorType != USB_DT_DEVICE)) {
+ adb_close(fd);
+ continue;
+ }
+
+ vid = __le16_to_cpu(device->idVendor);
+ pid = __le16_to_cpu(device->idProduct);
+ pid = devdesc[10] | (devdesc[11] << 8);
+ DBGX("[ %s is V:%04x P:%04x ]\n", devname, vid, pid);
+
+ // should have config descriptor next
+ config = (struct usb_config_descriptor *)bufptr;
+ bufptr += USB_DT_CONFIG_SIZE;
+ if (config->bLength != USB_DT_CONFIG_SIZE || config->bDescriptorType != USB_DT_CONFIG) {
+ D("usb_config_descriptor not found\n");
+ adb_close(fd);
+ continue;
+ }
+
+ // loop through all the descriptors and look for the ADB interface
+ while (bufptr < bufend) {
+ unsigned char length = bufptr[0];
+ unsigned char type = bufptr[1];
+
+ if (type == USB_DT_INTERFACE) {
+ interface = (struct usb_interface_descriptor *)bufptr;
+ bufptr += length;
+
+ if (length != USB_DT_INTERFACE_SIZE) {
+ D("interface descriptor has wrong size\n");
+ break;
+ }
+
+ DBGX("bInterfaceClass: %d, bInterfaceSubClass: %d,"
+ "bInterfaceProtocol: %d, bNumEndpoints: %d\n",
+ interface->bInterfaceClass, interface->bInterfaceSubClass,
+ interface->bInterfaceProtocol, interface->bNumEndpoints);
+
+ if (interface->bNumEndpoints == 2 &&
+ is_adb_interface(vid, pid, interface->bInterfaceClass,
+ interface->bInterfaceSubClass, interface->bInterfaceProtocol)) {
+
+ DBGX("looking for bulk endpoints\n");
+ // looks like ADB...
+ ep1 = (struct usb_endpoint_descriptor *)bufptr;
+ bufptr += USB_DT_ENDPOINT_SIZE;
+ ep2 = (struct usb_endpoint_descriptor *)bufptr;
+ bufptr += USB_DT_ENDPOINT_SIZE;
+
+ if (bufptr > devdesc + desclength ||
+ ep1->bLength != USB_DT_ENDPOINT_SIZE ||
+ ep1->bDescriptorType != USB_DT_ENDPOINT ||
+ ep2->bLength != USB_DT_ENDPOINT_SIZE ||
+ ep2->bDescriptorType != USB_DT_ENDPOINT) {
+ D("endpoints not found\n");
+ break;
+ }
+
+ // both endpoints should be bulk
+ if (ep1->bmAttributes != USB_ENDPOINT_XFER_BULK ||
+ ep2->bmAttributes != USB_ENDPOINT_XFER_BULK) {
+ D("bulk endpoints not found\n");
+ continue;
+ }
+ /* aproto 01 needs 0 termination */
+ if(interface->bInterfaceProtocol == 0x01) {
+ zero_mask = ep1->wMaxPacketSize - 1;
+ }
+
+ // we have a match. now we just need to figure out which is in and which is out.
+ if (ep1->bEndpointAddress & USB_ENDPOINT_DIR_MASK) {
+ local_ep_in = ep1->bEndpointAddress;
+ local_ep_out = ep2->bEndpointAddress;
+ } else {
+ local_ep_in = ep2->bEndpointAddress;
+ local_ep_out = ep1->bEndpointAddress;
+ }
+
+ register_device_callback(devname, local_ep_in, local_ep_out,
+ interface->bInterfaceNumber, device->iSerialNumber, zero_mask);
+ break;
+ }
+ } else {
+ bufptr += length;
+ }
+ } // end of while
+
+ adb_close(fd);
+ } // end of devdir while
+ closedir(devdir);
+ } //end of busdir while
+ closedir(busdir);
+}
+
+void usb_cleanup()
+{
+}
+
+static int usb_bulk_write(usb_handle *h, const void *data, int len)
+{
+ struct usbdevfs_urb *urb = &h->urb_out;
+ int res;
+ struct timeval tv;
+ struct timespec ts;
+
+ memset(urb, 0, sizeof(*urb));
+ urb->type = USBDEVFS_URB_TYPE_BULK;
+ urb->endpoint = h->ep_out;
+ urb->status = -1;
+ urb->buffer = (void*) data;
+ urb->buffer_length = len;
+
+ D("++ write ++\n");
+
+ adb_mutex_lock(&h->lock);
+ if(h->dead) {
+ res = -1;
+ goto fail;
+ }
+ do {
+ res = ioctl(h->desc, USBDEVFS_SUBMITURB, urb);
+ } while((res < 0) && (errno == EINTR));
+
+ if(res < 0) {
+ goto fail;
+ }
+
+ res = -1;
+ h->urb_out_busy = 1;
+ for(;;) {
+ /* time out after five seconds */
+ gettimeofday(&tv, NULL);
+ ts.tv_sec = tv.tv_sec + 5;
+ ts.tv_nsec = tv.tv_usec * 1000L;
+ res = pthread_cond_timedwait(&h->notify, &h->lock, &ts);
+ if(res < 0 || h->dead) {
+ break;
+ }
+ if(h->urb_out_busy == 0) {
+ if(urb->status == 0) {
+ res = urb->actual_length;
+ }
+ break;
+ }
+ }
+fail:
+ adb_mutex_unlock(&h->lock);
+ D("-- write --\n");
+ return res;
+}
+
+static int usb_bulk_read(usb_handle *h, void *data, int len)
+{
+ struct usbdevfs_urb *urb = &h->urb_in;
+ struct usbdevfs_urb *out = NULL;
+ int res;
+
+ memset(urb, 0, sizeof(*urb));
+ urb->type = USBDEVFS_URB_TYPE_BULK;
+ urb->endpoint = h->ep_in;
+ urb->status = -1;
+ urb->buffer = data;
+ urb->buffer_length = len;
+
+
+ adb_mutex_lock(&h->lock);
+ if(h->dead) {
+ res = -1;
+ goto fail;
+ }
+ do {
+ res = ioctl(h->desc, USBDEVFS_SUBMITURB, urb);
+ } while((res < 0) && (errno == EINTR));
+
+ if(res < 0) {
+ goto fail;
+ }
+
+ h->urb_in_busy = 1;
+ for(;;) {
+ D("[ reap urb - wait ]\n");
+ h->reaper_thread = pthread_self();
+ adb_mutex_unlock(&h->lock);
+ res = ioctl(h->desc, USBDEVFS_REAPURB, &out);
+ adb_mutex_lock(&h->lock);
+ h->reaper_thread = 0;
+ if(h->dead) {
+ res = -1;
+ break;
+ }
+ if(res < 0) {
+ if(errno == EINTR) {
+ continue;
+ }
+ D("[ reap urb - error ]\n");
+ break;
+ }
+ D("[ urb @%p status = %d, actual = %d ]\n",
+ out, out->status, out->actual_length);
+
+ if(out == &h->urb_in) {
+ D("[ reap urb - IN complete ]\n");
+ h->urb_in_busy = 0;
+ if(urb->status == 0) {
+ res = urb->actual_length;
+ } else {
+ res = -1;
+ }
+ break;
+ }
+ if(out == &h->urb_out) {
+ D("[ reap urb - OUT compelete ]\n");
+ h->urb_out_busy = 0;
+ adb_cond_broadcast(&h->notify);
+ }
+ }
+fail:
+ adb_mutex_unlock(&h->lock);
+ return res;
+}
+
+
+int usb_write(usb_handle *h, const void *_data, int len)
+{
+ unsigned char *data = (unsigned char*) _data;
+ int n;
+ int need_zero = 0;
+
+ if(h->zero_mask) {
+ /* if we need 0-markers and our transfer
+ ** is an even multiple of the packet size,
+ ** we make note of it
+ */
+ if(!(len & h->zero_mask)) {
+ need_zero = 1;
+ }
+ }
+
+ while(len > 0) {
+ int xfer = (len > 4096) ? 4096 : len;
+
+ n = usb_bulk_write(h, data, xfer);
+ if(n != xfer) {
+ D("ERROR: n = %d, errno = %d (%s)\n",
+ n, errno, strerror(errno));
+ return -1;
+ }
+
+ len -= xfer;
+ data += xfer;
+ }
+
+ if(need_zero){
+ n = usb_bulk_write(h, _data, 0);
+ return n;
+ }
+
+ return 0;
+}
+
+int usb_read(usb_handle *h, void *_data, int len)
+{
+ unsigned char *data = (unsigned char*) _data;
+ int n;
+
+ D("++ usb_read ++\n");
+ while(len > 0) {
+ int xfer = (len > 4096) ? 4096 : len;
+
+ D("[ usb read %d fd = %d], fname=%s\n", xfer, h->desc, h->fname);
+ n = usb_bulk_read(h, data, xfer);
+ D("[ usb read %d ] = %d, fname=%s\n", xfer, n, h->fname);
+ if(n != xfer) {
+ if((errno == ETIMEDOUT) && (h->desc != -1)) {
+ D("[ timeout ]\n");
+ if(n > 0){
+ data += n;
+ len -= n;
+ }
+ continue;
+ }
+ D("ERROR: n = %d, errno = %d (%s)\n",
+ n, errno, strerror(errno));
+ return -1;
+ }
+
+ len -= xfer;
+ data += xfer;
+ }
+
+ D("-- usb_read --\n");
+ return 0;
+}
+
+void usb_kick(usb_handle *h)
+{
+ D("[ kicking %p (fd = %d) ]\n", h, h->desc);
+ adb_mutex_lock(&h->lock);
+ if(h->dead == 0) {
+ h->dead = 1;
+
+ if (h->writeable) {
+ /* HACK ALERT!
+ ** Sometimes we get stuck in ioctl(USBDEVFS_REAPURB).
+ ** This is a workaround for that problem.
+ */
+ if (h->reaper_thread) {
+ pthread_kill(h->reaper_thread, SIGALRM);
+ }
+
+ /* cancel any pending transactions
+ ** these will quietly fail if the txns are not active,
+ ** but this ensures that a reader blocked on REAPURB
+ ** will get unblocked
+ */
+ ioctl(h->desc, USBDEVFS_DISCARDURB, &h->urb_in);
+ ioctl(h->desc, USBDEVFS_DISCARDURB, &h->urb_out);
+ h->urb_in.status = -ENODEV;
+ h->urb_out.status = -ENODEV;
+ h->urb_in_busy = 0;
+ h->urb_out_busy = 0;
+ adb_cond_broadcast(&h->notify);
+ } else {
+ unregister_usb_transport(h);
+ }
+ }
+ adb_mutex_unlock(&h->lock);
+}
+
+int usb_close(usb_handle *h)
+{
+ D("[ usb close ... ]\n");
+ adb_mutex_lock(&usb_lock);
+ h->next->prev = h->prev;
+ h->prev->next = h->next;
+ h->prev = 0;
+ h->next = 0;
+
+ adb_close(h->desc);
+ D("[ usb closed %p (fd = %d) ]\n", h, h->desc);
+ adb_mutex_unlock(&usb_lock);
+
+ free(h);
+ return 0;
+}
+
+static void register_device(const char *dev_name,
+ unsigned char ep_in, unsigned char ep_out,
+ int interface, int serial_index, unsigned zero_mask)
+{
+ usb_handle* usb = 0;
+ int n = 0;
+ char serial[256];
+
+ /* Since Linux will not reassign the device ID (and dev_name)
+ ** as long as the device is open, we can add to the list here
+ ** once we open it and remove from the list when we're finally
+ ** closed and everything will work out fine.
+ **
+ ** If we have a usb_handle on the list 'o handles with a matching
+ ** name, we have no further work to do.
+ */
+ adb_mutex_lock(&usb_lock);
+ for(usb = handle_list.next; usb != &handle_list; usb = usb->next){
+ if(!strcmp(usb->fname, dev_name)) {
+ adb_mutex_unlock(&usb_lock);
+ return;
+ }
+ }
+ adb_mutex_unlock(&usb_lock);
+
+ D("[ usb located new device %s (%d/%d/%d) ]\n",
+ dev_name, ep_in, ep_out, interface);
+ usb = calloc(1, sizeof(usb_handle));
+ strcpy(usb->fname, dev_name);
+ usb->ep_in = ep_in;
+ usb->ep_out = ep_out;
+ usb->zero_mask = zero_mask;
+ usb->writeable = 1;
+
+ adb_cond_init(&usb->notify, 0);
+ adb_mutex_init(&usb->lock, 0);
+ /* initialize mark to 1 so we don't get garbage collected after the device scan */
+ usb->mark = 1;
+ usb->reaper_thread = 0;
+
+ usb->desc = unix_open(usb->fname, O_RDWR);
+ if(usb->desc < 0) {
+ /* if we fail, see if have read-only access */
+ usb->desc = unix_open(usb->fname, O_RDONLY);
+ if(usb->desc < 0) goto fail;
+ usb->writeable = 0;
+ D("[ usb open read-only %s fd = %d]\n", usb->fname, usb->desc);
+ } else {
+ D("[ usb open %s fd = %d]\n", usb->fname, usb->desc);
+ n = ioctl(usb->desc, USBDEVFS_CLAIMINTERFACE, &interface);
+ if(n != 0) goto fail;
+ }
+
+ /* read the device's serial number */
+ serial[0] = 0;
+ memset(serial, 0, sizeof(serial));
+ if (serial_index) {
+ struct usbdevfs_ctrltransfer ctrl;
+ __u16 buffer[128];
+ __u16 languages[128];
+ int i, result;
+ int languageCount = 0;
+
+ memset(languages, 0, sizeof(languages));
+ memset(&ctrl, 0, sizeof(ctrl));
+
+ // read list of supported languages
+ ctrl.bRequestType = USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_DEVICE;
+ ctrl.bRequest = USB_REQ_GET_DESCRIPTOR;
+ ctrl.wValue = (USB_DT_STRING << 8) | 0;
+ ctrl.wIndex = 0;
+ ctrl.wLength = sizeof(languages);
+ ctrl.data = languages;
+
+ result = ioctl(usb->desc, USBDEVFS_CONTROL, &ctrl);
+ if (result > 0)
+ languageCount = (result - 2) / 2;
+
+ for (i = 1; i <= languageCount; i++) {
+ memset(buffer, 0, sizeof(buffer));
+ memset(&ctrl, 0, sizeof(ctrl));
+
+ ctrl.bRequestType = USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_DEVICE;
+ ctrl.bRequest = USB_REQ_GET_DESCRIPTOR;
+ ctrl.wValue = (USB_DT_STRING << 8) | serial_index;
+ ctrl.wIndex = languages[i];
+ ctrl.wLength = sizeof(buffer);
+ ctrl.data = buffer;
+
+ result = ioctl(usb->desc, USBDEVFS_CONTROL, &ctrl);
+ if (result > 0) {
+ int i;
+ // skip first word, and copy the rest to the serial string, changing shorts to bytes.
+ result /= 2;
+ for (i = 1; i < result; i++)
+ serial[i - 1] = buffer[i];
+ serial[i - 1] = 0;
+ break;
+ }
+ }
+ }
+
+ /* add to the end of the active handles */
+ adb_mutex_lock(&usb_lock);
+ usb->next = &handle_list;
+ usb->prev = handle_list.prev;
+ usb->prev->next = usb;
+ usb->next->prev = usb;
+ adb_mutex_unlock(&usb_lock);
+
+ register_usb_transport(usb, serial, usb->writeable);
+ return;
+
+fail:
+ D("[ usb open %s error=%d, err_str = %s]\n",
+ usb->fname, errno, strerror(errno));
+ if(usb->desc >= 0) {
+ adb_close(usb->desc);
+ }
+ free(usb);
+}
+
+void* device_poll_thread(void* unused)
+{
+ D("Created device thread\n");
+ for(;;) {
+ /* XXX use inotify */
+ find_usb_device("/dev/bus/usb", register_device);
+ kick_disconnected_devices();
+ sleep(1);
+ }
+ return NULL;
+}
+
+static void sigalrm_handler(int signo)
+{
+ // don't need to do anything here
+}
+
+void usb_init()
+{
+ adb_thread_t tid;
+ struct sigaction actions;
+
+ memset(&actions, 0, sizeof(actions));
+ sigemptyset(&actions.sa_mask);
+ actions.sa_flags = 0;
+ actions.sa_handler = sigalrm_handler;
+ sigaction(SIGALRM,& actions, NULL);
+
+ if(adb_thread_create(&tid, device_poll_thread, NULL)){
+ fatal_errno("cannot create input thread");
+ }
+}
+
diff --git a/adb/usb_linux_client.c b/adb/usb_linux_client.c
new file mode 100644
index 0000000..0a21c6f
--- /dev/null
+++ b/adb/usb_linux_client.c
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <errno.h>
+
+#include "sysdeps.h"
+
+#define TRACE_TAG TRACE_USB
+#include "adb.h"
+
+
+struct usb_handle
+{
+ int fd;
+ adb_cond_t notify;
+ adb_mutex_t lock;
+};
+
+void usb_cleanup()
+{
+ // nothing to do here
+}
+
+static void *usb_open_thread(void *x)
+{
+ struct usb_handle *usb = (struct usb_handle *)x;
+ int fd;
+
+ while (1) {
+ // wait until the USB device needs opening
+ adb_mutex_lock(&usb->lock);
+ while (usb->fd != -1)
+ adb_cond_wait(&usb->notify, &usb->lock);
+ adb_mutex_unlock(&usb->lock);
+
+ D("[ usb_thread - opening device ]\n");
+ do {
+ /* XXX use inotify? */
+ fd = unix_open("/dev/android_adb", O_RDWR);
+ if (fd < 0) {
+ // to support older kernels
+ fd = unix_open("/dev/android", O_RDWR);
+ }
+ if (fd < 0) {
+ adb_sleep_ms(1000);
+ }
+ } while (fd < 0);
+ D("[ opening device succeeded ]\n");
+
+ close_on_exec(fd);
+ usb->fd = fd;
+
+ D("[ usb_thread - registering device ]\n");
+ register_usb_transport(usb, 0, 1);
+ }
+
+ // never gets here
+ return 0;
+}
+
+int usb_write(usb_handle *h, const void *data, int len)
+{
+ int n;
+
+ D("[ write %d ]\n", len);
+ n = adb_write(h->fd, data, len);
+ if(n != len) {
+ D("ERROR: n = %d, errno = %d (%s)\n",
+ n, errno, strerror(errno));
+ return -1;
+ }
+ D("[ done ]\n");
+ return 0;
+}
+
+int usb_read(usb_handle *h, void *data, int len)
+{
+ int n;
+
+ D("[ read %d ]\n", len);
+ n = adb_read(h->fd, data, len);
+ if(n != len) {
+ D("ERROR: n = %d, errno = %d (%s)\n",
+ n, errno, strerror(errno));
+ return -1;
+ }
+ return 0;
+}
+
+void usb_init()
+{
+ usb_handle *h;
+ adb_thread_t tid;
+ int fd;
+
+ h = calloc(1, sizeof(usb_handle));
+ h->fd = -1;
+ adb_cond_init(&h->notify, 0);
+ adb_mutex_init(&h->lock, 0);
+
+ // Open the file /dev/android_adb_enable to trigger
+ // the enabling of the adb USB function in the kernel.
+ // We never touch this file again - just leave it open
+ // indefinitely so the kernel will know when we are running
+ // and when we are not.
+ fd = unix_open("/dev/android_adb_enable", O_RDWR);
+ if (fd < 0) {
+ D("failed to open /dev/android_adb_enable\n");
+ } else {
+ close_on_exec(fd);
+ }
+
+ D("[ usb_init - starting thread ]\n");
+ if(adb_thread_create(&tid, usb_open_thread, h)){
+ fatal_errno("cannot create usb thread");
+ }
+}
+
+void usb_kick(usb_handle *h)
+{
+ D("usb_kick\n");
+ adb_mutex_lock(&h->lock);
+ adb_close(h->fd);
+ h->fd = -1;
+
+ // notify usb_open_thread that we are disconnected
+ adb_cond_signal(&h->notify);
+ adb_mutex_unlock(&h->lock);
+}
+
+int usb_close(usb_handle *h)
+{
+ // nothing to do here
+ return 0;
+}
diff --git a/adb/usb_osx.c b/adb/usb_osx.c
new file mode 100644
index 0000000..00d02da
--- /dev/null
+++ b/adb/usb_osx.c
@@ -0,0 +1,537 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <CoreFoundation/CoreFoundation.h>
+
+#include <IOKit/IOKitLib.h>
+#include <IOKit/IOCFPlugIn.h>
+#include <IOKit/usb/IOUSBLib.h>
+#include <IOKit/IOMessage.h>
+#include <mach/mach_port.h>
+
+#include "sysdeps.h"
+
+#include <stdio.h>
+
+#define TRACE_TAG TRACE_USB
+#include "adb.h"
+#include "usb_vendors.h"
+
+#define DBG D
+
+static IONotificationPortRef notificationPort = 0;
+static io_iterator_t* notificationIterators;
+
+struct usb_handle
+{
+ UInt8 bulkIn;
+ UInt8 bulkOut;
+ IOUSBInterfaceInterface **interface;
+ io_object_t usbNotification;
+ unsigned int zero_mask;
+};
+
+static CFRunLoopRef currentRunLoop = 0;
+static pthread_mutex_t start_lock;
+static pthread_cond_t start_cond;
+
+
+static void AndroidInterfaceAdded(void *refCon, io_iterator_t iterator);
+static void AndroidInterfaceNotify(void *refCon, io_iterator_t iterator,
+ natural_t messageType,
+ void *messageArgument);
+static usb_handle* CheckInterface(IOUSBInterfaceInterface **iface,
+ UInt16 vendor, UInt16 product);
+
+static int
+InitUSB()
+{
+ CFMutableDictionaryRef matchingDict;
+ CFRunLoopSourceRef runLoopSource;
+ SInt32 vendor, if_subclass, if_protocol;
+ unsigned i;
+
+ //* To set up asynchronous notifications, create a notification port and
+ //* add its run loop event source to the program's run loop
+ notificationPort = IONotificationPortCreate(kIOMasterPortDefault);
+ runLoopSource = IONotificationPortGetRunLoopSource(notificationPort);
+ CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, kCFRunLoopDefaultMode);
+
+ memset(notificationIterators, 0, sizeof(notificationIterators));
+
+ //* loop through all supported vendors
+ for (i = 0; i < vendorIdCount; i++) {
+ //* Create our matching dictionary to find the Android device's
+ //* adb interface
+ //* IOServiceAddMatchingNotification consumes the reference, so we do
+ //* not need to release this
+ matchingDict = IOServiceMatching(kIOUSBInterfaceClassName);
+
+ if (!matchingDict) {
+ DBG("ERR: Couldn't create USB matching dictionary.\n");
+ return -1;
+ }
+
+ //* Match based on vendor id, interface subclass and protocol
+ vendor = vendorIds[i];
+ if_subclass = ADB_SUBCLASS;
+ if_protocol = ADB_PROTOCOL;
+ CFDictionarySetValue(matchingDict, CFSTR(kUSBVendorID),
+ CFNumberCreate(kCFAllocatorDefault,
+ kCFNumberSInt32Type, &vendor));
+ CFDictionarySetValue(matchingDict, CFSTR(kUSBInterfaceSubClass),
+ CFNumberCreate(kCFAllocatorDefault,
+ kCFNumberSInt32Type, &if_subclass));
+ CFDictionarySetValue(matchingDict, CFSTR(kUSBInterfaceProtocol),
+ CFNumberCreate(kCFAllocatorDefault,
+ kCFNumberSInt32Type, &if_protocol));
+ IOServiceAddMatchingNotification(
+ notificationPort,
+ kIOFirstMatchNotification,
+ matchingDict,
+ AndroidInterfaceAdded,
+ NULL,
+ &notificationIterators[i]);
+
+ //* Iterate over set of matching interfaces to access already-present
+ //* devices and to arm the notification
+ AndroidInterfaceAdded(NULL, notificationIterators[i]);
+ }
+
+ return 0;
+}
+
+static void
+AndroidInterfaceAdded(void *refCon, io_iterator_t iterator)
+{
+ kern_return_t kr;
+ io_service_t usbDevice;
+ io_service_t usbInterface;
+ IOCFPlugInInterface **plugInInterface = NULL;
+ IOUSBInterfaceInterface220 **iface = NULL;
+ IOUSBDeviceInterface197 **dev = NULL;
+ HRESULT result;
+ SInt32 score;
+ UInt16 vendor;
+ UInt16 product;
+ UInt8 serialIndex;
+ char serial[256];
+
+ while ((usbInterface = IOIteratorNext(iterator))) {
+ //* Create an intermediate interface plugin
+ kr = IOCreatePlugInInterfaceForService(usbInterface,
+ kIOUSBInterfaceUserClientTypeID,
+ kIOCFPlugInInterfaceID,
+ &plugInInterface, &score);
+ IOObjectRelease(usbInterface);
+ if ((kIOReturnSuccess != kr) || (!plugInInterface)) {
+ DBG("ERR: Unable to create an interface plug-in (%08x)\n", kr);
+ continue;
+ }
+
+ //* This gets us the interface object
+ result = (*plugInInterface)->QueryInterface(plugInInterface,
+ CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID), (LPVOID)
+ &iface);
+ //* We only needed the plugin to get the interface, so discard it
+ (*plugInInterface)->Release(plugInInterface);
+ if (result || !iface) {
+ DBG("ERR: Couldn't query the interface (%08x)\n", (int) result);
+ continue;
+ }
+
+ //* this gets us an ioservice, with which we will find the actual
+ //* device; after getting a plugin, and querying the interface, of
+ //* course.
+ //* Gotta love OS X
+ kr = (*iface)->GetDevice(iface, &usbDevice);
+ if (kIOReturnSuccess != kr || !usbDevice) {
+ DBG("ERR: Couldn't grab device from interface (%08x)\n", kr);
+ continue;
+ }
+
+ plugInInterface = NULL;
+ score = 0;
+ //* create an intermediate device plugin
+ kr = IOCreatePlugInInterfaceForService(usbDevice,
+ kIOUSBDeviceUserClientTypeID,
+ kIOCFPlugInInterfaceID,
+ &plugInInterface, &score);
+ //* only needed this to find the plugin
+ (void)IOObjectRelease(usbDevice);
+ if ((kIOReturnSuccess != kr) || (!plugInInterface)) {
+ DBG("ERR: Unable to create a device plug-in (%08x)\n", kr);
+ continue;
+ }
+
+ result = (*plugInInterface)->QueryInterface(plugInInterface,
+ CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID), (LPVOID) &dev);
+ //* only needed this to query the plugin
+ (*plugInInterface)->Release(plugInInterface);
+ if (result || !dev) {
+ DBG("ERR: Couldn't create a device interface (%08x)\n",
+ (int) result);
+ continue;
+ }
+
+ //* Now after all that, we actually have a ref to the device and
+ //* the interface that matched our criteria
+
+ kr = (*dev)->GetDeviceVendor(dev, &vendor);
+ kr = (*dev)->GetDeviceProduct(dev, &product);
+ kr = (*dev)->USBGetSerialNumberStringIndex(dev, &serialIndex);
+
+ if (serialIndex > 0) {
+ IOUSBDevRequest req;
+ UInt16 buffer[256];
+ UInt16 languages[128];
+
+ memset(languages, 0, sizeof(languages));
+
+ req.bmRequestType =
+ USBmakebmRequestType(kUSBIn, kUSBStandard, kUSBDevice);
+ req.bRequest = kUSBRqGetDescriptor;
+ req.wValue = (kUSBStringDesc << 8) | 0;
+ req.wIndex = 0;
+ req.pData = languages;
+ req.wLength = sizeof(languages);
+ kr = (*dev)->DeviceRequest(dev, &req);
+
+ if (kr == kIOReturnSuccess && req.wLenDone > 0) {
+
+ int langCount = (req.wLenDone - 2) / 2, lang;
+
+ for (lang = 1; lang <= langCount; lang++) {
+
+ memset(buffer, 0, sizeof(buffer));
+ memset(&req, 0, sizeof(req));
+
+ req.bmRequestType =
+ USBmakebmRequestType(kUSBIn, kUSBStandard, kUSBDevice);
+ req.bRequest = kUSBRqGetDescriptor;
+ req.wValue = (kUSBStringDesc << 8) | serialIndex;
+ req.wIndex = languages[lang];
+ req.pData = buffer;
+ req.wLength = sizeof(buffer);
+ kr = (*dev)->DeviceRequest(dev, &req);
+
+ if (kr == kIOReturnSuccess && req.wLenDone > 0) {
+ int i, count;
+
+ // skip first word, and copy the rest to the serial string,
+ // changing shorts to bytes.
+ count = (req.wLenDone - 1) / 2;
+ for (i = 0; i < count; i++)
+ serial[i] = buffer[i + 1];
+ serial[i] = 0;
+ break;
+ }
+ }
+ }
+ }
+ (*dev)->Release(dev);
+
+ DBG("INFO: Found vid=%04x pid=%04x serial=%s\n", vendor, product,
+ serial);
+
+ usb_handle* handle = CheckInterface((IOUSBInterfaceInterface**)iface,
+ vendor, product);
+ if (handle == NULL) {
+ DBG("ERR: Could not find device interface: %08x\n", kr);
+ (*iface)->Release(iface);
+ continue;
+ }
+
+ DBG("AndroidDeviceAdded calling register_usb_transport\n");
+ register_usb_transport(handle, (serial[0] ? serial : NULL), 1);
+
+ // Register for an interest notification of this device being removed.
+ // Pass the reference to our private data as the refCon for the
+ // notification.
+ kr = IOServiceAddInterestNotification(notificationPort,
+ usbInterface,
+ kIOGeneralInterest,
+ AndroidInterfaceNotify,
+ handle,
+ &handle->usbNotification);
+
+ if (kIOReturnSuccess != kr) {
+ DBG("ERR: Unable to create interest notification (%08x)\n", kr);
+ }
+ }
+}
+
+static void
+AndroidInterfaceNotify(void *refCon, io_service_t service, natural_t messageType, void *messageArgument)
+{
+ usb_handle *handle = (usb_handle *)refCon;
+
+ if (messageType == kIOMessageServiceIsTerminated) {
+ if (!handle) {
+ DBG("ERR: NULL handle\n");
+ return;
+ }
+ DBG("AndroidInterfaceNotify\n");
+ IOObjectRelease(handle->usbNotification);
+ usb_kick(handle);
+ }
+}
+
+//* TODO: simplify this further since we only register to get ADB interface
+//* subclass+protocol events
+static usb_handle*
+CheckInterface(IOUSBInterfaceInterface **interface, UInt16 vendor, UInt16 product)
+{
+ usb_handle* handle = NULL;
+ IOReturn kr;
+ UInt8 interfaceNumEndpoints, interfaceClass, interfaceSubClass, interfaceProtocol;
+ UInt8 endpoint;
+
+
+ //* Now open the interface. This will cause the pipes associated with
+ //* the endpoints in the interface descriptor to be instantiated
+ kr = (*interface)->USBInterfaceOpen(interface);
+ if (kr != kIOReturnSuccess) {
+ DBG("ERR: Could not open interface: (%08x)\n", kr);
+ return NULL;
+ }
+
+ //* Get the number of endpoints associated with this interface
+ kr = (*interface)->GetNumEndpoints(interface, &interfaceNumEndpoints);
+ if (kr != kIOReturnSuccess) {
+ DBG("ERR: Unable to get number of endpoints: (%08x)\n", kr);
+ goto err_get_num_ep;
+ }
+
+ //* Get interface class, subclass and protocol
+ if ((*interface)->GetInterfaceClass(interface, &interfaceClass) != kIOReturnSuccess ||
+ (*interface)->GetInterfaceSubClass(interface, &interfaceSubClass) != kIOReturnSuccess ||
+ (*interface)->GetInterfaceProtocol(interface, &interfaceProtocol) != kIOReturnSuccess) {
+ DBG("ERR: Unable to get interface class, subclass and protocol\n");
+ goto err_get_interface_class;
+ }
+
+ //* check to make sure interface class, subclass and protocol match ADB
+ //* avoid opening mass storage endpoints
+ if (!is_adb_interface(vendor, product, interfaceClass,
+ interfaceSubClass, interfaceProtocol))
+ goto err_bad_adb_interface;
+
+ handle = calloc(1, sizeof(usb_handle));
+
+ //* Iterate over the endpoints for this interface and find the first
+ //* bulk in/out pipes available. These will be our read/write pipes.
+ for (endpoint = 0; endpoint <= interfaceNumEndpoints; endpoint++) {
+ UInt8 transferType;
+ UInt16 maxPacketSize;
+ UInt8 interval;
+ UInt8 number;
+ UInt8 direction;
+
+ kr = (*interface)->GetPipeProperties(interface, endpoint, &direction,
+ &number, &transferType, &maxPacketSize, &interval);
+
+ if (kIOReturnSuccess == kr) {
+ if (kUSBBulk != transferType)
+ continue;
+
+ if (kUSBIn == direction)
+ handle->bulkIn = endpoint;
+
+ if (kUSBOut == direction)
+ handle->bulkOut = endpoint;
+
+ handle->zero_mask = maxPacketSize - 1;
+ } else {
+ DBG("ERR: FindDeviceInterface - could not get pipe properties\n");
+ goto err_get_pipe_props;
+ }
+ }
+
+ handle->interface = interface;
+ return handle;
+
+err_get_pipe_props:
+ free(handle);
+err_bad_adb_interface:
+err_get_interface_class:
+err_get_num_ep:
+ (*interface)->USBInterfaceClose(interface);
+ return NULL;
+}
+
+
+void* RunLoopThread(void* unused)
+{
+ unsigned i;
+
+ InitUSB();
+
+ currentRunLoop = CFRunLoopGetCurrent();
+
+ // Signal the parent that we are running
+ adb_mutex_lock(&start_lock);
+ adb_cond_signal(&start_cond);
+ adb_mutex_unlock(&start_lock);
+
+ CFRunLoopRun();
+ currentRunLoop = 0;
+
+ for (i = 0; i < vendorIdCount; i++) {
+ IOObjectRelease(notificationIterators[i]);
+ }
+ IONotificationPortDestroy(notificationPort);
+
+ DBG("RunLoopThread done\n");
+ return NULL;
+}
+
+
+static int initialized = 0;
+void usb_init()
+{
+ if (!initialized)
+ {
+ adb_thread_t tid;
+
+ notificationIterators = (io_iterator_t*)malloc(
+ vendorIdCount * sizeof(io_iterator_t));
+
+ adb_mutex_init(&start_lock, NULL);
+ adb_cond_init(&start_cond, NULL);
+
+ if(adb_thread_create(&tid, RunLoopThread, NULL))
+ fatal_errno("cannot create input thread");
+
+ // Wait for initialization to finish
+ adb_mutex_lock(&start_lock);
+ adb_cond_wait(&start_cond, &start_lock);
+ adb_mutex_unlock(&start_lock);
+
+ adb_mutex_destroy(&start_lock);
+ adb_cond_destroy(&start_cond);
+
+ initialized = 1;
+ }
+}
+
+void usb_cleanup()
+{
+ DBG("usb_cleanup\n");
+ close_usb_devices();
+ if (currentRunLoop)
+ CFRunLoopStop(currentRunLoop);
+
+ if (notificationIterators != NULL) {
+ free(notificationIterators);
+ notificationIterators = NULL;
+ }
+}
+
+int usb_write(usb_handle *handle, const void *buf, int len)
+{
+ IOReturn result;
+
+ if (!len)
+ return 0;
+
+ if (!handle)
+ return -1;
+
+ if (NULL == handle->interface) {
+ DBG("ERR: usb_write interface was null\n");
+ return -1;
+ }
+
+ if (0 == handle->bulkOut) {
+ DBG("ERR: bulkOut endpoint not assigned\n");
+ return -1;
+ }
+
+ result =
+ (*handle->interface)->WritePipe(
+ handle->interface, handle->bulkOut, (void *)buf, len);
+
+ if ((result == 0) && (handle->zero_mask)) {
+ /* we need 0-markers and our transfer */
+ if(!(len & handle->zero_mask)) {
+ result =
+ (*handle->interface)->WritePipe(
+ handle->interface, handle->bulkOut, (void *)buf, 0);
+ }
+ }
+
+ if (0 == result)
+ return 0;
+
+ DBG("ERR: usb_write failed with status %d\n", result);
+ return -1;
+}
+
+int usb_read(usb_handle *handle, void *buf, int len)
+{
+ IOReturn result;
+ UInt32 numBytes = len;
+
+ if (!len) {
+ return 0;
+ }
+
+ if (!handle) {
+ return -1;
+ }
+
+ if (NULL == handle->interface) {
+ DBG("ERR: usb_read interface was null\n");
+ return -1;
+ }
+
+ if (0 == handle->bulkIn) {
+ DBG("ERR: bulkIn endpoint not assigned\n");
+ return -1;
+ }
+
+ result =
+ (*handle->interface)->ReadPipe(handle->interface,
+ handle->bulkIn, buf, &numBytes);
+
+ if (0 == result)
+ return 0;
+ else {
+ DBG("ERR: usb_read failed with status %d\n", result);
+ }
+
+ return -1;
+}
+
+int usb_close(usb_handle *handle)
+{
+ return 0;
+}
+
+void usb_kick(usb_handle *handle)
+{
+ /* release the interface */
+ if (!handle)
+ return;
+
+ if (handle->interface)
+ {
+ (*handle->interface)->USBInterfaceClose(handle->interface);
+ (*handle->interface)->Release(handle->interface);
+ handle->interface = 0;
+ }
+}
diff --git a/adb/usb_vendors.c b/adb/usb_vendors.c
new file mode 100644
index 0000000..cbc2464
--- /dev/null
+++ b/adb/usb_vendors.c
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2009 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 "usb_vendors.h"
+
+#include <stdio.h>
+
+#ifdef _WIN32
+# define WIN32_LEAN_AND_MEAN
+# include "windows.h"
+# include "shlobj.h"
+#else
+# include <unistd.h>
+# include <sys/stat.h>
+#endif
+
+#include "sysdeps.h"
+#include "adb.h"
+
+#define ANDROID_PATH ".android"
+#define ANDROID_ADB_INI "adb_usb.ini"
+
+#define TRACE_TAG TRACE_USB
+
+// Google's USB Vendor ID
+#define VENDOR_ID_GOOGLE 0x18d1
+// HTC's USB Vendor ID
+#define VENDOR_ID_HTC 0x0bb4
+// Samsung's USB Vendor ID
+#define VENDOR_ID_SAMSUNG 0x04e8
+// Motorola's USB Vendor ID
+#define VENDOR_ID_MOTOROLA 0x22b8
+// LG's USB Vendor ID
+#define VENDOR_ID_LGE 0x1004
+// Huawei's USB Vendor ID
+#define VENDOR_ID_HUAWEI 0x12D1
+// Acer's USB Vendor ID
+#define VENDOR_ID_ACER 0x0502
+// Sony Ericsson's USB Vendor ID
+#define VENDOR_ID_SONY_ERICSSON 0x0FCE
+// Foxconn's USB Vendor ID
+#define VENDOR_ID_FOXCONN 0x0489
+// Dell's USB Vendor ID
+#define VENDOR_ID_DELL 0x413c
+// Nvidia's USB Vendor ID
+#define VENDOR_ID_NVIDIA 0x0955
+// Garmin-Asus's USB Vendor ID
+#define VENDOR_ID_GARMIN_ASUS 0x091E
+// Sharp's USB Vendor ID
+#define VENDOR_ID_SHARP 0x04dd
+// ZTE's USB Vendor ID
+#define VENDOR_ID_ZTE 0x19D2
+// Kyocera's USB Vendor ID
+#define VENDOR_ID_KYOCERA 0x0482
+// Pantech's USB Vendor ID
+#define VENDOR_ID_PANTECH 0x10A9
+// Qualcomm's USB Vendor ID
+#define VENDOR_ID_QUALCOMM 0x05c6
+// NEC's USB Vendor ID
+#define VENDOR_ID_NEC 0x0409
+// Panasonic Mobile Communication's USB Vendor ID
+#define VENDOR_ID_PMC 0x04DA
+// Toshiba's USB Vendor ID
+#define VENDOR_ID_TOSHIBA 0x0930
+// SK Telesys's USB Vendor ID
+#define VENDOR_ID_SK_TELESYS 0x1F53
+// KT Tech's USB Vendor ID
+#define VENDOR_ID_KT_TECH 0x2116
+// Asus's USB Vendor ID
+#define VENDOR_ID_ASUS 0x0b05
+// Philips's USB Vendor ID
+#define VENDOR_ID_PHILIPS 0x0471
+
+
+/** built-in vendor list */
+int builtInVendorIds[] = {
+ VENDOR_ID_GOOGLE,
+ VENDOR_ID_HTC,
+ VENDOR_ID_SAMSUNG,
+ VENDOR_ID_MOTOROLA,
+ VENDOR_ID_LGE,
+ VENDOR_ID_HUAWEI,
+ VENDOR_ID_ACER,
+ VENDOR_ID_SONY_ERICSSON,
+ VENDOR_ID_FOXCONN,
+ VENDOR_ID_DELL,
+ VENDOR_ID_NVIDIA,
+ VENDOR_ID_GARMIN_ASUS,
+ VENDOR_ID_SHARP,
+ VENDOR_ID_ZTE,
+ VENDOR_ID_KYOCERA,
+ VENDOR_ID_PANTECH,
+ VENDOR_ID_QUALCOMM,
+ VENDOR_ID_NEC,
+ VENDOR_ID_PMC,
+ VENDOR_ID_TOSHIBA,
+ VENDOR_ID_SK_TELESYS,
+ VENDOR_ID_KT_TECH,
+ VENDOR_ID_ASUS,
+ VENDOR_ID_PHILIPS,
+};
+
+#define BUILT_IN_VENDOR_COUNT (sizeof(builtInVendorIds)/sizeof(builtInVendorIds[0]))
+
+/* max number of supported vendor ids (built-in + 3rd party). increase as needed */
+#define VENDOR_COUNT_MAX 128
+
+int vendorIds[VENDOR_COUNT_MAX];
+unsigned vendorIdCount = 0;
+
+int get_adb_usb_ini(char* buff, size_t len);
+
+void usb_vendors_init(void)
+{
+ if (VENDOR_COUNT_MAX < BUILT_IN_VENDOR_COUNT) {
+ fprintf(stderr, "VENDOR_COUNT_MAX not big enough for built-in vendor list.\n");
+ exit(2);
+ }
+
+ /* add the built-in vendors at the beginning of the array */
+ memcpy(vendorIds, builtInVendorIds, sizeof(builtInVendorIds));
+
+ /* default array size is the number of built-in vendors */
+ vendorIdCount = BUILT_IN_VENDOR_COUNT;
+
+ if (VENDOR_COUNT_MAX == BUILT_IN_VENDOR_COUNT)
+ return;
+
+ char temp[PATH_MAX];
+ if (get_adb_usb_ini(temp, sizeof(temp)) == 0) {
+ FILE * f = fopen(temp, "rt");
+
+ if (f != NULL) {
+ /* The vendor id file is pretty basic. 1 vendor id per line.
+ Lines starting with # are comments */
+ while (fgets(temp, sizeof(temp), f) != NULL) {
+ if (temp[0] == '#')
+ continue;
+
+ long value = strtol(temp, NULL, 0);
+ if (errno == EINVAL || errno == ERANGE || value > INT_MAX || value < 0) {
+ fprintf(stderr, "Invalid content in %s. Quitting.\n", ANDROID_ADB_INI);
+ exit(2);
+ }
+
+ vendorIds[vendorIdCount++] = (int)value;
+
+ /* make sure we don't go beyond the array */
+ if (vendorIdCount == VENDOR_COUNT_MAX) {
+ break;
+ }
+ }
+ }
+ }
+}
+
+/* Utils methods */
+
+/* builds the path to the adb vendor id file. returns 0 if success */
+int build_path(char* buff, size_t len, const char* format, const char* home)
+{
+ if (snprintf(buff, len, format, home, ANDROID_PATH, ANDROID_ADB_INI) >= len) {
+ return 1;
+ }
+
+ return 0;
+}
+
+/* fills buff with the path to the adb vendor id file. returns 0 if success */
+int get_adb_usb_ini(char* buff, size_t len)
+{
+#ifdef _WIN32
+ const char* home = getenv("ANDROID_SDK_HOME");
+ if (home != NULL) {
+ return build_path(buff, len, "%s\\%s\\%s", home);
+ } else {
+ char path[MAX_PATH];
+ SHGetFolderPath( NULL, CSIDL_PROFILE, NULL, 0, path);
+ return build_path(buff, len, "%s\\%s\\%s", path);
+ }
+#else
+ const char* home = getenv("HOME");
+ if (home == NULL)
+ home = "/tmp";
+
+ return build_path(buff, len, "%s/%s/%s", home);
+#endif
+}
diff --git a/adb/usb_vendors.h b/adb/usb_vendors.h
new file mode 100644
index 0000000..43790b9
--- /dev/null
+++ b/adb/usb_vendors.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2009 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 __USB_VENDORS_H
+#define __USB_VENDORS_H
+
+extern int vendorIds[];
+extern unsigned vendorIdCount;
+
+void usb_vendors_init(void);
+
+#endif \ No newline at end of file
diff --git a/adb/usb_windows.c b/adb/usb_windows.c
new file mode 100644
index 0000000..38c4cf4
--- /dev/null
+++ b/adb/usb_windows.c
@@ -0,0 +1,513 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <windows.h>
+#include <winerror.h>
+#include <errno.h>
+#include <usb100.h>
+#include <adb_api.h>
+#include <stdio.h>
+
+#include "sysdeps.h"
+
+#define TRACE_TAG TRACE_USB
+#include "adb.h"
+
+/** Structure usb_handle describes our connection to the usb device via
+ AdbWinApi.dll. This structure is returned from usb_open() routine and
+ is expected in each subsequent call that is accessing the device.
+*/
+struct usb_handle {
+ /// Previous entry in the list of opened usb handles
+ usb_handle *prev;
+
+ /// Next entry in the list of opened usb handles
+ usb_handle *next;
+
+ /// Handle to USB interface
+ ADBAPIHANDLE adb_interface;
+
+ /// Handle to USB read pipe (endpoint)
+ ADBAPIHANDLE adb_read_pipe;
+
+ /// Handle to USB write pipe (endpoint)
+ ADBAPIHANDLE adb_write_pipe;
+
+ /// Interface name
+ char* interface_name;
+
+ /// Mask for determining when to use zero length packets
+ unsigned zero_mask;
+};
+
+/// Class ID assigned to the device by androidusb.sys
+static const GUID usb_class_id = ANDROID_USB_CLASS_ID;
+
+/// List of opened usb handles
+static usb_handle handle_list = {
+ .prev = &handle_list,
+ .next = &handle_list,
+};
+
+/// Locker for the list of opened usb handles
+ADB_MUTEX_DEFINE( usb_lock );
+
+/// Checks if there is opened usb handle in handle_list for this device.
+int known_device(const char* dev_name);
+
+/// Checks if there is opened usb handle in handle_list for this device.
+/// usb_lock mutex must be held before calling this routine.
+int known_device_locked(const char* dev_name);
+
+/// Registers opened usb handle (adds it to handle_list).
+int register_new_device(usb_handle* handle);
+
+/// Checks if interface (device) matches certain criteria
+int recognized_device(usb_handle* handle);
+
+/// Enumerates present and available interfaces (devices), opens new ones and
+/// registers usb transport for them.
+void find_devices();
+
+/// Entry point for thread that polls (every second) for new usb interfaces.
+/// This routine calls find_devices in infinite loop.
+void* device_poll_thread(void* unused);
+
+/// Initializes this module
+void usb_init();
+
+/// Cleans up this module
+void usb_cleanup();
+
+/// Opens usb interface (device) by interface (device) name.
+usb_handle* do_usb_open(const wchar_t* interface_name);
+
+/// Writes data to the opened usb handle
+int usb_write(usb_handle* handle, const void* data, int len);
+
+/// Reads data using the opened usb handle
+int usb_read(usb_handle *handle, void* data, int len);
+
+/// Cleans up opened usb handle
+void usb_cleanup_handle(usb_handle* handle);
+
+/// Cleans up (but don't close) opened usb handle
+void usb_kick(usb_handle* handle);
+
+/// Closes opened usb handle
+int usb_close(usb_handle* handle);
+
+/// Gets interface (device) name for an opened usb handle
+const char *usb_name(usb_handle* handle);
+
+int known_device_locked(const char* dev_name) {
+ usb_handle* usb;
+
+ if (NULL != dev_name) {
+ // Iterate through the list looking for the name match.
+ for(usb = handle_list.next; usb != &handle_list; usb = usb->next) {
+ // In Windows names are not case sensetive!
+ if((NULL != usb->interface_name) &&
+ (0 == stricmp(usb->interface_name, dev_name))) {
+ return 1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+int known_device(const char* dev_name) {
+ int ret = 0;
+
+ if (NULL != dev_name) {
+ adb_mutex_lock(&usb_lock);
+ ret = known_device_locked(dev_name);
+ adb_mutex_unlock(&usb_lock);
+ }
+
+ return ret;
+}
+
+int register_new_device(usb_handle* handle) {
+ if (NULL == handle)
+ return 0;
+
+ adb_mutex_lock(&usb_lock);
+
+ // Check if device is already in the list
+ if (known_device_locked(handle->interface_name)) {
+ adb_mutex_unlock(&usb_lock);
+ return 0;
+ }
+
+ // Not in the list. Add this handle to the list.
+ handle->next = &handle_list;
+ handle->prev = handle_list.prev;
+ handle->prev->next = handle;
+ handle->next->prev = handle;
+
+ adb_mutex_unlock(&usb_lock);
+
+ return 1;
+}
+
+void* device_poll_thread(void* unused) {
+ D("Created device thread\n");
+
+ while(1) {
+ find_devices();
+ adb_sleep_ms(1000);
+ }
+
+ return NULL;
+}
+
+void usb_init() {
+ adb_thread_t tid;
+
+ if(adb_thread_create(&tid, device_poll_thread, NULL)) {
+ fatal_errno("cannot create input thread");
+ }
+}
+
+void usb_cleanup() {
+}
+
+usb_handle* do_usb_open(const wchar_t* interface_name) {
+ // Allocate our handle
+ usb_handle* ret = (usb_handle*)malloc(sizeof(usb_handle));
+ if (NULL == ret)
+ return NULL;
+
+ // Set linkers back to the handle
+ ret->next = ret;
+ ret->prev = ret;
+
+ // Create interface.
+ ret->adb_interface = AdbCreateInterfaceByName(interface_name);
+
+ if (NULL == ret->adb_interface) {
+ free(ret);
+ errno = GetLastError();
+ return NULL;
+ }
+
+ // Open read pipe (endpoint)
+ ret->adb_read_pipe =
+ AdbOpenDefaultBulkReadEndpoint(ret->adb_interface,
+ AdbOpenAccessTypeReadWrite,
+ AdbOpenSharingModeReadWrite);
+ if (NULL != ret->adb_read_pipe) {
+ // Open write pipe (endpoint)
+ ret->adb_write_pipe =
+ AdbOpenDefaultBulkWriteEndpoint(ret->adb_interface,
+ AdbOpenAccessTypeReadWrite,
+ AdbOpenSharingModeReadWrite);
+ if (NULL != ret->adb_write_pipe) {
+ // Save interface name
+ unsigned long name_len = 0;
+
+ // First get expected name length
+ AdbGetInterfaceName(ret->adb_interface,
+ NULL,
+ &name_len,
+ true);
+ if (0 != name_len) {
+ ret->interface_name = (char*)malloc(name_len);
+
+ if (NULL != ret->interface_name) {
+ // Now save the name
+ if (AdbGetInterfaceName(ret->adb_interface,
+ ret->interface_name,
+ &name_len,
+ true)) {
+ // We're done at this point
+ return ret;
+ }
+ } else {
+ SetLastError(ERROR_OUTOFMEMORY);
+ }
+ }
+ }
+ }
+
+ // Something went wrong.
+ errno = GetLastError();
+ usb_cleanup_handle(ret);
+ free(ret);
+ SetLastError(errno);
+
+ return NULL;
+}
+
+int usb_write(usb_handle* handle, const void* data, int len) {
+ unsigned long time_out = 500 + len * 8;
+ unsigned long written = 0;
+ int ret;
+
+ D("usb_write %d\n", len);
+ if (NULL != handle) {
+ // Perform write
+ ret = AdbWriteEndpointSync(handle->adb_write_pipe,
+ (void*)data,
+ (unsigned long)len,
+ &written,
+ time_out);
+ errno = GetLastError();
+
+ if (ret) {
+ // Make sure that we've written what we were asked to write
+ D("usb_write got: %ld, expected: %d\n", written, len);
+ if (written == (unsigned long)len) {
+ if(handle->zero_mask && (len & handle->zero_mask) == 0) {
+ // Send a zero length packet
+ AdbWriteEndpointSync(handle->adb_write_pipe,
+ (void*)data,
+ 0,
+ &written,
+ time_out);
+ }
+ return 0;
+ }
+ } else {
+ // assume ERROR_INVALID_HANDLE indicates we are disconnected
+ if (errno == ERROR_INVALID_HANDLE)
+ usb_kick(handle);
+ }
+ } else {
+ D("usb_write NULL handle\n");
+ SetLastError(ERROR_INVALID_HANDLE);
+ }
+
+ D("usb_write failed: %d\n", errno);
+
+ return -1;
+}
+
+int usb_read(usb_handle *handle, void* data, int len) {
+ unsigned long time_out = 500 + len * 8;
+ unsigned long read = 0;
+ int ret;
+
+ D("usb_read %d\n", len);
+ if (NULL != handle) {
+ while (len > 0) {
+ int xfer = (len > 4096) ? 4096 : len;
+
+ ret = AdbReadEndpointSync(handle->adb_read_pipe,
+ (void*)data,
+ (unsigned long)xfer,
+ &read,
+ time_out);
+ errno = GetLastError();
+ D("usb_write got: %ld, expected: %d, errno: %d\n", read, xfer, errno);
+ if (ret) {
+ data += read;
+ len -= read;
+
+ if (len == 0)
+ return 0;
+ } else if (errno != ERROR_SEM_TIMEOUT) {
+ // assume ERROR_INVALID_HANDLE indicates we are disconnected
+ if (errno == ERROR_INVALID_HANDLE)
+ usb_kick(handle);
+ break;
+ }
+ }
+ } else {
+ D("usb_read NULL handle\n");
+ SetLastError(ERROR_INVALID_HANDLE);
+ }
+
+ D("usb_read failed: %d\n", errno);
+
+ return -1;
+}
+
+void usb_cleanup_handle(usb_handle* handle) {
+ if (NULL != handle) {
+ if (NULL != handle->interface_name)
+ free(handle->interface_name);
+ if (NULL != handle->adb_write_pipe)
+ AdbCloseHandle(handle->adb_write_pipe);
+ if (NULL != handle->adb_read_pipe)
+ AdbCloseHandle(handle->adb_read_pipe);
+ if (NULL != handle->adb_interface)
+ AdbCloseHandle(handle->adb_interface);
+
+ handle->interface_name = NULL;
+ handle->adb_write_pipe = NULL;
+ handle->adb_read_pipe = NULL;
+ handle->adb_interface = NULL;
+ }
+}
+
+void usb_kick(usb_handle* handle) {
+ if (NULL != handle) {
+ adb_mutex_lock(&usb_lock);
+
+ usb_cleanup_handle(handle);
+
+ adb_mutex_unlock(&usb_lock);
+ } else {
+ SetLastError(ERROR_INVALID_HANDLE);
+ errno = ERROR_INVALID_HANDLE;
+ }
+}
+
+int usb_close(usb_handle* handle) {
+ D("usb_close\n");
+
+ if (NULL != handle) {
+ // Remove handle from the list
+ adb_mutex_lock(&usb_lock);
+
+ if ((handle->next != handle) && (handle->prev != handle)) {
+ handle->next->prev = handle->prev;
+ handle->prev->next = handle->next;
+ handle->prev = handle;
+ handle->next = handle;
+ }
+
+ adb_mutex_unlock(&usb_lock);
+
+ // Cleanup handle
+ usb_cleanup_handle(handle);
+ free(handle);
+ }
+
+ return 0;
+}
+
+const char *usb_name(usb_handle* handle) {
+ if (NULL == handle) {
+ SetLastError(ERROR_INVALID_HANDLE);
+ errno = ERROR_INVALID_HANDLE;
+ return NULL;
+ }
+
+ return (const char*)handle->interface_name;
+}
+
+int recognized_device(usb_handle* handle) {
+ if (NULL == handle)
+ return 0;
+
+ // Check vendor and product id first
+ USB_DEVICE_DESCRIPTOR device_desc;
+
+ if (!AdbGetUsbDeviceDescriptor(handle->adb_interface,
+ &device_desc)) {
+ return 0;
+ }
+
+ // Then check interface properties
+ USB_INTERFACE_DESCRIPTOR interf_desc;
+
+ if (!AdbGetUsbInterfaceDescriptor(handle->adb_interface,
+ &interf_desc)) {
+ return 0;
+ }
+
+ // Must have two endpoints
+ if (2 != interf_desc.bNumEndpoints) {
+ return 0;
+ }
+
+ if (is_adb_interface(device_desc.idVendor, device_desc.idProduct,
+ interf_desc.bInterfaceClass, interf_desc.bInterfaceSubClass, interf_desc.bInterfaceProtocol)) {
+
+ if(interf_desc.bInterfaceProtocol == 0x01) {
+ AdbEndpointInformation endpoint_info;
+ // assuming zero is a valid bulk endpoint ID
+ if (AdbGetEndpointInformation(handle->adb_interface, 0, &endpoint_info)) {
+ handle->zero_mask = endpoint_info.max_packet_size - 1;
+ }
+ }
+
+ return 1;
+ }
+
+ return 0;
+}
+
+void find_devices() {
+ usb_handle* handle = NULL;
+ char entry_buffer[2048];
+ char interf_name[2048];
+ AdbInterfaceInfo* next_interface = (AdbInterfaceInfo*)(&entry_buffer[0]);
+ unsigned long entry_buffer_size = sizeof(entry_buffer);
+ char* copy_name;
+
+ // Enumerate all present and active interfaces.
+ ADBAPIHANDLE enum_handle =
+ AdbEnumInterfaces(usb_class_id, true, true, true);
+
+ if (NULL == enum_handle)
+ return;
+
+ while (AdbNextInterface(enum_handle, next_interface, &entry_buffer_size)) {
+ // TODO: FIXME - temp hack converting wchar_t into char.
+ // It would be better to change AdbNextInterface so it will return
+ // interface name as single char string.
+ const wchar_t* wchar_name = next_interface->device_name;
+ for(copy_name = interf_name;
+ L'\0' != *wchar_name;
+ wchar_name++, copy_name++) {
+ *copy_name = (char)(*wchar_name);
+ }
+ *copy_name = '\0';
+
+ // Lets see if we already have this device in the list
+ if (!known_device(interf_name)) {
+ // This seems to be a new device. Open it!
+ handle = do_usb_open(next_interface->device_name);
+ if (NULL != handle) {
+ // Lets see if this interface (device) belongs to us
+ if (recognized_device(handle)) {
+ D("adding a new device %s\n", interf_name);
+ char serial_number[512];
+ unsigned long serial_number_len = sizeof(serial_number);
+ if (AdbGetSerialNumber(handle->adb_interface,
+ serial_number,
+ &serial_number_len,
+ true)) {
+ // Lets make sure that we don't duplicate this device
+ if (register_new_device(handle)) {
+ register_usb_transport(handle, serial_number, 1);
+ } else {
+ D("register_new_device failed for %s\n", interf_name);
+ usb_cleanup_handle(handle);
+ free(handle);
+ }
+ } else {
+ D("cannot get serial number\n");
+ usb_cleanup_handle(handle);
+ free(handle);
+ }
+ } else {
+ usb_cleanup_handle(handle);
+ free(handle);
+ }
+ }
+ }
+
+ entry_buffer_size = sizeof(entry_buffer);
+ }
+
+ AdbCloseHandle(enum_handle);
+}
diff --git a/adb/utils.c b/adb/utils.c
new file mode 100644
index 0000000..91518ba
--- /dev/null
+++ b/adb/utils.c
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "utils.h"
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+
+char*
+buff_addc (char* buff, char* buffEnd, int c)
+{
+ int avail = buffEnd - buff;
+
+ if (avail <= 0) /* already in overflow mode */
+ return buff;
+
+ if (avail == 1) { /* overflowing, the last byte is reserved for zero */
+ buff[0] = 0;
+ return buff + 1;
+ }
+
+ buff[0] = (char) c; /* add char and terminating zero */
+ buff[1] = 0;
+ return buff + 1;
+}
+
+char*
+buff_adds (char* buff, char* buffEnd, const char* s)
+{
+ int slen = strlen(s);
+
+ return buff_addb(buff, buffEnd, s, slen);
+}
+
+char*
+buff_addb (char* buff, char* buffEnd, const void* data, int len)
+{
+ int avail = (buffEnd - buff);
+
+ if (avail <= 0 || len <= 0) /* already overflowing */
+ return buff;
+
+ if (len > avail)
+ len = avail;
+
+ memcpy(buff, data, len);
+
+ buff += len;
+
+ /* ensure there is a terminating zero */
+ if (buff >= buffEnd) { /* overflow */
+ buff[-1] = 0;
+ } else
+ buff[0] = 0;
+
+ return buff;
+}
+
+char*
+buff_add (char* buff, char* buffEnd, const char* format, ... )
+{
+ int avail;
+
+ avail = (buffEnd - buff);
+
+ if (avail > 0) {
+ va_list args;
+ int nn;
+
+ va_start(args, format);
+ nn = vsnprintf( buff, avail, format, args);
+ va_end(args);
+
+ if (nn < 0) {
+ /* some C libraries return -1 in case of overflow,
+ * but they will also do that if the format spec is
+ * invalid. We assume ADB is not buggy enough to
+ * trigger that last case. */
+ nn = avail;
+ }
+ else if (nn > avail) {
+ nn = avail;
+ }
+
+ buff += nn;
+
+ /* ensure that there is a terminating zero */
+ if (buff >= buffEnd)
+ buff[-1] = 0;
+ else
+ buff[0] = 0;
+ }
+ return buff;
+}
diff --git a/adb/utils.h b/adb/utils.h
new file mode 100644
index 0000000..f70ecd2
--- /dev/null
+++ b/adb/utils.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef _ADB_UTILS_H
+#define _ADB_UTILS_H
+
+/* bounded buffer functions */
+
+/* all these functions are used to append data to a bounded buffer.
+ *
+ * after each operation, the buffer is guaranteed to be zero-terminated,
+ * even in the case of an overflow. they all return the new buffer position
+ * which allows one to use them in succession, only checking for overflows
+ * at the end. For example:
+ *
+ * BUFF_DECL(temp,p,end,1024);
+ * char* p;
+ *
+ * p = buff_addc(temp, end, '"');
+ * p = buff_adds(temp, end, string);
+ * p = buff_addc(temp, end, '"');
+ *
+ * if (p >= end) {
+ * overflow detected. note that 'temp' is
+ * zero-terminated for safety.
+ * }
+ * return strdup(temp);
+ */
+
+/* tries to add a character to the buffer, in case of overflow
+ * this will only write a terminating zero and return buffEnd.
+ */
+char* buff_addc (char* buff, char* buffEnd, int c);
+
+/* tries to add a string to the buffer */
+char* buff_adds (char* buff, char* buffEnd, const char* s);
+
+/* tries to add a bytes to the buffer. the input can contain zero bytes,
+ * but a terminating zero will always be appended at the end anyway
+ */
+char* buff_addb (char* buff, char* buffEnd, const void* data, int len);
+
+/* tries to add a formatted string to a bounded buffer */
+char* buff_add (char* buff, char* buffEnd, const char* format, ... );
+
+/* convenience macro used to define a bounded buffer, as well as
+ * a 'cursor' and 'end' variables all in one go.
+ *
+ * note: this doesn't place an initial terminating zero in the buffer,
+ * you need to use one of the buff_ functions for this. or simply
+ * do _cursor[0] = 0 manually.
+ */
+#define BUFF_DECL(_buff,_cursor,_end,_size) \
+ char _buff[_size], *_cursor=_buff, *_end = _cursor + (_size)
+
+#endif /* _ADB_UTILS_H */
diff --git a/autogen.sh b/autogen.sh
new file mode 100755
index 0000000..0846992
--- /dev/null
+++ b/autogen.sh
@@ -0,0 +1,16 @@
+#!/bin/sh
+
+rm -rf autom4te.cache
+rm -f aclocal.m4 ltmain.sh
+
+touch README
+
+echo "Running aclocal..." ; aclocal $ACLOCAL_FLAGS || exit 1
+echo "Running autoheader..." ; autoheader || exit 1
+echo "Running autoconf..." ; autoconf || exit 1
+echo "Running libtoolize..." ; (libtoolize --copy --automake || glibtoolize --automake) || exit 1
+echo "Running automake..." ; automake --add-missing --copy --gnu || exit 1
+
+if [ -z "$NOCONFIGURE" ]; then
+ ./configure "$@"
+fi
diff --git a/bionic/dlmalloc.c b/bionic/dlmalloc.c
new file mode 100644
index 0000000..d30ec30
--- /dev/null
+++ b/bionic/dlmalloc.c
@@ -0,0 +1,5469 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+/*
+ This is a version (aka dlmalloc) of malloc/free/realloc written by
+ Doug Lea and released to the public domain, as explained at
+ http://creativecommons.org/licenses/publicdomain. Send questions,
+ comments, complaints, performance data, etc to dl@cs.oswego.edu
+
+* Version 2.8.3 Thu Sep 22 11:16:15 2005 Doug Lea (dl at gee)
+
+ Note: There may be an updated version of this malloc obtainable at
+ ftp://gee.cs.oswego.edu/pub/misc/malloc.c
+ Check before installing!
+
+* Quickstart
+
+ This library is all in one file to simplify the most common usage:
+ ftp it, compile it (-O3), and link it into another program. All of
+ the compile-time options default to reasonable values for use on
+ most platforms. You might later want to step through various
+ compile-time and dynamic tuning options.
+
+ For convenience, an include file for code using this malloc is at:
+ ftp://gee.cs.oswego.edu/pub/misc/malloc-2.8.3.h
+ You don't really need this .h file unless you call functions not
+ defined in your system include files. The .h file contains only the
+ excerpts from this file needed for using this malloc on ANSI C/C++
+ systems, so long as you haven't changed compile-time options about
+ naming and tuning parameters. If you do, then you can create your
+ own malloc.h that does include all settings by cutting at the point
+ indicated below. Note that you may already by default be using a C
+ library containing a malloc that is based on some version of this
+ malloc (for example in linux). You might still want to use the one
+ in this file to customize settings or to avoid overheads associated
+ with library versions.
+
+* Vital statistics:
+
+ Supported pointer/size_t representation: 4 or 8 bytes
+ size_t MUST be an unsigned type of the same width as
+ pointers. (If you are using an ancient system that declares
+ size_t as a signed type, or need it to be a different width
+ than pointers, you can use a previous release of this malloc
+ (e.g. 2.7.2) supporting these.)
+
+ Alignment: 8 bytes (default)
+ This suffices for nearly all current machines and C compilers.
+ However, you can define MALLOC_ALIGNMENT to be wider than this
+ if necessary (up to 128bytes), at the expense of using more space.
+
+ Minimum overhead per allocated chunk: 4 or 8 bytes (if 4byte sizes)
+ 8 or 16 bytes (if 8byte sizes)
+ Each malloced chunk has a hidden word of overhead holding size
+ and status information, and additional cross-check word
+ if FOOTERS is defined.
+
+ Minimum allocated size: 4-byte ptrs: 16 bytes (including overhead)
+ 8-byte ptrs: 32 bytes (including overhead)
+
+ Even a request for zero bytes (i.e., malloc(0)) returns a
+ pointer to something of the minimum allocatable size.
+ The maximum overhead wastage (i.e., number of extra bytes
+ allocated than were requested in malloc) is less than or equal
+ to the minimum size, except for requests >= mmap_threshold that
+ are serviced via mmap(), where the worst case wastage is about
+ 32 bytes plus the remainder from a system page (the minimal
+ mmap unit); typically 4096 or 8192 bytes.
+
+ Security: static-safe; optionally more or less
+ The "security" of malloc refers to the ability of malicious
+ code to accentuate the effects of errors (for example, freeing
+ space that is not currently malloc'ed or overwriting past the
+ ends of chunks) in code that calls malloc. This malloc
+ guarantees not to modify any memory locations below the base of
+ heap, i.e., static variables, even in the presence of usage
+ errors. The routines additionally detect most improper frees
+ and reallocs. All this holds as long as the static bookkeeping
+ for malloc itself is not corrupted by some other means. This
+ is only one aspect of security -- these checks do not, and
+ cannot, detect all possible programming errors.
+
+ If FOOTERS is defined nonzero, then each allocated chunk
+ carries an additional check word to verify that it was malloced
+ from its space. These check words are the same within each
+ execution of a program using malloc, but differ across
+ executions, so externally crafted fake chunks cannot be
+ freed. This improves security by rejecting frees/reallocs that
+ could corrupt heap memory, in addition to the checks preventing
+ writes to statics that are always on. This may further improve
+ security at the expense of time and space overhead. (Note that
+ FOOTERS may also be worth using with MSPACES.)
+
+ By default detected errors cause the program to abort (calling
+ "abort()"). You can override this to instead proceed past
+ errors by defining PROCEED_ON_ERROR. In this case, a bad free
+ has no effect, and a malloc that encounters a bad address
+ caused by user overwrites will ignore the bad address by
+ dropping pointers and indices to all known memory. This may
+ be appropriate for programs that should continue if at all
+ possible in the face of programming errors, although they may
+ run out of memory because dropped memory is never reclaimed.
+
+ If you don't like either of these options, you can define
+ CORRUPTION_ERROR_ACTION and USAGE_ERROR_ACTION to do anything
+ else. And if if you are sure that your program using malloc has
+ no errors or vulnerabilities, you can define INSECURE to 1,
+ which might (or might not) provide a small performance improvement.
+
+ Thread-safety: NOT thread-safe unless USE_LOCKS defined
+ When USE_LOCKS is defined, each public call to malloc, free,
+ etc is surrounded with either a pthread mutex or a win32
+ spinlock (depending on WIN32). This is not especially fast, and
+ can be a major bottleneck. It is designed only to provide
+ minimal protection in concurrent environments, and to provide a
+ basis for extensions. If you are using malloc in a concurrent
+ program, consider instead using ptmalloc, which is derived from
+ a version of this malloc. (See http://www.malloc.de).
+
+ System requirements: Any combination of MORECORE and/or MMAP/MUNMAP
+ This malloc can use unix sbrk or any emulation (invoked using
+ the CALL_MORECORE macro) and/or mmap/munmap or any emulation
+ (invoked using CALL_MMAP/CALL_MUNMAP) to get and release system
+ memory. On most unix systems, it tends to work best if both
+ MORECORE and MMAP are enabled. On Win32, it uses emulations
+ based on VirtualAlloc. It also uses common C library functions
+ like memset.
+
+ Compliance: I believe it is compliant with the Single Unix Specification
+ (See http://www.unix.org). Also SVID/XPG, ANSI C, and probably
+ others as well.
+
+* Overview of algorithms
+
+ This is not the fastest, most space-conserving, most portable, or
+ most tunable malloc ever written. However it is among the fastest
+ while also being among the most space-conserving, portable and
+ tunable. Consistent balance across these factors results in a good
+ general-purpose allocator for malloc-intensive programs.
+
+ In most ways, this malloc is a best-fit allocator. Generally, it
+ chooses the best-fitting existing chunk for a request, with ties
+ broken in approximately least-recently-used order. (This strategy
+ normally maintains low fragmentation.) However, for requests less
+ than 256bytes, it deviates from best-fit when there is not an
+ exactly fitting available chunk by preferring to use space adjacent
+ to that used for the previous small request, as well as by breaking
+ ties in approximately most-recently-used order. (These enhance
+ locality of series of small allocations.) And for very large requests
+ (>= 256Kb by default), it relies on system memory mapping
+ facilities, if supported. (This helps avoid carrying around and
+ possibly fragmenting memory used only for large chunks.)
+
+ All operations (except malloc_stats and mallinfo) have execution
+ times that are bounded by a constant factor of the number of bits in
+ a size_t, not counting any clearing in calloc or copying in realloc,
+ or actions surrounding MORECORE and MMAP that have times
+ proportional to the number of non-contiguous regions returned by
+ system allocation routines, which is often just 1.
+
+ The implementation is not very modular and seriously overuses
+ macros. Perhaps someday all C compilers will do as good a job
+ inlining modular code as can now be done by brute-force expansion,
+ but now, enough of them seem not to.
+
+ Some compilers issue a lot of warnings about code that is
+ dead/unreachable only on some platforms, and also about intentional
+ uses of negation on unsigned types. All known cases of each can be
+ ignored.
+
+ For a longer but out of date high-level description, see
+ http://gee.cs.oswego.edu/dl/html/malloc.html
+
+* MSPACES
+ If MSPACES is defined, then in addition to malloc, free, etc.,
+ this file also defines mspace_malloc, mspace_free, etc. These
+ are versions of malloc routines that take an "mspace" argument
+ obtained using create_mspace, to control all internal bookkeeping.
+ If ONLY_MSPACES is defined, only these versions are compiled.
+ So if you would like to use this allocator for only some allocations,
+ and your system malloc for others, you can compile with
+ ONLY_MSPACES and then do something like...
+ static mspace mymspace = create_mspace(0,0); // for example
+ #define mymalloc(bytes) mspace_malloc(mymspace, bytes)
+
+ (Note: If you only need one instance of an mspace, you can instead
+ use "USE_DL_PREFIX" to relabel the global malloc.)
+
+ You can similarly create thread-local allocators by storing
+ mspaces as thread-locals. For example:
+ static __thread mspace tlms = 0;
+ void* tlmalloc(size_t bytes) {
+ if (tlms == 0) tlms = create_mspace(0, 0);
+ return mspace_malloc(tlms, bytes);
+ }
+ void tlfree(void* mem) { mspace_free(tlms, mem); }
+
+ Unless FOOTERS is defined, each mspace is completely independent.
+ You cannot allocate from one and free to another (although
+ conformance is only weakly checked, so usage errors are not always
+ caught). If FOOTERS is defined, then each chunk carries around a tag
+ indicating its originating mspace, and frees are directed to their
+ originating spaces.
+
+ ------------------------- Compile-time options ---------------------------
+
+Be careful in setting #define values for numerical constants of type
+size_t. On some systems, literal values are not automatically extended
+to size_t precision unless they are explicitly casted.
+
+WIN32 default: defined if _WIN32 defined
+ Defining WIN32 sets up defaults for MS environment and compilers.
+ Otherwise defaults are for unix.
+
+MALLOC_ALIGNMENT default: (size_t)8
+ Controls the minimum alignment for malloc'ed chunks. It must be a
+ power of two and at least 8, even on machines for which smaller
+ alignments would suffice. It may be defined as larger than this
+ though. Note however that code and data structures are optimized for
+ the case of 8-byte alignment.
+
+MSPACES default: 0 (false)
+ If true, compile in support for independent allocation spaces.
+ This is only supported if HAVE_MMAP is true.
+
+ONLY_MSPACES default: 0 (false)
+ If true, only compile in mspace versions, not regular versions.
+
+USE_LOCKS default: 0 (false)
+ Causes each call to each public routine to be surrounded with
+ pthread or WIN32 mutex lock/unlock. (If set true, this can be
+ overridden on a per-mspace basis for mspace versions.)
+
+FOOTERS default: 0
+ If true, provide extra checking and dispatching by placing
+ information in the footers of allocated chunks. This adds
+ space and time overhead.
+
+INSECURE default: 0
+ If true, omit checks for usage errors and heap space overwrites.
+
+USE_DL_PREFIX default: NOT defined
+ Causes compiler to prefix all public routines with the string 'dl'.
+ This can be useful when you only want to use this malloc in one part
+ of a program, using your regular system malloc elsewhere.
+
+ABORT default: defined as abort()
+ Defines how to abort on failed checks. On most systems, a failed
+ check cannot die with an "assert" or even print an informative
+ message, because the underlying print routines in turn call malloc,
+ which will fail again. Generally, the best policy is to simply call
+ abort(). It's not very useful to do more than this because many
+ errors due to overwriting will show up as address faults (null, odd
+ addresses etc) rather than malloc-triggered checks, so will also
+ abort. Also, most compilers know that abort() does not return, so
+ can better optimize code conditionally calling it.
+
+PROCEED_ON_ERROR default: defined as 0 (false)
+ Controls whether detected bad addresses cause them to bypassed
+ rather than aborting. If set, detected bad arguments to free and
+ realloc are ignored. And all bookkeeping information is zeroed out
+ upon a detected overwrite of freed heap space, thus losing the
+ ability to ever return it from malloc again, but enabling the
+ application to proceed. If PROCEED_ON_ERROR is defined, the
+ static variable malloc_corruption_error_count is compiled in
+ and can be examined to see if errors have occurred. This option
+ generates slower code than the default abort policy.
+
+DEBUG default: NOT defined
+ The DEBUG setting is mainly intended for people trying to modify
+ this code or diagnose problems when porting to new platforms.
+ However, it may also be able to better isolate user errors than just
+ using runtime checks. The assertions in the check routines spell
+ out in more detail the assumptions and invariants underlying the
+ algorithms. The checking is fairly extensive, and will slow down
+ execution noticeably. Calling malloc_stats or mallinfo with DEBUG
+ set will attempt to check every non-mmapped allocated and free chunk
+ in the course of computing the summaries.
+
+ABORT_ON_ASSERT_FAILURE default: defined as 1 (true)
+ Debugging assertion failures can be nearly impossible if your
+ version of the assert macro causes malloc to be called, which will
+ lead to a cascade of further failures, blowing the runtime stack.
+ ABORT_ON_ASSERT_FAILURE cause assertions failures to call abort(),
+ which will usually make debugging easier.
+
+MALLOC_FAILURE_ACTION default: sets errno to ENOMEM, or no-op on win32
+ The action to take before "return 0" when malloc fails to be able to
+ return memory because there is none available.
+
+HAVE_MORECORE default: 1 (true) unless win32 or ONLY_MSPACES
+ True if this system supports sbrk or an emulation of it.
+
+MORECORE default: sbrk
+ The name of the sbrk-style system routine to call to obtain more
+ memory. See below for guidance on writing custom MORECORE
+ functions. The type of the argument to sbrk/MORECORE varies across
+ systems. It cannot be size_t, because it supports negative
+ arguments, so it is normally the signed type of the same width as
+ size_t (sometimes declared as "intptr_t"). It doesn't much matter
+ though. Internally, we only call it with arguments less than half
+ the max value of a size_t, which should work across all reasonable
+ possibilities, although sometimes generating compiler warnings. See
+ near the end of this file for guidelines for creating a custom
+ version of MORECORE.
+
+MORECORE_CONTIGUOUS default: 1 (true)
+ If true, take advantage of fact that consecutive calls to MORECORE
+ with positive arguments always return contiguous increasing
+ addresses. This is true of unix sbrk. It does not hurt too much to
+ set it true anyway, since malloc copes with non-contiguities.
+ Setting it false when definitely non-contiguous saves time
+ and possibly wasted space it would take to discover this though.
+
+MORECORE_CANNOT_TRIM default: NOT defined
+ True if MORECORE cannot release space back to the system when given
+ negative arguments. This is generally necessary only if you are
+ using a hand-crafted MORECORE function that cannot handle negative
+ arguments.
+
+HAVE_MMAP default: 1 (true)
+ True if this system supports mmap or an emulation of it. If so, and
+ HAVE_MORECORE is not true, MMAP is used for all system
+ allocation. If set and HAVE_MORECORE is true as well, MMAP is
+ primarily used to directly allocate very large blocks. It is also
+ used as a backup strategy in cases where MORECORE fails to provide
+ space from system. Note: A single call to MUNMAP is assumed to be
+ able to unmap memory that may have be allocated using multiple calls
+ to MMAP, so long as they are adjacent.
+
+HAVE_MREMAP default: 1 on linux, else 0
+ If true realloc() uses mremap() to re-allocate large blocks and
+ extend or shrink allocation spaces.
+
+MMAP_CLEARS default: 1 on unix
+ True if mmap clears memory so calloc doesn't need to. This is true
+ for standard unix mmap using /dev/zero.
+
+USE_BUILTIN_FFS default: 0 (i.e., not used)
+ Causes malloc to use the builtin ffs() function to compute indices.
+ Some compilers may recognize and intrinsify ffs to be faster than the
+ supplied C version. Also, the case of x86 using gcc is special-cased
+ to an asm instruction, so is already as fast as it can be, and so
+ this setting has no effect. (On most x86s, the asm version is only
+ slightly faster than the C version.)
+
+malloc_getpagesize default: derive from system includes, or 4096.
+ The system page size. To the extent possible, this malloc manages
+ memory from the system in page-size units. This may be (and
+ usually is) a function rather than a constant. This is ignored
+ if WIN32, where page size is determined using getSystemInfo during
+ initialization.
+
+USE_DEV_RANDOM default: 0 (i.e., not used)
+ Causes malloc to use /dev/random to initialize secure magic seed for
+ stamping footers. Otherwise, the current time is used.
+
+NO_MALLINFO default: 0
+ If defined, don't compile "mallinfo". This can be a simple way
+ of dealing with mismatches between system declarations and
+ those in this file.
+
+MALLINFO_FIELD_TYPE default: size_t
+ The type of the fields in the mallinfo struct. This was originally
+ defined as "int" in SVID etc, but is more usefully defined as
+ size_t. The value is used only if HAVE_USR_INCLUDE_MALLOC_H is not set
+
+REALLOC_ZERO_BYTES_FREES default: not defined
+ This should be set if a call to realloc with zero bytes should
+ be the same as a call to free. Some people think it should. Otherwise,
+ since this malloc returns a unique pointer for malloc(0), so does
+ realloc(p, 0).
+
+LACKS_UNISTD_H, LACKS_FCNTL_H, LACKS_SYS_PARAM_H, LACKS_SYS_MMAN_H
+LACKS_STRINGS_H, LACKS_STRING_H, LACKS_SYS_TYPES_H, LACKS_ERRNO_H
+LACKS_STDLIB_H default: NOT defined unless on WIN32
+ Define these if your system does not have these header files.
+ You might need to manually insert some of the declarations they provide.
+
+DEFAULT_GRANULARITY default: page size if MORECORE_CONTIGUOUS,
+ system_info.dwAllocationGranularity in WIN32,
+ otherwise 64K.
+ Also settable using mallopt(M_GRANULARITY, x)
+ The unit for allocating and deallocating memory from the system. On
+ most systems with contiguous MORECORE, there is no reason to
+ make this more than a page. However, systems with MMAP tend to
+ either require or encourage larger granularities. You can increase
+ this value to prevent system allocation functions to be called so
+ often, especially if they are slow. The value must be at least one
+ page and must be a power of two. Setting to 0 causes initialization
+ to either page size or win32 region size. (Note: In previous
+ versions of malloc, the equivalent of this option was called
+ "TOP_PAD")
+
+DEFAULT_TRIM_THRESHOLD default: 2MB
+ Also settable using mallopt(M_TRIM_THRESHOLD, x)
+ The maximum amount of unused top-most memory to keep before
+ releasing via malloc_trim in free(). Automatic trimming is mainly
+ useful in long-lived programs using contiguous MORECORE. Because
+ trimming via sbrk can be slow on some systems, and can sometimes be
+ wasteful (in cases where programs immediately afterward allocate
+ more large chunks) the value should be high enough so that your
+ overall system performance would improve by releasing this much
+ memory. As a rough guide, you might set to a value close to the
+ average size of a process (program) running on your system.
+ Releasing this much memory would allow such a process to run in
+ memory. Generally, it is worth tuning trim thresholds when a
+ program undergoes phases where several large chunks are allocated
+ and released in ways that can reuse each other's storage, perhaps
+ mixed with phases where there are no such chunks at all. The trim
+ value must be greater than page size to have any useful effect. To
+ disable trimming completely, you can set to MAX_SIZE_T. Note that the trick
+ some people use of mallocing a huge space and then freeing it at
+ program startup, in an attempt to reserve system memory, doesn't
+ have the intended effect under automatic trimming, since that memory
+ will immediately be returned to the system.
+
+DEFAULT_MMAP_THRESHOLD default: 256K
+ Also settable using mallopt(M_MMAP_THRESHOLD, x)
+ The request size threshold for using MMAP to directly service a
+ request. Requests of at least this size that cannot be allocated
+ using already-existing space will be serviced via mmap. (If enough
+ normal freed space already exists it is used instead.) Using mmap
+ segregates relatively large chunks of memory so that they can be
+ individually obtained and released from the host system. A request
+ serviced through mmap is never reused by any other request (at least
+ not directly; the system may just so happen to remap successive
+ requests to the same locations). Segregating space in this way has
+ the benefits that: Mmapped space can always be individually released
+ back to the system, which helps keep the system level memory demands
+ of a long-lived program low. Also, mapped memory doesn't become
+ `locked' between other chunks, as can happen with normally allocated
+ chunks, which means that even trimming via malloc_trim would not
+ release them. However, it has the disadvantage that the space
+ cannot be reclaimed, consolidated, and then used to service later
+ requests, as happens with normal chunks. The advantages of mmap
+ nearly always outweigh disadvantages for "large" chunks, but the
+ value of "large" may vary across systems. The default is an
+ empirically derived value that works well in most systems. You can
+ disable mmap by setting to MAX_SIZE_T.
+
+*/
+
+#ifdef ANDROID
+#define USE_BUILTIN_FFS 1
+#ifdef __arm__
+#include <machine/cpu-features.h>
+#endif
+#endif /* ANDROID */
+
+#ifndef WIN32
+#ifdef _WIN32
+#define WIN32 1
+#endif /* _WIN32 */
+#endif /* WIN32 */
+#ifdef WIN32
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#define HAVE_MMAP 1
+#define HAVE_MORECORE 0
+#define LACKS_UNISTD_H
+#define LACKS_SYS_PARAM_H
+#define LACKS_SYS_MMAN_H
+#define LACKS_STRING_H
+#define LACKS_STRINGS_H
+#define LACKS_SYS_TYPES_H
+#define LACKS_ERRNO_H
+#define MALLOC_FAILURE_ACTION
+#define MMAP_CLEARS 0 /* WINCE and some others apparently don't clear */
+#endif /* WIN32 */
+
+#if defined(DARWIN) || defined(_DARWIN)
+/* Mac OSX docs advise not to use sbrk; it seems better to use mmap */
+#ifndef HAVE_MORECORE
+#define HAVE_MORECORE 0
+#define HAVE_MMAP 1
+#endif /* HAVE_MORECORE */
+#endif /* DARWIN */
+
+#ifndef LACKS_SYS_TYPES_H
+#include <sys/types.h> /* For size_t */
+#endif /* LACKS_SYS_TYPES_H */
+
+/* The maximum possible size_t value has all bits set */
+#define MAX_SIZE_T (~(size_t)0)
+
+#ifndef ONLY_MSPACES
+#define ONLY_MSPACES 0
+#endif /* ONLY_MSPACES */
+#ifndef MSPACES
+#if ONLY_MSPACES
+#define MSPACES 1
+#else /* ONLY_MSPACES */
+#define MSPACES 0
+#endif /* ONLY_MSPACES */
+#endif /* MSPACES */
+#ifndef MALLOC_ALIGNMENT
+#define MALLOC_ALIGNMENT ((size_t)8U)
+#endif /* MALLOC_ALIGNMENT */
+#ifndef FOOTERS
+#define FOOTERS 0
+#endif /* FOOTERS */
+#ifndef USE_MAX_ALLOWED_FOOTPRINT
+#define USE_MAX_ALLOWED_FOOTPRINT 0
+#endif
+#ifndef ABORT
+#define ABORT abort()
+#endif /* ABORT */
+#ifndef ABORT_ON_ASSERT_FAILURE
+#define ABORT_ON_ASSERT_FAILURE 1
+#endif /* ABORT_ON_ASSERT_FAILURE */
+#ifndef PROCEED_ON_ERROR
+#define PROCEED_ON_ERROR 0
+#endif /* PROCEED_ON_ERROR */
+#ifndef USE_LOCKS
+#define USE_LOCKS 0
+#endif /* USE_LOCKS */
+#ifndef INSECURE
+#define INSECURE 0
+#endif /* INSECURE */
+#ifndef HAVE_MMAP
+#define HAVE_MMAP 1
+#endif /* HAVE_MMAP */
+#ifndef MMAP_CLEARS
+#define MMAP_CLEARS 1
+#endif /* MMAP_CLEARS */
+#ifndef HAVE_MREMAP
+#ifdef linux
+#define HAVE_MREMAP 1
+#else /* linux */
+#define HAVE_MREMAP 0
+#endif /* linux */
+#endif /* HAVE_MREMAP */
+#ifndef MALLOC_FAILURE_ACTION
+#define MALLOC_FAILURE_ACTION errno = ENOMEM;
+#endif /* MALLOC_FAILURE_ACTION */
+#ifndef HAVE_MORECORE
+#if ONLY_MSPACES
+#define HAVE_MORECORE 0
+#else /* ONLY_MSPACES */
+#define HAVE_MORECORE 1
+#endif /* ONLY_MSPACES */
+#endif /* HAVE_MORECORE */
+#if !HAVE_MORECORE
+#define MORECORE_CONTIGUOUS 0
+#else /* !HAVE_MORECORE */
+#ifndef MORECORE
+#define MORECORE sbrk
+#endif /* MORECORE */
+#ifndef MORECORE_CONTIGUOUS
+#define MORECORE_CONTIGUOUS 1
+#endif /* MORECORE_CONTIGUOUS */
+#endif /* HAVE_MORECORE */
+#ifndef DEFAULT_GRANULARITY
+#if MORECORE_CONTIGUOUS
+#define DEFAULT_GRANULARITY (0) /* 0 means to compute in init_mparams */
+#else /* MORECORE_CONTIGUOUS */
+#define DEFAULT_GRANULARITY ((size_t)64U * (size_t)1024U)
+#endif /* MORECORE_CONTIGUOUS */
+#endif /* DEFAULT_GRANULARITY */
+#ifndef DEFAULT_TRIM_THRESHOLD
+#ifndef MORECORE_CANNOT_TRIM
+#define DEFAULT_TRIM_THRESHOLD ((size_t)2U * (size_t)1024U * (size_t)1024U)
+#else /* MORECORE_CANNOT_TRIM */
+#define DEFAULT_TRIM_THRESHOLD MAX_SIZE_T
+#endif /* MORECORE_CANNOT_TRIM */
+#endif /* DEFAULT_TRIM_THRESHOLD */
+#ifndef DEFAULT_MMAP_THRESHOLD
+#if HAVE_MMAP
+#define DEFAULT_MMAP_THRESHOLD ((size_t)64U * (size_t)1024U)
+#else /* HAVE_MMAP */
+#define DEFAULT_MMAP_THRESHOLD MAX_SIZE_T
+#endif /* HAVE_MMAP */
+#endif /* DEFAULT_MMAP_THRESHOLD */
+#ifndef USE_BUILTIN_FFS
+#define USE_BUILTIN_FFS 0
+#endif /* USE_BUILTIN_FFS */
+#ifndef USE_DEV_RANDOM
+#define USE_DEV_RANDOM 0
+#endif /* USE_DEV_RANDOM */
+#ifndef NO_MALLINFO
+#define NO_MALLINFO 0
+#endif /* NO_MALLINFO */
+#ifndef MALLINFO_FIELD_TYPE
+#define MALLINFO_FIELD_TYPE size_t
+#endif /* MALLINFO_FIELD_TYPE */
+
+/*
+ mallopt tuning options. SVID/XPG defines four standard parameter
+ numbers for mallopt, normally defined in malloc.h. None of these
+ are used in this malloc, so setting them has no effect. But this
+ malloc does support the following options.
+*/
+
+#define M_TRIM_THRESHOLD (-1)
+#define M_GRANULARITY (-2)
+#define M_MMAP_THRESHOLD (-3)
+
+/* ------------------------ Mallinfo declarations ------------------------ */
+
+#if !NO_MALLINFO
+/*
+ This version of malloc supports the standard SVID/XPG mallinfo
+ routine that returns a struct containing usage properties and
+ statistics. It should work on any system that has a
+ /usr/include/malloc.h defining struct mallinfo. The main
+ declaration needed is the mallinfo struct that is returned (by-copy)
+ by mallinfo(). The malloinfo struct contains a bunch of fields that
+ are not even meaningful in this version of malloc. These fields are
+ are instead filled by mallinfo() with other numbers that might be of
+ interest.
+
+ HAVE_USR_INCLUDE_MALLOC_H should be set if you have a
+ /usr/include/malloc.h file that includes a declaration of struct
+ mallinfo. If so, it is included; else a compliant version is
+ declared below. These must be precisely the same for mallinfo() to
+ work. The original SVID version of this struct, defined on most
+ systems with mallinfo, declares all fields as ints. But some others
+ define as unsigned long. If your system defines the fields using a
+ type of different width than listed here, you MUST #include your
+ system version and #define HAVE_USR_INCLUDE_MALLOC_H.
+*/
+
+/* #define HAVE_USR_INCLUDE_MALLOC_H */
+
+#if !ANDROID
+#ifdef HAVE_USR_INCLUDE_MALLOC_H
+#include "/usr/include/malloc.h"
+#else /* HAVE_USR_INCLUDE_MALLOC_H */
+
+struct mallinfo {
+ MALLINFO_FIELD_TYPE arena; /* non-mmapped space allocated from system */
+ MALLINFO_FIELD_TYPE ordblks; /* number of free chunks */
+ MALLINFO_FIELD_TYPE smblks; /* always 0 */
+ MALLINFO_FIELD_TYPE hblks; /* always 0 */
+ MALLINFO_FIELD_TYPE hblkhd; /* space in mmapped regions */
+ MALLINFO_FIELD_TYPE usmblks; /* maximum total allocated space */
+ MALLINFO_FIELD_TYPE fsmblks; /* always 0 */
+ MALLINFO_FIELD_TYPE uordblks; /* total allocated space */
+ MALLINFO_FIELD_TYPE fordblks; /* total free space */
+ MALLINFO_FIELD_TYPE keepcost; /* releasable (via malloc_trim) space */
+};
+
+#endif /* HAVE_USR_INCLUDE_MALLOC_H */
+#endif /* NO_MALLINFO */
+#endif /* ANDROID */
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#if !ONLY_MSPACES
+
+/* ------------------- Declarations of public routines ------------------- */
+
+/* Check an additional macro for the five primary functions */
+#ifndef USE_DL_PREFIX
+#define dlcalloc calloc
+#define dlfree free
+#define dlmalloc malloc
+#define dlmemalign memalign
+#define dlrealloc realloc
+#endif
+
+#ifndef USE_DL_PREFIX
+#define dlvalloc valloc
+#define dlpvalloc pvalloc
+#define dlmallinfo mallinfo
+#define dlmallopt mallopt
+#define dlmalloc_trim malloc_trim
+#define dlmalloc_walk_free_pages \
+ malloc_walk_free_pages
+#define dlmalloc_walk_heap \
+ malloc_walk_heap
+#define dlmalloc_stats malloc_stats
+#define dlmalloc_usable_size malloc_usable_size
+#define dlmalloc_footprint malloc_footprint
+#define dlmalloc_max_allowed_footprint \
+ malloc_max_allowed_footprint
+#define dlmalloc_set_max_allowed_footprint \
+ malloc_set_max_allowed_footprint
+#define dlmalloc_max_footprint malloc_max_footprint
+#define dlindependent_calloc independent_calloc
+#define dlindependent_comalloc independent_comalloc
+#endif /* USE_DL_PREFIX */
+
+
+/*
+ malloc(size_t n)
+ Returns a pointer to a newly allocated chunk of at least n bytes, or
+ null if no space is available, in which case errno is set to ENOMEM
+ on ANSI C systems.
+
+ If n is zero, malloc returns a minimum-sized chunk. (The minimum
+ size is 16 bytes on most 32bit systems, and 32 bytes on 64bit
+ systems.) Note that size_t is an unsigned type, so calls with
+ arguments that would be negative if signed are interpreted as
+ requests for huge amounts of space, which will often fail. The
+ maximum supported value of n differs across systems, but is in all
+ cases less than the maximum representable value of a size_t.
+*/
+void* dlmalloc(size_t);
+
+/*
+ free(void* p)
+ Releases the chunk of memory pointed to by p, that had been previously
+ allocated using malloc or a related routine such as realloc.
+ It has no effect if p is null. If p was not malloced or already
+ freed, free(p) will by default cause the current program to abort.
+*/
+void dlfree(void*);
+
+/*
+ calloc(size_t n_elements, size_t element_size);
+ Returns a pointer to n_elements * element_size bytes, with all locations
+ set to zero.
+*/
+void* dlcalloc(size_t, size_t);
+
+/*
+ realloc(void* p, size_t n)
+ Returns a pointer to a chunk of size n that contains the same data
+ as does chunk p up to the minimum of (n, p's size) bytes, or null
+ if no space is available.
+
+ The returned pointer may or may not be the same as p. The algorithm
+ prefers extending p in most cases when possible, otherwise it
+ employs the equivalent of a malloc-copy-free sequence.
+
+ If p is null, realloc is equivalent to malloc.
+
+ If space is not available, realloc returns null, errno is set (if on
+ ANSI) and p is NOT freed.
+
+ if n is for fewer bytes than already held by p, the newly unused
+ space is lopped off and freed if possible. realloc with a size
+ argument of zero (re)allocates a minimum-sized chunk.
+
+ The old unix realloc convention of allowing the last-free'd chunk
+ to be used as an argument to realloc is not supported.
+*/
+
+void* dlrealloc(void*, size_t);
+
+/*
+ memalign(size_t alignment, size_t n);
+ Returns a pointer to a newly allocated chunk of n bytes, aligned
+ in accord with the alignment argument.
+
+ The alignment argument should be a power of two. If the argument is
+ not a power of two, the nearest greater power is used.
+ 8-byte alignment is guaranteed by normal malloc calls, so don't
+ bother calling memalign with an argument of 8 or less.
+
+ Overreliance on memalign is a sure way to fragment space.
+*/
+void* dlmemalign(size_t, size_t);
+
+/*
+ valloc(size_t n);
+ Equivalent to memalign(pagesize, n), where pagesize is the page
+ size of the system. If the pagesize is unknown, 4096 is used.
+*/
+void* dlvalloc(size_t);
+
+/*
+ mallopt(int parameter_number, int parameter_value)
+ Sets tunable parameters The format is to provide a
+ (parameter-number, parameter-value) pair. mallopt then sets the
+ corresponding parameter to the argument value if it can (i.e., so
+ long as the value is meaningful), and returns 1 if successful else
+ 0. SVID/XPG/ANSI defines four standard param numbers for mallopt,
+ normally defined in malloc.h. None of these are use in this malloc,
+ so setting them has no effect. But this malloc also supports other
+ options in mallopt. See below for details. Briefly, supported
+ parameters are as follows (listed defaults are for "typical"
+ configurations).
+
+ Symbol param # default allowed param values
+ M_TRIM_THRESHOLD -1 2*1024*1024 any (MAX_SIZE_T disables)
+ M_GRANULARITY -2 page size any power of 2 >= page size
+ M_MMAP_THRESHOLD -3 256*1024 any (or 0 if no MMAP support)
+*/
+int dlmallopt(int, int);
+
+/*
+ malloc_footprint();
+ Returns the number of bytes obtained from the system. The total
+ number of bytes allocated by malloc, realloc etc., is less than this
+ value. Unlike mallinfo, this function returns only a precomputed
+ result, so can be called frequently to monitor memory consumption.
+ Even if locks are otherwise defined, this function does not use them,
+ so results might not be up to date.
+*/
+size_t dlmalloc_footprint(void);
+
+#if USE_MAX_ALLOWED_FOOTPRINT
+/*
+ malloc_max_allowed_footprint();
+ Returns the number of bytes that the heap is allowed to obtain
+ from the system. malloc_footprint() should always return a
+ size less than or equal to max_allowed_footprint, unless the
+ max_allowed_footprint was set to a value smaller than the
+ footprint at the time.
+*/
+size_t dlmalloc_max_allowed_footprint();
+
+/*
+ malloc_set_max_allowed_footprint();
+ Set the maximum number of bytes that the heap is allowed to
+ obtain from the system. The size will be rounded up to a whole
+ page, and the rounded number will be returned from future calls
+ to malloc_max_allowed_footprint(). If the new max_allowed_footprint
+ is larger than the current footprint, the heap will never grow
+ larger than max_allowed_footprint. If the new max_allowed_footprint
+ is smaller than the current footprint, the heap will not grow
+ further.
+
+ TODO: try to force the heap to give up memory in the shrink case,
+ and update this comment once that happens.
+*/
+void dlmalloc_set_max_allowed_footprint(size_t bytes);
+#endif /* USE_MAX_ALLOWED_FOOTPRINT */
+
+/*
+ malloc_max_footprint();
+ Returns the maximum number of bytes obtained from the system. This
+ value will be greater than current footprint if deallocated space
+ has been reclaimed by the system. The peak number of bytes allocated
+ by malloc, realloc etc., is less than this value. Unlike mallinfo,
+ this function returns only a precomputed result, so can be called
+ frequently to monitor memory consumption. Even if locks are
+ otherwise defined, this function does not use them, so results might
+ not be up to date.
+*/
+size_t dlmalloc_max_footprint(void);
+
+#if !NO_MALLINFO
+/*
+ mallinfo()
+ Returns (by copy) a struct containing various summary statistics:
+
+ arena: current total non-mmapped bytes allocated from system
+ ordblks: the number of free chunks
+ smblks: always zero.
+ hblks: current number of mmapped regions
+ hblkhd: total bytes held in mmapped regions
+ usmblks: the maximum total allocated space. This will be greater
+ than current total if trimming has occurred.
+ fsmblks: always zero
+ uordblks: current total allocated space (normal or mmapped)
+ fordblks: total free space
+ keepcost: the maximum number of bytes that could ideally be released
+ back to system via malloc_trim. ("ideally" means that
+ it ignores page restrictions etc.)
+
+ Because these fields are ints, but internal bookkeeping may
+ be kept as longs, the reported values may wrap around zero and
+ thus be inaccurate.
+*/
+struct mallinfo dlmallinfo(void);
+#endif /* NO_MALLINFO */
+
+/*
+ independent_calloc(size_t n_elements, size_t element_size, void* chunks[]);
+
+ independent_calloc is similar to calloc, but instead of returning a
+ single cleared space, it returns an array of pointers to n_elements
+ independent elements that can hold contents of size elem_size, each
+ of which starts out cleared, and can be independently freed,
+ realloc'ed etc. The elements are guaranteed to be adjacently
+ allocated (this is not guaranteed to occur with multiple callocs or
+ mallocs), which may also improve cache locality in some
+ applications.
+
+ The "chunks" argument is optional (i.e., may be null, which is
+ probably the most typical usage). If it is null, the returned array
+ is itself dynamically allocated and should also be freed when it is
+ no longer needed. Otherwise, the chunks array must be of at least
+ n_elements in length. It is filled in with the pointers to the
+ chunks.
+
+ In either case, independent_calloc returns this pointer array, or
+ null if the allocation failed. If n_elements is zero and "chunks"
+ is null, it returns a chunk representing an array with zero elements
+ (which should be freed if not wanted).
+
+ Each element must be individually freed when it is no longer
+ needed. If you'd like to instead be able to free all at once, you
+ should instead use regular calloc and assign pointers into this
+ space to represent elements. (In this case though, you cannot
+ independently free elements.)
+
+ independent_calloc simplifies and speeds up implementations of many
+ kinds of pools. It may also be useful when constructing large data
+ structures that initially have a fixed number of fixed-sized nodes,
+ but the number is not known at compile time, and some of the nodes
+ may later need to be freed. For example:
+
+ struct Node { int item; struct Node* next; };
+
+ struct Node* build_list() {
+ struct Node** pool;
+ int n = read_number_of_nodes_needed();
+ if (n <= 0) return 0;
+ pool = (struct Node**)(independent_calloc(n, sizeof(struct Node), 0);
+ if (pool == 0) die();
+ // organize into a linked list...
+ struct Node* first = pool[0];
+ for (i = 0; i < n-1; ++i)
+ pool[i]->next = pool[i+1];
+ free(pool); // Can now free the array (or not, if it is needed later)
+ return first;
+ }
+*/
+void** dlindependent_calloc(size_t, size_t, void**);
+
+/*
+ independent_comalloc(size_t n_elements, size_t sizes[], void* chunks[]);
+
+ independent_comalloc allocates, all at once, a set of n_elements
+ chunks with sizes indicated in the "sizes" array. It returns
+ an array of pointers to these elements, each of which can be
+ independently freed, realloc'ed etc. The elements are guaranteed to
+ be adjacently allocated (this is not guaranteed to occur with
+ multiple callocs or mallocs), which may also improve cache locality
+ in some applications.
+
+ The "chunks" argument is optional (i.e., may be null). If it is null
+ the returned array is itself dynamically allocated and should also
+ be freed when it is no longer needed. Otherwise, the chunks array
+ must be of at least n_elements in length. It is filled in with the
+ pointers to the chunks.
+
+ In either case, independent_comalloc returns this pointer array, or
+ null if the allocation failed. If n_elements is zero and chunks is
+ null, it returns a chunk representing an array with zero elements
+ (which should be freed if not wanted).
+
+ Each element must be individually freed when it is no longer
+ needed. If you'd like to instead be able to free all at once, you
+ should instead use a single regular malloc, and assign pointers at
+ particular offsets in the aggregate space. (In this case though, you
+ cannot independently free elements.)
+
+ independent_comallac differs from independent_calloc in that each
+ element may have a different size, and also that it does not
+ automatically clear elements.
+
+ independent_comalloc can be used to speed up allocation in cases
+ where several structs or objects must always be allocated at the
+ same time. For example:
+
+ struct Head { ... }
+ struct Foot { ... }
+
+ void send_message(char* msg) {
+ int msglen = strlen(msg);
+ size_t sizes[3] = { sizeof(struct Head), msglen, sizeof(struct Foot) };
+ void* chunks[3];
+ if (independent_comalloc(3, sizes, chunks) == 0)
+ die();
+ struct Head* head = (struct Head*)(chunks[0]);
+ char* body = (char*)(chunks[1]);
+ struct Foot* foot = (struct Foot*)(chunks[2]);
+ // ...
+ }
+
+ In general though, independent_comalloc is worth using only for
+ larger values of n_elements. For small values, you probably won't
+ detect enough difference from series of malloc calls to bother.
+
+ Overuse of independent_comalloc can increase overall memory usage,
+ since it cannot reuse existing noncontiguous small chunks that
+ might be available for some of the elements.
+*/
+void** dlindependent_comalloc(size_t, size_t*, void**);
+
+
+/*
+ pvalloc(size_t n);
+ Equivalent to valloc(minimum-page-that-holds(n)), that is,
+ round up n to nearest pagesize.
+ */
+void* dlpvalloc(size_t);
+
+/*
+ malloc_trim(size_t pad);
+
+ If possible, gives memory back to the system (via negative arguments
+ to sbrk) if there is unused memory at the `high' end of the malloc
+ pool or in unused MMAP segments. You can call this after freeing
+ large blocks of memory to potentially reduce the system-level memory
+ requirements of a program. However, it cannot guarantee to reduce
+ memory. Under some allocation patterns, some large free blocks of
+ memory will be locked between two used chunks, so they cannot be
+ given back to the system.
+
+ The `pad' argument to malloc_trim represents the amount of free
+ trailing space to leave untrimmed. If this argument is zero, only
+ the minimum amount of memory to maintain internal data structures
+ will be left. Non-zero arguments can be supplied to maintain enough
+ trailing space to service future expected allocations without having
+ to re-obtain memory from the system.
+
+ Malloc_trim returns 1 if it actually released any memory, else 0.
+*/
+int dlmalloc_trim(size_t);
+
+/*
+ malloc_walk_free_pages(handler, harg)
+
+ Calls the provided handler on each free region in the heap. The
+ memory between start and end are guaranteed not to contain any
+ important data, so the handler is free to alter the contents
+ in any way. This can be used to advise the OS that large free
+ regions may be swapped out.
+
+ The value in harg will be passed to each call of the handler.
+ */
+void dlmalloc_walk_free_pages(void(*)(void*, void*, void*), void*);
+
+/*
+ malloc_walk_heap(handler, harg)
+
+ Calls the provided handler on each object or free region in the
+ heap. The handler will receive the chunk pointer and length, the
+ object pointer and length, and the value in harg on each call.
+ */
+void dlmalloc_walk_heap(void(*)(const void*, size_t,
+ const void*, size_t, void*),
+ void*);
+
+/*
+ malloc_usable_size(void* p);
+
+ Returns the number of bytes you can actually use in
+ an allocated chunk, which may be more than you requested (although
+ often not) due to alignment and minimum size constraints.
+ You can use this many bytes without worrying about
+ overwriting other allocated objects. This is not a particularly great
+ programming practice. malloc_usable_size can be more useful in
+ debugging and assertions, for example:
+
+ p = malloc(n);
+ assert(malloc_usable_size(p) >= 256);
+*/
+size_t dlmalloc_usable_size(void*);
+
+/*
+ malloc_stats();
+ Prints on stderr the amount of space obtained from the system (both
+ via sbrk and mmap), the maximum amount (which may be more than
+ current if malloc_trim and/or munmap got called), and the current
+ number of bytes allocated via malloc (or realloc, etc) but not yet
+ freed. Note that this is the number of bytes allocated, not the
+ number requested. It will be larger than the number requested
+ because of alignment and bookkeeping overhead. Because it includes
+ alignment wastage as being in use, this figure may be greater than
+ zero even when no user-level chunks are allocated.
+
+ The reported current and maximum system memory can be inaccurate if
+ a program makes other calls to system memory allocation functions
+ (normally sbrk) outside of malloc.
+
+ malloc_stats prints only the most commonly interesting statistics.
+ More information can be obtained by calling mallinfo.
+*/
+void dlmalloc_stats(void);
+
+#endif /* ONLY_MSPACES */
+
+#if MSPACES
+
+/*
+ mspace is an opaque type representing an independent
+ region of space that supports mspace_malloc, etc.
+*/
+typedef void* mspace;
+
+/*
+ create_mspace creates and returns a new independent space with the
+ given initial capacity, or, if 0, the default granularity size. It
+ returns null if there is no system memory available to create the
+ space. If argument locked is non-zero, the space uses a separate
+ lock to control access. The capacity of the space will grow
+ dynamically as needed to service mspace_malloc requests. You can
+ control the sizes of incremental increases of this space by
+ compiling with a different DEFAULT_GRANULARITY or dynamically
+ setting with mallopt(M_GRANULARITY, value).
+*/
+mspace create_mspace(size_t capacity, int locked);
+
+/*
+ destroy_mspace destroys the given space, and attempts to return all
+ of its memory back to the system, returning the total number of
+ bytes freed. After destruction, the results of access to all memory
+ used by the space become undefined.
+*/
+size_t destroy_mspace(mspace msp);
+
+/*
+ create_mspace_with_base uses the memory supplied as the initial base
+ of a new mspace. Part (less than 128*sizeof(size_t) bytes) of this
+ space is used for bookkeeping, so the capacity must be at least this
+ large. (Otherwise 0 is returned.) When this initial space is
+ exhausted, additional memory will be obtained from the system.
+ Destroying this space will deallocate all additionally allocated
+ space (if possible) but not the initial base.
+*/
+mspace create_mspace_with_base(void* base, size_t capacity, int locked);
+
+/*
+ mspace_malloc behaves as malloc, but operates within
+ the given space.
+*/
+void* mspace_malloc(mspace msp, size_t bytes);
+
+/*
+ mspace_free behaves as free, but operates within
+ the given space.
+
+ If compiled with FOOTERS==1, mspace_free is not actually needed.
+ free may be called instead of mspace_free because freed chunks from
+ any space are handled by their originating spaces.
+*/
+void mspace_free(mspace msp, void* mem);
+
+/*
+ mspace_realloc behaves as realloc, but operates within
+ the given space.
+
+ If compiled with FOOTERS==1, mspace_realloc is not actually
+ needed. realloc may be called instead of mspace_realloc because
+ realloced chunks from any space are handled by their originating
+ spaces.
+*/
+void* mspace_realloc(mspace msp, void* mem, size_t newsize);
+
+#if ANDROID /* Added for Android, not part of dlmalloc as released */
+/*
+ mspace_merge_objects will merge allocated memory mema and memb
+ together, provided memb immediately follows mema. It is roughly as
+ if memb has been freed and mema has been realloced to a larger size.
+ On successfully merging, mema will be returned. If either argument
+ is null or memb does not immediately follow mema, null will be
+ returned.
+
+ Both mema and memb should have been previously allocated using
+ malloc or a related routine such as realloc. If either mema or memb
+ was not malloced or was previously freed, the result is undefined,
+ but like mspace_free, the default is to abort the program.
+*/
+void* mspace_merge_objects(mspace msp, void* mema, void* memb);
+#endif
+
+/*
+ mspace_calloc behaves as calloc, but operates within
+ the given space.
+*/
+void* mspace_calloc(mspace msp, size_t n_elements, size_t elem_size);
+
+/*
+ mspace_memalign behaves as memalign, but operates within
+ the given space.
+*/
+void* mspace_memalign(mspace msp, size_t alignment, size_t bytes);
+
+/*
+ mspace_independent_calloc behaves as independent_calloc, but
+ operates within the given space.
+*/
+void** mspace_independent_calloc(mspace msp, size_t n_elements,
+ size_t elem_size, void* chunks[]);
+
+/*
+ mspace_independent_comalloc behaves as independent_comalloc, but
+ operates within the given space.
+*/
+void** mspace_independent_comalloc(mspace msp, size_t n_elements,
+ size_t sizes[], void* chunks[]);
+
+/*
+ mspace_footprint() returns the number of bytes obtained from the
+ system for this space.
+*/
+size_t mspace_footprint(mspace msp);
+
+/*
+ mspace_max_footprint() returns the peak number of bytes obtained from the
+ system for this space.
+*/
+size_t mspace_max_footprint(mspace msp);
+
+
+#if !NO_MALLINFO
+/*
+ mspace_mallinfo behaves as mallinfo, but reports properties of
+ the given space.
+*/
+struct mallinfo mspace_mallinfo(mspace msp);
+#endif /* NO_MALLINFO */
+
+/*
+ mspace_malloc_stats behaves as malloc_stats, but reports
+ properties of the given space.
+*/
+void mspace_malloc_stats(mspace msp);
+
+/*
+ mspace_trim behaves as malloc_trim, but
+ operates within the given space.
+*/
+int mspace_trim(mspace msp, size_t pad);
+
+/*
+ An alias for mallopt.
+*/
+int mspace_mallopt(int, int);
+
+#endif /* MSPACES */
+
+#ifdef __cplusplus
+}; /* end of extern "C" */
+#endif /* __cplusplus */
+
+/*
+ ========================================================================
+ To make a fully customizable malloc.h header file, cut everything
+ above this line, put into file malloc.h, edit to suit, and #include it
+ on the next line, as well as in programs that use this malloc.
+ ========================================================================
+*/
+
+/* #include "malloc.h" */
+
+/*------------------------------ internal #includes ---------------------- */
+
+#ifdef WIN32
+#pragma warning( disable : 4146 ) /* no "unsigned" warnings */
+#endif /* WIN32 */
+
+#include <stdio.h> /* for printing in malloc_stats */
+
+#ifndef LACKS_ERRNO_H
+#include <errno.h> /* for MALLOC_FAILURE_ACTION */
+#endif /* LACKS_ERRNO_H */
+#if FOOTERS
+#include <time.h> /* for magic initialization */
+#endif /* FOOTERS */
+#ifndef LACKS_STDLIB_H
+#include <stdlib.h> /* for abort() */
+#endif /* LACKS_STDLIB_H */
+#ifdef DEBUG
+#if ABORT_ON_ASSERT_FAILURE
+#define assert(x) if(!(x)) ABORT
+#else /* ABORT_ON_ASSERT_FAILURE */
+#include <assert.h>
+#endif /* ABORT_ON_ASSERT_FAILURE */
+#else /* DEBUG */
+#define assert(x)
+#endif /* DEBUG */
+#ifndef LACKS_STRING_H
+#include <string.h> /* for memset etc */
+#endif /* LACKS_STRING_H */
+#if USE_BUILTIN_FFS
+#ifndef LACKS_STRINGS_H
+#include <strings.h> /* for ffs */
+#endif /* LACKS_STRINGS_H */
+#endif /* USE_BUILTIN_FFS */
+#if HAVE_MMAP
+#ifndef LACKS_SYS_MMAN_H
+#include <sys/mman.h> /* for mmap */
+#endif /* LACKS_SYS_MMAN_H */
+#ifndef LACKS_FCNTL_H
+#include <fcntl.h>
+#endif /* LACKS_FCNTL_H */
+#endif /* HAVE_MMAP */
+#if HAVE_MORECORE
+#ifndef LACKS_UNISTD_H
+#include <unistd.h> /* for sbrk */
+#else /* LACKS_UNISTD_H */
+#if !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__NetBSD__)
+extern void* sbrk(ptrdiff_t);
+#endif /* FreeBSD etc */
+#endif /* LACKS_UNISTD_H */
+#endif /* HAVE_MMAP */
+
+#ifndef WIN32
+#ifndef malloc_getpagesize
+# ifdef _SC_PAGESIZE /* some SVR4 systems omit an underscore */
+# ifndef _SC_PAGE_SIZE
+# define _SC_PAGE_SIZE _SC_PAGESIZE
+# endif
+# endif
+# ifdef _SC_PAGE_SIZE
+# define malloc_getpagesize sysconf(_SC_PAGE_SIZE)
+# else
+# if defined(BSD) || defined(DGUX) || defined(HAVE_GETPAGESIZE)
+ extern size_t getpagesize();
+# define malloc_getpagesize getpagesize()
+# else
+# ifdef WIN32 /* use supplied emulation of getpagesize */
+# define malloc_getpagesize getpagesize()
+# else
+# ifndef LACKS_SYS_PARAM_H
+# include <sys/param.h>
+# endif
+# ifdef EXEC_PAGESIZE
+# define malloc_getpagesize EXEC_PAGESIZE
+# else
+# ifdef NBPG
+# ifndef CLSIZE
+# define malloc_getpagesize NBPG
+# else
+# define malloc_getpagesize (NBPG * CLSIZE)
+# endif
+# else
+# ifdef NBPC
+# define malloc_getpagesize NBPC
+# else
+# ifdef PAGESIZE
+# define malloc_getpagesize PAGESIZE
+# else /* just guess */
+# define malloc_getpagesize ((size_t)4096U)
+# endif
+# endif
+# endif
+# endif
+# endif
+# endif
+# endif
+#endif
+#endif
+
+/* ------------------- size_t and alignment properties -------------------- */
+
+/* The byte and bit size of a size_t */
+#define SIZE_T_SIZE (sizeof(size_t))
+#define SIZE_T_BITSIZE (sizeof(size_t) << 3)
+
+/* Some constants coerced to size_t */
+/* Annoying but necessary to avoid errors on some plaftorms */
+#define SIZE_T_ZERO ((size_t)0)
+#define SIZE_T_ONE ((size_t)1)
+#define SIZE_T_TWO ((size_t)2)
+#define TWO_SIZE_T_SIZES (SIZE_T_SIZE<<1)
+#define FOUR_SIZE_T_SIZES (SIZE_T_SIZE<<2)
+#define SIX_SIZE_T_SIZES (FOUR_SIZE_T_SIZES+TWO_SIZE_T_SIZES)
+#define HALF_MAX_SIZE_T (MAX_SIZE_T / 2U)
+
+/* The bit mask value corresponding to MALLOC_ALIGNMENT */
+#define CHUNK_ALIGN_MASK (MALLOC_ALIGNMENT - SIZE_T_ONE)
+
+/* True if address a has acceptable alignment */
+#define is_aligned(A) (((size_t)((A)) & (CHUNK_ALIGN_MASK)) == 0)
+
+/* the number of bytes to offset an address to align it */
+#define align_offset(A)\
+ ((((size_t)(A) & CHUNK_ALIGN_MASK) == 0)? 0 :\
+ ((MALLOC_ALIGNMENT - ((size_t)(A) & CHUNK_ALIGN_MASK)) & CHUNK_ALIGN_MASK))
+
+/* -------------------------- MMAP preliminaries ------------------------- */
+
+/*
+ If HAVE_MORECORE or HAVE_MMAP are false, we just define calls and
+ checks to fail so compiler optimizer can delete code rather than
+ using so many "#if"s.
+*/
+
+
+/* MORECORE and MMAP must return MFAIL on failure */
+#define MFAIL ((void*)(MAX_SIZE_T))
+#define CMFAIL ((char*)(MFAIL)) /* defined for convenience */
+
+#if !HAVE_MMAP
+#define IS_MMAPPED_BIT (SIZE_T_ZERO)
+#define USE_MMAP_BIT (SIZE_T_ZERO)
+#define CALL_MMAP(s) MFAIL
+#define CALL_MUNMAP(a, s) (-1)
+#define DIRECT_MMAP(s) MFAIL
+
+#else /* HAVE_MMAP */
+#define IS_MMAPPED_BIT (SIZE_T_ONE)
+#define USE_MMAP_BIT (SIZE_T_ONE)
+
+#ifndef WIN32
+#define CALL_MUNMAP(a, s) munmap((a), (s))
+#define MMAP_PROT (PROT_READ|PROT_WRITE)
+#if !defined(MAP_ANONYMOUS) && defined(MAP_ANON)
+#define MAP_ANONYMOUS MAP_ANON
+#endif /* MAP_ANON */
+#ifdef MAP_ANONYMOUS
+#define MMAP_FLAGS (MAP_PRIVATE|MAP_ANONYMOUS)
+#define CALL_MMAP(s) mmap(0, (s), MMAP_PROT, MMAP_FLAGS, -1, 0)
+#else /* MAP_ANONYMOUS */
+/*
+ Nearly all versions of mmap support MAP_ANONYMOUS, so the following
+ is unlikely to be needed, but is supplied just in case.
+*/
+#define MMAP_FLAGS (MAP_PRIVATE)
+static int dev_zero_fd = -1; /* Cached file descriptor for /dev/zero. */
+#define CALL_MMAP(s) ((dev_zero_fd < 0) ? \
+ (dev_zero_fd = open("/dev/zero", O_RDWR), \
+ mmap(0, (s), MMAP_PROT, MMAP_FLAGS, dev_zero_fd, 0)) : \
+ mmap(0, (s), MMAP_PROT, MMAP_FLAGS, dev_zero_fd, 0))
+#endif /* MAP_ANONYMOUS */
+
+#define DIRECT_MMAP(s) CALL_MMAP(s)
+#else /* WIN32 */
+
+/* Win32 MMAP via VirtualAlloc */
+static void* win32mmap(size_t size) {
+ void* ptr = VirtualAlloc(0, size, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE);
+ return (ptr != 0)? ptr: MFAIL;
+}
+
+/* For direct MMAP, use MEM_TOP_DOWN to minimize interference */
+static void* win32direct_mmap(size_t size) {
+ void* ptr = VirtualAlloc(0, size, MEM_RESERVE|MEM_COMMIT|MEM_TOP_DOWN,
+ PAGE_READWRITE);
+ return (ptr != 0)? ptr: MFAIL;
+}
+
+/* This function supports releasing coalesed segments */
+static int win32munmap(void* ptr, size_t size) {
+ MEMORY_BASIC_INFORMATION minfo;
+ char* cptr = ptr;
+ while (size) {
+ if (VirtualQuery(cptr, &minfo, sizeof(minfo)) == 0)
+ return -1;
+ if (minfo.BaseAddress != cptr || minfo.AllocationBase != cptr ||
+ minfo.State != MEM_COMMIT || minfo.RegionSize > size)
+ return -1;
+ if (VirtualFree(cptr, 0, MEM_RELEASE) == 0)
+ return -1;
+ cptr += minfo.RegionSize;
+ size -= minfo.RegionSize;
+ }
+ return 0;
+}
+
+#define CALL_MMAP(s) win32mmap(s)
+#define CALL_MUNMAP(a, s) win32munmap((a), (s))
+#define DIRECT_MMAP(s) win32direct_mmap(s)
+#endif /* WIN32 */
+#endif /* HAVE_MMAP */
+
+#if HAVE_MMAP && HAVE_MREMAP
+#define CALL_MREMAP(addr, osz, nsz, mv) mremap((addr), (osz), (nsz), (mv))
+#else /* HAVE_MMAP && HAVE_MREMAP */
+#define CALL_MREMAP(addr, osz, nsz, mv) MFAIL
+#endif /* HAVE_MMAP && HAVE_MREMAP */
+
+#if HAVE_MORECORE
+#define CALL_MORECORE(S) MORECORE(S)
+#else /* HAVE_MORECORE */
+#define CALL_MORECORE(S) MFAIL
+#endif /* HAVE_MORECORE */
+
+/* mstate bit set if continguous morecore disabled or failed */
+#define USE_NONCONTIGUOUS_BIT (4U)
+
+/* segment bit set in create_mspace_with_base */
+#define EXTERN_BIT (8U)
+
+
+/* --------------------------- Lock preliminaries ------------------------ */
+
+#if USE_LOCKS
+
+/*
+ When locks are defined, there are up to two global locks:
+
+ * If HAVE_MORECORE, morecore_mutex protects sequences of calls to
+ MORECORE. In many cases sys_alloc requires two calls, that should
+ not be interleaved with calls by other threads. This does not
+ protect against direct calls to MORECORE by other threads not
+ using this lock, so there is still code to cope the best we can on
+ interference.
+
+ * magic_init_mutex ensures that mparams.magic and other
+ unique mparams values are initialized only once.
+*/
+
+#ifndef WIN32
+/* By default use posix locks */
+#include <pthread.h>
+#define MLOCK_T pthread_mutex_t
+#define INITIAL_LOCK(l) pthread_mutex_init(l, NULL)
+#define ACQUIRE_LOCK(l) pthread_mutex_lock(l)
+#define RELEASE_LOCK(l) pthread_mutex_unlock(l)
+
+#if HAVE_MORECORE
+static MLOCK_T morecore_mutex = PTHREAD_MUTEX_INITIALIZER;
+#endif /* HAVE_MORECORE */
+
+static MLOCK_T magic_init_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+#else /* WIN32 */
+/*
+ Because lock-protected regions have bounded times, and there
+ are no recursive lock calls, we can use simple spinlocks.
+*/
+
+#define MLOCK_T long
+static int win32_acquire_lock (MLOCK_T *sl) {
+ for (;;) {
+#ifdef InterlockedCompareExchangePointer
+ if (!InterlockedCompareExchange(sl, 1, 0))
+ return 0;
+#else /* Use older void* version */
+ if (!InterlockedCompareExchange((void**)sl, (void*)1, (void*)0))
+ return 0;
+#endif /* InterlockedCompareExchangePointer */
+ Sleep (0);
+ }
+}
+
+static void win32_release_lock (MLOCK_T *sl) {
+ InterlockedExchange (sl, 0);
+}
+
+#define INITIAL_LOCK(l) *(l)=0
+#define ACQUIRE_LOCK(l) win32_acquire_lock(l)
+#define RELEASE_LOCK(l) win32_release_lock(l)
+#if HAVE_MORECORE
+static MLOCK_T morecore_mutex;
+#endif /* HAVE_MORECORE */
+static MLOCK_T magic_init_mutex;
+#endif /* WIN32 */
+
+#define USE_LOCK_BIT (2U)
+#else /* USE_LOCKS */
+#define USE_LOCK_BIT (0U)
+#define INITIAL_LOCK(l)
+#endif /* USE_LOCKS */
+
+#if USE_LOCKS && HAVE_MORECORE
+#define ACQUIRE_MORECORE_LOCK() ACQUIRE_LOCK(&morecore_mutex);
+#define RELEASE_MORECORE_LOCK() RELEASE_LOCK(&morecore_mutex);
+#else /* USE_LOCKS && HAVE_MORECORE */
+#define ACQUIRE_MORECORE_LOCK()
+#define RELEASE_MORECORE_LOCK()
+#endif /* USE_LOCKS && HAVE_MORECORE */
+
+#if USE_LOCKS
+#define ACQUIRE_MAGIC_INIT_LOCK() ACQUIRE_LOCK(&magic_init_mutex);
+#define RELEASE_MAGIC_INIT_LOCK() RELEASE_LOCK(&magic_init_mutex);
+#else /* USE_LOCKS */
+#define ACQUIRE_MAGIC_INIT_LOCK()
+#define RELEASE_MAGIC_INIT_LOCK()
+#endif /* USE_LOCKS */
+
+
+/* ----------------------- Chunk representations ------------------------ */
+
+/*
+ (The following includes lightly edited explanations by Colin Plumb.)
+
+ The malloc_chunk declaration below is misleading (but accurate and
+ necessary). It declares a "view" into memory allowing access to
+ necessary fields at known offsets from a given base.
+
+ Chunks of memory are maintained using a `boundary tag' method as
+ originally described by Knuth. (See the paper by Paul Wilson
+ ftp://ftp.cs.utexas.edu/pub/garbage/allocsrv.ps for a survey of such
+ techniques.) Sizes of free chunks are stored both in the front of
+ each chunk and at the end. This makes consolidating fragmented
+ chunks into bigger chunks fast. The head fields also hold bits
+ representing whether chunks are free or in use.
+
+ Here are some pictures to make it clearer. They are "exploded" to
+ show that the state of a chunk can be thought of as extending from
+ the high 31 bits of the head field of its header through the
+ prev_foot and PINUSE_BIT bit of the following chunk header.
+
+ A chunk that's in use looks like:
+
+ chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Size of previous chunk (if P = 1) |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |P|
+ | Size of this chunk 1| +-+
+ mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | |
+ +- -+
+ | |
+ +- -+
+ | :
+ +- size - sizeof(size_t) available payload bytes -+
+ : |
+ chunk-> +- -+
+ | |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |1|
+ | Size of next chunk (may or may not be in use) | +-+
+ mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ And if it's free, it looks like this:
+
+ chunk-> +- -+
+ | User payload (must be in use, or we would have merged!) |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |P|
+ | Size of this chunk 0| +-+
+ mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Next pointer |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Prev pointer |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | :
+ +- size - sizeof(struct chunk) unused bytes -+
+ : |
+ chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Size of this chunk |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |0|
+ | Size of next chunk (must be in use, or we would have merged)| +-+
+ mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | :
+ +- User payload -+
+ : |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ |0|
+ +-+
+ Note that since we always merge adjacent free chunks, the chunks
+ adjacent to a free chunk must be in use.
+
+ Given a pointer to a chunk (which can be derived trivially from the
+ payload pointer) we can, in O(1) time, find out whether the adjacent
+ chunks are free, and if so, unlink them from the lists that they
+ are on and merge them with the current chunk.
+
+ Chunks always begin on even word boundaries, so the mem portion
+ (which is returned to the user) is also on an even word boundary, and
+ thus at least double-word aligned.
+
+ The P (PINUSE_BIT) bit, stored in the unused low-order bit of the
+ chunk size (which is always a multiple of two words), is an in-use
+ bit for the *previous* chunk. If that bit is *clear*, then the
+ word before the current chunk size contains the previous chunk
+ size, and can be used to find the front of the previous chunk.
+ The very first chunk allocated always has this bit set, preventing
+ access to non-existent (or non-owned) memory. If pinuse is set for
+ any given chunk, then you CANNOT determine the size of the
+ previous chunk, and might even get a memory addressing fault when
+ trying to do so.
+
+ The C (CINUSE_BIT) bit, stored in the unused second-lowest bit of
+ the chunk size redundantly records whether the current chunk is
+ inuse. This redundancy enables usage checks within free and realloc,
+ and reduces indirection when freeing and consolidating chunks.
+
+ Each freshly allocated chunk must have both cinuse and pinuse set.
+ That is, each allocated chunk borders either a previously allocated
+ and still in-use chunk, or the base of its memory arena. This is
+ ensured by making all allocations from the the `lowest' part of any
+ found chunk. Further, no free chunk physically borders another one,
+ so each free chunk is known to be preceded and followed by either
+ inuse chunks or the ends of memory.
+
+ Note that the `foot' of the current chunk is actually represented
+ as the prev_foot of the NEXT chunk. This makes it easier to
+ deal with alignments etc but can be very confusing when trying
+ to extend or adapt this code.
+
+ The exceptions to all this are
+
+ 1. The special chunk `top' is the top-most available chunk (i.e.,
+ the one bordering the end of available memory). It is treated
+ specially. Top is never included in any bin, is used only if
+ no other chunk is available, and is released back to the
+ system if it is very large (see M_TRIM_THRESHOLD). In effect,
+ the top chunk is treated as larger (and thus less well
+ fitting) than any other available chunk. The top chunk
+ doesn't update its trailing size field since there is no next
+ contiguous chunk that would have to index off it. However,
+ space is still allocated for it (TOP_FOOT_SIZE) to enable
+ separation or merging when space is extended.
+
+ 3. Chunks allocated via mmap, which have the lowest-order bit
+ (IS_MMAPPED_BIT) set in their prev_foot fields, and do not set
+ PINUSE_BIT in their head fields. Because they are allocated
+ one-by-one, each must carry its own prev_foot field, which is
+ also used to hold the offset this chunk has within its mmapped
+ region, which is needed to preserve alignment. Each mmapped
+ chunk is trailed by the first two fields of a fake next-chunk
+ for sake of usage checks.
+
+*/
+
+struct malloc_chunk {
+ size_t prev_foot; /* Size of previous chunk (if free). */
+ size_t head; /* Size and inuse bits. */
+ struct malloc_chunk* fd; /* double links -- used only if free. */
+ struct malloc_chunk* bk;
+};
+
+typedef struct malloc_chunk mchunk;
+typedef struct malloc_chunk* mchunkptr;
+typedef struct malloc_chunk* sbinptr; /* The type of bins of chunks */
+typedef unsigned int bindex_t; /* Described below */
+typedef unsigned int binmap_t; /* Described below */
+typedef unsigned int flag_t; /* The type of various bit flag sets */
+
+/* ------------------- Chunks sizes and alignments ----------------------- */
+
+#define MCHUNK_SIZE (sizeof(mchunk))
+
+#if FOOTERS
+#define CHUNK_OVERHEAD (TWO_SIZE_T_SIZES)
+#else /* FOOTERS */
+#define CHUNK_OVERHEAD (SIZE_T_SIZE)
+#endif /* FOOTERS */
+
+/* MMapped chunks need a second word of overhead ... */
+#define MMAP_CHUNK_OVERHEAD (TWO_SIZE_T_SIZES)
+/* ... and additional padding for fake next-chunk at foot */
+#define MMAP_FOOT_PAD (FOUR_SIZE_T_SIZES)
+
+/* The smallest size we can malloc is an aligned minimal chunk */
+#define MIN_CHUNK_SIZE\
+ ((MCHUNK_SIZE + CHUNK_ALIGN_MASK) & ~CHUNK_ALIGN_MASK)
+
+/* conversion from malloc headers to user pointers, and back */
+#define chunk2mem(p) ((void*)((char*)(p) + TWO_SIZE_T_SIZES))
+#define mem2chunk(mem) ((mchunkptr)((char*)(mem) - TWO_SIZE_T_SIZES))
+/* chunk associated with aligned address A */
+#define align_as_chunk(A) (mchunkptr)((A) + align_offset(chunk2mem(A)))
+
+/* Bounds on request (not chunk) sizes. */
+#define MAX_REQUEST ((-MIN_CHUNK_SIZE) << 2)
+#define MIN_REQUEST (MIN_CHUNK_SIZE - CHUNK_OVERHEAD - SIZE_T_ONE)
+
+/* pad request bytes into a usable size */
+#define pad_request(req) \
+ (((req) + CHUNK_OVERHEAD + CHUNK_ALIGN_MASK) & ~CHUNK_ALIGN_MASK)
+
+/* pad request, checking for minimum (but not maximum) */
+#define request2size(req) \
+ (((req) < MIN_REQUEST)? MIN_CHUNK_SIZE : pad_request(req))
+
+
+/* ------------------ Operations on head and foot fields ----------------- */
+
+/*
+ The head field of a chunk is or'ed with PINUSE_BIT when previous
+ adjacent chunk in use, and or'ed with CINUSE_BIT if this chunk is in
+ use. If the chunk was obtained with mmap, the prev_foot field has
+ IS_MMAPPED_BIT set, otherwise holding the offset of the base of the
+ mmapped region to the base of the chunk.
+*/
+
+#define PINUSE_BIT (SIZE_T_ONE)
+#define CINUSE_BIT (SIZE_T_TWO)
+#define INUSE_BITS (PINUSE_BIT|CINUSE_BIT)
+
+/* Head value for fenceposts */
+#define FENCEPOST_HEAD (INUSE_BITS|SIZE_T_SIZE)
+
+/* extraction of fields from head words */
+#define cinuse(p) ((p)->head & CINUSE_BIT)
+#define pinuse(p) ((p)->head & PINUSE_BIT)
+#define chunksize(p) ((p)->head & ~(INUSE_BITS))
+
+#define clear_pinuse(p) ((p)->head &= ~PINUSE_BIT)
+#define clear_cinuse(p) ((p)->head &= ~CINUSE_BIT)
+
+/* Treat space at ptr +/- offset as a chunk */
+#define chunk_plus_offset(p, s) ((mchunkptr)(((char*)(p)) + (s)))
+#define chunk_minus_offset(p, s) ((mchunkptr)(((char*)(p)) - (s)))
+
+/* Ptr to next or previous physical malloc_chunk. */
+#define next_chunk(p) ((mchunkptr)( ((char*)(p)) + ((p)->head & ~INUSE_BITS)))
+#define prev_chunk(p) ((mchunkptr)( ((char*)(p)) - ((p)->prev_foot) ))
+
+/* extract next chunk's pinuse bit */
+#define next_pinuse(p) ((next_chunk(p)->head) & PINUSE_BIT)
+
+/* Get/set size at footer */
+#define get_foot(p, s) (((mchunkptr)((char*)(p) + (s)))->prev_foot)
+#define set_foot(p, s) (((mchunkptr)((char*)(p) + (s)))->prev_foot = (s))
+
+/* Set size, pinuse bit, and foot */
+#define set_size_and_pinuse_of_free_chunk(p, s)\
+ ((p)->head = (s|PINUSE_BIT), set_foot(p, s))
+
+/* Set size, pinuse bit, foot, and clear next pinuse */
+#define set_free_with_pinuse(p, s, n)\
+ (clear_pinuse(n), set_size_and_pinuse_of_free_chunk(p, s))
+
+#define is_mmapped(p)\
+ (!((p)->head & PINUSE_BIT) && ((p)->prev_foot & IS_MMAPPED_BIT))
+
+/* Get the internal overhead associated with chunk p */
+#define overhead_for(p)\
+ (is_mmapped(p)? MMAP_CHUNK_OVERHEAD : CHUNK_OVERHEAD)
+
+/* Return true if malloced space is not necessarily cleared */
+#if MMAP_CLEARS
+#define calloc_must_clear(p) (!is_mmapped(p))
+#else /* MMAP_CLEARS */
+#define calloc_must_clear(p) (1)
+#endif /* MMAP_CLEARS */
+
+/* ---------------------- Overlaid data structures ----------------------- */
+
+/*
+ When chunks are not in use, they are treated as nodes of either
+ lists or trees.
+
+ "Small" chunks are stored in circular doubly-linked lists, and look
+ like this:
+
+ chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Size of previous chunk |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ `head:' | Size of chunk, in bytes |P|
+ mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Forward pointer to next chunk in list |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Back pointer to previous chunk in list |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Unused space (may be 0 bytes long) .
+ . .
+ . |
+nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ `foot:' | Size of chunk, in bytes |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ Larger chunks are kept in a form of bitwise digital trees (aka
+ tries) keyed on chunksizes. Because malloc_tree_chunks are only for
+ free chunks greater than 256 bytes, their size doesn't impose any
+ constraints on user chunk sizes. Each node looks like:
+
+ chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Size of previous chunk |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ `head:' | Size of chunk, in bytes |P|
+ mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Forward pointer to next chunk of same size |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Back pointer to previous chunk of same size |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Pointer to left child (child[0]) |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Pointer to right child (child[1]) |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Pointer to parent |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | bin index of this chunk |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Unused space .
+ . |
+nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ `foot:' | Size of chunk, in bytes |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ Each tree holding treenodes is a tree of unique chunk sizes. Chunks
+ of the same size are arranged in a circularly-linked list, with only
+ the oldest chunk (the next to be used, in our FIFO ordering)
+ actually in the tree. (Tree members are distinguished by a non-null
+ parent pointer.) If a chunk with the same size an an existing node
+ is inserted, it is linked off the existing node using pointers that
+ work in the same way as fd/bk pointers of small chunks.
+
+ Each tree contains a power of 2 sized range of chunk sizes (the
+ smallest is 0x100 <= x < 0x180), which is is divided in half at each
+ tree level, with the chunks in the smaller half of the range (0x100
+ <= x < 0x140 for the top nose) in the left subtree and the larger
+ half (0x140 <= x < 0x180) in the right subtree. This is, of course,
+ done by inspecting individual bits.
+
+ Using these rules, each node's left subtree contains all smaller
+ sizes than its right subtree. However, the node at the root of each
+ subtree has no particular ordering relationship to either. (The
+ dividing line between the subtree sizes is based on trie relation.)
+ If we remove the last chunk of a given size from the interior of the
+ tree, we need to replace it with a leaf node. The tree ordering
+ rules permit a node to be replaced by any leaf below it.
+
+ The smallest chunk in a tree (a common operation in a best-fit
+ allocator) can be found by walking a path to the leftmost leaf in
+ the tree. Unlike a usual binary tree, where we follow left child
+ pointers until we reach a null, here we follow the right child
+ pointer any time the left one is null, until we reach a leaf with
+ both child pointers null. The smallest chunk in the tree will be
+ somewhere along that path.
+
+ The worst case number of steps to add, find, or remove a node is
+ bounded by the number of bits differentiating chunks within
+ bins. Under current bin calculations, this ranges from 6 up to 21
+ (for 32 bit sizes) or up to 53 (for 64 bit sizes). The typical case
+ is of course much better.
+*/
+
+struct malloc_tree_chunk {
+ /* The first four fields must be compatible with malloc_chunk */
+ size_t prev_foot;
+ size_t head;
+ struct malloc_tree_chunk* fd;
+ struct malloc_tree_chunk* bk;
+
+ struct malloc_tree_chunk* child[2];
+ struct malloc_tree_chunk* parent;
+ bindex_t index;
+};
+
+typedef struct malloc_tree_chunk tchunk;
+typedef struct malloc_tree_chunk* tchunkptr;
+typedef struct malloc_tree_chunk* tbinptr; /* The type of bins of trees */
+
+/* A little helper macro for trees */
+#define leftmost_child(t) ((t)->child[0] != 0? (t)->child[0] : (t)->child[1])
+
+/* ----------------------------- Segments -------------------------------- */
+
+/*
+ Each malloc space may include non-contiguous segments, held in a
+ list headed by an embedded malloc_segment record representing the
+ top-most space. Segments also include flags holding properties of
+ the space. Large chunks that are directly allocated by mmap are not
+ included in this list. They are instead independently created and
+ destroyed without otherwise keeping track of them.
+
+ Segment management mainly comes into play for spaces allocated by
+ MMAP. Any call to MMAP might or might not return memory that is
+ adjacent to an existing segment. MORECORE normally contiguously
+ extends the current space, so this space is almost always adjacent,
+ which is simpler and faster to deal with. (This is why MORECORE is
+ used preferentially to MMAP when both are available -- see
+ sys_alloc.) When allocating using MMAP, we don't use any of the
+ hinting mechanisms (inconsistently) supported in various
+ implementations of unix mmap, or distinguish reserving from
+ committing memory. Instead, we just ask for space, and exploit
+ contiguity when we get it. It is probably possible to do
+ better than this on some systems, but no general scheme seems
+ to be significantly better.
+
+ Management entails a simpler variant of the consolidation scheme
+ used for chunks to reduce fragmentation -- new adjacent memory is
+ normally prepended or appended to an existing segment. However,
+ there are limitations compared to chunk consolidation that mostly
+ reflect the fact that segment processing is relatively infrequent
+ (occurring only when getting memory from system) and that we
+ don't expect to have huge numbers of segments:
+
+ * Segments are not indexed, so traversal requires linear scans. (It
+ would be possible to index these, but is not worth the extra
+ overhead and complexity for most programs on most platforms.)
+ * New segments are only appended to old ones when holding top-most
+ memory; if they cannot be prepended to others, they are held in
+ different segments.
+
+ Except for the top-most segment of an mstate, each segment record
+ is kept at the tail of its segment. Segments are added by pushing
+ segment records onto the list headed by &mstate.seg for the
+ containing mstate.
+
+ Segment flags control allocation/merge/deallocation policies:
+ * If EXTERN_BIT set, then we did not allocate this segment,
+ and so should not try to deallocate or merge with others.
+ (This currently holds only for the initial segment passed
+ into create_mspace_with_base.)
+ * If IS_MMAPPED_BIT set, the segment may be merged with
+ other surrounding mmapped segments and trimmed/de-allocated
+ using munmap.
+ * If neither bit is set, then the segment was obtained using
+ MORECORE so can be merged with surrounding MORECORE'd segments
+ and deallocated/trimmed using MORECORE with negative arguments.
+*/
+
+struct malloc_segment {
+ char* base; /* base address */
+ size_t size; /* allocated size */
+ struct malloc_segment* next; /* ptr to next segment */
+ flag_t sflags; /* mmap and extern flag */
+};
+
+#define is_mmapped_segment(S) ((S)->sflags & IS_MMAPPED_BIT)
+#define is_extern_segment(S) ((S)->sflags & EXTERN_BIT)
+
+typedef struct malloc_segment msegment;
+typedef struct malloc_segment* msegmentptr;
+
+/* ---------------------------- malloc_state ----------------------------- */
+
+/*
+ A malloc_state holds all of the bookkeeping for a space.
+ The main fields are:
+
+ Top
+ The topmost chunk of the currently active segment. Its size is
+ cached in topsize. The actual size of topmost space is
+ topsize+TOP_FOOT_SIZE, which includes space reserved for adding
+ fenceposts and segment records if necessary when getting more
+ space from the system. The size at which to autotrim top is
+ cached from mparams in trim_check, except that it is disabled if
+ an autotrim fails.
+
+ Designated victim (dv)
+ This is the preferred chunk for servicing small requests that
+ don't have exact fits. It is normally the chunk split off most
+ recently to service another small request. Its size is cached in
+ dvsize. The link fields of this chunk are not maintained since it
+ is not kept in a bin.
+
+ SmallBins
+ An array of bin headers for free chunks. These bins hold chunks
+ with sizes less than MIN_LARGE_SIZE bytes. Each bin contains
+ chunks of all the same size, spaced 8 bytes apart. To simplify
+ use in double-linked lists, each bin header acts as a malloc_chunk
+ pointing to the real first node, if it exists (else pointing to
+ itself). This avoids special-casing for headers. But to avoid
+ waste, we allocate only the fd/bk pointers of bins, and then use
+ repositioning tricks to treat these as the fields of a chunk.
+
+ TreeBins
+ Treebins are pointers to the roots of trees holding a range of
+ sizes. There are 2 equally spaced treebins for each power of two
+ from TREE_SHIFT to TREE_SHIFT+16. The last bin holds anything
+ larger.
+
+ Bin maps
+ There is one bit map for small bins ("smallmap") and one for
+ treebins ("treemap). Each bin sets its bit when non-empty, and
+ clears the bit when empty. Bit operations are then used to avoid
+ bin-by-bin searching -- nearly all "search" is done without ever
+ looking at bins that won't be selected. The bit maps
+ conservatively use 32 bits per map word, even if on 64bit system.
+ For a good description of some of the bit-based techniques used
+ here, see Henry S. Warren Jr's book "Hacker's Delight" (and
+ supplement at http://hackersdelight.org/). Many of these are
+ intended to reduce the branchiness of paths through malloc etc, as
+ well as to reduce the number of memory locations read or written.
+
+ Segments
+ A list of segments headed by an embedded malloc_segment record
+ representing the initial space.
+
+ Address check support
+ The least_addr field is the least address ever obtained from
+ MORECORE or MMAP. Attempted frees and reallocs of any address less
+ than this are trapped (unless INSECURE is defined).
+
+ Magic tag
+ A cross-check field that should always hold same value as mparams.magic.
+
+ Flags
+ Bits recording whether to use MMAP, locks, or contiguous MORECORE
+
+ Statistics
+ Each space keeps track of current and maximum system memory
+ obtained via MORECORE or MMAP.
+
+ Locking
+ If USE_LOCKS is defined, the "mutex" lock is acquired and released
+ around every public call using this mspace.
+*/
+
+/* Bin types, widths and sizes */
+#define NSMALLBINS (32U)
+#define NTREEBINS (32U)
+#define SMALLBIN_SHIFT (3U)
+#define SMALLBIN_WIDTH (SIZE_T_ONE << SMALLBIN_SHIFT)
+#define TREEBIN_SHIFT (8U)
+#define MIN_LARGE_SIZE (SIZE_T_ONE << TREEBIN_SHIFT)
+#define MAX_SMALL_SIZE (MIN_LARGE_SIZE - SIZE_T_ONE)
+#define MAX_SMALL_REQUEST (MAX_SMALL_SIZE - CHUNK_ALIGN_MASK - CHUNK_OVERHEAD)
+
+struct malloc_state {
+ binmap_t smallmap;
+ binmap_t treemap;
+ size_t dvsize;
+ size_t topsize;
+ char* least_addr;
+ mchunkptr dv;
+ mchunkptr top;
+ size_t trim_check;
+ size_t magic;
+ mchunkptr smallbins[(NSMALLBINS+1)*2];
+ tbinptr treebins[NTREEBINS];
+ size_t footprint;
+#if USE_MAX_ALLOWED_FOOTPRINT
+ size_t max_allowed_footprint;
+#endif
+ size_t max_footprint;
+ flag_t mflags;
+#if USE_LOCKS
+ MLOCK_T mutex; /* locate lock among fields that rarely change */
+#endif /* USE_LOCKS */
+ msegment seg;
+};
+
+typedef struct malloc_state* mstate;
+
+/* ------------- Global malloc_state and malloc_params ------------------- */
+
+/*
+ malloc_params holds global properties, including those that can be
+ dynamically set using mallopt. There is a single instance, mparams,
+ initialized in init_mparams.
+*/
+
+struct malloc_params {
+ size_t magic;
+ size_t page_size;
+ size_t granularity;
+ size_t mmap_threshold;
+ size_t trim_threshold;
+ flag_t default_mflags;
+};
+
+static struct malloc_params mparams;
+
+/* The global malloc_state used for all non-"mspace" calls */
+static struct malloc_state _gm_
+#if USE_MAX_ALLOWED_FOOTPRINT
+ = { .max_allowed_footprint = MAX_SIZE_T };
+#else
+ ;
+#endif
+
+#define gm (&_gm_)
+#define is_global(M) ((M) == &_gm_)
+#define is_initialized(M) ((M)->top != 0)
+
+/* -------------------------- system alloc setup ------------------------- */
+
+/* Operations on mflags */
+
+#define use_lock(M) ((M)->mflags & USE_LOCK_BIT)
+#define enable_lock(M) ((M)->mflags |= USE_LOCK_BIT)
+#define disable_lock(M) ((M)->mflags &= ~USE_LOCK_BIT)
+
+#define use_mmap(M) ((M)->mflags & USE_MMAP_BIT)
+#define enable_mmap(M) ((M)->mflags |= USE_MMAP_BIT)
+#define disable_mmap(M) ((M)->mflags &= ~USE_MMAP_BIT)
+
+#define use_noncontiguous(M) ((M)->mflags & USE_NONCONTIGUOUS_BIT)
+#define disable_contiguous(M) ((M)->mflags |= USE_NONCONTIGUOUS_BIT)
+
+#define set_lock(M,L)\
+ ((M)->mflags = (L)?\
+ ((M)->mflags | USE_LOCK_BIT) :\
+ ((M)->mflags & ~USE_LOCK_BIT))
+
+/* page-align a size */
+#define page_align(S)\
+ (((S) + (mparams.page_size)) & ~(mparams.page_size - SIZE_T_ONE))
+
+/* granularity-align a size */
+#define granularity_align(S)\
+ (((S) + (mparams.granularity)) & ~(mparams.granularity - SIZE_T_ONE))
+
+#define is_page_aligned(S)\
+ (((size_t)(S) & (mparams.page_size - SIZE_T_ONE)) == 0)
+#define is_granularity_aligned(S)\
+ (((size_t)(S) & (mparams.granularity - SIZE_T_ONE)) == 0)
+
+/* True if segment S holds address A */
+#define segment_holds(S, A)\
+ ((char*)(A) >= S->base && (char*)(A) < S->base + S->size)
+
+/* Return segment holding given address */
+static msegmentptr segment_holding(mstate m, char* addr) {
+ msegmentptr sp = &m->seg;
+ for (;;) {
+ if (addr >= sp->base && addr < sp->base + sp->size)
+ return sp;
+ if ((sp = sp->next) == 0)
+ return 0;
+ }
+}
+
+/* Return true if segment contains a segment link */
+static int has_segment_link(mstate m, msegmentptr ss) {
+ msegmentptr sp = &m->seg;
+ for (;;) {
+ if ((char*)sp >= ss->base && (char*)sp < ss->base + ss->size)
+ return 1;
+ if ((sp = sp->next) == 0)
+ return 0;
+ }
+}
+
+#ifndef MORECORE_CANNOT_TRIM
+#define should_trim(M,s) ((s) > (M)->trim_check)
+#else /* MORECORE_CANNOT_TRIM */
+#define should_trim(M,s) (0)
+#endif /* MORECORE_CANNOT_TRIM */
+
+/*
+ TOP_FOOT_SIZE is padding at the end of a segment, including space
+ that may be needed to place segment records and fenceposts when new
+ noncontiguous segments are added.
+*/
+#define TOP_FOOT_SIZE\
+ (align_offset(chunk2mem(0))+pad_request(sizeof(struct malloc_segment))+MIN_CHUNK_SIZE)
+
+
+/* ------------------------------- Hooks -------------------------------- */
+
+/*
+ PREACTION should be defined to return 0 on success, and nonzero on
+ failure. If you are not using locking, you can redefine these to do
+ anything you like.
+*/
+
+#if USE_LOCKS
+
+/* Ensure locks are initialized */
+#define GLOBALLY_INITIALIZE() (mparams.page_size == 0 && init_mparams())
+
+#define PREACTION(M) ((GLOBALLY_INITIALIZE() || use_lock(M))? ACQUIRE_LOCK(&(M)->mutex) : 0)
+#define POSTACTION(M) { if (use_lock(M)) RELEASE_LOCK(&(M)->mutex); }
+#else /* USE_LOCKS */
+
+#ifndef PREACTION
+#define PREACTION(M) (0)
+#endif /* PREACTION */
+
+#ifndef POSTACTION
+#define POSTACTION(M)
+#endif /* POSTACTION */
+
+#endif /* USE_LOCKS */
+
+/*
+ CORRUPTION_ERROR_ACTION is triggered upon detected bad addresses.
+ USAGE_ERROR_ACTION is triggered on detected bad frees and
+ reallocs. The argument p is an address that might have triggered the
+ fault. It is ignored by the two predefined actions, but might be
+ useful in custom actions that try to help diagnose errors.
+*/
+
+#if PROCEED_ON_ERROR
+
+/* A count of the number of corruption errors causing resets */
+int malloc_corruption_error_count;
+
+/* default corruption action */
+static void reset_on_error(mstate m);
+
+#define CORRUPTION_ERROR_ACTION(m) reset_on_error(m)
+#define USAGE_ERROR_ACTION(m, p)
+
+#else /* PROCEED_ON_ERROR */
+
+#ifndef CORRUPTION_ERROR_ACTION
+#define CORRUPTION_ERROR_ACTION(m) ABORT
+#endif /* CORRUPTION_ERROR_ACTION */
+
+#ifndef USAGE_ERROR_ACTION
+#define USAGE_ERROR_ACTION(m,p) ABORT
+#endif /* USAGE_ERROR_ACTION */
+
+#endif /* PROCEED_ON_ERROR */
+
+/* -------------------------- Debugging setup ---------------------------- */
+
+#if ! DEBUG
+
+#define check_free_chunk(M,P)
+#define check_inuse_chunk(M,P)
+#define check_malloced_chunk(M,P,N)
+#define check_mmapped_chunk(M,P)
+#define check_malloc_state(M)
+#define check_top_chunk(M,P)
+
+#else /* DEBUG */
+#define check_free_chunk(M,P) do_check_free_chunk(M,P)
+#define check_inuse_chunk(M,P) do_check_inuse_chunk(M,P)
+#define check_top_chunk(M,P) do_check_top_chunk(M,P)
+#define check_malloced_chunk(M,P,N) do_check_malloced_chunk(M,P,N)
+#define check_mmapped_chunk(M,P) do_check_mmapped_chunk(M,P)
+#define check_malloc_state(M) do_check_malloc_state(M)
+
+static void do_check_any_chunk(mstate m, mchunkptr p);
+static void do_check_top_chunk(mstate m, mchunkptr p);
+static void do_check_mmapped_chunk(mstate m, mchunkptr p);
+static void do_check_inuse_chunk(mstate m, mchunkptr p);
+static void do_check_free_chunk(mstate m, mchunkptr p);
+static void do_check_malloced_chunk(mstate m, void* mem, size_t s);
+static void do_check_tree(mstate m, tchunkptr t);
+static void do_check_treebin(mstate m, bindex_t i);
+static void do_check_smallbin(mstate m, bindex_t i);
+static void do_check_malloc_state(mstate m);
+static int bin_find(mstate m, mchunkptr x);
+static size_t traverse_and_check(mstate m);
+#endif /* DEBUG */
+
+/* ---------------------------- Indexing Bins ---------------------------- */
+
+#define is_small(s) (((s) >> SMALLBIN_SHIFT) < NSMALLBINS)
+#define small_index(s) ((s) >> SMALLBIN_SHIFT)
+#define small_index2size(i) ((i) << SMALLBIN_SHIFT)
+#define MIN_SMALL_INDEX (small_index(MIN_CHUNK_SIZE))
+
+/* addressing by index. See above about smallbin repositioning */
+#define smallbin_at(M, i) ((sbinptr)((char*)&((M)->smallbins[(i)<<1])))
+#define treebin_at(M,i) (&((M)->treebins[i]))
+
+/* assign tree index for size S to variable I */
+#if defined(__GNUC__) && defined(i386)
+#define compute_tree_index(S, I)\
+{\
+ size_t X = S >> TREEBIN_SHIFT;\
+ if (X == 0)\
+ I = 0;\
+ else if (X > 0xFFFF)\
+ I = NTREEBINS-1;\
+ else {\
+ unsigned int K;\
+ __asm__("bsrl %1,%0\n\t" : "=r" (K) : "rm" (X));\
+ I = (bindex_t)((K << 1) + ((S >> (K + (TREEBIN_SHIFT-1)) & 1)));\
+ }\
+}
+#else /* GNUC */
+#define compute_tree_index(S, I)\
+{\
+ size_t X = S >> TREEBIN_SHIFT;\
+ if (X == 0)\
+ I = 0;\
+ else if (X > 0xFFFF)\
+ I = NTREEBINS-1;\
+ else {\
+ unsigned int Y = (unsigned int)X;\
+ unsigned int N = ((Y - 0x100) >> 16) & 8;\
+ unsigned int K = (((Y <<= N) - 0x1000) >> 16) & 4;\
+ N += K;\
+ N += K = (((Y <<= K) - 0x4000) >> 16) & 2;\
+ K = 14 - N + ((Y <<= K) >> 15);\
+ I = (K << 1) + ((S >> (K + (TREEBIN_SHIFT-1)) & 1));\
+ }\
+}
+#endif /* GNUC */
+
+/* Bit representing maximum resolved size in a treebin at i */
+#define bit_for_tree_index(i) \
+ (i == NTREEBINS-1)? (SIZE_T_BITSIZE-1) : (((i) >> 1) + TREEBIN_SHIFT - 2)
+
+/* Shift placing maximum resolved bit in a treebin at i as sign bit */
+#define leftshift_for_tree_index(i) \
+ ((i == NTREEBINS-1)? 0 : \
+ ((SIZE_T_BITSIZE-SIZE_T_ONE) - (((i) >> 1) + TREEBIN_SHIFT - 2)))
+
+/* The size of the smallest chunk held in bin with index i */
+#define minsize_for_tree_index(i) \
+ ((SIZE_T_ONE << (((i) >> 1) + TREEBIN_SHIFT)) | \
+ (((size_t)((i) & SIZE_T_ONE)) << (((i) >> 1) + TREEBIN_SHIFT - 1)))
+
+
+/* ------------------------ Operations on bin maps ----------------------- */
+
+/* bit corresponding to given index */
+#define idx2bit(i) ((binmap_t)(1) << (i))
+
+/* Mark/Clear bits with given index */
+#define mark_smallmap(M,i) ((M)->smallmap |= idx2bit(i))
+#define clear_smallmap(M,i) ((M)->smallmap &= ~idx2bit(i))
+#define smallmap_is_marked(M,i) ((M)->smallmap & idx2bit(i))
+
+#define mark_treemap(M,i) ((M)->treemap |= idx2bit(i))
+#define clear_treemap(M,i) ((M)->treemap &= ~idx2bit(i))
+#define treemap_is_marked(M,i) ((M)->treemap & idx2bit(i))
+
+/* index corresponding to given bit */
+
+#if defined(__GNUC__) && defined(i386)
+#define compute_bit2idx(X, I)\
+{\
+ unsigned int J;\
+ __asm__("bsfl %1,%0\n\t" : "=r" (J) : "rm" (X));\
+ I = (bindex_t)J;\
+}
+
+#else /* GNUC */
+#if defined(__ARM_ARCH__) && __ARM_ARCH__ >= 7
+#define compute_bit2idx(X, I) \
+{ \
+ unsigned int J; \
+ __asm__ ("rbit %0, %1\n" \
+ "clz %0, %0" \
+ : "=r" (J) : "r" (X)); \
+ I = (bindex_t) J; \
+}
+
+#else /* ARM_ARCH */
+#if USE_BUILTIN_FFS
+#define compute_bit2idx(X, I) I = ffs(X)-1
+
+#else /* USE_BUILTIN_FFS */
+#define compute_bit2idx(X, I)\
+{\
+ unsigned int Y = X - 1;\
+ unsigned int K = Y >> (16-4) & 16;\
+ unsigned int N = K; Y >>= K;\
+ N += K = Y >> (8-3) & 8; Y >>= K;\
+ N += K = Y >> (4-2) & 4; Y >>= K;\
+ N += K = Y >> (2-1) & 2; Y >>= K;\
+ N += K = Y >> (1-0) & 1; Y >>= K;\
+ I = (bindex_t)(N + Y);\
+}
+#endif /* ARM_ARCH */
+#endif /* USE_BUILTIN_FFS */
+#endif /* GNUC */
+
+/* isolate the least set bit of a bitmap */
+#define least_bit(x) ((x) & -(x))
+
+/* mask with all bits to left of least bit of x on */
+#define left_bits(x) ((x<<1) | -(x<<1))
+
+/* mask with all bits to left of or equal to least bit of x on */
+#define same_or_left_bits(x) ((x) | -(x))
+
+
+/* ----------------------- Runtime Check Support ------------------------- */
+
+/*
+ For security, the main invariant is that malloc/free/etc never
+ writes to a static address other than malloc_state, unless static
+ malloc_state itself has been corrupted, which cannot occur via
+ malloc (because of these checks). In essence this means that we
+ believe all pointers, sizes, maps etc held in malloc_state, but
+ check all of those linked or offsetted from other embedded data
+ structures. These checks are interspersed with main code in a way
+ that tends to minimize their run-time cost.
+
+ When FOOTERS is defined, in addition to range checking, we also
+ verify footer fields of inuse chunks, which can be used guarantee
+ that the mstate controlling malloc/free is intact. This is a
+ streamlined version of the approach described by William Robertson
+ et al in "Run-time Detection of Heap-based Overflows" LISA'03
+ http://www.usenix.org/events/lisa03/tech/robertson.html The footer
+ of an inuse chunk holds the xor of its mstate and a random seed,
+ that is checked upon calls to free() and realloc(). This is
+ (probablistically) unguessable from outside the program, but can be
+ computed by any code successfully malloc'ing any chunk, so does not
+ itself provide protection against code that has already broken
+ security through some other means. Unlike Robertson et al, we
+ always dynamically check addresses of all offset chunks (previous,
+ next, etc). This turns out to be cheaper than relying on hashes.
+*/
+
+#if !INSECURE
+/* Check if address a is at least as high as any from MORECORE or MMAP */
+#define ok_address(M, a) ((char*)(a) >= (M)->least_addr)
+/* Check if address of next chunk n is higher than base chunk p */
+#define ok_next(p, n) ((char*)(p) < (char*)(n))
+/* Check if p has its cinuse bit on */
+#define ok_cinuse(p) cinuse(p)
+/* Check if p has its pinuse bit on */
+#define ok_pinuse(p) pinuse(p)
+
+#else /* !INSECURE */
+#define ok_address(M, a) (1)
+#define ok_next(b, n) (1)
+#define ok_cinuse(p) (1)
+#define ok_pinuse(p) (1)
+#endif /* !INSECURE */
+
+#if (FOOTERS && !INSECURE)
+/* Check if (alleged) mstate m has expected magic field */
+#define ok_magic(M) ((M)->magic == mparams.magic)
+#else /* (FOOTERS && !INSECURE) */
+#define ok_magic(M) (1)
+#endif /* (FOOTERS && !INSECURE) */
+
+
+/* In gcc, use __builtin_expect to minimize impact of checks */
+#if !INSECURE
+#if defined(__GNUC__) && __GNUC__ >= 3
+#define RTCHECK(e) __builtin_expect(e, 1)
+#else /* GNUC */
+#define RTCHECK(e) (e)
+#endif /* GNUC */
+#else /* !INSECURE */
+#define RTCHECK(e) (1)
+#endif /* !INSECURE */
+
+/* macros to set up inuse chunks with or without footers */
+
+#if !FOOTERS
+
+#define mark_inuse_foot(M,p,s)
+
+/* Set cinuse bit and pinuse bit of next chunk */
+#define set_inuse(M,p,s)\
+ ((p)->head = (((p)->head & PINUSE_BIT)|s|CINUSE_BIT),\
+ ((mchunkptr)(((char*)(p)) + (s)))->head |= PINUSE_BIT)
+
+/* Set cinuse and pinuse of this chunk and pinuse of next chunk */
+#define set_inuse_and_pinuse(M,p,s)\
+ ((p)->head = (s|PINUSE_BIT|CINUSE_BIT),\
+ ((mchunkptr)(((char*)(p)) + (s)))->head |= PINUSE_BIT)
+
+/* Set size, cinuse and pinuse bit of this chunk */
+#define set_size_and_pinuse_of_inuse_chunk(M, p, s)\
+ ((p)->head = (s|PINUSE_BIT|CINUSE_BIT))
+
+#else /* FOOTERS */
+
+/* Set foot of inuse chunk to be xor of mstate and seed */
+#define mark_inuse_foot(M,p,s)\
+ (((mchunkptr)((char*)(p) + (s)))->prev_foot = ((size_t)(M) ^ mparams.magic))
+
+#define get_mstate_for(p)\
+ ((mstate)(((mchunkptr)((char*)(p) +\
+ (chunksize(p))))->prev_foot ^ mparams.magic))
+
+#define set_inuse(M,p,s)\
+ ((p)->head = (((p)->head & PINUSE_BIT)|s|CINUSE_BIT),\
+ (((mchunkptr)(((char*)(p)) + (s)))->head |= PINUSE_BIT), \
+ mark_inuse_foot(M,p,s))
+
+#define set_inuse_and_pinuse(M,p,s)\
+ ((p)->head = (s|PINUSE_BIT|CINUSE_BIT),\
+ (((mchunkptr)(((char*)(p)) + (s)))->head |= PINUSE_BIT),\
+ mark_inuse_foot(M,p,s))
+
+#define set_size_and_pinuse_of_inuse_chunk(M, p, s)\
+ ((p)->head = (s|PINUSE_BIT|CINUSE_BIT),\
+ mark_inuse_foot(M, p, s))
+
+#endif /* !FOOTERS */
+
+/* ---------------------------- setting mparams -------------------------- */
+
+/* Initialize mparams */
+static int init_mparams(void) {
+ if (mparams.page_size == 0) {
+ size_t s;
+
+ mparams.mmap_threshold = DEFAULT_MMAP_THRESHOLD;
+ mparams.trim_threshold = DEFAULT_TRIM_THRESHOLD;
+#if MORECORE_CONTIGUOUS
+ mparams.default_mflags = USE_LOCK_BIT|USE_MMAP_BIT;
+#else /* MORECORE_CONTIGUOUS */
+ mparams.default_mflags = USE_LOCK_BIT|USE_MMAP_BIT|USE_NONCONTIGUOUS_BIT;
+#endif /* MORECORE_CONTIGUOUS */
+
+#if (FOOTERS && !INSECURE)
+ {
+#if USE_DEV_RANDOM
+ int fd;
+ unsigned char buf[sizeof(size_t)];
+ /* Try to use /dev/urandom, else fall back on using time */
+ if ((fd = open("/dev/urandom", O_RDONLY)) >= 0 &&
+ read(fd, buf, sizeof(buf)) == sizeof(buf)) {
+ s = *((size_t *) buf);
+ close(fd);
+ }
+ else
+#endif /* USE_DEV_RANDOM */
+ s = (size_t)(time(0) ^ (size_t)0x55555555U);
+
+ s |= (size_t)8U; /* ensure nonzero */
+ s &= ~(size_t)7U; /* improve chances of fault for bad values */
+
+ }
+#else /* (FOOTERS && !INSECURE) */
+ s = (size_t)0x58585858U;
+#endif /* (FOOTERS && !INSECURE) */
+ ACQUIRE_MAGIC_INIT_LOCK();
+ if (mparams.magic == 0) {
+ mparams.magic = s;
+ /* Set up lock for main malloc area */
+ INITIAL_LOCK(&gm->mutex);
+ gm->mflags = mparams.default_mflags;
+ }
+ RELEASE_MAGIC_INIT_LOCK();
+
+#ifndef WIN32
+ mparams.page_size = malloc_getpagesize;
+ mparams.granularity = ((DEFAULT_GRANULARITY != 0)?
+ DEFAULT_GRANULARITY : mparams.page_size);
+#else /* WIN32 */
+ {
+ SYSTEM_INFO system_info;
+ GetSystemInfo(&system_info);
+ mparams.page_size = system_info.dwPageSize;
+ mparams.granularity = system_info.dwAllocationGranularity;
+ }
+#endif /* WIN32 */
+
+ /* Sanity-check configuration:
+ size_t must be unsigned and as wide as pointer type.
+ ints must be at least 4 bytes.
+ alignment must be at least 8.
+ Alignment, min chunk size, and page size must all be powers of 2.
+ */
+ if ((sizeof(size_t) != sizeof(char*)) ||
+ (MAX_SIZE_T < MIN_CHUNK_SIZE) ||
+ (sizeof(int) < 4) ||
+ (MALLOC_ALIGNMENT < (size_t)8U) ||
+ ((MALLOC_ALIGNMENT & (MALLOC_ALIGNMENT-SIZE_T_ONE)) != 0) ||
+ ((MCHUNK_SIZE & (MCHUNK_SIZE-SIZE_T_ONE)) != 0) ||
+ ((mparams.granularity & (mparams.granularity-SIZE_T_ONE)) != 0) ||
+ ((mparams.page_size & (mparams.page_size-SIZE_T_ONE)) != 0))
+ ABORT;
+ }
+ return 0;
+}
+
+/* support for mallopt */
+static int change_mparam(int param_number, int value) {
+ size_t val = (size_t)value;
+ init_mparams();
+ switch(param_number) {
+ case M_TRIM_THRESHOLD:
+ mparams.trim_threshold = val;
+ return 1;
+ case M_GRANULARITY:
+ if (val >= mparams.page_size && ((val & (val-1)) == 0)) {
+ mparams.granularity = val;
+ return 1;
+ }
+ else
+ return 0;
+ case M_MMAP_THRESHOLD:
+ mparams.mmap_threshold = val;
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+#if DEBUG
+/* ------------------------- Debugging Support --------------------------- */
+
+/* Check properties of any chunk, whether free, inuse, mmapped etc */
+static void do_check_any_chunk(mstate m, mchunkptr p) {
+ assert((is_aligned(chunk2mem(p))) || (p->head == FENCEPOST_HEAD));
+ assert(ok_address(m, p));
+}
+
+/* Check properties of top chunk */
+static void do_check_top_chunk(mstate m, mchunkptr p) {
+ msegmentptr sp = segment_holding(m, (char*)p);
+ size_t sz = chunksize(p);
+ assert(sp != 0);
+ assert((is_aligned(chunk2mem(p))) || (p->head == FENCEPOST_HEAD));
+ assert(ok_address(m, p));
+ assert(sz == m->topsize);
+ assert(sz > 0);
+ assert(sz == ((sp->base + sp->size) - (char*)p) - TOP_FOOT_SIZE);
+ assert(pinuse(p));
+ assert(!next_pinuse(p));
+}
+
+/* Check properties of (inuse) mmapped chunks */
+static void do_check_mmapped_chunk(mstate m, mchunkptr p) {
+ size_t sz = chunksize(p);
+ size_t len = (sz + (p->prev_foot & ~IS_MMAPPED_BIT) + MMAP_FOOT_PAD);
+ assert(is_mmapped(p));
+ assert(use_mmap(m));
+ assert((is_aligned(chunk2mem(p))) || (p->head == FENCEPOST_HEAD));
+ assert(ok_address(m, p));
+ assert(!is_small(sz));
+ assert((len & (mparams.page_size-SIZE_T_ONE)) == 0);
+ assert(chunk_plus_offset(p, sz)->head == FENCEPOST_HEAD);
+ assert(chunk_plus_offset(p, sz+SIZE_T_SIZE)->head == 0);
+}
+
+/* Check properties of inuse chunks */
+static void do_check_inuse_chunk(mstate m, mchunkptr p) {
+ do_check_any_chunk(m, p);
+ assert(cinuse(p));
+ assert(next_pinuse(p));
+ /* If not pinuse and not mmapped, previous chunk has OK offset */
+ assert(is_mmapped(p) || pinuse(p) || next_chunk(prev_chunk(p)) == p);
+ if (is_mmapped(p))
+ do_check_mmapped_chunk(m, p);
+}
+
+/* Check properties of free chunks */
+static void do_check_free_chunk(mstate m, mchunkptr p) {
+ size_t sz = p->head & ~(PINUSE_BIT|CINUSE_BIT);
+ mchunkptr next = chunk_plus_offset(p, sz);
+ do_check_any_chunk(m, p);
+ assert(!cinuse(p));
+ assert(!next_pinuse(p));
+ assert (!is_mmapped(p));
+ if (p != m->dv && p != m->top) {
+ if (sz >= MIN_CHUNK_SIZE) {
+ assert((sz & CHUNK_ALIGN_MASK) == 0);
+ assert(is_aligned(chunk2mem(p)));
+ assert(next->prev_foot == sz);
+ assert(pinuse(p));
+ assert (next == m->top || cinuse(next));
+ assert(p->fd->bk == p);
+ assert(p->bk->fd == p);
+ }
+ else /* markers are always of size SIZE_T_SIZE */
+ assert(sz == SIZE_T_SIZE);
+ }
+}
+
+/* Check properties of malloced chunks at the point they are malloced */
+static void do_check_malloced_chunk(mstate m, void* mem, size_t s) {
+ if (mem != 0) {
+ mchunkptr p = mem2chunk(mem);
+ size_t sz = p->head & ~(PINUSE_BIT|CINUSE_BIT);
+ do_check_inuse_chunk(m, p);
+ assert((sz & CHUNK_ALIGN_MASK) == 0);
+ assert(sz >= MIN_CHUNK_SIZE);
+ assert(sz >= s);
+ /* unless mmapped, size is less than MIN_CHUNK_SIZE more than request */
+ assert(is_mmapped(p) || sz < (s + MIN_CHUNK_SIZE));
+ }
+}
+
+/* Check a tree and its subtrees. */
+static void do_check_tree(mstate m, tchunkptr t) {
+ tchunkptr head = 0;
+ tchunkptr u = t;
+ bindex_t tindex = t->index;
+ size_t tsize = chunksize(t);
+ bindex_t idx;
+ compute_tree_index(tsize, idx);
+ assert(tindex == idx);
+ assert(tsize >= MIN_LARGE_SIZE);
+ assert(tsize >= minsize_for_tree_index(idx));
+ assert((idx == NTREEBINS-1) || (tsize < minsize_for_tree_index((idx+1))));
+
+ do { /* traverse through chain of same-sized nodes */
+ do_check_any_chunk(m, ((mchunkptr)u));
+ assert(u->index == tindex);
+ assert(chunksize(u) == tsize);
+ assert(!cinuse(u));
+ assert(!next_pinuse(u));
+ assert(u->fd->bk == u);
+ assert(u->bk->fd == u);
+ if (u->parent == 0) {
+ assert(u->child[0] == 0);
+ assert(u->child[1] == 0);
+ }
+ else {
+ assert(head == 0); /* only one node on chain has parent */
+ head = u;
+ assert(u->parent != u);
+ assert (u->parent->child[0] == u ||
+ u->parent->child[1] == u ||
+ *((tbinptr*)(u->parent)) == u);
+ if (u->child[0] != 0) {
+ assert(u->child[0]->parent == u);
+ assert(u->child[0] != u);
+ do_check_tree(m, u->child[0]);
+ }
+ if (u->child[1] != 0) {
+ assert(u->child[1]->parent == u);
+ assert(u->child[1] != u);
+ do_check_tree(m, u->child[1]);
+ }
+ if (u->child[0] != 0 && u->child[1] != 0) {
+ assert(chunksize(u->child[0]) < chunksize(u->child[1]));
+ }
+ }
+ u = u->fd;
+ } while (u != t);
+ assert(head != 0);
+}
+
+/* Check all the chunks in a treebin. */
+static void do_check_treebin(mstate m, bindex_t i) {
+ tbinptr* tb = treebin_at(m, i);
+ tchunkptr t = *tb;
+ int empty = (m->treemap & (1U << i)) == 0;
+ if (t == 0)
+ assert(empty);
+ if (!empty)
+ do_check_tree(m, t);
+}
+
+/* Check all the chunks in a smallbin. */
+static void do_check_smallbin(mstate m, bindex_t i) {
+ sbinptr b = smallbin_at(m, i);
+ mchunkptr p = b->bk;
+ unsigned int empty = (m->smallmap & (1U << i)) == 0;
+ if (p == b)
+ assert(empty);
+ if (!empty) {
+ for (; p != b; p = p->bk) {
+ size_t size = chunksize(p);
+ mchunkptr q;
+ /* each chunk claims to be free */
+ do_check_free_chunk(m, p);
+ /* chunk belongs in bin */
+ assert(small_index(size) == i);
+ assert(p->bk == b || chunksize(p->bk) == chunksize(p));
+ /* chunk is followed by an inuse chunk */
+ q = next_chunk(p);
+ if (q->head != FENCEPOST_HEAD)
+ do_check_inuse_chunk(m, q);
+ }
+ }
+}
+
+/* Find x in a bin. Used in other check functions. */
+static int bin_find(mstate m, mchunkptr x) {
+ size_t size = chunksize(x);
+ if (is_small(size)) {
+ bindex_t sidx = small_index(size);
+ sbinptr b = smallbin_at(m, sidx);
+ if (smallmap_is_marked(m, sidx)) {
+ mchunkptr p = b;
+ do {
+ if (p == x)
+ return 1;
+ } while ((p = p->fd) != b);
+ }
+ }
+ else {
+ bindex_t tidx;
+ compute_tree_index(size, tidx);
+ if (treemap_is_marked(m, tidx)) {
+ tchunkptr t = *treebin_at(m, tidx);
+ size_t sizebits = size << leftshift_for_tree_index(tidx);
+ while (t != 0 && chunksize(t) != size) {
+ t = t->child[(sizebits >> (SIZE_T_BITSIZE-SIZE_T_ONE)) & 1];
+ sizebits <<= 1;
+ }
+ if (t != 0) {
+ tchunkptr u = t;
+ do {
+ if (u == (tchunkptr)x)
+ return 1;
+ } while ((u = u->fd) != t);
+ }
+ }
+ }
+ return 0;
+}
+
+/* Traverse each chunk and check it; return total */
+static size_t traverse_and_check(mstate m) {
+ size_t sum = 0;
+ if (is_initialized(m)) {
+ msegmentptr s = &m->seg;
+ sum += m->topsize + TOP_FOOT_SIZE;
+ while (s != 0) {
+ mchunkptr q = align_as_chunk(s->base);
+ mchunkptr lastq = 0;
+ assert(pinuse(q));
+ while (segment_holds(s, q) &&
+ q != m->top && q->head != FENCEPOST_HEAD) {
+ sum += chunksize(q);
+ if (cinuse(q)) {
+ assert(!bin_find(m, q));
+ do_check_inuse_chunk(m, q);
+ }
+ else {
+ assert(q == m->dv || bin_find(m, q));
+ assert(lastq == 0 || cinuse(lastq)); /* Not 2 consecutive free */
+ do_check_free_chunk(m, q);
+ }
+ lastq = q;
+ q = next_chunk(q);
+ }
+ s = s->next;
+ }
+ }
+ return sum;
+}
+
+/* Check all properties of malloc_state. */
+static void do_check_malloc_state(mstate m) {
+ bindex_t i;
+ size_t total;
+ /* check bins */
+ for (i = 0; i < NSMALLBINS; ++i)
+ do_check_smallbin(m, i);
+ for (i = 0; i < NTREEBINS; ++i)
+ do_check_treebin(m, i);
+
+ if (m->dvsize != 0) { /* check dv chunk */
+ do_check_any_chunk(m, m->dv);
+ assert(m->dvsize == chunksize(m->dv));
+ assert(m->dvsize >= MIN_CHUNK_SIZE);
+ assert(bin_find(m, m->dv) == 0);
+ }
+
+ if (m->top != 0) { /* check top chunk */
+ do_check_top_chunk(m, m->top);
+ assert(m->topsize == chunksize(m->top));
+ assert(m->topsize > 0);
+ assert(bin_find(m, m->top) == 0);
+ }
+
+ total = traverse_and_check(m);
+ assert(total <= m->footprint);
+ assert(m->footprint <= m->max_footprint);
+#if USE_MAX_ALLOWED_FOOTPRINT
+ //TODO: change these assertions if we allow for shrinking.
+ assert(m->footprint <= m->max_allowed_footprint);
+ assert(m->max_footprint <= m->max_allowed_footprint);
+#endif
+}
+#endif /* DEBUG */
+
+/* ----------------------------- statistics ------------------------------ */
+
+#if !NO_MALLINFO
+static struct mallinfo internal_mallinfo(mstate m) {
+ struct mallinfo nm = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+ if (!PREACTION(m)) {
+ check_malloc_state(m);
+ if (is_initialized(m)) {
+ size_t nfree = SIZE_T_ONE; /* top always free */
+ size_t mfree = m->topsize + TOP_FOOT_SIZE;
+ size_t sum = mfree;
+ msegmentptr s = &m->seg;
+ while (s != 0) {
+ mchunkptr q = align_as_chunk(s->base);
+ while (segment_holds(s, q) &&
+ q != m->top && q->head != FENCEPOST_HEAD) {
+ size_t sz = chunksize(q);
+ sum += sz;
+ if (!cinuse(q)) {
+ mfree += sz;
+ ++nfree;
+ }
+ q = next_chunk(q);
+ }
+ s = s->next;
+ }
+
+ nm.arena = sum;
+ nm.ordblks = nfree;
+ nm.hblkhd = m->footprint - sum;
+ nm.usmblks = m->max_footprint;
+ nm.uordblks = m->footprint - mfree;
+ nm.fordblks = mfree;
+ nm.keepcost = m->topsize;
+ }
+
+ POSTACTION(m);
+ }
+ return nm;
+}
+#endif /* !NO_MALLINFO */
+
+static void internal_malloc_stats(mstate m) {
+ if (!PREACTION(m)) {
+ size_t maxfp = 0;
+ size_t fp = 0;
+ size_t used = 0;
+ check_malloc_state(m);
+ if (is_initialized(m)) {
+ msegmentptr s = &m->seg;
+ maxfp = m->max_footprint;
+ fp = m->footprint;
+ used = fp - (m->topsize + TOP_FOOT_SIZE);
+
+ while (s != 0) {
+ mchunkptr q = align_as_chunk(s->base);
+ while (segment_holds(s, q) &&
+ q != m->top && q->head != FENCEPOST_HEAD) {
+ if (!cinuse(q))
+ used -= chunksize(q);
+ q = next_chunk(q);
+ }
+ s = s->next;
+ }
+ }
+
+ fprintf(stderr, "max system bytes = %10lu\n", (unsigned long)(maxfp));
+ fprintf(stderr, "system bytes = %10lu\n", (unsigned long)(fp));
+ fprintf(stderr, "in use bytes = %10lu\n", (unsigned long)(used));
+
+ POSTACTION(m);
+ }
+}
+
+/* ----------------------- Operations on smallbins ----------------------- */
+
+/*
+ Various forms of linking and unlinking are defined as macros. Even
+ the ones for trees, which are very long but have very short typical
+ paths. This is ugly but reduces reliance on inlining support of
+ compilers.
+*/
+
+/* Link a free chunk into a smallbin */
+#define insert_small_chunk(M, P, S) {\
+ bindex_t I = small_index(S);\
+ mchunkptr B = smallbin_at(M, I);\
+ mchunkptr F = B;\
+ assert(S >= MIN_CHUNK_SIZE);\
+ if (!smallmap_is_marked(M, I))\
+ mark_smallmap(M, I);\
+ else if (RTCHECK(ok_address(M, B->fd)))\
+ F = B->fd;\
+ else {\
+ CORRUPTION_ERROR_ACTION(M);\
+ }\
+ B->fd = P;\
+ F->bk = P;\
+ P->fd = F;\
+ P->bk = B;\
+}
+
+/* Unlink a chunk from a smallbin
+ * Added check: if F->bk != P or B->fd != P, we have double linked list
+ * corruption, and abort.
+ */
+#define unlink_small_chunk(M, P, S) {\
+ mchunkptr F = P->fd;\
+ mchunkptr B = P->bk;\
+ bindex_t I = small_index(S);\
+ if (__builtin_expect (F->bk != P || B->fd != P, 0))\
+ CORRUPTION_ERROR_ACTION(M);\
+ assert(P != B);\
+ assert(P != F);\
+ assert(chunksize(P) == small_index2size(I));\
+ if (F == B)\
+ clear_smallmap(M, I);\
+ else if (RTCHECK((F == smallbin_at(M,I) || ok_address(M, F)) &&\
+ (B == smallbin_at(M,I) || ok_address(M, B)))) {\
+ F->bk = B;\
+ B->fd = F;\
+ }\
+ else {\
+ CORRUPTION_ERROR_ACTION(M);\
+ }\
+}
+
+/* Unlink the first chunk from a smallbin
+ * Added check: if F->bk != P or B->fd != P, we have double linked list
+ * corruption, and abort.
+ */
+#define unlink_first_small_chunk(M, B, P, I) {\
+ mchunkptr F = P->fd;\
+ if (__builtin_expect (F->bk != P || B->fd != P, 0))\
+ CORRUPTION_ERROR_ACTION(M);\
+ assert(P != B);\
+ assert(P != F);\
+ assert(chunksize(P) == small_index2size(I));\
+ if (B == F)\
+ clear_smallmap(M, I);\
+ else if (RTCHECK(ok_address(M, F))) {\
+ B->fd = F;\
+ F->bk = B;\
+ }\
+ else {\
+ CORRUPTION_ERROR_ACTION(M);\
+ }\
+}
+
+/* Replace dv node, binning the old one */
+/* Used only when dvsize known to be small */
+#define replace_dv(M, P, S) {\
+ size_t DVS = M->dvsize;\
+ if (DVS != 0) {\
+ mchunkptr DV = M->dv;\
+ assert(is_small(DVS));\
+ insert_small_chunk(M, DV, DVS);\
+ }\
+ M->dvsize = S;\
+ M->dv = P;\
+}
+
+/* ------------------------- Operations on trees ------------------------- */
+
+/* Insert chunk into tree */
+#define insert_large_chunk(M, X, S) {\
+ tbinptr* H;\
+ bindex_t I;\
+ compute_tree_index(S, I);\
+ H = treebin_at(M, I);\
+ X->index = I;\
+ X->child[0] = X->child[1] = 0;\
+ if (!treemap_is_marked(M, I)) {\
+ mark_treemap(M, I);\
+ *H = X;\
+ X->parent = (tchunkptr)H;\
+ X->fd = X->bk = X;\
+ }\
+ else {\
+ tchunkptr T = *H;\
+ size_t K = S << leftshift_for_tree_index(I);\
+ for (;;) {\
+ if (chunksize(T) != S) {\
+ tchunkptr* C = &(T->child[(K >> (SIZE_T_BITSIZE-SIZE_T_ONE)) & 1]);\
+ K <<= 1;\
+ if (*C != 0)\
+ T = *C;\
+ else if (RTCHECK(ok_address(M, C))) {\
+ *C = X;\
+ X->parent = T;\
+ X->fd = X->bk = X;\
+ break;\
+ }\
+ else {\
+ CORRUPTION_ERROR_ACTION(M);\
+ break;\
+ }\
+ }\
+ else {\
+ tchunkptr F = T->fd;\
+ if (RTCHECK(ok_address(M, T) && ok_address(M, F))) {\
+ T->fd = F->bk = X;\
+ X->fd = F;\
+ X->bk = T;\
+ X->parent = 0;\
+ break;\
+ }\
+ else {\
+ CORRUPTION_ERROR_ACTION(M);\
+ break;\
+ }\
+ }\
+ }\
+ }\
+}
+
+/*
+ Unlink steps:
+
+ 1. If x is a chained node, unlink it from its same-sized fd/bk links
+ and choose its bk node as its replacement.
+ 2. If x was the last node of its size, but not a leaf node, it must
+ be replaced with a leaf node (not merely one with an open left or
+ right), to make sure that lefts and rights of descendents
+ correspond properly to bit masks. We use the rightmost descendent
+ of x. We could use any other leaf, but this is easy to locate and
+ tends to counteract removal of leftmosts elsewhere, and so keeps
+ paths shorter than minimally guaranteed. This doesn't loop much
+ because on average a node in a tree is near the bottom.
+ 3. If x is the base of a chain (i.e., has parent links) relink
+ x's parent and children to x's replacement (or null if none).
+
+ Added check: if F->bk != X or R->fd != X, we have double linked list
+ corruption, and abort.
+*/
+
+#define unlink_large_chunk(M, X) {\
+ tchunkptr XP = X->parent;\
+ tchunkptr R;\
+ if (X->bk != X) {\
+ tchunkptr F = X->fd;\
+ R = X->bk;\
+ if (__builtin_expect (F->bk != X || R->fd != X, 0))\
+ CORRUPTION_ERROR_ACTION(M);\
+ if (RTCHECK(ok_address(M, F))) {\
+ F->bk = R;\
+ R->fd = F;\
+ }\
+ else {\
+ CORRUPTION_ERROR_ACTION(M);\
+ }\
+ }\
+ else {\
+ tchunkptr* RP;\
+ if (((R = *(RP = &(X->child[1]))) != 0) ||\
+ ((R = *(RP = &(X->child[0]))) != 0)) {\
+ tchunkptr* CP;\
+ while ((*(CP = &(R->child[1])) != 0) ||\
+ (*(CP = &(R->child[0])) != 0)) {\
+ R = *(RP = CP);\
+ }\
+ if (RTCHECK(ok_address(M, RP)))\
+ *RP = 0;\
+ else {\
+ CORRUPTION_ERROR_ACTION(M);\
+ }\
+ }\
+ }\
+ if (XP != 0) {\
+ tbinptr* H = treebin_at(M, X->index);\
+ if (X == *H) {\
+ if ((*H = R) == 0) \
+ clear_treemap(M, X->index);\
+ }\
+ else if (RTCHECK(ok_address(M, XP))) {\
+ if (XP->child[0] == X) \
+ XP->child[0] = R;\
+ else \
+ XP->child[1] = R;\
+ }\
+ else\
+ CORRUPTION_ERROR_ACTION(M);\
+ if (R != 0) {\
+ if (RTCHECK(ok_address(M, R))) {\
+ tchunkptr C0, C1;\
+ R->parent = XP;\
+ if ((C0 = X->child[0]) != 0) {\
+ if (RTCHECK(ok_address(M, C0))) {\
+ R->child[0] = C0;\
+ C0->parent = R;\
+ }\
+ else\
+ CORRUPTION_ERROR_ACTION(M);\
+ }\
+ if ((C1 = X->child[1]) != 0) {\
+ if (RTCHECK(ok_address(M, C1))) {\
+ R->child[1] = C1;\
+ C1->parent = R;\
+ }\
+ else\
+ CORRUPTION_ERROR_ACTION(M);\
+ }\
+ }\
+ else\
+ CORRUPTION_ERROR_ACTION(M);\
+ }\
+ }\
+}
+
+/* Relays to large vs small bin operations */
+
+#define insert_chunk(M, P, S)\
+ if (is_small(S)) insert_small_chunk(M, P, S)\
+ else { tchunkptr TP = (tchunkptr)(P); insert_large_chunk(M, TP, S); }
+
+#define unlink_chunk(M, P, S)\
+ if (is_small(S)) unlink_small_chunk(M, P, S)\
+ else { tchunkptr TP = (tchunkptr)(P); unlink_large_chunk(M, TP); }
+
+
+/* Relays to internal calls to malloc/free from realloc, memalign etc */
+
+#if ONLY_MSPACES
+#define internal_malloc(m, b) mspace_malloc(m, b)
+#define internal_free(m, mem) mspace_free(m,mem);
+#else /* ONLY_MSPACES */
+#if MSPACES
+#define internal_malloc(m, b)\
+ (m == gm)? dlmalloc(b) : mspace_malloc(m, b)
+#define internal_free(m, mem)\
+ if (m == gm) dlfree(mem); else mspace_free(m,mem);
+#else /* MSPACES */
+#define internal_malloc(m, b) dlmalloc(b)
+#define internal_free(m, mem) dlfree(mem)
+#endif /* MSPACES */
+#endif /* ONLY_MSPACES */
+
+/* ----------------------- Direct-mmapping chunks ----------------------- */
+
+/*
+ Directly mmapped chunks are set up with an offset to the start of
+ the mmapped region stored in the prev_foot field of the chunk. This
+ allows reconstruction of the required argument to MUNMAP when freed,
+ and also allows adjustment of the returned chunk to meet alignment
+ requirements (especially in memalign). There is also enough space
+ allocated to hold a fake next chunk of size SIZE_T_SIZE to maintain
+ the PINUSE bit so frees can be checked.
+*/
+
+/* Malloc using mmap */
+static void* mmap_alloc(mstate m, size_t nb) {
+ size_t mmsize = granularity_align(nb + SIX_SIZE_T_SIZES + CHUNK_ALIGN_MASK);
+#if USE_MAX_ALLOWED_FOOTPRINT
+ size_t new_footprint = m->footprint + mmsize;
+ if (new_footprint <= m->footprint || /* Check for wrap around 0 */
+ new_footprint > m->max_allowed_footprint)
+ return 0;
+#endif
+ if (mmsize > nb) { /* Check for wrap around 0 */
+ char* mm = (char*)(DIRECT_MMAP(mmsize));
+ if (mm != CMFAIL) {
+ size_t offset = align_offset(chunk2mem(mm));
+ size_t psize = mmsize - offset - MMAP_FOOT_PAD;
+ mchunkptr p = (mchunkptr)(mm + offset);
+ p->prev_foot = offset | IS_MMAPPED_BIT;
+ (p)->head = (psize|CINUSE_BIT);
+ mark_inuse_foot(m, p, psize);
+ chunk_plus_offset(p, psize)->head = FENCEPOST_HEAD;
+ chunk_plus_offset(p, psize+SIZE_T_SIZE)->head = 0;
+
+ if (mm < m->least_addr)
+ m->least_addr = mm;
+ if ((m->footprint += mmsize) > m->max_footprint)
+ m->max_footprint = m->footprint;
+ assert(is_aligned(chunk2mem(p)));
+ check_mmapped_chunk(m, p);
+ return chunk2mem(p);
+ }
+ }
+ return 0;
+}
+
+/* Realloc using mmap */
+static mchunkptr mmap_resize(mstate m, mchunkptr oldp, size_t nb) {
+ size_t oldsize = chunksize(oldp);
+ if (is_small(nb)) /* Can't shrink mmap regions below small size */
+ return 0;
+ /* Keep old chunk if big enough but not too big */
+ if (oldsize >= nb + SIZE_T_SIZE &&
+ (oldsize - nb) <= (mparams.granularity << 1))
+ return oldp;
+ else {
+ size_t offset = oldp->prev_foot & ~IS_MMAPPED_BIT;
+ size_t oldmmsize = oldsize + offset + MMAP_FOOT_PAD;
+ size_t newmmsize = granularity_align(nb + SIX_SIZE_T_SIZES +
+ CHUNK_ALIGN_MASK);
+ char* cp = (char*)CALL_MREMAP((char*)oldp - offset,
+ oldmmsize, newmmsize, 1);
+ if (cp != CMFAIL) {
+ mchunkptr newp = (mchunkptr)(cp + offset);
+ size_t psize = newmmsize - offset - MMAP_FOOT_PAD;
+ newp->head = (psize|CINUSE_BIT);
+ mark_inuse_foot(m, newp, psize);
+ chunk_plus_offset(newp, psize)->head = FENCEPOST_HEAD;
+ chunk_plus_offset(newp, psize+SIZE_T_SIZE)->head = 0;
+
+ if (cp < m->least_addr)
+ m->least_addr = cp;
+ if ((m->footprint += newmmsize - oldmmsize) > m->max_footprint)
+ m->max_footprint = m->footprint;
+ check_mmapped_chunk(m, newp);
+ return newp;
+ }
+ }
+ return 0;
+}
+
+/* -------------------------- mspace management -------------------------- */
+
+/* Initialize top chunk and its size */
+static void init_top(mstate m, mchunkptr p, size_t psize) {
+ /* Ensure alignment */
+ size_t offset = align_offset(chunk2mem(p));
+ p = (mchunkptr)((char*)p + offset);
+ psize -= offset;
+
+ m->top = p;
+ m->topsize = psize;
+ p->head = psize | PINUSE_BIT;
+ /* set size of fake trailing chunk holding overhead space only once */
+ chunk_plus_offset(p, psize)->head = TOP_FOOT_SIZE;
+ m->trim_check = mparams.trim_threshold; /* reset on each update */
+}
+
+/* Initialize bins for a new mstate that is otherwise zeroed out */
+static void init_bins(mstate m) {
+ /* Establish circular links for smallbins */
+ bindex_t i;
+ for (i = 0; i < NSMALLBINS; ++i) {
+ sbinptr bin = smallbin_at(m,i);
+ bin->fd = bin->bk = bin;
+ }
+}
+
+#if PROCEED_ON_ERROR
+
+/* default corruption action */
+static void reset_on_error(mstate m) {
+ int i;
+ ++malloc_corruption_error_count;
+ /* Reinitialize fields to forget about all memory */
+ m->smallbins = m->treebins = 0;
+ m->dvsize = m->topsize = 0;
+ m->seg.base = 0;
+ m->seg.size = 0;
+ m->seg.next = 0;
+ m->top = m->dv = 0;
+ for (i = 0; i < NTREEBINS; ++i)
+ *treebin_at(m, i) = 0;
+ init_bins(m);
+}
+#endif /* PROCEED_ON_ERROR */
+
+/* Allocate chunk and prepend remainder with chunk in successor base. */
+static void* prepend_alloc(mstate m, char* newbase, char* oldbase,
+ size_t nb) {
+ mchunkptr p = align_as_chunk(newbase);
+ mchunkptr oldfirst = align_as_chunk(oldbase);
+ size_t psize = (char*)oldfirst - (char*)p;
+ mchunkptr q = chunk_plus_offset(p, nb);
+ size_t qsize = psize - nb;
+ set_size_and_pinuse_of_inuse_chunk(m, p, nb);
+
+ assert((char*)oldfirst > (char*)q);
+ assert(pinuse(oldfirst));
+ assert(qsize >= MIN_CHUNK_SIZE);
+
+ /* consolidate remainder with first chunk of old base */
+ if (oldfirst == m->top) {
+ size_t tsize = m->topsize += qsize;
+ m->top = q;
+ q->head = tsize | PINUSE_BIT;
+ check_top_chunk(m, q);
+ }
+ else if (oldfirst == m->dv) {
+ size_t dsize = m->dvsize += qsize;
+ m->dv = q;
+ set_size_and_pinuse_of_free_chunk(q, dsize);
+ }
+ else {
+ if (!cinuse(oldfirst)) {
+ size_t nsize = chunksize(oldfirst);
+ unlink_chunk(m, oldfirst, nsize);
+ oldfirst = chunk_plus_offset(oldfirst, nsize);
+ qsize += nsize;
+ }
+ set_free_with_pinuse(q, qsize, oldfirst);
+ insert_chunk(m, q, qsize);
+ check_free_chunk(m, q);
+ }
+
+ check_malloced_chunk(m, chunk2mem(p), nb);
+ return chunk2mem(p);
+}
+
+
+/* Add a segment to hold a new noncontiguous region */
+static void add_segment(mstate m, char* tbase, size_t tsize, flag_t mmapped) {
+ /* Determine locations and sizes of segment, fenceposts, old top */
+ char* old_top = (char*)m->top;
+ msegmentptr oldsp = segment_holding(m, old_top);
+ char* old_end = oldsp->base + oldsp->size;
+ size_t ssize = pad_request(sizeof(struct malloc_segment));
+ char* rawsp = old_end - (ssize + FOUR_SIZE_T_SIZES + CHUNK_ALIGN_MASK);
+ size_t offset = align_offset(chunk2mem(rawsp));
+ char* asp = rawsp + offset;
+ char* csp = (asp < (old_top + MIN_CHUNK_SIZE))? old_top : asp;
+ mchunkptr sp = (mchunkptr)csp;
+ msegmentptr ss = (msegmentptr)(chunk2mem(sp));
+ mchunkptr tnext = chunk_plus_offset(sp, ssize);
+ mchunkptr p = tnext;
+ int nfences = 0;
+
+ /* reset top to new space */
+ init_top(m, (mchunkptr)tbase, tsize - TOP_FOOT_SIZE);
+
+ /* Set up segment record */
+ assert(is_aligned(ss));
+ set_size_and_pinuse_of_inuse_chunk(m, sp, ssize);
+ *ss = m->seg; /* Push current record */
+ m->seg.base = tbase;
+ m->seg.size = tsize;
+ m->seg.sflags = mmapped;
+ m->seg.next = ss;
+
+ /* Insert trailing fenceposts */
+ for (;;) {
+ mchunkptr nextp = chunk_plus_offset(p, SIZE_T_SIZE);
+ p->head = FENCEPOST_HEAD;
+ ++nfences;
+ if ((char*)(&(nextp->head)) < old_end)
+ p = nextp;
+ else
+ break;
+ }
+ assert(nfences >= 2);
+
+ /* Insert the rest of old top into a bin as an ordinary free chunk */
+ if (csp != old_top) {
+ mchunkptr q = (mchunkptr)old_top;
+ size_t psize = csp - old_top;
+ mchunkptr tn = chunk_plus_offset(q, psize);
+ set_free_with_pinuse(q, psize, tn);
+ insert_chunk(m, q, psize);
+ }
+
+ check_top_chunk(m, m->top);
+}
+
+/* -------------------------- System allocation -------------------------- */
+
+/* Get memory from system using MORECORE or MMAP */
+static void* sys_alloc(mstate m, size_t nb) {
+ char* tbase = CMFAIL;
+ size_t tsize = 0;
+ flag_t mmap_flag = 0;
+
+ init_mparams();
+
+ /* Directly map large chunks */
+ if (use_mmap(m) && nb >= mparams.mmap_threshold) {
+ void* mem = mmap_alloc(m, nb);
+ if (mem != 0)
+ return mem;
+ }
+
+#if USE_MAX_ALLOWED_FOOTPRINT
+ /* Make sure the footprint doesn't grow past max_allowed_footprint.
+ * This covers all cases except for where we need to page align, below.
+ */
+ {
+ size_t new_footprint = m->footprint +
+ granularity_align(nb + TOP_FOOT_SIZE + SIZE_T_ONE);
+ if (new_footprint <= m->footprint || /* Check for wrap around 0 */
+ new_footprint > m->max_allowed_footprint)
+ return 0;
+ }
+#endif
+
+ /*
+ Try getting memory in any of three ways (in most-preferred to
+ least-preferred order):
+ 1. A call to MORECORE that can normally contiguously extend memory.
+ (disabled if not MORECORE_CONTIGUOUS or not HAVE_MORECORE or
+ or main space is mmapped or a previous contiguous call failed)
+ 2. A call to MMAP new space (disabled if not HAVE_MMAP).
+ Note that under the default settings, if MORECORE is unable to
+ fulfill a request, and HAVE_MMAP is true, then mmap is
+ used as a noncontiguous system allocator. This is a useful backup
+ strategy for systems with holes in address spaces -- in this case
+ sbrk cannot contiguously expand the heap, but mmap may be able to
+ find space.
+ 3. A call to MORECORE that cannot usually contiguously extend memory.
+ (disabled if not HAVE_MORECORE)
+ */
+
+ if (MORECORE_CONTIGUOUS && !use_noncontiguous(m)) {
+ char* br = CMFAIL;
+ msegmentptr ss = (m->top == 0)? 0 : segment_holding(m, (char*)m->top);
+ size_t asize = 0;
+ ACQUIRE_MORECORE_LOCK();
+
+ if (ss == 0) { /* First time through or recovery */
+ char* base = (char*)CALL_MORECORE(0);
+ if (base != CMFAIL) {
+ asize = granularity_align(nb + TOP_FOOT_SIZE + SIZE_T_ONE);
+ /* Adjust to end on a page boundary */
+ if (!is_page_aligned(base)) {
+ asize += (page_align((size_t)base) - (size_t)base);
+#if USE_MAX_ALLOWED_FOOTPRINT
+ /* If the alignment pushes us over max_allowed_footprint,
+ * poison the upcoming call to MORECORE and continue.
+ */
+ {
+ size_t new_footprint = m->footprint + asize;
+ if (new_footprint <= m->footprint || /* Check for wrap around 0 */
+ new_footprint > m->max_allowed_footprint) {
+ asize = HALF_MAX_SIZE_T;
+ }
+ }
+#endif
+ }
+ /* Can't call MORECORE if size is negative when treated as signed */
+ if (asize < HALF_MAX_SIZE_T &&
+ (br = (char*)(CALL_MORECORE(asize))) == base) {
+ tbase = base;
+ tsize = asize;
+ }
+ }
+ }
+ else {
+ /* Subtract out existing available top space from MORECORE request. */
+ asize = granularity_align(nb - m->topsize + TOP_FOOT_SIZE + SIZE_T_ONE);
+ /* Use mem here only if it did continuously extend old space */
+ if (asize < HALF_MAX_SIZE_T &&
+ (br = (char*)(CALL_MORECORE(asize))) == ss->base+ss->size) {
+ tbase = br;
+ tsize = asize;
+ }
+ }
+
+ if (tbase == CMFAIL) { /* Cope with partial failure */
+ if (br != CMFAIL) { /* Try to use/extend the space we did get */
+ if (asize < HALF_MAX_SIZE_T &&
+ asize < nb + TOP_FOOT_SIZE + SIZE_T_ONE) {
+ size_t esize = granularity_align(nb + TOP_FOOT_SIZE + SIZE_T_ONE - asize);
+ if (esize < HALF_MAX_SIZE_T) {
+ char* end = (char*)CALL_MORECORE(esize);
+ if (end != CMFAIL)
+ asize += esize;
+ else { /* Can't use; try to release */
+ CALL_MORECORE(-asize);
+ br = CMFAIL;
+ }
+ }
+ }
+ }
+ if (br != CMFAIL) { /* Use the space we did get */
+ tbase = br;
+ tsize = asize;
+ }
+ else
+ disable_contiguous(m); /* Don't try contiguous path in the future */
+ }
+
+ RELEASE_MORECORE_LOCK();
+ }
+
+ if (HAVE_MMAP && tbase == CMFAIL) { /* Try MMAP */
+ size_t req = nb + TOP_FOOT_SIZE + SIZE_T_ONE;
+ size_t rsize = granularity_align(req);
+ if (rsize > nb) { /* Fail if wraps around zero */
+ char* mp = (char*)(CALL_MMAP(rsize));
+ if (mp != CMFAIL) {
+ tbase = mp;
+ tsize = rsize;
+ mmap_flag = IS_MMAPPED_BIT;
+ }
+ }
+ }
+
+ if (HAVE_MORECORE && tbase == CMFAIL) { /* Try noncontiguous MORECORE */
+ size_t asize = granularity_align(nb + TOP_FOOT_SIZE + SIZE_T_ONE);
+ if (asize < HALF_MAX_SIZE_T) {
+ char* br = CMFAIL;
+ char* end = CMFAIL;
+ ACQUIRE_MORECORE_LOCK();
+ br = (char*)(CALL_MORECORE(asize));
+ end = (char*)(CALL_MORECORE(0));
+ RELEASE_MORECORE_LOCK();
+ if (br != CMFAIL && end != CMFAIL && br < end) {
+ size_t ssize = end - br;
+ if (ssize > nb + TOP_FOOT_SIZE) {
+ tbase = br;
+ tsize = ssize;
+ }
+ }
+ }
+ }
+
+ if (tbase != CMFAIL) {
+
+ if ((m->footprint += tsize) > m->max_footprint)
+ m->max_footprint = m->footprint;
+
+ if (!is_initialized(m)) { /* first-time initialization */
+ m->seg.base = m->least_addr = tbase;
+ m->seg.size = tsize;
+ m->seg.sflags = mmap_flag;
+ m->magic = mparams.magic;
+ init_bins(m);
+ if (is_global(m))
+ init_top(m, (mchunkptr)tbase, tsize - TOP_FOOT_SIZE);
+ else {
+ /* Offset top by embedded malloc_state */
+ mchunkptr mn = next_chunk(mem2chunk(m));
+ init_top(m, mn, (size_t)((tbase + tsize) - (char*)mn) -TOP_FOOT_SIZE);
+ }
+ }
+
+ else {
+ /* Try to merge with an existing segment */
+ msegmentptr sp = &m->seg;
+ while (sp != 0 && tbase != sp->base + sp->size)
+ sp = sp->next;
+ if (sp != 0 &&
+ !is_extern_segment(sp) &&
+ (sp->sflags & IS_MMAPPED_BIT) == mmap_flag &&
+ segment_holds(sp, m->top)) { /* append */
+ sp->size += tsize;
+ init_top(m, m->top, m->topsize + tsize);
+ }
+ else {
+ if (tbase < m->least_addr)
+ m->least_addr = tbase;
+ sp = &m->seg;
+ while (sp != 0 && sp->base != tbase + tsize)
+ sp = sp->next;
+ if (sp != 0 &&
+ !is_extern_segment(sp) &&
+ (sp->sflags & IS_MMAPPED_BIT) == mmap_flag) {
+ char* oldbase = sp->base;
+ sp->base = tbase;
+ sp->size += tsize;
+ return prepend_alloc(m, tbase, oldbase, nb);
+ }
+ else
+ add_segment(m, tbase, tsize, mmap_flag);
+ }
+ }
+
+ if (nb < m->topsize) { /* Allocate from new or extended top space */
+ size_t rsize = m->topsize -= nb;
+ mchunkptr p = m->top;
+ mchunkptr r = m->top = chunk_plus_offset(p, nb);
+ r->head = rsize | PINUSE_BIT;
+ set_size_and_pinuse_of_inuse_chunk(m, p, nb);
+ check_top_chunk(m, m->top);
+ check_malloced_chunk(m, chunk2mem(p), nb);
+ return chunk2mem(p);
+ }
+ }
+
+ MALLOC_FAILURE_ACTION;
+ return 0;
+}
+
+/* ----------------------- system deallocation -------------------------- */
+
+/* Unmap and unlink any mmapped segments that don't contain used chunks */
+static size_t release_unused_segments(mstate m) {
+ size_t released = 0;
+ msegmentptr pred = &m->seg;
+ msegmentptr sp = pred->next;
+ while (sp != 0) {
+ char* base = sp->base;
+ size_t size = sp->size;
+ msegmentptr next = sp->next;
+ if (is_mmapped_segment(sp) && !is_extern_segment(sp)) {
+ mchunkptr p = align_as_chunk(base);
+ size_t psize = chunksize(p);
+ /* Can unmap if first chunk holds entire segment and not pinned */
+ if (!cinuse(p) && (char*)p + psize >= base + size - TOP_FOOT_SIZE) {
+ tchunkptr tp = (tchunkptr)p;
+ assert(segment_holds(sp, (char*)sp));
+ if (p == m->dv) {
+ m->dv = 0;
+ m->dvsize = 0;
+ }
+ else {
+ unlink_large_chunk(m, tp);
+ }
+ if (CALL_MUNMAP(base, size) == 0) {
+ released += size;
+ m->footprint -= size;
+ /* unlink obsoleted record */
+ sp = pred;
+ sp->next = next;
+ }
+ else { /* back out if cannot unmap */
+ insert_large_chunk(m, tp, psize);
+ }
+ }
+ }
+ pred = sp;
+ sp = next;
+ }
+ return released;
+}
+
+static int sys_trim(mstate m, size_t pad) {
+ size_t released = 0;
+ if (pad < MAX_REQUEST && is_initialized(m)) {
+ pad += TOP_FOOT_SIZE; /* ensure enough room for segment overhead */
+
+ if (m->topsize > pad) {
+ /* Shrink top space in granularity-size units, keeping at least one */
+ size_t unit = mparams.granularity;
+ size_t extra = ((m->topsize - pad + (unit - SIZE_T_ONE)) / unit -
+ SIZE_T_ONE) * unit;
+ msegmentptr sp = segment_holding(m, (char*)m->top);
+
+ if (!is_extern_segment(sp)) {
+ if (is_mmapped_segment(sp)) {
+ if (HAVE_MMAP &&
+ sp->size >= extra &&
+ !has_segment_link(m, sp)) { /* can't shrink if pinned */
+ size_t newsize = sp->size - extra;
+ /* Prefer mremap, fall back to munmap */
+ if ((CALL_MREMAP(sp->base, sp->size, newsize, 0) != MFAIL) ||
+ (CALL_MUNMAP(sp->base + newsize, extra) == 0)) {
+ released = extra;
+ }
+ }
+ }
+ else if (HAVE_MORECORE) {
+ if (extra >= HALF_MAX_SIZE_T) /* Avoid wrapping negative */
+ extra = (HALF_MAX_SIZE_T) + SIZE_T_ONE - unit;
+ ACQUIRE_MORECORE_LOCK();
+ {
+ /* Make sure end of memory is where we last set it. */
+ char* old_br = (char*)(CALL_MORECORE(0));
+ if (old_br == sp->base + sp->size) {
+ char* rel_br = (char*)(CALL_MORECORE(-extra));
+ char* new_br = (char*)(CALL_MORECORE(0));
+ if (rel_br != CMFAIL && new_br < old_br)
+ released = old_br - new_br;
+ }
+ }
+ RELEASE_MORECORE_LOCK();
+ }
+ }
+
+ if (released != 0) {
+ sp->size -= released;
+ m->footprint -= released;
+ init_top(m, m->top, m->topsize - released);
+ check_top_chunk(m, m->top);
+ }
+ }
+
+ /* Unmap any unused mmapped segments */
+ if (HAVE_MMAP)
+ released += release_unused_segments(m);
+
+ /* On failure, disable autotrim to avoid repeated failed future calls */
+ if (released == 0)
+ m->trim_check = MAX_SIZE_T;
+ }
+
+ return (released != 0)? 1 : 0;
+}
+
+/* ---------------------------- malloc support --------------------------- */
+
+/* allocate a large request from the best fitting chunk in a treebin */
+static void* tmalloc_large(mstate m, size_t nb) {
+ tchunkptr v = 0;
+ size_t rsize = -nb; /* Unsigned negation */
+ tchunkptr t;
+ bindex_t idx;
+ compute_tree_index(nb, idx);
+
+ if ((t = *treebin_at(m, idx)) != 0) {
+ /* Traverse tree for this bin looking for node with size == nb */
+ size_t sizebits = nb << leftshift_for_tree_index(idx);
+ tchunkptr rst = 0; /* The deepest untaken right subtree */
+ for (;;) {
+ tchunkptr rt;
+ size_t trem = chunksize(t) - nb;
+ if (trem < rsize) {
+ v = t;
+ if ((rsize = trem) == 0)
+ break;
+ }
+ rt = t->child[1];
+ t = t->child[(sizebits >> (SIZE_T_BITSIZE-SIZE_T_ONE)) & 1];
+ if (rt != 0 && rt != t)
+ rst = rt;
+ if (t == 0) {
+ t = rst; /* set t to least subtree holding sizes > nb */
+ break;
+ }
+ sizebits <<= 1;
+ }
+ }
+
+ if (t == 0 && v == 0) { /* set t to root of next non-empty treebin */
+ binmap_t leftbits = left_bits(idx2bit(idx)) & m->treemap;
+ if (leftbits != 0) {
+ bindex_t i;
+ binmap_t leastbit = least_bit(leftbits);
+ compute_bit2idx(leastbit, i);
+ t = *treebin_at(m, i);
+ }
+ }
+
+ while (t != 0) { /* find smallest of tree or subtree */
+ size_t trem = chunksize(t) - nb;
+ if (trem < rsize) {
+ rsize = trem;
+ v = t;
+ }
+ t = leftmost_child(t);
+ }
+
+ /* If dv is a better fit, return 0 so malloc will use it */
+ if (v != 0 && rsize < (size_t)(m->dvsize - nb)) {
+ if (RTCHECK(ok_address(m, v))) { /* split */
+ mchunkptr r = chunk_plus_offset(v, nb);
+ assert(chunksize(v) == rsize + nb);
+ if (RTCHECK(ok_next(v, r))) {
+ unlink_large_chunk(m, v);
+ if (rsize < MIN_CHUNK_SIZE)
+ set_inuse_and_pinuse(m, v, (rsize + nb));
+ else {
+ set_size_and_pinuse_of_inuse_chunk(m, v, nb);
+ set_size_and_pinuse_of_free_chunk(r, rsize);
+ insert_chunk(m, r, rsize);
+ }
+ return chunk2mem(v);
+ }
+ }
+ CORRUPTION_ERROR_ACTION(m);
+ }
+ return 0;
+}
+
+/* allocate a small request from the best fitting chunk in a treebin */
+static void* tmalloc_small(mstate m, size_t nb) {
+ tchunkptr t, v;
+ size_t rsize;
+ bindex_t i;
+ binmap_t leastbit = least_bit(m->treemap);
+ compute_bit2idx(leastbit, i);
+
+ v = t = *treebin_at(m, i);
+ rsize = chunksize(t) - nb;
+
+ while ((t = leftmost_child(t)) != 0) {
+ size_t trem = chunksize(t) - nb;
+ if (trem < rsize) {
+ rsize = trem;
+ v = t;
+ }
+ }
+
+ if (RTCHECK(ok_address(m, v))) {
+ mchunkptr r = chunk_plus_offset(v, nb);
+ assert(chunksize(v) == rsize + nb);
+ if (RTCHECK(ok_next(v, r))) {
+ unlink_large_chunk(m, v);
+ if (rsize < MIN_CHUNK_SIZE)
+ set_inuse_and_pinuse(m, v, (rsize + nb));
+ else {
+ set_size_and_pinuse_of_inuse_chunk(m, v, nb);
+ set_size_and_pinuse_of_free_chunk(r, rsize);
+ replace_dv(m, r, rsize);
+ }
+ return chunk2mem(v);
+ }
+ }
+
+ CORRUPTION_ERROR_ACTION(m);
+ return 0;
+}
+
+/* --------------------------- realloc support --------------------------- */
+
+static void* internal_realloc(mstate m, void* oldmem, size_t bytes) {
+ if (bytes >= MAX_REQUEST) {
+ MALLOC_FAILURE_ACTION;
+ return 0;
+ }
+ if (!PREACTION(m)) {
+ mchunkptr oldp = mem2chunk(oldmem);
+ size_t oldsize = chunksize(oldp);
+ mchunkptr next = chunk_plus_offset(oldp, oldsize);
+ mchunkptr newp = 0;
+ void* extra = 0;
+
+ /* Try to either shrink or extend into top. Else malloc-copy-free */
+
+ if (RTCHECK(ok_address(m, oldp) && ok_cinuse(oldp) &&
+ ok_next(oldp, next) && ok_pinuse(next))) {
+ size_t nb = request2size(bytes);
+ if (is_mmapped(oldp))
+ newp = mmap_resize(m, oldp, nb);
+ else if (oldsize >= nb) { /* already big enough */
+ size_t rsize = oldsize - nb;
+ newp = oldp;
+ if (rsize >= MIN_CHUNK_SIZE) {
+ mchunkptr remainder = chunk_plus_offset(newp, nb);
+ set_inuse(m, newp, nb);
+ set_inuse(m, remainder, rsize);
+ extra = chunk2mem(remainder);
+ }
+ }
+ else if (next == m->top && oldsize + m->topsize > nb) {
+ /* Expand into top */
+ size_t newsize = oldsize + m->topsize;
+ size_t newtopsize = newsize - nb;
+ mchunkptr newtop = chunk_plus_offset(oldp, nb);
+ set_inuse(m, oldp, nb);
+ newtop->head = newtopsize |PINUSE_BIT;
+ m->top = newtop;
+ m->topsize = newtopsize;
+ newp = oldp;
+ }
+ }
+ else {
+ USAGE_ERROR_ACTION(m, oldmem);
+ POSTACTION(m);
+ return 0;
+ }
+
+ POSTACTION(m);
+
+ if (newp != 0) {
+ if (extra != 0) {
+ internal_free(m, extra);
+ }
+ check_inuse_chunk(m, newp);
+ return chunk2mem(newp);
+ }
+ else {
+ void* newmem = internal_malloc(m, bytes);
+ if (newmem != 0) {
+ size_t oc = oldsize - overhead_for(oldp);
+ memcpy(newmem, oldmem, (oc < bytes)? oc : bytes);
+ internal_free(m, oldmem);
+ }
+ return newmem;
+ }
+ }
+ return 0;
+}
+
+/* --------------------------- memalign support -------------------------- */
+
+static void* internal_memalign(mstate m, size_t alignment, size_t bytes) {
+ if (alignment <= MALLOC_ALIGNMENT) /* Can just use malloc */
+ return internal_malloc(m, bytes);
+ if (alignment < MIN_CHUNK_SIZE) /* must be at least a minimum chunk size */
+ alignment = MIN_CHUNK_SIZE;
+ if ((alignment & (alignment-SIZE_T_ONE)) != 0) {/* Ensure a power of 2 */
+ size_t a = MALLOC_ALIGNMENT << 1;
+ while (a < alignment) a <<= 1;
+ alignment = a;
+ }
+
+ if (bytes >= MAX_REQUEST - alignment) {
+ if (m != 0) { /* Test isn't needed but avoids compiler warning */
+ MALLOC_FAILURE_ACTION;
+ }
+ }
+ else {
+ size_t nb = request2size(bytes);
+ size_t req = nb + alignment + MIN_CHUNK_SIZE - CHUNK_OVERHEAD;
+ char* mem = (char*)internal_malloc(m, req);
+ if (mem != 0) {
+ void* leader = 0;
+ void* trailer = 0;
+ mchunkptr p = mem2chunk(mem);
+
+ if (PREACTION(m)) return 0;
+ if ((((size_t)(mem)) % alignment) != 0) { /* misaligned */
+ /*
+ Find an aligned spot inside chunk. Since we need to give
+ back leading space in a chunk of at least MIN_CHUNK_SIZE, if
+ the first calculation places us at a spot with less than
+ MIN_CHUNK_SIZE leader, we can move to the next aligned spot.
+ We've allocated enough total room so that this is always
+ possible.
+ */
+ char* br = (char*)mem2chunk((size_t)(((size_t)(mem +
+ alignment -
+ SIZE_T_ONE)) &
+ -alignment));
+ char* pos = ((size_t)(br - (char*)(p)) >= MIN_CHUNK_SIZE)?
+ br : br+alignment;
+ mchunkptr newp = (mchunkptr)pos;
+ size_t leadsize = pos - (char*)(p);
+ size_t newsize = chunksize(p) - leadsize;
+
+ if (is_mmapped(p)) { /* For mmapped chunks, just adjust offset */
+ newp->prev_foot = p->prev_foot + leadsize;
+ newp->head = (newsize|CINUSE_BIT);
+ }
+ else { /* Otherwise, give back leader, use the rest */
+ set_inuse(m, newp, newsize);
+ set_inuse(m, p, leadsize);
+ leader = chunk2mem(p);
+ }
+ p = newp;
+ }
+
+ /* Give back spare room at the end */
+ if (!is_mmapped(p)) {
+ size_t size = chunksize(p);
+ if (size > nb + MIN_CHUNK_SIZE) {
+ size_t remainder_size = size - nb;
+ mchunkptr remainder = chunk_plus_offset(p, nb);
+ set_inuse(m, p, nb);
+ set_inuse(m, remainder, remainder_size);
+ trailer = chunk2mem(remainder);
+ }
+ }
+
+ assert (chunksize(p) >= nb);
+ assert((((size_t)(chunk2mem(p))) % alignment) == 0);
+ check_inuse_chunk(m, p);
+ POSTACTION(m);
+ if (leader != 0) {
+ internal_free(m, leader);
+ }
+ if (trailer != 0) {
+ internal_free(m, trailer);
+ }
+ return chunk2mem(p);
+ }
+ }
+ return 0;
+}
+
+/* ------------------------ comalloc/coalloc support --------------------- */
+
+static void** ialloc(mstate m,
+ size_t n_elements,
+ size_t* sizes,
+ int opts,
+ void* chunks[]) {
+ /*
+ This provides common support for independent_X routines, handling
+ all of the combinations that can result.
+
+ The opts arg has:
+ bit 0 set if all elements are same size (using sizes[0])
+ bit 1 set if elements should be zeroed
+ */
+
+ size_t element_size; /* chunksize of each element, if all same */
+ size_t contents_size; /* total size of elements */
+ size_t array_size; /* request size of pointer array */
+ void* mem; /* malloced aggregate space */
+ mchunkptr p; /* corresponding chunk */
+ size_t remainder_size; /* remaining bytes while splitting */
+ void** marray; /* either "chunks" or malloced ptr array */
+ mchunkptr array_chunk; /* chunk for malloced ptr array */
+ flag_t was_enabled; /* to disable mmap */
+ size_t size;
+ size_t i;
+
+ /* compute array length, if needed */
+ if (chunks != 0) {
+ if (n_elements == 0)
+ return chunks; /* nothing to do */
+ marray = chunks;
+ array_size = 0;
+ }
+ else {
+ /* if empty req, must still return chunk representing empty array */
+ if (n_elements == 0)
+ return (void**)internal_malloc(m, 0);
+ marray = 0;
+ array_size = request2size(n_elements * (sizeof(void*)));
+ }
+
+ /* compute total element size */
+ if (opts & 0x1) { /* all-same-size */
+ element_size = request2size(*sizes);
+ contents_size = n_elements * element_size;
+ }
+ else { /* add up all the sizes */
+ element_size = 0;
+ contents_size = 0;
+ for (i = 0; i != n_elements; ++i)
+ contents_size += request2size(sizes[i]);
+ }
+
+ size = contents_size + array_size;
+
+ /*
+ Allocate the aggregate chunk. First disable direct-mmapping so
+ malloc won't use it, since we would not be able to later
+ free/realloc space internal to a segregated mmap region.
+ */
+ was_enabled = use_mmap(m);
+ disable_mmap(m);
+ mem = internal_malloc(m, size - CHUNK_OVERHEAD);
+ if (was_enabled)
+ enable_mmap(m);
+ if (mem == 0)
+ return 0;
+
+ if (PREACTION(m)) return 0;
+ p = mem2chunk(mem);
+ remainder_size = chunksize(p);
+
+ assert(!is_mmapped(p));
+
+ if (opts & 0x2) { /* optionally clear the elements */
+ memset((size_t*)mem, 0, remainder_size - SIZE_T_SIZE - array_size);
+ }
+
+ /* If not provided, allocate the pointer array as final part of chunk */
+ if (marray == 0) {
+ size_t array_chunk_size;
+ array_chunk = chunk_plus_offset(p, contents_size);
+ array_chunk_size = remainder_size - contents_size;
+ marray = (void**) (chunk2mem(array_chunk));
+ set_size_and_pinuse_of_inuse_chunk(m, array_chunk, array_chunk_size);
+ remainder_size = contents_size;
+ }
+
+ /* split out elements */
+ for (i = 0; ; ++i) {
+ marray[i] = chunk2mem(p);
+ if (i != n_elements-1) {
+ if (element_size != 0)
+ size = element_size;
+ else
+ size = request2size(sizes[i]);
+ remainder_size -= size;
+ set_size_and_pinuse_of_inuse_chunk(m, p, size);
+ p = chunk_plus_offset(p, size);
+ }
+ else { /* the final element absorbs any overallocation slop */
+ set_size_and_pinuse_of_inuse_chunk(m, p, remainder_size);
+ break;
+ }
+ }
+
+#if DEBUG
+ if (marray != chunks) {
+ /* final element must have exactly exhausted chunk */
+ if (element_size != 0) {
+ assert(remainder_size == element_size);
+ }
+ else {
+ assert(remainder_size == request2size(sizes[i]));
+ }
+ check_inuse_chunk(m, mem2chunk(marray));
+ }
+ for (i = 0; i != n_elements; ++i)
+ check_inuse_chunk(m, mem2chunk(marray[i]));
+
+#endif /* DEBUG */
+
+ POSTACTION(m);
+ return marray;
+}
+
+
+/* -------------------------- public routines ---------------------------- */
+
+#if !ONLY_MSPACES
+
+void* dlmalloc(size_t bytes) {
+ /*
+ Basic algorithm:
+ If a small request (< 256 bytes minus per-chunk overhead):
+ 1. If one exists, use a remainderless chunk in associated smallbin.
+ (Remainderless means that there are too few excess bytes to
+ represent as a chunk.)
+ 2. If it is big enough, use the dv chunk, which is normally the
+ chunk adjacent to the one used for the most recent small request.
+ 3. If one exists, split the smallest available chunk in a bin,
+ saving remainder in dv.
+ 4. If it is big enough, use the top chunk.
+ 5. If available, get memory from system and use it
+ Otherwise, for a large request:
+ 1. Find the smallest available binned chunk that fits, and use it
+ if it is better fitting than dv chunk, splitting if necessary.
+ 2. If better fitting than any binned chunk, use the dv chunk.
+ 3. If it is big enough, use the top chunk.
+ 4. If request size >= mmap threshold, try to directly mmap this chunk.
+ 5. If available, get memory from system and use it
+
+ The ugly goto's here ensure that postaction occurs along all paths.
+ */
+
+ if (!PREACTION(gm)) {
+ void* mem;
+ size_t nb;
+ if (bytes <= MAX_SMALL_REQUEST) {
+ bindex_t idx;
+ binmap_t smallbits;
+ nb = (bytes < MIN_REQUEST)? MIN_CHUNK_SIZE : pad_request(bytes);
+ idx = small_index(nb);
+ smallbits = gm->smallmap >> idx;
+
+ if ((smallbits & 0x3U) != 0) { /* Remainderless fit to a smallbin. */
+ mchunkptr b, p;
+ idx += ~smallbits & 1; /* Uses next bin if idx empty */
+ b = smallbin_at(gm, idx);
+ p = b->fd;
+ assert(chunksize(p) == small_index2size(idx));
+ unlink_first_small_chunk(gm, b, p, idx);
+ set_inuse_and_pinuse(gm, p, small_index2size(idx));
+ mem = chunk2mem(p);
+ check_malloced_chunk(gm, mem, nb);
+ goto postaction;
+ }
+
+ else if (nb > gm->dvsize) {
+ if (smallbits != 0) { /* Use chunk in next nonempty smallbin */
+ mchunkptr b, p, r;
+ size_t rsize;
+ bindex_t i;
+ binmap_t leftbits = (smallbits << idx) & left_bits(idx2bit(idx));
+ binmap_t leastbit = least_bit(leftbits);
+ compute_bit2idx(leastbit, i);
+ b = smallbin_at(gm, i);
+ p = b->fd;
+ assert(chunksize(p) == small_index2size(i));
+ unlink_first_small_chunk(gm, b, p, i);
+ rsize = small_index2size(i) - nb;
+ /* Fit here cannot be remainderless if 4byte sizes */
+ if (SIZE_T_SIZE != 4 && rsize < MIN_CHUNK_SIZE)
+ set_inuse_and_pinuse(gm, p, small_index2size(i));
+ else {
+ set_size_and_pinuse_of_inuse_chunk(gm, p, nb);
+ r = chunk_plus_offset(p, nb);
+ set_size_and_pinuse_of_free_chunk(r, rsize);
+ replace_dv(gm, r, rsize);
+ }
+ mem = chunk2mem(p);
+ check_malloced_chunk(gm, mem, nb);
+ goto postaction;
+ }
+
+ else if (gm->treemap != 0 && (mem = tmalloc_small(gm, nb)) != 0) {
+ check_malloced_chunk(gm, mem, nb);
+ goto postaction;
+ }
+ }
+ }
+ else if (bytes >= MAX_REQUEST)
+ nb = MAX_SIZE_T; /* Too big to allocate. Force failure (in sys alloc) */
+ else {
+ nb = pad_request(bytes);
+ if (gm->treemap != 0 && (mem = tmalloc_large(gm, nb)) != 0) {
+ check_malloced_chunk(gm, mem, nb);
+ goto postaction;
+ }
+ }
+
+ if (nb <= gm->dvsize) {
+ size_t rsize = gm->dvsize - nb;
+ mchunkptr p = gm->dv;
+ if (rsize >= MIN_CHUNK_SIZE) { /* split dv */
+ mchunkptr r = gm->dv = chunk_plus_offset(p, nb);
+ gm->dvsize = rsize;
+ set_size_and_pinuse_of_free_chunk(r, rsize);
+ set_size_and_pinuse_of_inuse_chunk(gm, p, nb);
+ }
+ else { /* exhaust dv */
+ size_t dvs = gm->dvsize;
+ gm->dvsize = 0;
+ gm->dv = 0;
+ set_inuse_and_pinuse(gm, p, dvs);
+ }
+ mem = chunk2mem(p);
+ check_malloced_chunk(gm, mem, nb);
+ goto postaction;
+ }
+
+ else if (nb < gm->topsize) { /* Split top */
+ size_t rsize = gm->topsize -= nb;
+ mchunkptr p = gm->top;
+ mchunkptr r = gm->top = chunk_plus_offset(p, nb);
+ r->head = rsize | PINUSE_BIT;
+ set_size_and_pinuse_of_inuse_chunk(gm, p, nb);
+ mem = chunk2mem(p);
+ check_top_chunk(gm, gm->top);
+ check_malloced_chunk(gm, mem, nb);
+ goto postaction;
+ }
+
+ mem = sys_alloc(gm, nb);
+
+ postaction:
+ POSTACTION(gm);
+ return mem;
+ }
+
+ return 0;
+}
+
+void dlfree(void* mem) {
+ /*
+ Consolidate freed chunks with preceeding or succeeding bordering
+ free chunks, if they exist, and then place in a bin. Intermixed
+ with special cases for top, dv, mmapped chunks, and usage errors.
+ */
+
+ if (mem != 0) {
+ mchunkptr p = mem2chunk(mem);
+#if FOOTERS
+ mstate fm = get_mstate_for(p);
+ if (!ok_magic(fm)) {
+ USAGE_ERROR_ACTION(fm, p);
+ return;
+ }
+#else /* FOOTERS */
+#define fm gm
+#endif /* FOOTERS */
+ if (!PREACTION(fm)) {
+ check_inuse_chunk(fm, p);
+ if (RTCHECK(ok_address(fm, p) && ok_cinuse(p))) {
+ size_t psize = chunksize(p);
+ mchunkptr next = chunk_plus_offset(p, psize);
+ if (!pinuse(p)) {
+ size_t prevsize = p->prev_foot;
+ if ((prevsize & IS_MMAPPED_BIT) != 0) {
+ prevsize &= ~IS_MMAPPED_BIT;
+ psize += prevsize + MMAP_FOOT_PAD;
+ if (CALL_MUNMAP((char*)p - prevsize, psize) == 0)
+ fm->footprint -= psize;
+ goto postaction;
+ }
+ else {
+ mchunkptr prev = chunk_minus_offset(p, prevsize);
+ psize += prevsize;
+ p = prev;
+ if (RTCHECK(ok_address(fm, prev))) { /* consolidate backward */
+ if (p != fm->dv) {
+ unlink_chunk(fm, p, prevsize);
+ }
+ else if ((next->head & INUSE_BITS) == INUSE_BITS) {
+ fm->dvsize = psize;
+ set_free_with_pinuse(p, psize, next);
+ goto postaction;
+ }
+ }
+ else
+ goto erroraction;
+ }
+ }
+
+ if (RTCHECK(ok_next(p, next) && ok_pinuse(next))) {
+ if (!cinuse(next)) { /* consolidate forward */
+ if (next == fm->top) {
+ size_t tsize = fm->topsize += psize;
+ fm->top = p;
+ p->head = tsize | PINUSE_BIT;
+ if (p == fm->dv) {
+ fm->dv = 0;
+ fm->dvsize = 0;
+ }
+ if (should_trim(fm, tsize))
+ sys_trim(fm, 0);
+ goto postaction;
+ }
+ else if (next == fm->dv) {
+ size_t dsize = fm->dvsize += psize;
+ fm->dv = p;
+ set_size_and_pinuse_of_free_chunk(p, dsize);
+ goto postaction;
+ }
+ else {
+ size_t nsize = chunksize(next);
+ psize += nsize;
+ unlink_chunk(fm, next, nsize);
+ set_size_and_pinuse_of_free_chunk(p, psize);
+ if (p == fm->dv) {
+ fm->dvsize = psize;
+ goto postaction;
+ }
+ }
+ }
+ else
+ set_free_with_pinuse(p, psize, next);
+ insert_chunk(fm, p, psize);
+ check_free_chunk(fm, p);
+ goto postaction;
+ }
+ }
+ erroraction:
+ USAGE_ERROR_ACTION(fm, p);
+ postaction:
+ POSTACTION(fm);
+ }
+ }
+#if !FOOTERS
+#undef fm
+#endif /* FOOTERS */
+}
+
+void* dlcalloc(size_t n_elements, size_t elem_size) {
+ void *mem;
+ if (n_elements && MAX_SIZE_T / n_elements < elem_size) {
+ /* Fail on overflow */
+ MALLOC_FAILURE_ACTION;
+ return NULL;
+ }
+ elem_size *= n_elements;
+ mem = dlmalloc(elem_size);
+ if (mem && calloc_must_clear(mem2chunk(mem)))
+ memset(mem, 0, elem_size);
+ return mem;
+}
+
+void* dlrealloc(void* oldmem, size_t bytes) {
+ if (oldmem == 0)
+ return dlmalloc(bytes);
+#ifdef REALLOC_ZERO_BYTES_FREES
+ if (bytes == 0) {
+ dlfree(oldmem);
+ return 0;
+ }
+#endif /* REALLOC_ZERO_BYTES_FREES */
+ else {
+#if ! FOOTERS
+ mstate m = gm;
+#else /* FOOTERS */
+ mstate m = get_mstate_for(mem2chunk(oldmem));
+ if (!ok_magic(m)) {
+ USAGE_ERROR_ACTION(m, oldmem);
+ return 0;
+ }
+#endif /* FOOTERS */
+ return internal_realloc(m, oldmem, bytes);
+ }
+}
+
+void* dlmemalign(size_t alignment, size_t bytes) {
+ return internal_memalign(gm, alignment, bytes);
+}
+
+void** dlindependent_calloc(size_t n_elements, size_t elem_size,
+ void* chunks[]) {
+ size_t sz = elem_size; /* serves as 1-element array */
+ return ialloc(gm, n_elements, &sz, 3, chunks);
+}
+
+void** dlindependent_comalloc(size_t n_elements, size_t sizes[],
+ void* chunks[]) {
+ return ialloc(gm, n_elements, sizes, 0, chunks);
+}
+
+void* dlvalloc(size_t bytes) {
+ size_t pagesz;
+ init_mparams();
+ pagesz = mparams.page_size;
+ return dlmemalign(pagesz, bytes);
+}
+
+void* dlpvalloc(size_t bytes) {
+ size_t pagesz;
+ init_mparams();
+ pagesz = mparams.page_size;
+ return dlmemalign(pagesz, (bytes + pagesz - SIZE_T_ONE) & ~(pagesz - SIZE_T_ONE));
+}
+
+int dlmalloc_trim(size_t pad) {
+ int result = 0;
+ if (!PREACTION(gm)) {
+ result = sys_trim(gm, pad);
+ POSTACTION(gm);
+ }
+ return result;
+}
+
+size_t dlmalloc_footprint(void) {
+ return gm->footprint;
+}
+
+#if USE_MAX_ALLOWED_FOOTPRINT
+size_t dlmalloc_max_allowed_footprint(void) {
+ return gm->max_allowed_footprint;
+}
+
+void dlmalloc_set_max_allowed_footprint(size_t bytes) {
+ if (bytes > gm->footprint) {
+ /* Increase the size in multiples of the granularity,
+ * which is the smallest unit we request from the system.
+ */
+ gm->max_allowed_footprint = gm->footprint +
+ granularity_align(bytes - gm->footprint);
+ }
+ else {
+ //TODO: allow for reducing the max footprint
+ gm->max_allowed_footprint = gm->footprint;
+ }
+}
+#endif
+
+size_t dlmalloc_max_footprint(void) {
+ return gm->max_footprint;
+}
+
+#if !NO_MALLINFO
+struct mallinfo dlmallinfo(void) {
+ return internal_mallinfo(gm);
+}
+#endif /* NO_MALLINFO */
+
+void dlmalloc_stats() {
+ internal_malloc_stats(gm);
+}
+
+size_t dlmalloc_usable_size(void* mem) {
+ if (mem != 0) {
+ mchunkptr p = mem2chunk(mem);
+ if (cinuse(p))
+ return chunksize(p) - overhead_for(p);
+ }
+ return 0;
+}
+
+int dlmallopt(int param_number, int value) {
+ return change_mparam(param_number, value);
+}
+
+#endif /* !ONLY_MSPACES */
+
+/* ----------------------------- user mspaces ---------------------------- */
+
+#if MSPACES
+
+static mstate init_user_mstate(char* tbase, size_t tsize) {
+ size_t msize = pad_request(sizeof(struct malloc_state));
+ mchunkptr mn;
+ mchunkptr msp = align_as_chunk(tbase);
+ mstate m = (mstate)(chunk2mem(msp));
+ memset(m, 0, msize);
+ INITIAL_LOCK(&m->mutex);
+ msp->head = (msize|PINUSE_BIT|CINUSE_BIT);
+ m->seg.base = m->least_addr = tbase;
+ m->seg.size = m->footprint = m->max_footprint = tsize;
+#if USE_MAX_ALLOWED_FOOTPRINT
+ m->max_allowed_footprint = MAX_SIZE_T;
+#endif
+ m->magic = mparams.magic;
+ m->mflags = mparams.default_mflags;
+ disable_contiguous(m);
+ init_bins(m);
+ mn = next_chunk(mem2chunk(m));
+ init_top(m, mn, (size_t)((tbase + tsize) - (char*)mn) - TOP_FOOT_SIZE);
+ check_top_chunk(m, m->top);
+ return m;
+}
+
+mspace create_mspace(size_t capacity, int locked) {
+ mstate m = 0;
+ size_t msize = pad_request(sizeof(struct malloc_state));
+ init_mparams(); /* Ensure pagesize etc initialized */
+
+ if (capacity < (size_t) -(msize + TOP_FOOT_SIZE + mparams.page_size)) {
+ size_t rs = ((capacity == 0)? mparams.granularity :
+ (capacity + TOP_FOOT_SIZE + msize));
+ size_t tsize = granularity_align(rs);
+ char* tbase = (char*)(CALL_MMAP(tsize));
+ if (tbase != CMFAIL) {
+ m = init_user_mstate(tbase, tsize);
+ m->seg.sflags = IS_MMAPPED_BIT;
+ set_lock(m, locked);
+ }
+ }
+ return (mspace)m;
+}
+
+mspace create_mspace_with_base(void* base, size_t capacity, int locked) {
+ mstate m = 0;
+ size_t msize = pad_request(sizeof(struct malloc_state));
+ init_mparams(); /* Ensure pagesize etc initialized */
+
+ if (capacity > msize + TOP_FOOT_SIZE &&
+ capacity < (size_t) -(msize + TOP_FOOT_SIZE + mparams.page_size)) {
+ m = init_user_mstate((char*)base, capacity);
+ m->seg.sflags = EXTERN_BIT;
+ set_lock(m, locked);
+ }
+ return (mspace)m;
+}
+
+size_t destroy_mspace(mspace msp) {
+ size_t freed = 0;
+ mstate ms = (mstate)msp;
+ if (ok_magic(ms)) {
+ msegmentptr sp = &ms->seg;
+ while (sp != 0) {
+ char* base = sp->base;
+ size_t size = sp->size;
+ flag_t flag = sp->sflags;
+ sp = sp->next;
+ if ((flag & IS_MMAPPED_BIT) && !(flag & EXTERN_BIT) &&
+ CALL_MUNMAP(base, size) == 0)
+ freed += size;
+ }
+ }
+ else {
+ USAGE_ERROR_ACTION(ms,ms);
+ }
+ return freed;
+}
+
+/*
+ mspace versions of routines are near-clones of the global
+ versions. This is not so nice but better than the alternatives.
+*/
+
+
+void* mspace_malloc(mspace msp, size_t bytes) {
+ mstate ms = (mstate)msp;
+ if (!ok_magic(ms)) {
+ USAGE_ERROR_ACTION(ms,ms);
+ return 0;
+ }
+ if (!PREACTION(ms)) {
+ void* mem;
+ size_t nb;
+ if (bytes <= MAX_SMALL_REQUEST) {
+ bindex_t idx;
+ binmap_t smallbits;
+ nb = (bytes < MIN_REQUEST)? MIN_CHUNK_SIZE : pad_request(bytes);
+ idx = small_index(nb);
+ smallbits = ms->smallmap >> idx;
+
+ if ((smallbits & 0x3U) != 0) { /* Remainderless fit to a smallbin. */
+ mchunkptr b, p;
+ idx += ~smallbits & 1; /* Uses next bin if idx empty */
+ b = smallbin_at(ms, idx);
+ p = b->fd;
+ assert(chunksize(p) == small_index2size(idx));
+ unlink_first_small_chunk(ms, b, p, idx);
+ set_inuse_and_pinuse(ms, p, small_index2size(idx));
+ mem = chunk2mem(p);
+ check_malloced_chunk(ms, mem, nb);
+ goto postaction;
+ }
+
+ else if (nb > ms->dvsize) {
+ if (smallbits != 0) { /* Use chunk in next nonempty smallbin */
+ mchunkptr b, p, r;
+ size_t rsize;
+ bindex_t i;
+ binmap_t leftbits = (smallbits << idx) & left_bits(idx2bit(idx));
+ binmap_t leastbit = least_bit(leftbits);
+ compute_bit2idx(leastbit, i);
+ b = smallbin_at(ms, i);
+ p = b->fd;
+ assert(chunksize(p) == small_index2size(i));
+ unlink_first_small_chunk(ms, b, p, i);
+ rsize = small_index2size(i) - nb;
+ /* Fit here cannot be remainderless if 4byte sizes */
+ if (SIZE_T_SIZE != 4 && rsize < MIN_CHUNK_SIZE)
+ set_inuse_and_pinuse(ms, p, small_index2size(i));
+ else {
+ set_size_and_pinuse_of_inuse_chunk(ms, p, nb);
+ r = chunk_plus_offset(p, nb);
+ set_size_and_pinuse_of_free_chunk(r, rsize);
+ replace_dv(ms, r, rsize);
+ }
+ mem = chunk2mem(p);
+ check_malloced_chunk(ms, mem, nb);
+ goto postaction;
+ }
+
+ else if (ms->treemap != 0 && (mem = tmalloc_small(ms, nb)) != 0) {
+ check_malloced_chunk(ms, mem, nb);
+ goto postaction;
+ }
+ }
+ }
+ else if (bytes >= MAX_REQUEST)
+ nb = MAX_SIZE_T; /* Too big to allocate. Force failure (in sys alloc) */
+ else {
+ nb = pad_request(bytes);
+ if (ms->treemap != 0 && (mem = tmalloc_large(ms, nb)) != 0) {
+ check_malloced_chunk(ms, mem, nb);
+ goto postaction;
+ }
+ }
+
+ if (nb <= ms->dvsize) {
+ size_t rsize = ms->dvsize - nb;
+ mchunkptr p = ms->dv;
+ if (rsize >= MIN_CHUNK_SIZE) { /* split dv */
+ mchunkptr r = ms->dv = chunk_plus_offset(p, nb);
+ ms->dvsize = rsize;
+ set_size_and_pinuse_of_free_chunk(r, rsize);
+ set_size_and_pinuse_of_inuse_chunk(ms, p, nb);
+ }
+ else { /* exhaust dv */
+ size_t dvs = ms->dvsize;
+ ms->dvsize = 0;
+ ms->dv = 0;
+ set_inuse_and_pinuse(ms, p, dvs);
+ }
+ mem = chunk2mem(p);
+ check_malloced_chunk(ms, mem, nb);
+ goto postaction;
+ }
+
+ else if (nb < ms->topsize) { /* Split top */
+ size_t rsize = ms->topsize -= nb;
+ mchunkptr p = ms->top;
+ mchunkptr r = ms->top = chunk_plus_offset(p, nb);
+ r->head = rsize | PINUSE_BIT;
+ set_size_and_pinuse_of_inuse_chunk(ms, p, nb);
+ mem = chunk2mem(p);
+ check_top_chunk(ms, ms->top);
+ check_malloced_chunk(ms, mem, nb);
+ goto postaction;
+ }
+
+ mem = sys_alloc(ms, nb);
+
+ postaction:
+ POSTACTION(ms);
+ return mem;
+ }
+
+ return 0;
+}
+
+void mspace_free(mspace msp, void* mem) {
+ if (mem != 0) {
+ mchunkptr p = mem2chunk(mem);
+#if FOOTERS
+ mstate fm = get_mstate_for(p);
+#else /* FOOTERS */
+ mstate fm = (mstate)msp;
+#endif /* FOOTERS */
+ if (!ok_magic(fm)) {
+ USAGE_ERROR_ACTION(fm, p);
+ return;
+ }
+ if (!PREACTION(fm)) {
+ check_inuse_chunk(fm, p);
+ if (RTCHECK(ok_address(fm, p) && ok_cinuse(p))) {
+ size_t psize = chunksize(p);
+ mchunkptr next = chunk_plus_offset(p, psize);
+ if (!pinuse(p)) {
+ size_t prevsize = p->prev_foot;
+ if ((prevsize & IS_MMAPPED_BIT) != 0) {
+ prevsize &= ~IS_MMAPPED_BIT;
+ psize += prevsize + MMAP_FOOT_PAD;
+ if (CALL_MUNMAP((char*)p - prevsize, psize) == 0)
+ fm->footprint -= psize;
+ goto postaction;
+ }
+ else {
+ mchunkptr prev = chunk_minus_offset(p, prevsize);
+ psize += prevsize;
+ p = prev;
+ if (RTCHECK(ok_address(fm, prev))) { /* consolidate backward */
+ if (p != fm->dv) {
+ unlink_chunk(fm, p, prevsize);
+ }
+ else if ((next->head & INUSE_BITS) == INUSE_BITS) {
+ fm->dvsize = psize;
+ set_free_with_pinuse(p, psize, next);
+ goto postaction;
+ }
+ }
+ else
+ goto erroraction;
+ }
+ }
+
+ if (RTCHECK(ok_next(p, next) && ok_pinuse(next))) {
+ if (!cinuse(next)) { /* consolidate forward */
+ if (next == fm->top) {
+ size_t tsize = fm->topsize += psize;
+ fm->top = p;
+ p->head = tsize | PINUSE_BIT;
+ if (p == fm->dv) {
+ fm->dv = 0;
+ fm->dvsize = 0;
+ }
+ if (should_trim(fm, tsize))
+ sys_trim(fm, 0);
+ goto postaction;
+ }
+ else if (next == fm->dv) {
+ size_t dsize = fm->dvsize += psize;
+ fm->dv = p;
+ set_size_and_pinuse_of_free_chunk(p, dsize);
+ goto postaction;
+ }
+ else {
+ size_t nsize = chunksize(next);
+ psize += nsize;
+ unlink_chunk(fm, next, nsize);
+ set_size_and_pinuse_of_free_chunk(p, psize);
+ if (p == fm->dv) {
+ fm->dvsize = psize;
+ goto postaction;
+ }
+ }
+ }
+ else
+ set_free_with_pinuse(p, psize, next);
+ insert_chunk(fm, p, psize);
+ check_free_chunk(fm, p);
+ goto postaction;
+ }
+ }
+ erroraction:
+ USAGE_ERROR_ACTION(fm, p);
+ postaction:
+ POSTACTION(fm);
+ }
+ }
+}
+
+void* mspace_calloc(mspace msp, size_t n_elements, size_t elem_size) {
+ void *mem;
+ mstate ms = (mstate)msp;
+ if (!ok_magic(ms)) {
+ USAGE_ERROR_ACTION(ms,ms);
+ return 0;
+ }
+ if (n_elements && MAX_SIZE_T / n_elements < elem_size) {
+ /* Fail on overflow */
+ MALLOC_FAILURE_ACTION;
+ return NULL;
+ }
+ elem_size *= n_elements;
+ mem = internal_malloc(ms, elem_size);
+ if (mem && calloc_must_clear(mem2chunk(mem)))
+ memset(mem, 0, elem_size);
+ return mem;
+}
+
+void* mspace_realloc(mspace msp, void* oldmem, size_t bytes) {
+ if (oldmem == 0)
+ return mspace_malloc(msp, bytes);
+#ifdef REALLOC_ZERO_BYTES_FREES
+ if (bytes == 0) {
+ mspace_free(msp, oldmem);
+ return 0;
+ }
+#endif /* REALLOC_ZERO_BYTES_FREES */
+ else {
+#if FOOTERS
+ mchunkptr p = mem2chunk(oldmem);
+ mstate ms = get_mstate_for(p);
+#else /* FOOTERS */
+ mstate ms = (mstate)msp;
+#endif /* FOOTERS */
+ if (!ok_magic(ms)) {
+ USAGE_ERROR_ACTION(ms,ms);
+ return 0;
+ }
+ return internal_realloc(ms, oldmem, bytes);
+ }
+}
+
+#if ANDROID
+void* mspace_merge_objects(mspace msp, void* mema, void* memb)
+{
+ /* PREACTION/POSTACTION aren't necessary because we are only
+ modifying fields of inuse chunks owned by the current thread, in
+ which case no other malloc operations can touch them.
+ */
+ if (mema == NULL || memb == NULL) {
+ return NULL;
+ }
+ mchunkptr pa = mem2chunk(mema);
+ mchunkptr pb = mem2chunk(memb);
+
+#if FOOTERS
+ mstate fm = get_mstate_for(pa);
+#else /* FOOTERS */
+ mstate fm = (mstate)msp;
+#endif /* FOOTERS */
+ if (!ok_magic(fm)) {
+ USAGE_ERROR_ACTION(fm, pa);
+ return NULL;
+ }
+ check_inuse_chunk(fm, pa);
+ if (RTCHECK(ok_address(fm, pa) && ok_cinuse(pa))) {
+ if (next_chunk(pa) != pb) {
+ /* Since pb may not be in fm, we can't check ok_address(fm, pb);
+ since ok_cinuse(pb) would be unsafe before an address check,
+ return NULL rather than invoke USAGE_ERROR_ACTION if pb is not
+ in use or is a bogus address.
+ */
+ return NULL;
+ }
+ /* Since b follows a, they share the mspace. */
+#if FOOTERS
+ assert(fm == get_mstate_for(pb));
+#endif /* FOOTERS */
+ check_inuse_chunk(fm, pb);
+ if (RTCHECK(ok_address(fm, pb) && ok_cinuse(pb))) {
+ size_t sz = chunksize(pb);
+ pa->head += sz;
+ /* Make sure pa still passes. */
+ check_inuse_chunk(fm, pa);
+ return mema;
+ }
+ else {
+ USAGE_ERROR_ACTION(fm, pb);
+ return NULL;
+ }
+ }
+ else {
+ USAGE_ERROR_ACTION(fm, pa);
+ return NULL;
+ }
+}
+#endif /* ANDROID */
+
+void* mspace_memalign(mspace msp, size_t alignment, size_t bytes) {
+ mstate ms = (mstate)msp;
+ if (!ok_magic(ms)) {
+ USAGE_ERROR_ACTION(ms,ms);
+ return 0;
+ }
+ return internal_memalign(ms, alignment, bytes);
+}
+
+void** mspace_independent_calloc(mspace msp, size_t n_elements,
+ size_t elem_size, void* chunks[]) {
+ size_t sz = elem_size; /* serves as 1-element array */
+ mstate ms = (mstate)msp;
+ if (!ok_magic(ms)) {
+ USAGE_ERROR_ACTION(ms,ms);
+ return 0;
+ }
+ return ialloc(ms, n_elements, &sz, 3, chunks);
+}
+
+void** mspace_independent_comalloc(mspace msp, size_t n_elements,
+ size_t sizes[], void* chunks[]) {
+ mstate ms = (mstate)msp;
+ if (!ok_magic(ms)) {
+ USAGE_ERROR_ACTION(ms,ms);
+ return 0;
+ }
+ return ialloc(ms, n_elements, sizes, 0, chunks);
+}
+
+int mspace_trim(mspace msp, size_t pad) {
+ int result = 0;
+ mstate ms = (mstate)msp;
+ if (ok_magic(ms)) {
+ if (!PREACTION(ms)) {
+ result = sys_trim(ms, pad);
+ POSTACTION(ms);
+ }
+ }
+ else {
+ USAGE_ERROR_ACTION(ms,ms);
+ }
+ return result;
+}
+
+void mspace_malloc_stats(mspace msp) {
+ mstate ms = (mstate)msp;
+ if (ok_magic(ms)) {
+ internal_malloc_stats(ms);
+ }
+ else {
+ USAGE_ERROR_ACTION(ms,ms);
+ }
+}
+
+size_t mspace_footprint(mspace msp) {
+ size_t result;
+ mstate ms = (mstate)msp;
+ if (ok_magic(ms)) {
+ result = ms->footprint;
+ }
+ else {
+ USAGE_ERROR_ACTION(ms,ms);
+ }
+ return result;
+}
+
+#if USE_MAX_ALLOWED_FOOTPRINT
+size_t mspace_max_allowed_footprint(mspace msp) {
+ size_t result;
+ mstate ms = (mstate)msp;
+ if (ok_magic(ms)) {
+ result = ms->max_allowed_footprint;
+ }
+ else {
+ USAGE_ERROR_ACTION(ms,ms);
+ }
+ return result;
+}
+
+void mspace_set_max_allowed_footprint(mspace msp, size_t bytes) {
+ mstate ms = (mstate)msp;
+ if (ok_magic(ms)) {
+ if (bytes > ms->footprint) {
+ /* Increase the size in multiples of the granularity,
+ * which is the smallest unit we request from the system.
+ */
+ ms->max_allowed_footprint = ms->footprint +
+ granularity_align(bytes - ms->footprint);
+ }
+ else {
+ //TODO: allow for reducing the max footprint
+ ms->max_allowed_footprint = ms->footprint;
+ }
+ }
+ else {
+ USAGE_ERROR_ACTION(ms,ms);
+ }
+}
+#endif
+
+size_t mspace_max_footprint(mspace msp) {
+ size_t result;
+ mstate ms = (mstate)msp;
+ if (ok_magic(ms)) {
+ result = ms->max_footprint;
+ }
+ else {
+ USAGE_ERROR_ACTION(ms,ms);
+ }
+ return result;
+}
+
+
+#if !NO_MALLINFO
+struct mallinfo mspace_mallinfo(mspace msp) {
+ mstate ms = (mstate)msp;
+ if (!ok_magic(ms)) {
+ USAGE_ERROR_ACTION(ms,ms);
+ }
+ return internal_mallinfo(ms);
+}
+#endif /* NO_MALLINFO */
+
+int mspace_mallopt(int param_number, int value) {
+ return change_mparam(param_number, value);
+}
+
+#endif /* MSPACES */
+
+#if MSPACES && ONLY_MSPACES
+void mspace_walk_free_pages(mspace msp,
+ void(*handler)(void *start, void *end, void *arg), void *harg)
+{
+ mstate m = (mstate)msp;
+ if (!ok_magic(m)) {
+ USAGE_ERROR_ACTION(m,m);
+ return;
+ }
+#else
+void dlmalloc_walk_free_pages(void(*handler)(void *start, void *end, void *arg),
+ void *harg)
+{
+ mstate m = (mstate)gm;
+#endif
+ if (!PREACTION(m)) {
+ if (is_initialized(m)) {
+ msegmentptr s = &m->seg;
+ while (s != 0) {
+ mchunkptr p = align_as_chunk(s->base);
+ while (segment_holds(s, p) &&
+ p != m->top && p->head != FENCEPOST_HEAD) {
+ void *chunkptr, *userptr;
+ size_t chunklen, userlen;
+ chunkptr = p;
+ chunklen = chunksize(p);
+ if (!cinuse(p)) {
+ void *start;
+ if (is_small(chunklen)) {
+ start = (void *)(p + 1);
+ }
+ else {
+ start = (void *)((tchunkptr)p + 1);
+ }
+ handler(start, next_chunk(p), harg);
+ }
+ p = next_chunk(p);
+ }
+ if (p == m->top) {
+ handler((void *)(p + 1), next_chunk(p), harg);
+ }
+ s = s->next;
+ }
+ }
+ POSTACTION(m);
+ }
+}
+
+
+#if MSPACES && ONLY_MSPACES
+void mspace_walk_heap(mspace msp,
+ void(*handler)(const void *chunkptr, size_t chunklen,
+ const void *userptr, size_t userlen,
+ void *arg),
+ void *harg)
+{
+ msegmentptr s;
+ mstate m = (mstate)msp;
+ if (!ok_magic(m)) {
+ USAGE_ERROR_ACTION(m,m);
+ return;
+ }
+#else
+void dlmalloc_walk_heap(void(*handler)(const void *chunkptr, size_t chunklen,
+ const void *userptr, size_t userlen,
+ void *arg),
+ void *harg)
+{
+ msegmentptr s;
+ mstate m = (mstate)gm;
+#endif
+
+ s = &m->seg;
+ while (s != 0) {
+ mchunkptr p = align_as_chunk(s->base);
+ while (segment_holds(s, p) &&
+ p != m->top && p->head != FENCEPOST_HEAD) {
+ void *chunkptr, *userptr;
+ size_t chunklen, userlen;
+ chunkptr = p;
+ chunklen = chunksize(p);
+ if (cinuse(p)) {
+ userptr = chunk2mem(p);
+ userlen = chunklen - overhead_for(p);
+ }
+ else {
+ userptr = NULL;
+ userlen = 0;
+ }
+ handler(chunkptr, chunklen, userptr, userlen, harg);
+ p = next_chunk(p);
+ }
+ if (p == m->top) {
+ /* The top chunk is just a big free chunk for our purposes.
+ */
+ handler(m->top, m->topsize, NULL, 0, harg);
+ }
+ s = s->next;
+ }
+}
+
+/* -------------------- Alternative MORECORE functions ------------------- */
+
+/*
+ Guidelines for creating a custom version of MORECORE:
+
+ * For best performance, MORECORE should allocate in multiples of pagesize.
+ * MORECORE may allocate more memory than requested. (Or even less,
+ but this will usually result in a malloc failure.)
+ * MORECORE must not allocate memory when given argument zero, but
+ instead return one past the end address of memory from previous
+ nonzero call.
+ * For best performance, consecutive calls to MORECORE with positive
+ arguments should return increasing addresses, indicating that
+ space has been contiguously extended.
+ * Even though consecutive calls to MORECORE need not return contiguous
+ addresses, it must be OK for malloc'ed chunks to span multiple
+ regions in those cases where they do happen to be contiguous.
+ * MORECORE need not handle negative arguments -- it may instead
+ just return MFAIL when given negative arguments.
+ Negative arguments are always multiples of pagesize. MORECORE
+ must not misinterpret negative args as large positive unsigned
+ args. You can suppress all such calls from even occurring by defining
+ MORECORE_CANNOT_TRIM,
+
+ As an example alternative MORECORE, here is a custom allocator
+ kindly contributed for pre-OSX macOS. It uses virtually but not
+ necessarily physically contiguous non-paged memory (locked in,
+ present and won't get swapped out). You can use it by uncommenting
+ this section, adding some #includes, and setting up the appropriate
+ defines above:
+
+ #define MORECORE osMoreCore
+
+ There is also a shutdown routine that should somehow be called for
+ cleanup upon program exit.
+
+ #define MAX_POOL_ENTRIES 100
+ #define MINIMUM_MORECORE_SIZE (64 * 1024U)
+ static int next_os_pool;
+ void *our_os_pools[MAX_POOL_ENTRIES];
+
+ void *osMoreCore(int size)
+ {
+ void *ptr = 0;
+ static void *sbrk_top = 0;
+
+ if (size > 0)
+ {
+ if (size < MINIMUM_MORECORE_SIZE)
+ size = MINIMUM_MORECORE_SIZE;
+ if (CurrentExecutionLevel() == kTaskLevel)
+ ptr = PoolAllocateResident(size + RM_PAGE_SIZE, 0);
+ if (ptr == 0)
+ {
+ return (void *) MFAIL;
+ }
+ // save ptrs so they can be freed during cleanup
+ our_os_pools[next_os_pool] = ptr;
+ next_os_pool++;
+ ptr = (void *) ((((size_t) ptr) + RM_PAGE_MASK) & ~RM_PAGE_MASK);
+ sbrk_top = (char *) ptr + size;
+ return ptr;
+ }
+ else if (size < 0)
+ {
+ // we don't currently support shrink behavior
+ return (void *) MFAIL;
+ }
+ else
+ {
+ return sbrk_top;
+ }
+ }
+
+ // cleanup any allocated memory pools
+ // called as last thing before shutting down driver
+
+ void osCleanupMem(void)
+ {
+ void **ptr;
+
+ for (ptr = our_os_pools; ptr < &our_os_pools[MAX_POOL_ENTRIES]; ptr++)
+ if (*ptr)
+ {
+ PoolDeallocate(*ptr);
+ *ptr = 0;
+ }
+ }
+
+*/
+
+
+/* -----------------------------------------------------------------------
+History:
+ V2.8.3 Thu Sep 22 11:16:32 2005 Doug Lea (dl at gee)
+ * Add max_footprint functions
+ * Ensure all appropriate literals are size_t
+ * Fix conditional compilation problem for some #define settings
+ * Avoid concatenating segments with the one provided
+ in create_mspace_with_base
+ * Rename some variables to avoid compiler shadowing warnings
+ * Use explicit lock initialization.
+ * Better handling of sbrk interference.
+ * Simplify and fix segment insertion, trimming and mspace_destroy
+ * Reinstate REALLOC_ZERO_BYTES_FREES option from 2.7.x
+ * Thanks especially to Dennis Flanagan for help on these.
+
+ V2.8.2 Sun Jun 12 16:01:10 2005 Doug Lea (dl at gee)
+ * Fix memalign brace error.
+
+ V2.8.1 Wed Jun 8 16:11:46 2005 Doug Lea (dl at gee)
+ * Fix improper #endif nesting in C++
+ * Add explicit casts needed for C++
+
+ V2.8.0 Mon May 30 14:09:02 2005 Doug Lea (dl at gee)
+ * Use trees for large bins
+ * Support mspaces
+ * Use segments to unify sbrk-based and mmap-based system allocation,
+ removing need for emulation on most platforms without sbrk.
+ * Default safety checks
+ * Optional footer checks. Thanks to William Robertson for the idea.
+ * Internal code refactoring
+ * Incorporate suggestions and platform-specific changes.
+ Thanks to Dennis Flanagan, Colin Plumb, Niall Douglas,
+ Aaron Bachmann, Emery Berger, and others.
+ * Speed up non-fastbin processing enough to remove fastbins.
+ * Remove useless cfree() to avoid conflicts with other apps.
+ * Remove internal memcpy, memset. Compilers handle builtins better.
+ * Remove some options that no one ever used and rename others.
+
+ V2.7.2 Sat Aug 17 09:07:30 2002 Doug Lea (dl at gee)
+ * Fix malloc_state bitmap array misdeclaration
+
+ V2.7.1 Thu Jul 25 10:58:03 2002 Doug Lea (dl at gee)
+ * Allow tuning of FIRST_SORTED_BIN_SIZE
+ * Use PTR_UINT as type for all ptr->int casts. Thanks to John Belmonte.
+ * Better detection and support for non-contiguousness of MORECORE.
+ Thanks to Andreas Mueller, Conal Walsh, and Wolfram Gloger
+ * Bypass most of malloc if no frees. Thanks To Emery Berger.
+ * Fix freeing of old top non-contiguous chunk im sysmalloc.
+ * Raised default trim and map thresholds to 256K.
+ * Fix mmap-related #defines. Thanks to Lubos Lunak.
+ * Fix copy macros; added LACKS_FCNTL_H. Thanks to Neal Walfield.
+ * Branch-free bin calculation
+ * Default trim and mmap thresholds now 256K.
+
+ V2.7.0 Sun Mar 11 14:14:06 2001 Doug Lea (dl at gee)
+ * Introduce independent_comalloc and independent_calloc.
+ Thanks to Michael Pachos for motivation and help.
+ * Make optional .h file available
+ * Allow > 2GB requests on 32bit systems.
+ * new WIN32 sbrk, mmap, munmap, lock code from <Walter@GeNeSys-e.de>.
+ Thanks also to Andreas Mueller <a.mueller at paradatec.de>,
+ and Anonymous.
+ * Allow override of MALLOC_ALIGNMENT (Thanks to Ruud Waij for
+ helping test this.)
+ * memalign: check alignment arg
+ * realloc: don't try to shift chunks backwards, since this
+ leads to more fragmentation in some programs and doesn't
+ seem to help in any others.
+ * Collect all cases in malloc requiring system memory into sysmalloc
+ * Use mmap as backup to sbrk
+ * Place all internal state in malloc_state
+ * Introduce fastbins (although similar to 2.5.1)
+ * Many minor tunings and cosmetic improvements
+ * Introduce USE_PUBLIC_MALLOC_WRAPPERS, USE_MALLOC_LOCK
+ * Introduce MALLOC_FAILURE_ACTION, MORECORE_CONTIGUOUS
+ Thanks to Tony E. Bennett <tbennett@nvidia.com> and others.
+ * Include errno.h to support default failure action.
+
+ V2.6.6 Sun Dec 5 07:42:19 1999 Doug Lea (dl at gee)
+ * return null for negative arguments
+ * Added Several WIN32 cleanups from Martin C. Fong <mcfong at yahoo.com>
+ * Add 'LACKS_SYS_PARAM_H' for those systems without 'sys/param.h'
+ (e.g. WIN32 platforms)
+ * Cleanup header file inclusion for WIN32 platforms
+ * Cleanup code to avoid Microsoft Visual C++ compiler complaints
+ * Add 'USE_DL_PREFIX' to quickly allow co-existence with existing
+ memory allocation routines
+ * Set 'malloc_getpagesize' for WIN32 platforms (needs more work)
+ * Use 'assert' rather than 'ASSERT' in WIN32 code to conform to
+ usage of 'assert' in non-WIN32 code
+ * Improve WIN32 'sbrk()' emulation's 'findRegion()' routine to
+ avoid infinite loop
+ * Always call 'fREe()' rather than 'free()'
+
+ V2.6.5 Wed Jun 17 15:57:31 1998 Doug Lea (dl at gee)
+ * Fixed ordering problem with boundary-stamping
+
+ V2.6.3 Sun May 19 08:17:58 1996 Doug Lea (dl at gee)
+ * Added pvalloc, as recommended by H.J. Liu
+ * Added 64bit pointer support mainly from Wolfram Gloger
+ * Added anonymously donated WIN32 sbrk emulation
+ * Malloc, calloc, getpagesize: add optimizations from Raymond Nijssen
+ * malloc_extend_top: fix mask error that caused wastage after
+ foreign sbrks
+ * Add linux mremap support code from HJ Liu
+
+ V2.6.2 Tue Dec 5 06:52:55 1995 Doug Lea (dl at gee)
+ * Integrated most documentation with the code.
+ * Add support for mmap, with help from
+ Wolfram Gloger (Gloger@lrz.uni-muenchen.de).
+ * Use last_remainder in more cases.
+ * Pack bins using idea from colin@nyx10.cs.du.edu
+ * Use ordered bins instead of best-fit threshhold
+ * Eliminate block-local decls to simplify tracing and debugging.
+ * Support another case of realloc via move into top
+ * Fix error occuring when initial sbrk_base not word-aligned.
+ * Rely on page size for units instead of SBRK_UNIT to
+ avoid surprises about sbrk alignment conventions.
+ * Add mallinfo, mallopt. Thanks to Raymond Nijssen
+ (raymond@es.ele.tue.nl) for the suggestion.
+ * Add `pad' argument to malloc_trim and top_pad mallopt parameter.
+ * More precautions for cases where other routines call sbrk,
+ courtesy of Wolfram Gloger (Gloger@lrz.uni-muenchen.de).
+ * Added macros etc., allowing use in linux libc from
+ H.J. Lu (hjl@gnu.ai.mit.edu)
+ * Inverted this history list
+
+ V2.6.1 Sat Dec 2 14:10:57 1995 Doug Lea (dl at gee)
+ * Re-tuned and fixed to behave more nicely with V2.6.0 changes.
+ * Removed all preallocation code since under current scheme
+ the work required to undo bad preallocations exceeds
+ the work saved in good cases for most test programs.
+ * No longer use return list or unconsolidated bins since
+ no scheme using them consistently outperforms those that don't
+ given above changes.
+ * Use best fit for very large chunks to prevent some worst-cases.
+ * Added some support for debugging
+
+ V2.6.0 Sat Nov 4 07:05:23 1995 Doug Lea (dl at gee)
+ * Removed footers when chunks are in use. Thanks to
+ Paul Wilson (wilson@cs.texas.edu) for the suggestion.
+
+ V2.5.4 Wed Nov 1 07:54:51 1995 Doug Lea (dl at gee)
+ * Added malloc_trim, with help from Wolfram Gloger
+ (wmglo@Dent.MED.Uni-Muenchen.DE).
+
+ V2.5.3 Tue Apr 26 10:16:01 1994 Doug Lea (dl at g)
+
+ V2.5.2 Tue Apr 5 16:20:40 1994 Doug Lea (dl at g)
+ * realloc: try to expand in both directions
+ * malloc: swap order of clean-bin strategy;
+ * realloc: only conditionally expand backwards
+ * Try not to scavenge used bins
+ * Use bin counts as a guide to preallocation
+ * Occasionally bin return list chunks in first scan
+ * Add a few optimizations from colin@nyx10.cs.du.edu
+
+ V2.5.1 Sat Aug 14 15:40:43 1993 Doug Lea (dl at g)
+ * faster bin computation & slightly different binning
+ * merged all consolidations to one part of malloc proper
+ (eliminating old malloc_find_space & malloc_clean_bin)
+ * Scan 2 returns chunks (not just 1)
+ * Propagate failure in realloc if malloc returns 0
+ * Add stuff to allow compilation on non-ANSI compilers
+ from kpv@research.att.com
+
+ V2.5 Sat Aug 7 07:41:59 1993 Doug Lea (dl at g.oswego.edu)
+ * removed potential for odd address access in prev_chunk
+ * removed dependency on getpagesize.h
+ * misc cosmetics and a bit more internal documentation
+ * anticosmetics: mangled names in macros to evade debugger strangeness
+ * tested on sparc, hp-700, dec-mips, rs6000
+ with gcc & native cc (hp, dec only) allowing
+ Detlefs & Zorn comparison study (in SIGPLAN Notices.)
+
+ Trial version Fri Aug 28 13:14:29 1992 Doug Lea (dl at g.oswego.edu)
+ * Based loosely on libg++-1.2X malloc. (It retains some of the overall
+ structure of old version, but most details differ.)
+
+*/
diff --git a/configure.ac b/configure.ac
new file mode 100644
index 0000000..a638d63
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,32 @@
+AC_PREREQ(2.53)
+AC_INIT(adb, 2.3.7, http://source.android.com/)
+AM_INIT_AUTOMAKE()
+AM_CONFIG_HEADER(config.h)
+AM_MAINTAINER_MODE
+
+AC_GNU_SOURCE
+AC_ISC_POSIX
+AC_PROG_CC
+AC_STDC_HEADERS
+AC_PROG_LIBTOOL
+AC_LIBTOOL_DLOPEN
+
+dnl Check for zlib.
+AC_CHECK_HEADER(
+ zlib.h,
+ AC_CHECK_LIB(
+ z,
+ inflateEnd,
+ LIBS="-lz $LIBS",
+ AC_MSG_ERROR(Required library zlib missing.)),
+ AC_MSG_ERROR(Header file for required library zlib missing.))
+
+# output stuff
+AC_CONFIG_FILES([
+Makefile
+libzipfile/Makefile
+libcutils/Makefile
+adb/Makefile
+])
+
+AC_OUTPUT
diff --git a/include/android/log.h b/include/android/log.h
new file mode 100644
index 0000000..0ea4c29
--- /dev/null
+++ b/include/android/log.h
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _ANDROID_LOG_H
+#define _ANDROID_LOG_H
+
+/******************************************************************
+ *
+ * IMPORTANT NOTICE:
+ *
+ * This file is part of Android's set of stable system headers
+ * exposed by the Android NDK (Native Development Kit) since
+ * platform release 1.5
+ *
+ * Third-party source AND binary code relies on the definitions
+ * here to be FROZEN ON ALL UPCOMING PLATFORM RELEASES.
+ *
+ * - DO NOT MODIFY ENUMS (EXCEPT IF YOU ADD NEW 32-BIT VALUES)
+ * - DO NOT MODIFY CONSTANTS OR FUNCTIONAL MACROS
+ * - DO NOT CHANGE THE SIGNATURE OF FUNCTIONS IN ANY WAY
+ * - DO NOT CHANGE THE LAYOUT OR SIZE OF STRUCTURES
+ */
+
+/*
+ * Support routines to send messages to the Android in-kernel log buffer,
+ * which can later be accessed through the 'logcat' utility.
+ *
+ * Each log message must have
+ * - a priority
+ * - a log tag
+ * - some text
+ *
+ * The tag normally corresponds to the component that emits the log message,
+ * and should be reasonably small.
+ *
+ * Log message text may be truncated to less than an implementation-specific
+ * limit (e.g. 1023 characters max).
+ *
+ * Note that a newline character ("\n") will be appended automatically to your
+ * log message, if not already there. It is not possible to send several messages
+ * and have them appear on a single line in logcat.
+ *
+ * PLEASE USE LOGS WITH MODERATION:
+ *
+ * - Sending log messages eats CPU and slow down your application and the
+ * system.
+ *
+ * - The circular log buffer is pretty small (<64KB), sending many messages
+ * might push off other important log messages from the rest of the system.
+ *
+ * - In release builds, only send log messages to account for exceptional
+ * conditions.
+ *
+ * NOTE: These functions MUST be implemented by /system/lib/liblog.so
+ */
+
+#include <stdarg.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Android log priority values, in ascending priority order.
+ */
+typedef enum android_LogPriority {
+ ANDROID_LOG_UNKNOWN = 0,
+ ANDROID_LOG_DEFAULT, /* only for SetMinPriority() */
+ ANDROID_LOG_VERBOSE,
+ ANDROID_LOG_DEBUG,
+ ANDROID_LOG_INFO,
+ ANDROID_LOG_WARN,
+ ANDROID_LOG_ERROR,
+ ANDROID_LOG_FATAL,
+ ANDROID_LOG_SILENT, /* only for SetMinPriority(); must be last */
+} android_LogPriority;
+
+/*
+ * Send a simple string to the log.
+ */
+int __android_log_write(int prio, const char *tag, const char *text);
+
+/*
+ * Send a formatted string to the log, used like printf(fmt,...)
+ */
+int __android_log_print(int prio, const char *tag, const char *fmt, ...)
+#if defined(__GNUC__)
+ __attribute__ ((format(printf, 3, 4)))
+#endif
+ ;
+
+/*
+ * A variant of __android_log_print() that takes a va_list to list
+ * additional parameters.
+ */
+int __android_log_vprint(int prio, const char *tag,
+ const char *fmt, va_list ap);
+
+/*
+ * Log an assertion failure and SIGTRAP the process to have a chance
+ * to inspect it, if a debugger is attached. This uses the FATAL priority.
+ */
+void __android_log_assert(const char *cond, const char *tag,
+ const char *fmt, ...)
+#if defined(__GNUC__)
+ __attribute__ ((noreturn))
+ __attribute__ ((format(printf, 3, 4)))
+#endif
+ ;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _ANDROID_LOG_H */
diff --git a/include/cutils/abort_socket.h b/include/cutils/abort_socket.h
new file mode 100644
index 0000000..fbb1112
--- /dev/null
+++ b/include/cutils/abort_socket.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2009, 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.
+ */
+
+/* Helper to perform abortable blocking operations on a socket:
+ * asocket_connect()
+ * asocket_accept()
+ * asocket_read()
+ * asocket_write()
+ * These calls are similar to the regular syscalls, but can be aborted with:
+ * asocket_abort()
+ *
+ * Calling close() on a regular POSIX socket does not abort blocked syscalls on
+ * that socket in other threads.
+ *
+ * After calling asocket_abort() the socket cannot be reused.
+ *
+ * Call asocket_destory() *after* all threads have finished with the socket to
+ * finish closing the socket and free the asocket structure.
+ *
+ * The helper is implemented by setting the socket non-blocking to initiate
+ * syscalls connect(), accept(), read(), write(), then using a blocking poll()
+ * on both the primary socket and a local pipe. This makes the poll() abortable
+ * by writing a byte to the local pipe in asocket_abort().
+ *
+ * asocket_create() sets the fd to non-blocking mode. It must not be changed to
+ * blocking mode.
+ *
+ * Using asocket will triple the number of file descriptors required per
+ * socket, due to the local pipe. It may be possible to use a global pipe per
+ * process rather than per socket, but we have not been able to come up with a
+ * race-free implementation yet.
+ *
+ * All functions except asocket_init() and asocket_destroy() are thread safe.
+ */
+
+#include <stdlib.h>
+#include <sys/socket.h>
+
+#ifndef __CUTILS_ABORT_SOCKET_H__
+#define __CUTILS_ABORT_SOCKET_H__
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct asocket {
+ int fd; /* primary socket fd */
+ int abort_fd[2]; /* pipe used to abort */
+};
+
+/* Create an asocket from fd.
+ * Sets the socket to non-blocking mode.
+ * Returns NULL on error with errno set.
+ */
+struct asocket *asocket_init(int fd);
+
+/* Blocking socket I/O with timeout.
+ * Calling asocket_abort() from another thread will cause each of these
+ * functions to immediately return with value -1 and errno ECANCELED.
+ * timeout is in ms, use -1 to indicate no timeout. On timeout -1 is returned
+ * with errno ETIMEDOUT.
+ * EINTR is handled in-call.
+ * Other semantics are identical to the regular syscalls.
+ */
+int asocket_connect(struct asocket *s, const struct sockaddr *addr,
+ socklen_t addrlen, int timeout);
+
+int asocket_accept(struct asocket *s, struct sockaddr *addr,
+ socklen_t *addrlen, int timeout);
+
+int asocket_read(struct asocket *s, void *buf, size_t count, int timeout);
+
+int asocket_write(struct asocket *s, const void *buf, size_t count,
+ int timeout);
+
+/* Abort above calls and shutdown socket.
+ * Further I/O operations on this socket will immediately fail after this call.
+ * asocket_destroy() should be used to release resources once all threads
+ * have returned from blocking calls on the socket.
+ */
+void asocket_abort(struct asocket *s);
+
+/* Close socket and free asocket structure.
+ * Must not be called until all calls on this structure have completed.
+ */
+void asocket_destroy(struct asocket *s);
+
+#ifdef __cplusplus
+}
+#endif
+#endif //__CUTILS_ABORT_SOCKET__H__
diff --git a/include/cutils/array.h b/include/cutils/array.h
new file mode 100644
index 0000000..c97ff34
--- /dev/null
+++ b/include/cutils/array.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * A pointer array which intelligently expands its capacity ad needed.
+ */
+
+#ifndef __ARRAY_H
+#define __ARRAY_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdlib.h>
+
+/** An array. */
+typedef struct Array Array;
+
+/** Constructs a new array. Returns NULL if we ran out of memory. */
+Array* arrayCreate();
+
+/** Frees an array. Does not free elements themselves. */
+void arrayFree(Array* array);
+
+/** Adds a pointer. Returns 0 is successful, < 0 otherwise. */
+int arrayAdd(Array* array, void* pointer);
+
+/** Gets the pointer at the specified index. */
+void* arrayGet(Array* array, int index);
+
+/** Removes the pointer at the given index and returns it. */
+void* arrayRemove(Array* array, int index);
+
+/** Sets pointer at the given index. Returns old pointer. */
+void* arraySet(Array* array, int index, void* pointer);
+
+/** Sets the array size. Sets new pointers to NULL. Returns 0 if successful, < 0 otherwise . */
+int arraySetSize(Array* array, int size);
+
+/** Returns the size of the given array. */
+int arraySize(Array* array);
+
+/**
+ * Returns a pointer to a C-style array which will be valid until this array
+ * changes.
+ */
+const void** arrayUnwrap(Array* array);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __ARRAY_H */
diff --git a/include/cutils/ashmem.h b/include/cutils/ashmem.h
new file mode 100644
index 0000000..25b233e
--- /dev/null
+++ b/include/cutils/ashmem.h
@@ -0,0 +1,45 @@
+/* cutils/ashmem.h
+ **
+ ** Copyright 2008 The Android Open Source Project
+ **
+ ** This file is dual licensed. It may be redistributed and/or modified
+ ** under the terms of the Apache 2.0 License OR version 2 of the GNU
+ ** General Public License.
+ */
+
+#ifndef _CUTILS_ASHMEM_H
+#define _CUTILS_ASHMEM_H
+
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int ashmem_create_region(const char *name, size_t size);
+int ashmem_set_prot_region(int fd, int prot);
+int ashmem_pin_region(int fd, size_t offset, size_t len);
+int ashmem_unpin_region(int fd, size_t offset, size_t len);
+int ashmem_get_size_region(int fd);
+
+#ifdef __cplusplus
+}
+#endif
+
+#ifndef __ASHMEMIOC /* in case someone included <linux/ashmem.h> too */
+
+#define ASHMEM_NAME_LEN 256
+
+#define ASHMEM_NAME_DEF "dev/ashmem"
+
+/* Return values from ASHMEM_PIN: Was the mapping purged while unpinned? */
+#define ASHMEM_NOT_PURGED 0
+#define ASHMEM_WAS_PURGED 1
+
+/* Return values from ASHMEM_UNPIN: Is the mapping now pinned or unpinned? */
+#define ASHMEM_IS_UNPINNED 0
+#define ASHMEM_IS_PINNED 1
+
+#endif /* ! __ASHMEMIOC */
+
+#endif /* _CUTILS_ASHMEM_H */
diff --git a/include/cutils/atomic-arm.h b/include/cutils/atomic-arm.h
new file mode 100644
index 0000000..5595021
--- /dev/null
+++ b/include/cutils/atomic-arm.h
@@ -0,0 +1,280 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_CUTILS_ATOMIC_ARM_H
+#define ANDROID_CUTILS_ATOMIC_ARM_H
+
+#include <stdint.h>
+#include <machine/cpu-features.h>
+
+extern inline void android_compiler_barrier(void)
+{
+ __asm__ __volatile__ ("" : : : "memory");
+}
+
+#if ANDROID_SMP == 0
+extern inline void android_memory_barrier(void)
+{
+ android_compiler_barrier();
+}
+#elif defined(__ARM_HAVE_DMB)
+extern inline void android_memory_barrier(void)
+{
+ __asm__ __volatile__ ("dmb" : : : "memory");
+}
+#elif defined(__ARM_HAVE_LDREX_STREX)
+extern inline void android_memory_barrier(void)
+{
+ __asm__ __volatile__ ("mcr p15, 0, %0, c7, c10, 5"
+ : : "r" (0) : "memory");
+}
+#else
+extern inline void android_memory_barrier(void)
+{
+ typedef void (kuser_memory_barrier)(void);
+ (*(kuser_memory_barrier *)0xffff0fa0)();
+}
+#endif
+
+extern inline int32_t android_atomic_acquire_load(volatile const int32_t *ptr)
+{
+ int32_t value = *ptr;
+ android_memory_barrier();
+ return value;
+}
+
+extern inline int32_t android_atomic_release_load(volatile const int32_t *ptr)
+{
+ android_memory_barrier();
+ return *ptr;
+}
+
+extern inline void android_atomic_acquire_store(int32_t value,
+ volatile int32_t *ptr)
+{
+ *ptr = value;
+ android_memory_barrier();
+}
+
+extern inline void android_atomic_release_store(int32_t value,
+ volatile int32_t *ptr)
+{
+ android_memory_barrier();
+ *ptr = value;
+}
+
+#if defined(__thumb__)
+extern int android_atomic_cas(int32_t old_value, int32_t new_value,
+ volatile int32_t *ptr);
+#elif defined(__ARM_HAVE_LDREX_STREX)
+extern inline int android_atomic_cas(int32_t old_value, int32_t new_value,
+ volatile int32_t *ptr)
+{
+ int32_t prev, status;
+ do {
+ __asm__ __volatile__ ("ldrex %0, [%3]\n"
+ "mov %1, #0\n"
+ "teq %0, %4\n"
+ "strexeq %1, %5, [%3]"
+ : "=&r" (prev), "=&r" (status), "+m"(*ptr)
+ : "r" (ptr), "Ir" (old_value), "r" (new_value)
+ : "cc");
+ } while (__builtin_expect(status != 0, 0));
+ return prev != old_value;
+}
+#else
+extern inline int android_atomic_cas(int32_t old_value, int32_t new_value,
+ volatile int32_t *ptr)
+{
+ typedef int (kuser_cmpxchg)(int32_t, int32_t, volatile int32_t *);
+ int32_t prev, status;
+ prev = *ptr;
+ do {
+ status = (*(kuser_cmpxchg *)0xffff0fc0)(old_value, new_value, ptr);
+ if (__builtin_expect(status == 0, 1))
+ return 0;
+ prev = *ptr;
+ } while (prev == old_value);
+ return 1;
+}
+#endif
+
+extern inline int android_atomic_acquire_cas(int32_t old_value,
+ int32_t new_value,
+ volatile int32_t *ptr)
+{
+ int status = android_atomic_cas(old_value, new_value, ptr);
+ android_memory_barrier();
+ return status;
+}
+
+extern inline int android_atomic_release_cas(int32_t old_value,
+ int32_t new_value,
+ volatile int32_t *ptr)
+{
+ android_memory_barrier();
+ return android_atomic_cas(old_value, new_value, ptr);
+}
+
+/* Yeah, deprecated but old props use it */
+extern inline int android_atomic_cmpxchg(int32_t old_value,
+ int32_t new_value,
+ volatile int32_t *ptr)
+{
+ android_memory_barrier();
+ return android_atomic_cas(old_value, new_value, ptr);
+}
+
+
+#if defined(__thumb__)
+extern int32_t android_atomic_swap(int32_t new_value,
+ volatile int32_t *ptr);
+#elif defined(__ARM_HAVE_LDREX_STREX)
+extern inline int32_t android_atomic_swap(int32_t new_value,
+ volatile int32_t *ptr)
+{
+ int32_t prev, status;
+ do {
+ __asm__ __volatile__ ("ldrex %0, [%3]\n"
+ "strex %1, %4, [%3]"
+ : "=&r" (prev), "=&r" (status), "+m" (*ptr)
+ : "r" (ptr), "r" (new_value)
+ : "cc");
+ } while (__builtin_expect(status != 0, 0));
+ android_memory_barrier();
+ return prev;
+}
+#else
+extern inline int32_t android_atomic_swap(int32_t new_value,
+ volatile int32_t *ptr)
+{
+ int32_t prev;
+ __asm__ __volatile__ ("swp %0, %2, [%3]"
+ : "=&r" (prev), "+m" (*ptr)
+ : "r" (new_value), "r" (ptr)
+ : "cc");
+ android_memory_barrier();
+ return prev;
+}
+#endif
+
+#if defined(__thumb__)
+extern int32_t android_atomic_add(int32_t increment,
+ volatile int32_t *ptr);
+#elif defined(__ARM_HAVE_LDREX_STREX)
+extern inline int32_t android_atomic_add(int32_t increment,
+ volatile int32_t *ptr)
+{
+ int32_t prev, tmp, status;
+ android_memory_barrier();
+ do {
+ __asm__ __volatile__ ("ldrex %0, [%4]\n"
+ "add %1, %0, %5\n"
+ "strex %2, %1, [%4]"
+ : "=&r" (prev), "=&r" (tmp),
+ "=&r" (status), "+m" (*ptr)
+ : "r" (ptr), "Ir" (increment)
+ : "cc");
+ } while (__builtin_expect(status != 0, 0));
+ return prev;
+}
+#else
+extern inline int32_t android_atomic_add(int32_t increment,
+ volatile int32_t *ptr)
+{
+ int32_t prev, status;
+ android_memory_barrier();
+ do {
+ prev = *ptr;
+ status = android_atomic_cas(prev, prev + increment, ptr);
+ } while (__builtin_expect(status != 0, 0));
+ return prev;
+}
+#endif
+
+extern inline int32_t android_atomic_inc(volatile int32_t *addr)
+{
+ return android_atomic_add(1, addr);
+}
+
+extern inline int32_t android_atomic_dec(volatile int32_t *addr)
+{
+ return android_atomic_add(-1, addr);
+}
+
+#if defined(__thumb__)
+extern int32_t android_atomic_and(int32_t value, volatile int32_t *ptr);
+#elif defined(__ARM_HAVE_LDREX_STREX)
+extern inline int32_t android_atomic_and(int32_t value, volatile int32_t *ptr)
+{
+ int32_t prev, tmp, status;
+ android_memory_barrier();
+ do {
+ __asm__ __volatile__ ("ldrex %0, [%4]\n"
+ "and %1, %0, %5\n"
+ "strex %2, %1, [%4]"
+ : "=&r" (prev), "=&r" (tmp),
+ "=&r" (status), "+m" (*ptr)
+ : "r" (ptr), "Ir" (value)
+ : "cc");
+ } while (__builtin_expect(status != 0, 0));
+ return prev;
+}
+#else
+extern inline int32_t android_atomic_and(int32_t value, volatile int32_t *ptr)
+{
+ int32_t prev, status;
+ android_memory_barrier();
+ do {
+ prev = *ptr;
+ status = android_atomic_cas(prev, prev & value, ptr);
+ } while (__builtin_expect(status != 0, 0));
+ return prev;
+}
+#endif
+
+#if defined(__thumb__)
+extern int32_t android_atomic_or(int32_t value, volatile int32_t *ptr);
+#elif defined(__ARM_HAVE_LDREX_STREX)
+extern inline int32_t android_atomic_or(int32_t value, volatile int32_t *ptr)
+{
+ int32_t prev, tmp, status;
+ android_memory_barrier();
+ do {
+ __asm__ __volatile__ ("ldrex %0, [%4]\n"
+ "orr %1, %0, %5\n"
+ "strex %2, %1, [%4]"
+ : "=&r" (prev), "=&r" (tmp),
+ "=&r" (status), "+m" (*ptr)
+ : "r" (ptr), "Ir" (value)
+ : "cc");
+ } while (__builtin_expect(status != 0, 0));
+ return prev;
+}
+#else
+extern inline int32_t android_atomic_or(int32_t value, volatile int32_t *ptr)
+{
+ int32_t prev, status;
+ android_memory_barrier();
+ do {
+ prev = *ptr;
+ status = android_atomic_cas(prev, prev | value, ptr);
+ } while (__builtin_expect(status != 0, 0));
+ return prev;
+}
+#endif
+
+#endif /* ANDROID_CUTILS_ATOMIC_ARM_H */
diff --git a/include/cutils/atomic-inline.h b/include/cutils/atomic-inline.h
new file mode 100644
index 0000000..715e0aa
--- /dev/null
+++ b/include/cutils/atomic-inline.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_CUTILS_ATOMIC_INLINE_H
+#define ANDROID_CUTILS_ATOMIC_INLINE_H
+
+/*
+ * Inline declarations and macros for some special-purpose atomic
+ * operations. These are intended for rare circumstances where a
+ * memory barrier needs to be issued inline rather than as a function
+ * call.
+ *
+ * Most code should not use these.
+ *
+ * Anything that does include this file must set ANDROID_SMP to either
+ * 0 or 1, indicating compilation for UP or SMP, respectively.
+ *
+ * Macros defined in this header:
+ *
+ * void ANDROID_MEMBAR_FULL(void)
+ * Full memory barrier. Provides a compiler reordering barrier, and
+ * on SMP systems emits an appropriate instruction.
+ */
+
+#if !defined(ANDROID_SMP)
+# error "Must define ANDROID_SMP before including atomic-inline.h"
+#endif
+
+#if defined(__arm__)
+#include <cutils/atomic-arm.h>
+#elif defined(__i386__) || defined(__x86_64__)
+#include <cutils/atomic-x86.h>
+#elif defined(__sh__)
+/* implementation is in atomic-android-sh.c */
+#else
+#error atomic operations are unsupported
+#endif
+
+#if ANDROID_SMP == 0
+#define ANDROID_MEMBAR_FULL android_compiler_barrier
+#else
+#define ANDROID_MEMBAR_FULL android_memory_barrier
+#endif
+
+#endif /* ANDROID_CUTILS_ATOMIC_INLINE_H */
diff --git a/include/cutils/atomic-x86.h b/include/cutils/atomic-x86.h
new file mode 100644
index 0000000..12a1985
--- /dev/null
+++ b/include/cutils/atomic-x86.h
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_CUTILS_ATOMIC_X86_H
+#define ANDROID_CUTILS_ATOMIC_X86_H
+
+#include <stdint.h>
+
+extern inline void android_compiler_barrier(void)
+{
+ __asm__ __volatile__ ("" : : : "memory");
+}
+
+#if ANDROID_SMP == 0
+extern inline void android_memory_barrier(void)
+{
+ android_compiler_barrier();
+}
+#else
+extern inline void android_memory_barrier(void)
+{
+ __asm__ __volatile__ ("mfence" : : : "memory");
+}
+#endif
+
+extern inline int32_t android_atomic_acquire_load(volatile const int32_t *ptr)
+{
+ int32_t value = *ptr;
+ android_compiler_barrier();
+ return value;
+}
+
+extern inline int32_t android_atomic_release_load(volatile const int32_t *ptr)
+{
+ android_memory_barrier();
+ return *ptr;
+}
+
+extern inline void android_atomic_acquire_store(int32_t value,
+ volatile int32_t *ptr)
+{
+ *ptr = value;
+ android_memory_barrier();
+}
+
+extern inline void android_atomic_release_store(int32_t value,
+ volatile int32_t *ptr)
+{
+ android_compiler_barrier();
+ *ptr = value;
+}
+
+extern inline int android_atomic_cas(int32_t old_value, int32_t new_value,
+ volatile int32_t *ptr)
+{
+ int32_t prev;
+ __asm__ __volatile__ ("lock; cmpxchgl %1, %2"
+ : "=a" (prev)
+ : "q" (new_value), "m" (*ptr), "0" (old_value)
+ : "memory");
+ return prev != old_value;
+}
+
+extern inline int android_atomic_acquire_cas(int32_t old_value,
+ int32_t new_value,
+ volatile int32_t *ptr)
+{
+ /* Loads are not reordered with other loads. */
+ return android_atomic_cas(old_value, new_value, ptr);
+}
+
+extern inline int android_atomic_release_cas(int32_t old_value,
+ int32_t new_value,
+ volatile int32_t *ptr)
+{
+ /* Stores are not reordered with other stores. */
+ return android_atomic_cas(old_value, new_value, ptr);
+}
+
+extern inline int32_t android_atomic_swap(int32_t new_value,
+ volatile int32_t *ptr)
+{
+ __asm__ __volatile__ ("xchgl %1, %0"
+ : "=r" (new_value)
+ : "m" (*ptr), "0" (new_value)
+ : "memory");
+ /* new_value now holds the old value of *ptr */
+ return new_value;
+}
+
+extern inline int32_t android_atomic_add(int32_t increment,
+ volatile int32_t *ptr)
+{
+ __asm__ __volatile__ ("lock; xaddl %0, %1"
+ : "+r" (increment), "+m" (*ptr)
+ : : "memory");
+ /* increment now holds the old value of *ptr */
+ return increment;
+}
+
+extern inline int32_t android_atomic_inc(volatile int32_t *addr)
+{
+ return android_atomic_add(1, addr);
+}
+
+extern inline int32_t android_atomic_dec(volatile int32_t *addr)
+{
+ return android_atomic_add(-1, addr);
+}
+
+extern inline int32_t android_atomic_and(int32_t value,
+ volatile int32_t *ptr)
+{
+ int32_t prev, status;
+ do {
+ prev = *ptr;
+ status = android_atomic_cas(prev, prev & value, ptr);
+ } while (__builtin_expect(status != 0, 0));
+ return prev;
+}
+
+extern inline int32_t android_atomic_or(int32_t value, volatile int32_t *ptr)
+{
+ int32_t prev, status;
+ do {
+ prev = *ptr;
+ status = android_atomic_cas(prev, prev | value, ptr);
+ } while (__builtin_expect(status != 0, 0));
+ return prev;
+}
+
+#endif /* ANDROID_CUTILS_ATOMIC_X86_H */
diff --git a/include/cutils/atomic.h b/include/cutils/atomic.h
new file mode 100644
index 0000000..48f87f7
--- /dev/null
+++ b/include/cutils/atomic.h
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_CUTILS_ATOMIC_H
+#define ANDROID_CUTILS_ATOMIC_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * A handful of basic atomic operations. The appropriate pthread
+ * functions should be used instead of these whenever possible.
+ *
+ * The "acquire" and "release" terms can be defined intuitively in terms
+ * of the placement of memory barriers in a simple lock implementation:
+ * - wait until compare-and-swap(lock-is-free --> lock-is-held) succeeds
+ * - barrier
+ * - [do work]
+ * - barrier
+ * - store(lock-is-free)
+ * In very crude terms, the initial (acquire) barrier prevents any of the
+ * "work" from happening before the lock is held, and the later (release)
+ * barrier ensures that all of the work happens before the lock is released.
+ * (Think of cached writes, cache read-ahead, and instruction reordering
+ * around the CAS and store instructions.)
+ *
+ * The barriers must apply to both the compiler and the CPU. Note it is
+ * legal for instructions that occur before an "acquire" barrier to be
+ * moved down below it, and for instructions that occur after a "release"
+ * barrier to be moved up above it.
+ *
+ * The ARM-driven implementation we use here is short on subtlety,
+ * and actually requests a full barrier from the compiler and the CPU.
+ * The only difference between acquire and release is in whether they
+ * are issued before or after the atomic operation with which they
+ * are associated. To ease the transition to C/C++ atomic intrinsics,
+ * you should not rely on this, and instead assume that only the minimal
+ * acquire/release protection is provided.
+ *
+ * NOTE: all int32_t* values are expected to be aligned on 32-bit boundaries.
+ * If they are not, atomicity is not guaranteed.
+ */
+
+/*
+ * Basic arithmetic and bitwise operations. These all provide a
+ * barrier with "release" ordering, and return the previous value.
+ *
+ * These have the same characteristics (e.g. what happens on overflow)
+ * as the equivalent non-atomic C operations.
+ */
+int32_t android_atomic_inc(volatile int32_t* addr);
+int32_t android_atomic_dec(volatile int32_t* addr);
+int32_t android_atomic_add(int32_t value, volatile int32_t* addr);
+int32_t android_atomic_and(int32_t value, volatile int32_t* addr);
+int32_t android_atomic_or(int32_t value, volatile int32_t* addr);
+
+/*
+ * Perform an atomic load with "acquire" or "release" ordering.
+ *
+ * This is only necessary if you need the memory barrier. A 32-bit read
+ * from a 32-bit aligned address is atomic on all supported platforms.
+ */
+int32_t android_atomic_acquire_load(volatile const int32_t* addr);
+int32_t android_atomic_release_load(volatile const int32_t* addr);
+
+/*
+ * Perform an atomic store with "acquire" or "release" ordering.
+ *
+ * This is only necessary if you need the memory barrier. A 32-bit write
+ * to a 32-bit aligned address is atomic on all supported platforms.
+ */
+void android_atomic_acquire_store(int32_t value, volatile int32_t* addr);
+void android_atomic_release_store(int32_t value, volatile int32_t* addr);
+
+/*
+ * Unconditional swap operation with release ordering.
+ *
+ * Stores the new value at *addr, and returns the previous value.
+ */
+int32_t android_atomic_swap(int32_t value, volatile int32_t* addr);
+
+/*
+ * Compare-and-set operation with "acquire" or "release" ordering.
+ *
+ * This returns zero if the new value was successfully stored, which will
+ * only happen when *addr == oldvalue.
+ *
+ * (The return value is inverted from implementations on other platforms,
+ * but matches the ARM ldrex/strex result.)
+ *
+ * Implementations that use the release CAS in a loop may be less efficient
+ * than possible, because we re-issue the memory barrier on each iteration.
+ */
+int android_atomic_acquire_cas(int32_t oldvalue, int32_t newvalue,
+ volatile int32_t* addr);
+int android_atomic_release_cas(int32_t oldvalue, int32_t newvalue,
+ volatile int32_t* addr);
+#if defined(__ARM_ARCH__) || defined (__ARM_EABI__)
+int android_atomic_cmpxchg(int32_t oldvalue, int32_t newvalue,
+ volatile int32_t* addr);
+#else
+#define android_atomic_cmpxchg android_atomic_release_cas
+#endif
+
+/*
+ * Aliases for code using an older version of this header. These are now
+ * deprecated and should not be used. The definitions will be removed
+ * in a future release.
+ */
+#define android_atomic_write android_atomic_release_store
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // ANDROID_CUTILS_ATOMIC_H
diff --git a/include/cutils/compiler.h b/include/cutils/compiler.h
new file mode 100644
index 0000000..09112d5
--- /dev/null
+++ b/include/cutils/compiler.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_CUTILS_COMPILER_H
+#define ANDROID_CUTILS_COMPILER_H
+
+/*
+ * helps the compiler's optimizer predicting branches
+ */
+
+#ifdef __cplusplus
+# define CC_LIKELY( exp ) (__builtin_expect( !!(exp), true ))
+# define CC_UNLIKELY( exp ) (__builtin_expect( !!(exp), false ))
+#else
+# define CC_LIKELY( exp ) (__builtin_expect( !!(exp), 1 ))
+# define CC_UNLIKELY( exp ) (__builtin_expect( !!(exp), 0 ))
+#endif
+
+#endif // ANDROID_CUTILS_COMPILER_H
diff --git a/include/cutils/config_utils.h b/include/cutils/config_utils.h
new file mode 100644
index 0000000..f3fb370
--- /dev/null
+++ b/include/cutils/config_utils.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CUTILS_CONFIG_UTILS_H
+#define __CUTILS_CONFIG_UTILS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct cnode cnode;
+
+
+struct cnode
+{
+ cnode *next;
+ cnode *first_child;
+ cnode *last_child;
+ const char *name;
+ const char *value;
+};
+
+/* parse a text string into a config node tree */
+void config_load(cnode *root, char *data);
+
+/* parse a file into a config node tree */
+void config_load_file(cnode *root, const char *fn);
+
+/* create a single config node */
+cnode* config_node(const char *name, const char *value);
+
+/* locate a named child of a config node */
+cnode* config_find(cnode *root, const char *name);
+
+/* look up a child by name and return the boolean value */
+int config_bool(cnode *root, const char *name, int _default);
+
+/* look up a child by name and return the string value */
+const char* config_str(cnode *root, const char *name, const char *_default);
+
+/* add a named child to a config node (or modify it if it already exists) */
+void config_set(cnode *root, const char *name, const char *value);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/cutils/cpu_info.h b/include/cutils/cpu_info.h
new file mode 100644
index 0000000..78c1884
--- /dev/null
+++ b/include/cutils/cpu_info.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CUTILS_CPU_INFO_H
+#define __CUTILS_CPU_INFO_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* returns a string contiaining an ASCII representation of the CPU serial number,
+** or NULL if cpu info not available.
+** The string is a static variable, so don't call free() on it.
+*/
+extern const char* get_cpu_serial_number(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CUTILS_CPU_INFO_H */
diff --git a/include/cutils/dir_hash.h b/include/cutils/dir_hash.h
new file mode 100644
index 0000000..fbb4d02
--- /dev/null
+++ b/include/cutils/dir_hash.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+typedef enum {
+ SHA_1,
+} HashAlgorithm;
+
+int get_file_hash(HashAlgorithm algorithm, const char *path,
+ char *output_string, size_t max_output_string);
+
+int get_recursive_hash_manifest(HashAlgorithm algorithm,
+ const char *directory_path,
+ char **output_string);
diff --git a/include/cutils/event_tag_map.h b/include/cutils/event_tag_map.h
new file mode 100644
index 0000000..1653c61
--- /dev/null
+++ b/include/cutils/event_tag_map.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBS_CUTILS_EVENTTAGMAP_H
+#define _LIBS_CUTILS_EVENTTAGMAP_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define EVENT_TAG_MAP_FILE "/system/etc/event-log-tags"
+
+struct EventTagMap;
+typedef struct EventTagMap EventTagMap;
+
+/*
+ * Open the specified file as an event log tag map.
+ *
+ * Returns NULL on failure.
+ */
+EventTagMap* android_openEventTagMap(const char* fileName);
+
+/*
+ * Close the map.
+ */
+void android_closeEventTagMap(EventTagMap* map);
+
+/*
+ * Look up a tag by index. Returns the tag string, or NULL if not found.
+ */
+const char* android_lookupEventTag(const EventTagMap* map, int tag);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /*_LIBS_CUTILS_EVENTTAGMAP_H*/
diff --git a/include/cutils/hashmap.h b/include/cutils/hashmap.h
new file mode 100644
index 0000000..5cb344c
--- /dev/null
+++ b/include/cutils/hashmap.h
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Hash map.
+ */
+
+#ifndef __HASHMAP_H
+#define __HASHMAP_H
+
+#include <stdbool.h>
+#include <stdlib.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** A hash map. */
+typedef struct Hashmap Hashmap;
+
+/**
+ * Creates a new hash map. Returns NULL if memory allocation fails.
+ *
+ * @param initialCapacity number of expected entries
+ * @param hash function which hashes keys
+ * @param equals function which compares keys for equality
+ */
+Hashmap* hashmapCreate(size_t initialCapacity,
+ int (*hash)(void* key), bool (*equals)(void* keyA, void* keyB));
+
+/**
+ * Frees the hash map. Does not free the keys or values themselves.
+ */
+void hashmapFree(Hashmap* map);
+
+/**
+ * Hashes the memory pointed to by key with the given size. Useful for
+ * implementing hash functions.
+ */
+int hashmapHash(void* key, size_t keySize);
+
+/**
+ * Puts value for the given key in the map. Returns pre-existing value if
+ * any.
+ *
+ * If memory allocation fails, this function returns NULL, the map's size
+ * does not increase, and errno is set to ENOMEM.
+ */
+void* hashmapPut(Hashmap* map, void* key, void* value);
+
+/**
+ * Gets a value from the map. Returns NULL if no entry for the given key is
+ * found or if the value itself is NULL.
+ */
+void* hashmapGet(Hashmap* map, void* key);
+
+/**
+ * Returns true if the map contains an entry for the given key.
+ */
+bool hashmapContainsKey(Hashmap* map, void* key);
+
+/**
+ * Gets the value for a key. If a value is not found, this function gets a
+ * value and creates an entry using the given callback.
+ *
+ * If memory allocation fails, the callback is not called, this function
+ * returns NULL, and errno is set to ENOMEM.
+ */
+void* hashmapMemoize(Hashmap* map, void* key,
+ void* (*initialValue)(void* key, void* context), void* context);
+
+/**
+ * Removes an entry from the map. Returns the removed value or NULL if no
+ * entry was present.
+ */
+void* hashmapRemove(Hashmap* map, void* key);
+
+/**
+ * Gets the number of entries in this map.
+ */
+size_t hashmapSize(Hashmap* map);
+
+/**
+ * Invokes the given callback on each entry in the map. Stops iterating if
+ * the callback returns false.
+ */
+void hashmapForEach(Hashmap* map,
+ bool (*callback)(void* key, void* value, void* context),
+ void* context);
+
+/**
+ * Concurrency support.
+ */
+
+/**
+ * Locks the hash map so only the current thread can access it.
+ */
+void hashmapLock(Hashmap* map);
+
+/**
+ * Unlocks the hash map so other threads can access it.
+ */
+void hashmapUnlock(Hashmap* map);
+
+/**
+ * Key utilities.
+ */
+
+/**
+ * Hashes int keys. 'key' is a pointer to int.
+ */
+int hashmapIntHash(void* key);
+
+/**
+ * Compares two int keys for equality.
+ */
+bool hashmapIntEquals(void* keyA, void* keyB);
+
+/**
+ * For debugging.
+ */
+
+/**
+ * Gets current capacity.
+ */
+size_t hashmapCurrentCapacity(Hashmap* map);
+
+/**
+ * Counts the number of entry collisions.
+ */
+size_t hashmapCountCollisions(Hashmap* map);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __HASHMAP_H */
diff --git a/include/cutils/iosched_policy.h b/include/cutils/iosched_policy.h
new file mode 100644
index 0000000..07c5d1f
--- /dev/null
+++ b/include/cutils/iosched_policy.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CUTILS_IOSCHED_POLICY_H
+#define __CUTILS_IOSCHED_POLICY_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+ IoSchedClass_NONE,
+ IoSchedClass_RT,
+ IoSchedClass_BE,
+ IoSchedClass_IDLE,
+} IoSchedClass;
+
+extern int android_set_ioprio(int pid, IoSchedClass clazz, int ioprio);
+extern int android_get_ioprio(int pid, IoSchedClass *clazz, int *ioprio);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CUTILS_IOSCHED_POLICY_H */
diff --git a/include/cutils/jstring.h b/include/cutils/jstring.h
new file mode 100644
index 0000000..ee0018f
--- /dev/null
+++ b/include/cutils/jstring.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CUTILS_STRING16_H
+#define __CUTILS_STRING16_H
+
+#include <stdint.h>
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef uint16_t char16_t;
+
+extern char * strndup16to8 (const char16_t* s, size_t n);
+extern size_t strnlen16to8 (const char16_t* s, size_t n);
+extern char * strncpy16to8 (char *dest, const char16_t*s, size_t n);
+
+extern char16_t * strdup8to16 (const char* s, size_t *out_len);
+extern size_t strlen8to16 (const char* utf8Str);
+extern char16_t * strcpy8to16 (char16_t *dest, const char*s, size_t *out_len);
+extern char16_t * strcpylen8to16 (char16_t *dest, const char*s, int length,
+ size_t *out_len);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CUTILS_STRING16_H */
diff --git a/include/cutils/log.h b/include/cutils/log.h
new file mode 100644
index 0000000..dd47c35
--- /dev/null
+++ b/include/cutils/log.h
@@ -0,0 +1,446 @@
+/*
+ * Copyright (C) 2005 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//
+// C/C++ logging functions. See the logging documentation for API details.
+//
+// We'd like these to be available from C code (in case we import some from
+// somewhere), so this has a C interface.
+//
+// The output will be correct when the log file is shared between multiple
+// threads and/or multiple processes so long as the operating system
+// supports O_APPEND. These calls have mutex-protected data structures
+// and so are NOT reentrant. Do not use LOG in a signal handler.
+//
+#ifndef _LIBS_CUTILS_LOG_H
+#define _LIBS_CUTILS_LOG_H
+
+#include <stdio.h>
+#include <time.h>
+#include <sys/types.h>
+#include <unistd.h>
+#ifdef HAVE_PTHREADS
+#include <pthread.h>
+#endif
+#include <stdarg.h>
+
+#include <cutils/uio.h>
+#include <cutils/logd.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// ---------------------------------------------------------------------
+
+/*
+ * Normally we strip LOGV (VERBOSE messages) from release builds.
+ * You can modify this (for example with "#define LOG_NDEBUG 0"
+ * at the top of your source file) to change that behavior.
+ */
+#ifndef LOG_NDEBUG
+#ifdef NDEBUG
+#define LOG_NDEBUG 1
+#else
+#define LOG_NDEBUG 0
+#endif
+#endif
+
+/*
+ * This is the local tag used for the following simplified
+ * logging macros. You can change this preprocessor definition
+ * before using the other macros to change the tag.
+ */
+#ifndef LOG_TAG
+#define LOG_TAG NULL
+#endif
+
+// ---------------------------------------------------------------------
+
+/*
+ * Simplified macro to send a verbose log message using the current LOG_TAG.
+ */
+#ifndef LOGV
+#if LOG_NDEBUG
+#define LOGV(...) ((void)0)
+#else
+#define LOGV(...) ((void)LOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__))
+#endif
+#endif
+
+#define CONDITION(cond) (__builtin_expect((cond)!=0, 0))
+
+#ifndef LOGV_IF
+#if LOG_NDEBUG
+#define LOGV_IF(cond, ...) ((void)0)
+#else
+#define LOGV_IF(cond, ...) \
+ ( (CONDITION(cond)) \
+ ? ((void)LOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) \
+ : (void)0 )
+#endif
+#endif
+
+/*
+ * Simplified macro to send a debug log message using the current LOG_TAG.
+ */
+#ifndef LOGD
+#define LOGD(...) ((void)LOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__))
+#endif
+
+#ifndef LOGD_IF
+#define LOGD_IF(cond, ...) \
+ ( (CONDITION(cond)) \
+ ? ((void)LOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__)) \
+ : (void)0 )
+#endif
+
+/*
+ * Simplified macro to send an info log message using the current LOG_TAG.
+ */
+#ifndef LOGI
+#define LOGI(...) ((void)LOG(LOG_INFO, LOG_TAG, __VA_ARGS__))
+#endif
+
+#ifndef LOGI_IF
+#define LOGI_IF(cond, ...) \
+ ( (CONDITION(cond)) \
+ ? ((void)LOG(LOG_INFO, LOG_TAG, __VA_ARGS__)) \
+ : (void)0 )
+#endif
+
+/*
+ * Simplified macro to send a warning log message using the current LOG_TAG.
+ */
+#ifndef LOGW
+#define LOGW(...) ((void)LOG(LOG_WARN, LOG_TAG, __VA_ARGS__))
+#endif
+
+#ifndef LOGW_IF
+#define LOGW_IF(cond, ...) \
+ ( (CONDITION(cond)) \
+ ? ((void)LOG(LOG_WARN, LOG_TAG, __VA_ARGS__)) \
+ : (void)0 )
+#endif
+
+/*
+ * Simplified macro to send an error log message using the current LOG_TAG.
+ */
+#ifndef LOGE
+#define LOGE(...) ((void)LOG(LOG_ERROR, LOG_TAG, __VA_ARGS__))
+#endif
+
+#ifndef LOGE_IF
+#define LOGE_IF(cond, ...) \
+ ( (CONDITION(cond)) \
+ ? ((void)LOG(LOG_ERROR, LOG_TAG, __VA_ARGS__)) \
+ : (void)0 )
+#endif
+
+// ---------------------------------------------------------------------
+
+/*
+ * Conditional based on whether the current LOG_TAG is enabled at
+ * verbose priority.
+ */
+#ifndef IF_LOGV
+#if LOG_NDEBUG
+#define IF_LOGV() if (false)
+#else
+#define IF_LOGV() IF_LOG(LOG_VERBOSE, LOG_TAG)
+#endif
+#endif
+
+/*
+ * Conditional based on whether the current LOG_TAG is enabled at
+ * debug priority.
+ */
+#ifndef IF_LOGD
+#define IF_LOGD() IF_LOG(LOG_DEBUG, LOG_TAG)
+#endif
+
+/*
+ * Conditional based on whether the current LOG_TAG is enabled at
+ * info priority.
+ */
+#ifndef IF_LOGI
+#define IF_LOGI() IF_LOG(LOG_INFO, LOG_TAG)
+#endif
+
+/*
+ * Conditional based on whether the current LOG_TAG is enabled at
+ * warn priority.
+ */
+#ifndef IF_LOGW
+#define IF_LOGW() IF_LOG(LOG_WARN, LOG_TAG)
+#endif
+
+/*
+ * Conditional based on whether the current LOG_TAG is enabled at
+ * error priority.
+ */
+#ifndef IF_LOGE
+#define IF_LOGE() IF_LOG(LOG_ERROR, LOG_TAG)
+#endif
+
+
+// ---------------------------------------------------------------------
+
+/*
+ * Simplified macro to send a verbose system log message using the current LOG_TAG.
+ */
+#ifndef SLOGV
+#if LOG_NDEBUG
+#define SLOGV(...) ((void)0)
+#else
+#define SLOGV(...) ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__))
+#endif
+#endif
+
+#define CONDITION(cond) (__builtin_expect((cond)!=0, 0))
+
+#ifndef SLOGV_IF
+#if LOG_NDEBUG
+#define SLOGV_IF(cond, ...) ((void)0)
+#else
+#define SLOGV_IF(cond, ...) \
+ ( (CONDITION(cond)) \
+ ? ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) \
+ : (void)0 )
+#endif
+#endif
+
+/*
+ * Simplified macro to send a debug system log message using the current LOG_TAG.
+ */
+#ifndef SLOGD
+#define SLOGD(...) ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__))
+#endif
+
+#ifndef SLOGD_IF
+#define SLOGD_IF(cond, ...) \
+ ( (CONDITION(cond)) \
+ ? ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)) \
+ : (void)0 )
+#endif
+
+/*
+ * Simplified macro to send an info system log message using the current LOG_TAG.
+ */
+#ifndef SLOGI
+#define SLOGI(...) ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__))
+#endif
+
+#ifndef SLOGI_IF
+#define SLOGI_IF(cond, ...) \
+ ( (CONDITION(cond)) \
+ ? ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)) \
+ : (void)0 )
+#endif
+
+/*
+ * Simplified macro to send a warning system log message using the current LOG_TAG.
+ */
+#ifndef SLOGW
+#define SLOGW(...) ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__))
+#endif
+
+#ifndef SLOGW_IF
+#define SLOGW_IF(cond, ...) \
+ ( (CONDITION(cond)) \
+ ? ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)) \
+ : (void)0 )
+#endif
+
+/*
+ * Simplified macro to send an error system log message using the current LOG_TAG.
+ */
+#ifndef SLOGE
+#define SLOGE(...) ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__))
+#endif
+
+#ifndef SLOGE_IF
+#define SLOGE_IF(cond, ...) \
+ ( (CONDITION(cond)) \
+ ? ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)) \
+ : (void)0 )
+#endif
+
+
+
+// ---------------------------------------------------------------------
+
+/*
+ * Log a fatal error. If the given condition fails, this stops program
+ * execution like a normal assertion, but also generating the given message.
+ * It is NOT stripped from release builds. Note that the condition test
+ * is -inverted- from the normal assert() semantics.
+ */
+#define LOG_ALWAYS_FATAL_IF(cond, ...) \
+ ( (CONDITION(cond)) \
+ ? ((void)android_printAssert(#cond, LOG_TAG, __VA_ARGS__)) \
+ : (void)0 )
+
+#define LOG_ALWAYS_FATAL(...) \
+ ( ((void)android_printAssert(NULL, LOG_TAG, __VA_ARGS__)) )
+
+/*
+ * Versions of LOG_ALWAYS_FATAL_IF and LOG_ALWAYS_FATAL that
+ * are stripped out of release builds.
+ */
+#if LOG_NDEBUG
+
+#define LOG_FATAL_IF(cond, ...) ((void)0)
+#define LOG_FATAL(...) ((void)0)
+
+#else
+
+#define LOG_FATAL_IF(cond, ...) LOG_ALWAYS_FATAL_IF(cond, __VA_ARGS__)
+#define LOG_FATAL(...) LOG_ALWAYS_FATAL(__VA_ARGS__)
+
+#endif
+
+/*
+ * Assertion that generates a log message when the assertion fails.
+ * Stripped out of release builds. Uses the current LOG_TAG.
+ */
+#define LOG_ASSERT(cond, ...) LOG_FATAL_IF(!(cond), __VA_ARGS__)
+//#define LOG_ASSERT(cond) LOG_FATAL_IF(!(cond), "Assertion failed: " #cond)
+
+// ---------------------------------------------------------------------
+
+/*
+ * Basic log message macro.
+ *
+ * Example:
+ * LOG(LOG_WARN, NULL, "Failed with error %d", errno);
+ *
+ * The second argument may be NULL or "" to indicate the "global" tag.
+ */
+#ifndef LOG
+#define LOG(priority, tag, ...) \
+ LOG_PRI(ANDROID_##priority, tag, __VA_ARGS__)
+#endif
+
+/*
+ * Log macro that allows you to specify a number for the priority.
+ */
+#ifndef LOG_PRI
+#define LOG_PRI(priority, tag, ...) \
+ android_printLog(priority, tag, __VA_ARGS__)
+#endif
+
+/*
+ * Log macro that allows you to pass in a varargs ("args" is a va_list).
+ */
+#ifndef LOG_PRI_VA
+#define LOG_PRI_VA(priority, tag, fmt, args) \
+ android_vprintLog(priority, NULL, tag, fmt, args)
+#endif
+
+/*
+ * Conditional given a desired logging priority and tag.
+ */
+#ifndef IF_LOG
+#define IF_LOG(priority, tag) \
+ if (android_testLog(ANDROID_##priority, tag))
+#endif
+
+// ---------------------------------------------------------------------
+
+/*
+ * Event logging.
+ */
+
+/*
+ * Event log entry types. These must match up with the declarations in
+ * java/android/android/util/EventLog.java.
+ */
+typedef enum {
+ EVENT_TYPE_INT = 0,
+ EVENT_TYPE_LONG = 1,
+ EVENT_TYPE_STRING = 2,
+ EVENT_TYPE_LIST = 3,
+} AndroidEventLogType;
+
+
+#define LOG_EVENT_INT(_tag, _value) { \
+ int intBuf = _value; \
+ (void) android_btWriteLog(_tag, EVENT_TYPE_INT, &intBuf, \
+ sizeof(intBuf)); \
+ }
+#define LOG_EVENT_LONG(_tag, _value) { \
+ long long longBuf = _value; \
+ (void) android_btWriteLog(_tag, EVENT_TYPE_LONG, &longBuf, \
+ sizeof(longBuf)); \
+ }
+#define LOG_EVENT_STRING(_tag, _value) \
+ ((void) 0) /* not implemented -- must combine len with string */
+/* TODO: something for LIST */
+
+/*
+ * ===========================================================================
+ *
+ * The stuff in the rest of this file should not be used directly.
+ */
+
+#define android_printLog(prio, tag, fmt...) \
+ __android_log_print(prio, tag, fmt)
+
+#define android_vprintLog(prio, cond, tag, fmt...) \
+ __android_log_vprint(prio, tag, fmt)
+
+#define android_printAssert(cond, tag, fmt...) \
+ __android_log_assert(cond, tag, fmt)
+
+#define android_writeLog(prio, tag, text) \
+ __android_log_write(prio, tag, text)
+
+#define android_bWriteLog(tag, payload, len) \
+ __android_log_bwrite(tag, payload, len)
+#define android_btWriteLog(tag, type, payload, len) \
+ __android_log_btwrite(tag, type, payload, len)
+
+// TODO: remove these prototypes and their users
+#define android_testLog(prio, tag) (1)
+#define android_writevLog(vec,num) do{}while(0)
+#define android_write1Log(str,len) do{}while (0)
+#define android_setMinPriority(tag, prio) do{}while(0)
+//#define android_logToCallback(func) do{}while(0)
+#define android_logToFile(tag, file) (0)
+#define android_logToFd(tag, fd) (0)
+
+typedef enum {
+ LOG_ID_MAIN = 0,
+ LOG_ID_RADIO = 1,
+ LOG_ID_EVENTS = 2,
+ LOG_ID_SYSTEM = 3,
+
+ LOG_ID_MAX
+} log_id_t;
+
+/*
+ * Send a simple string to the log.
+ */
+int __android_log_buf_write(int bufID, int prio, const char *tag, const char *text);
+int __android_log_buf_print(int bufID, int prio, const char *tag, const char *fmt, ...);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _LIBS_CUTILS_LOG_H
diff --git a/include/cutils/logd.h b/include/cutils/logd.h
new file mode 100644
index 0000000..8737639
--- /dev/null
+++ b/include/cutils/logd.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _ANDROID_CUTILS_LOGD_H
+#define _ANDROID_CUTILS_LOGD_H
+
+/* the stable/frozen log-related definitions have been
+ * moved to this header, which is exposed by the NDK
+ */
+#include <android/log.h>
+
+/* the rest is only used internally by the system */
+#include <time.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <sys/types.h>
+#ifdef HAVE_PTHREADS
+#include <pthread.h>
+#endif
+#include <cutils/uio.h>
+#include <stdarg.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int __android_log_bwrite(int32_t tag, const void *payload, size_t len);
+int __android_log_btwrite(int32_t tag, char type, const void *payload,
+ size_t len);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LOGD_H */
diff --git a/include/cutils/logger.h b/include/cutils/logger.h
new file mode 100644
index 0000000..b60f7ad
--- /dev/null
+++ b/include/cutils/logger.h
@@ -0,0 +1,47 @@
+/* utils/logger.h
+**
+** Copyright 2007, The Android Open Source Project
+**
+** This file is dual licensed. It may be redistributed and/or modified
+** under the terms of the Apache 2.0 License OR version 2 of the GNU
+** General Public License.
+*/
+
+#ifndef _UTILS_LOGGER_H
+#define _UTILS_LOGGER_H
+
+#include <stdint.h>
+
+struct logger_entry {
+ uint16_t len; /* length of the payload */
+ uint16_t __pad; /* no matter what, we get 2 bytes of padding */
+ int32_t pid; /* generating process's pid */
+ int32_t tid; /* generating process's tid */
+ int32_t sec; /* seconds since Epoch */
+ int32_t nsec; /* nanoseconds */
+ char msg[0]; /* the entry's payload */
+};
+
+#define LOGGER_LOG_MAIN "log/main"
+#define LOGGER_LOG_RADIO "log/radio"
+#define LOGGER_LOG_EVENTS "log/events"
+#define LOGGER_LOG_SYSTEM "log/system"
+
+#define LOGGER_ENTRY_MAX_LEN (4*1024)
+#define LOGGER_ENTRY_MAX_PAYLOAD \
+ (LOGGER_ENTRY_MAX_LEN - sizeof(struct logger_entry))
+
+#ifdef HAVE_IOCTL
+
+#include <sys/ioctl.h>
+
+#define __LOGGERIO 0xAE
+
+#define LOGGER_GET_LOG_BUF_SIZE _IO(__LOGGERIO, 1) /* size of log */
+#define LOGGER_GET_LOG_LEN _IO(__LOGGERIO, 2) /* used log len */
+#define LOGGER_GET_NEXT_ENTRY_LEN _IO(__LOGGERIO, 3) /* next entry len */
+#define LOGGER_FLUSH_LOG _IO(__LOGGERIO, 4) /* flush log */
+
+#endif // HAVE_IOCTL
+
+#endif /* _UTILS_LOGGER_H */
diff --git a/include/cutils/logprint.h b/include/cutils/logprint.h
new file mode 100644
index 0000000..1102154
--- /dev/null
+++ b/include/cutils/logprint.h
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LOGPRINT_H
+#define _LOGPRINT_H
+
+#include <cutils/log.h>
+#include <cutils/logger.h>
+#include <cutils/event_tag_map.h>
+#include <pthread.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+ FORMAT_OFF = 0,
+ FORMAT_BRIEF,
+ FORMAT_PROCESS,
+ FORMAT_TAG,
+ FORMAT_THREAD,
+ FORMAT_RAW,
+ FORMAT_TIME,
+ FORMAT_THREADTIME,
+ FORMAT_LONG,
+} AndroidLogPrintFormat;
+
+typedef enum {
+ OUTPUT_COLOR_ON = 0,
+ OUTPUT_COLOR_OFF,
+} AndroidLogColoredOutput;
+
+typedef struct AndroidLogFormat_t AndroidLogFormat;
+
+typedef struct AndroidLogEntry_t {
+ time_t tv_sec;
+ long tv_nsec;
+ android_LogPriority priority;
+ pid_t pid;
+ pthread_t tid;
+ const char * tag;
+ size_t messageLen;
+ const char * message;
+} AndroidLogEntry;
+
+AndroidLogFormat *android_log_format_new();
+
+void android_log_format_free(AndroidLogFormat *p_format);
+
+void android_log_setPrintFormat(AndroidLogFormat *p_format,
+ AndroidLogPrintFormat format);
+
+void android_log_setColoredOutput(AndroidLogFormat *p_format);
+
+/**
+ * Returns FORMAT_OFF on invalid string
+ */
+AndroidLogPrintFormat android_log_formatFromString(const char *s);
+
+/**
+ * filterExpression: a single filter expression
+ * eg "AT:d"
+ *
+ * returns 0 on success and -1 on invalid expression
+ *
+ * Assumes single threaded execution
+ *
+ */
+
+int android_log_addFilterRule(AndroidLogFormat *p_format,
+ const char *filterExpression);
+
+
+/**
+ * filterString: a whitespace-separated set of filter expressions
+ * eg "AT:d *:i"
+ *
+ * returns 0 on success and -1 on invalid expression
+ *
+ * Assumes single threaded execution
+ *
+ */
+
+int android_log_addFilterString(AndroidLogFormat *p_format,
+ const char *filterString);
+
+
+/**
+ * returns 1 if this log line should be printed based on its priority
+ * and tag, and 0 if it should not
+ */
+int android_log_shouldPrintLine (
+ AndroidLogFormat *p_format, const char *tag, android_LogPriority pri);
+
+
+/**
+ * Splits a wire-format buffer into an AndroidLogEntry
+ * entry allocated by caller. Pointers will point directly into buf
+ *
+ * Returns 0 on success and -1 on invalid wire format (entry will be
+ * in unspecified state)
+ */
+int android_log_processLogBuffer(struct logger_entry *buf,
+ AndroidLogEntry *entry);
+
+/**
+ * Like android_log_processLogBuffer, but for binary logs.
+ *
+ * If "map" is non-NULL, it will be used to convert the log tag number
+ * into a string.
+ */
+int android_log_processBinaryLogBuffer(struct logger_entry *buf,
+ AndroidLogEntry *entry, const EventTagMap* map, char* messageBuf,
+ int messageBufLen);
+
+
+/**
+ * Formats a log message into a buffer
+ *
+ * Uses defaultBuffer if it can, otherwise malloc()'s a new buffer
+ * If return value != defaultBuffer, caller must call free()
+ * Returns NULL on malloc error
+ */
+
+char *android_log_formatLogLine (
+ AndroidLogFormat *p_format,
+ char *defaultBuffer,
+ size_t defaultBufferSize,
+ const AndroidLogEntry *p_line,
+ size_t *p_outLength);
+
+
+/**
+ * Either print or do not print log line, based on filter
+ *
+ * Assumes single threaded execution
+ *
+ */
+int android_log_printLogLine(
+ AndroidLogFormat *p_format,
+ int fd,
+ const AndroidLogEntry *entry);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /*_LOGPRINT_H*/
diff --git a/include/cutils/memory.h b/include/cutils/memory.h
new file mode 100644
index 0000000..e725cdd
--- /dev/null
+++ b/include/cutils/memory.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_CUTILS_MEMORY_H
+#define ANDROID_CUTILS_MEMORY_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* size is given in bytes and must be multiple of 2 */
+void android_memset16(uint16_t* dst, uint16_t value, size_t size);
+
+/* size is given in bytes and must be multiple of 4 */
+void android_memset32(uint32_t* dst, uint32_t value, size_t size);
+
+#if !HAVE_STRLCPY
+/* Declaration of strlcpy() for platforms that don't already have it. */
+size_t strlcpy(char *dst, const char *src, size_t size);
+#endif
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // ANDROID_CUTILS_MEMORY_H
diff --git a/include/cutils/misc.h b/include/cutils/misc.h
new file mode 100644
index 0000000..2c48dfa
--- /dev/null
+++ b/include/cutils/misc.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CUTILS_MISC_H
+#define __CUTILS_MISC_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ /* Load an entire file into a malloc'd chunk of memory
+ * that is length_of_file + 1 (null terminator). If
+ * sz is non-zero, return the size of the file via sz.
+ * Returns 0 on failure.
+ */
+extern void *load_file(const char *fn, unsigned *sz);
+
+ /* Connects your process to the system debugger daemon
+ * so that on a crash it may be logged or interactively
+ * debugged (depending on system settings).
+ */
+extern void debuggerd_connect(void);
+
+
+ /* This is the range of UIDs (and GIDs) that are reserved
+ * for assigning to applications.
+ */
+#define FIRST_APPLICATION_UID 10000
+#define LAST_APPLICATION_UID 99999
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CUTILS_MISC_H */
diff --git a/include/cutils/mq.h b/include/cutils/mq.h
new file mode 100644
index 0000000..b27456d
--- /dev/null
+++ b/include/cutils/mq.h
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * IPC messaging library.
+ */
+
+#ifndef __MQ_H
+#define __MQ_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** A message. */
+typedef struct MqMessage MqMessage;
+
+/** A destination to which messages can be sent. */
+typedef struct MqDestination MqDestination;
+
+/* Array of bytes. */
+typedef struct MqBytes MqBytes;
+
+/**
+ * Hears messages.
+ *
+ * @param destination to which the message was sent
+ * @param message the message to hear
+ */
+typedef void MqMessageListener(MqDestination* destination, MqMessage* message);
+
+/**
+ * Hears a destination close.
+ *
+ * @param destination that closed
+ */
+typedef void MqCloseListener(MqDestination* destination);
+
+/** Message functions. */
+
+/**
+ * Creates a new Message.
+ *
+ * @param header as defined by user
+ * @param body as defined by user
+ * @param replyTo destination to which replies should be sent, NULL if none
+ */
+MqMessage* mqCreateMessage(MqBytes header, MqBytes body,
+ MqDestination* replyTo);
+
+/** Sends a message to a destination. */
+void mqSendMessage(MqMessage* message, MqDestination* destination);
+
+/** Destination functions. */
+
+/**
+ * Creates a new destination. Acquires a reference implicitly.
+ *
+ * @param messageListener function to call when a message is recieved
+ * @param closeListener function to call when the destination closes
+ * @param userData user-specific data to associate with the destination.
+ * Retrieve using mqGetDestinationUserData().
+ */
+MqDestination* mqCreateDestination(MqMessageListener* messageListener,
+ MqCloseListener* closeListener, void* userData);
+
+/**
+ * Gets user data which was associated with the given destination at
+ * construction time.
+ *
+ * It is only valid to call this function in the same process that the
+ * given destination was created in.
+ * This function returns a null pointer if you call it on a destination
+ * created in a remote process.
+ */
+void* mqGetUserData(MqDestination* destination);
+
+/**
+ * Returns 1 if the destination was created in this process, or 0 if
+ * the destination was created in a different process, in which case you have
+ * a remote stub.
+ */
+int mqIsDestinationLocal(MqDestination* destination);
+
+/**
+ * Increments the destination's reference count.
+ */
+void mqKeepDestination(MqDesintation* destination);
+
+/**
+ * Decrements the destination's reference count.
+ */
+void mqFreeDestination(MqDestination* desintation);
+
+/** Registry API. */
+
+/**
+ * Gets the destination bound to a name.
+ */
+MqDestination* mqGetDestination(char* name);
+
+/**
+ * Binds a destination to a name.
+ */
+void mqPutDestination(char* name, MqDestination* desintation);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __MQ_H */
diff --git a/include/cutils/mspace.h b/include/cutils/mspace.h
new file mode 100644
index 0000000..93fe48e
--- /dev/null
+++ b/include/cutils/mspace.h
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* A wrapper file for dlmalloc.h that defines prototypes for the
+ * mspace_*() functions, which provide an interface for creating
+ * multiple heaps.
+ */
+
+#ifndef MSPACE_H_
+#define MSPACE_H_
+
+/* It's a pain getting the mallinfo stuff to work
+ * with Linux, OSX, and klibc, so just turn it off
+ * for now.
+ * TODO: make mallinfo work
+ */
+#define NO_MALLINFO 1
+
+/* Allow setting the maximum heap footprint.
+ */
+#define USE_MAX_ALLOWED_FOOTPRINT 1
+
+#define USE_CONTIGUOUS_MSPACES 1
+#if USE_CONTIGUOUS_MSPACES
+#define HAVE_MMAP 0
+#define HAVE_MORECORE 1
+#define MORECORE_CONTIGUOUS 0
+#endif
+
+#define MSPACES 1
+#define ONLY_MSPACES 1
+#include "../../../../bionic/libc/bionic/dlmalloc.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ mspace_usable_size(void* p);
+
+ Returns the number of bytes you can actually use in
+ an allocated chunk, which may be more than you requested (although
+ often not) due to alignment and minimum size constraints.
+ You can use this many bytes without worrying about
+ overwriting other allocated objects. This is not a particularly great
+ programming practice. mspace_usable_size can be more useful in
+ debugging and assertions, for example:
+
+ p = mspace_malloc(msp, n);
+ assert(mspace_usable_size(msp, p) >= 256);
+*/
+size_t mspace_usable_size(mspace, const void*);
+
+#if USE_CONTIGUOUS_MSPACES
+/*
+ Similar to create_mspace(), but the underlying memory is
+ guaranteed to be contiguous. No more than max_capacity
+ bytes is ever allocated to the mspace.
+ */
+mspace create_contiguous_mspace(size_t starting_capacity, size_t max_capacity,
+ int locked);
+
+/*
+ Identical to create_contiguous_mspace, but labels the mapping 'mspace/name'
+ instead of 'mspace'
+*/
+mspace create_contiguous_mspace_with_name(size_t starting_capacity,
+ size_t max_capacity, int locked, const char *name);
+
+/*
+ Identical to create_contiguous_mspace, but uses previously mapped memory.
+*/
+mspace create_contiguous_mspace_with_base(size_t starting_capacity,
+ size_t max_capacity, int locked, void *base);
+
+size_t destroy_contiguous_mspace(mspace msp);
+
+/*
+ Returns the position of the "break" within the given mspace.
+*/
+void *contiguous_mspace_sbrk0(mspace msp);
+#endif
+
+/*
+ Call the handler for each block in the specified mspace.
+ chunkptr and chunklen refer to the heap-level chunk including
+ the chunk overhead, and userptr and userlen refer to the
+ user-usable part of the chunk. If the chunk is free, userptr
+ will be NULL and userlen will be 0. userlen is not guaranteed
+ to be the same value passed into malloc() for a given chunk;
+ it is >= the requested size.
+ */
+void mspace_walk_heap(mspace msp,
+ void(*handler)(const void *chunkptr, size_t chunklen,
+ const void *userptr, size_t userlen, void *arg), void *harg);
+
+/*
+ mspace_walk_free_pages(handler, harg)
+
+ Calls the provided handler on each free region in the specified
+ mspace. The memory between start and end are guaranteed not to
+ contain any important data, so the handler is free to alter the
+ contents in any way. This can be used to advise the OS that large
+ free regions may be swapped out.
+
+ The value in harg will be passed to each call of the handler.
+ */
+void mspace_walk_free_pages(mspace msp,
+ void(*handler)(void *start, void *end, void *arg), void *harg);
+
+#ifdef __cplusplus
+}; /* end of extern "C" */
+#endif
+
+#endif /* MSPACE_H_ */
diff --git a/include/cutils/native_handle.h b/include/cutils/native_handle.h
new file mode 100644
index 0000000..89d6b65
--- /dev/null
+++ b/include/cutils/native_handle.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2009 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 NATIVE_HANDLE_H_
+#define NATIVE_HANDLE_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct
+{
+ int version; /* sizeof(native_handle_t) */
+ int numFds; /* number of file-descriptors at &data[0] */
+ int numInts; /* number of ints at &data[numFds] */
+ int data[0]; /* numFds + numInts ints */
+} native_handle_t;
+
+
+/* keep the old definition for backward source-compatibility */
+typedef native_handle_t native_handle;
+
+/*
+ * native_handle_close
+ *
+ * closes the file descriptors contained in this native_handle_t
+ *
+ * return 0 on success, or a negative error code on failure
+ *
+ */
+int native_handle_close(const native_handle_t* h);
+
+
+/*
+ * native_handle_create
+ *
+ * creates a native_handle_t and initializes it. must be destroyed with
+ * native_handle_delete().
+ *
+ */
+native_handle_t* native_handle_create(int numFds, int numInts);
+
+/*
+ * native_handle_delete
+ *
+ * frees a native_handle_t allocated with native_handle_create().
+ * This ONLY frees the memory allocated for the native_handle_t, but doesn't
+ * close the file descriptors; which can be achieved with native_handle_close().
+ *
+ * return 0 on success, or a negative error code on failure
+ *
+ */
+int native_handle_delete(native_handle_t* h);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* NATIVE_HANDLE_H_ */
diff --git a/include/cutils/open_memstream.h b/include/cutils/open_memstream.h
new file mode 100644
index 0000000..b7998be
--- /dev/null
+++ b/include/cutils/open_memstream.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CUTILS_OPEN_MEMSTREAM_H__
+#define __CUTILS_OPEN_MEMSTREAM_H__
+
+#include <stdio.h>
+
+#ifndef HAVE_OPEN_MEMSTREAM
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+FILE* open_memstream(char** bufp, size_t* sizep);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /*!HAVE_OPEN_MEMSTREAM*/
+
+#endif /*__CUTILS_OPEN_MEMSTREAM_H__*/
diff --git a/include/cutils/process_name.h b/include/cutils/process_name.h
new file mode 100644
index 0000000..1e72e5c
--- /dev/null
+++ b/include/cutils/process_name.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Gives the current process a name.
+ */
+
+#ifndef __PROCESS_NAME_H
+#define __PROCESS_NAME_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Sets the current process name.
+ *
+ * Warning: This leaks a string every time you call it. Use judiciously!
+ */
+void set_process_name(const char* process_name);
+
+/** Gets the current process name. */
+const char* get_process_name(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __PROCESS_NAME_H */
diff --git a/include/cutils/properties.h b/include/cutils/properties.h
new file mode 100644
index 0000000..2869948
--- /dev/null
+++ b/include/cutils/properties.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CUTILS_PROPERTIES_H
+#define __CUTILS_PROPERTIES_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* System properties are *small* name value pairs managed by the
+** property service. If your data doesn't fit in the provided
+** space it is not appropriate for a system property.
+**
+** WARNING: system/bionic/include/sys/system_properties.h also defines
+** these, but with different names. (TODO: fix that)
+*/
+#define PROPERTY_KEY_MAX 32
+#define PROPERTY_VALUE_MAX 92
+
+/* property_get: returns the length of the value which will never be
+** greater than PROPERTY_VALUE_MAX - 1 and will always be zero terminated.
+** (the length does not include the terminating zero).
+**
+** If the property read fails or returns an empty value, the default
+** value is used (if nonnull).
+*/
+int property_get(const char *key, char *value, const char *default_value);
+
+/* property_set: returns 0 on success, < 0 on failure
+*/
+int property_set(const char *key, const char *value);
+
+/* property_set_sync: returns 0 on success, < 0 on failure
+**
+** synchronized version of property_set, ensure the property is set when
+** it returns to caller.
+*/
+int property_set_sync(const char *key, const char *value);
+
+int property_list(void (*propfn)(const char *key, const char *value, void *cookie), void *cookie);
+
+
+#ifdef HAVE_SYSTEM_PROPERTY_SERVER
+/*
+ * We have an external property server instead of built-in libc support.
+ * Used by the simulator.
+ */
+#define SYSTEM_PROPERTY_PIPE_NAME "/tmp/android-sysprop"
+
+enum {
+ kSystemPropertyUnknown = 0,
+ kSystemPropertyGet,
+ kSystemPropertySet,
+ kSystemPropertyList
+};
+#endif /*HAVE_SYSTEM_PROPERTY_SERVER*/
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/cutils/record_stream.h b/include/cutils/record_stream.h
new file mode 100644
index 0000000..bfac87a
--- /dev/null
+++ b/include/cutils/record_stream.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * A simple utility for reading fixed records out of a stream fd
+ */
+
+#ifndef _CUTILS_RECORD_STREAM_H
+#define _CUTILS_RECORD_STREAM_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+typedef struct RecordStream RecordStream;
+
+extern RecordStream *record_stream_new(int fd, size_t maxRecordLen);
+extern void record_stream_free(RecordStream *p_rs);
+
+extern int record_stream_get_next (RecordStream *p_rs, void ** p_outRecord,
+ size_t *p_outRecordLen);
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /*_CUTILS_RECORD_STREAM_H*/
+
diff --git a/include/cutils/sched_policy.h b/include/cutils/sched_policy.h
new file mode 100644
index 0000000..eaf3993
--- /dev/null
+++ b/include/cutils/sched_policy.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CUTILS_SCHED_POLICY_H
+#define __CUTILS_SCHED_POLICY_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+ SP_BACKGROUND = 0,
+ SP_FOREGROUND = 1,
+} SchedPolicy;
+
+extern int set_sched_policy(int tid, SchedPolicy policy);
+extern int get_sched_policy(int tid, SchedPolicy *policy);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CUTILS_SCHED_POLICY_H */
diff --git a/include/cutils/selector.h b/include/cutils/selector.h
new file mode 100644
index 0000000..dfc2a9d
--- /dev/null
+++ b/include/cutils/selector.h
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Framework for multiplexing I/O. A selector manages a set of file
+ * descriptors and calls out to user-provided callback functions to read and
+ * write data and handle errors.
+ */
+
+#ifndef __SELECTOR_H
+#define __SELECTOR_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdbool.h>
+
+/**
+ * Manages SelectableFds and invokes their callbacks at appropriate times.
+ */
+typedef struct Selector Selector;
+
+/**
+ * A selectable descriptor. Contains callbacks which the selector can invoke
+ * before calling select(), when the descriptor is readable or writable, and
+ * when the descriptor contains out-of-band data. Simply set a callback to
+ * NULL if you're not interested in that particular event.
+ *
+ * A selectable descriptor can indicate that it needs to be removed from the
+ * selector by setting the 'remove' flag. The selector will remove the
+ * descriptor at a later time and invoke the onRemove() callback.
+ *
+ * SelectableFd fields should only be modified from the selector loop.
+ */
+typedef struct SelectableFd SelectableFd;
+struct SelectableFd {
+
+ /** The file descriptor itself. */
+ int fd;
+
+ /** Pointer to user-specific data. Can be NULL. */
+ void* data;
+
+ /**
+ * Set this flag when you no longer wish to be selected. The selector
+ * will invoke onRemove() when the descriptor is actually removed.
+ */
+ bool remove;
+
+ /**
+ * Invoked by the selector before calling select. You can set up other
+ * callbacks from here as necessary.
+ */
+ void (*beforeSelect)(SelectableFd* self);
+
+ /**
+ * Invoked by the selector when the descriptor has data available. Set to
+ * NULL to indicate that you're not interested in reading.
+ */
+ void (*onReadable)(SelectableFd* self);
+
+ /**
+ * Invoked by the selector when the descriptor can accept data. Set to
+ * NULL to indicate that you're not interested in writing.
+ */
+ void (*onWritable)(SelectableFd* self);
+
+ /**
+ * Invoked by the selector when out-of-band (OOB) data is available. Set to
+ * NULL to indicate that you're not interested in OOB data.
+ */
+ void (*onExcept)(SelectableFd* self);
+
+ /**
+ * Invoked by the selector after the descriptor is removed from the
+ * selector but before the selector frees the SelectableFd memory.
+ */
+ void (*onRemove)(SelectableFd* self);
+
+ /**
+ * The selector which selected this fd. Set by the selector itself.
+ */
+ Selector* selector;
+};
+
+/**
+ * Creates a new selector.
+ */
+Selector* selectorCreate(void);
+
+/**
+ * Creates a new selectable fd, adds it to the given selector and returns a
+ * pointer. Outside of 'selector' and 'fd', all fields are set to 0 or NULL
+ * by default.
+ *
+ * The selectable fd should only be modified from the selector loop thread.
+ */
+SelectableFd* selectorAdd(Selector* selector, int fd);
+
+/**
+ * Wakes up the selector even though no I/O events occurred. Use this
+ * to indicate that you're ready to write to a descriptor.
+ */
+void selectorWakeUp(Selector* selector);
+
+/**
+ * Loops continuously selecting file descriptors and firing events.
+ * Does not return.
+ */
+void selectorLoop(Selector* selector);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __SELECTOR_H */
diff --git a/include/cutils/sockets.h b/include/cutils/sockets.h
new file mode 100644
index 0000000..aa8682e
--- /dev/null
+++ b/include/cutils/sockets.h
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CUTILS_SOCKETS_H
+#define __CUTILS_SOCKETS_H
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef HAVE_WINSOCK
+#include <winsock2.h>
+typedef int socklen_t;
+#elif HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
+#define ANDROID_SOCKET_ENV_PREFIX "ANDROID_SOCKET_"
+#define ANDROID_SOCKET_DIR "/dev/socket"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * android_get_control_socket - simple helper function to get the file
+ * descriptor of our init-managed Unix domain socket. `name' is the name of the
+ * socket, as given in init.rc. Returns -1 on error.
+ *
+ * This is inline and not in libcutils proper because we want to use this in
+ * third-party daemons with minimal modification.
+ */
+static inline int android_get_control_socket(const char *name)
+{
+ char key[64] = ANDROID_SOCKET_ENV_PREFIX;
+ const char *val;
+ int fd;
+
+ /* build our environment variable, counting cycles like a wolf ... */
+#if HAVE_STRLCPY
+ strlcpy(key + sizeof(ANDROID_SOCKET_ENV_PREFIX) - 1,
+ name,
+ sizeof(key) - sizeof(ANDROID_SOCKET_ENV_PREFIX));
+#else /* for the host, which may lack the almightly strncpy ... */
+ strncpy(key + sizeof(ANDROID_SOCKET_ENV_PREFIX) - 1,
+ name,
+ sizeof(key) - sizeof(ANDROID_SOCKET_ENV_PREFIX));
+ key[sizeof(key)-1] = '\0';
+#endif
+
+ val = getenv(key);
+ if (!val)
+ return -1;
+
+ errno = 0;
+ fd = strtol(val, NULL, 10);
+ if (errno)
+ return -1;
+
+ return fd;
+}
+
+/*
+ * See also android.os.LocalSocketAddress.Namespace
+ */
+// Linux "abstract" (non-filesystem) namespace
+#define ANDROID_SOCKET_NAMESPACE_ABSTRACT 0
+// Android "reserved" (/dev/socket) namespace
+#define ANDROID_SOCKET_NAMESPACE_RESERVED 1
+// Normal filesystem namespace
+#define ANDROID_SOCKET_NAMESPACE_FILESYSTEM 2
+
+extern int socket_loopback_client(int port, int type);
+extern int socket_network_client(const char *host, int port, int type);
+extern int socket_loopback_server(int port, int type);
+extern int socket_local_server(const char *name, int namespaceId, int type);
+extern int socket_local_server_bind(int s, const char *name, int namespaceId);
+extern int socket_local_client_connect(int fd,
+ const char *name, int namespaceId, int type);
+extern int socket_local_client(const char *name, int namespaceId, int type);
+extern int socket_inaddr_any_server(int port, int type);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CUTILS_SOCKETS_H */
diff --git a/include/cutils/threads.h b/include/cutils/threads.h
new file mode 100644
index 0000000..acf8f48
--- /dev/null
+++ b/include/cutils/threads.h
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBS_CUTILS_THREADS_H
+#define _LIBS_CUTILS_THREADS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/***********************************************************************/
+/***********************************************************************/
+/***** *****/
+/***** local thread storage *****/
+/***** *****/
+/***********************************************************************/
+/***********************************************************************/
+
+#ifdef HAVE_PTHREADS
+
+#include <pthread.h>
+
+typedef struct {
+ pthread_mutex_t lock;
+ int has_tls;
+ pthread_key_t tls;
+
+} thread_store_t;
+
+#define THREAD_STORE_INITIALIZER { PTHREAD_MUTEX_INITIALIZER, 0, 0 }
+
+#elif defined HAVE_WIN32_THREADS
+
+#include <windows.h>
+
+typedef struct {
+ int lock_init;
+ int has_tls;
+ DWORD tls;
+ CRITICAL_SECTION lock;
+
+} thread_store_t;
+
+#define THREAD_STORE_INITIALIZER { 0, 0, 0, {0, 0, 0, 0, 0, 0} }
+
+#else
+# error "no thread_store_t implementation for your platform !!"
+#endif
+
+typedef void (*thread_store_destruct_t)(void* value);
+
+extern void* thread_store_get(thread_store_t* store);
+
+extern void thread_store_set(thread_store_t* store,
+ void* value,
+ thread_store_destruct_t destroy);
+
+/***********************************************************************/
+/***********************************************************************/
+/***** *****/
+/***** mutexes *****/
+/***** *****/
+/***********************************************************************/
+/***********************************************************************/
+
+#ifdef HAVE_PTHREADS
+
+typedef pthread_mutex_t mutex_t;
+
+#define MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER
+
+static __inline__ void mutex_lock(mutex_t* lock)
+{
+ pthread_mutex_lock(lock);
+}
+static __inline__ void mutex_unlock(mutex_t* lock)
+{
+ pthread_mutex_unlock(lock);
+}
+static __inline__ int mutex_init(mutex_t* lock)
+{
+ return pthread_mutex_init(lock, NULL);
+}
+static __inline__ void mutex_destroy(mutex_t* lock)
+{
+ pthread_mutex_destroy(lock);
+}
+#endif
+
+#ifdef HAVE_WIN32_THREADS
+typedef struct {
+ int init;
+ CRITICAL_SECTION lock[1];
+} mutex_t;
+
+#define MUTEX_INITIALIZER { 0, {{ NULL, 0, 0, NULL, NULL, 0 }} }
+
+static __inline__ void mutex_lock(mutex_t* lock)
+{
+ if (!lock->init) {
+ lock->init = 1;
+ InitializeCriticalSection( lock->lock );
+ lock->init = 2;
+ } else while (lock->init != 2)
+ Sleep(10);
+
+ EnterCriticalSection(lock->lock);
+}
+
+static __inline__ void mutex_unlock(mutex_t* lock)
+{
+ LeaveCriticalSection(lock->lock);
+}
+static __inline__ int mutex_init(mutex_t* lock)
+{
+ InitializeCriticalSection(lock->lock);
+ lock->init = 2;
+ return 0;
+}
+static __inline__ void mutex_destroy(mutex_t* lock)
+{
+ if (lock->init) {
+ lock->init = 0;
+ DeleteCriticalSection(lock->lock);
+ }
+}
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBS_CUTILS_THREADS_H */
diff --git a/include/cutils/tztime.h b/include/cutils/tztime.h
new file mode 100644
index 0000000..cf103ca
--- /dev/null
+++ b/include/cutils/tztime.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _CUTILS_TZTIME_H
+#define _CUTILS_TZTIME_H
+
+#include <time.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+time_t mktime_tz(struct tm * const tmp, char const * tz);
+void localtime_tz(const time_t * const timep, struct tm * tmp, const char* tz);
+
+#ifndef HAVE_ANDROID_OS
+/* the following is defined in <time.h> in Bionic */
+
+struct strftime_locale {
+ const char *mon[12]; /* short names */
+ const char *month[12]; /* long names */
+ const char *standalone_month[12]; /* long standalone names */
+ const char *wday[7]; /* short names */
+ const char *weekday[7]; /* long names */
+ const char *X_fmt;
+ const char *x_fmt;
+ const char *c_fmt;
+ const char *am;
+ const char *pm;
+ const char *date_fmt;
+};
+
+size_t strftime_tz(char *s, size_t max, const char *format, const struct tm *tm, const struct strftime_locale *locale);
+
+#endif /* !HAVE_ANDROID_OS */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CUTILS_TZTIME_H */
+
diff --git a/include/cutils/uio.h b/include/cutils/uio.h
new file mode 100644
index 0000000..01a74d2
--- /dev/null
+++ b/include/cutils/uio.h
@@ -0,0 +1,48 @@
+/*
+ * 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.
+ */
+
+//
+// implementation of sys/uio.h for platforms that don't have it (Win32)
+//
+#ifndef _LIBS_CUTILS_UIO_H
+#define _LIBS_CUTILS_UIO_H
+
+#ifdef HAVE_SYS_UIO_H
+#include <sys/uio.h>
+#else
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stddef.h>
+
+struct iovec {
+ const void* iov_base;
+ size_t iov_len;
+};
+
+extern int readv( int fd, struct iovec* vecs, int count );
+extern int writev( int fd, const struct iovec* vecs, int count );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !HAVE_SYS_UIO_H */
+
+#endif /* _LIBS_UTILS_UIO_H */
+
diff --git a/include/cutils/zygote.h b/include/cutils/zygote.h
new file mode 100644
index 0000000..22721a6
--- /dev/null
+++ b/include/cutils/zygote.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CUTILS_ZYGOTE_H
+#define __CUTILS_ZYGOTE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int zygote_run_oneshot(int sendStdio, int argc, const char **argv);
+int zygote_run(int argc, const char **argv);
+int zygote_run_wait(int argc, const char **argv, void (*post_run_func)(int));
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CUTILS_ZYGOTE_H */
diff --git a/include/zipfile/zipfile.h b/include/zipfile/zipfile.h
new file mode 100644
index 0000000..0ae4ee4
--- /dev/null
+++ b/include/zipfile/zipfile.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _ZIPFILE_ZIPFILE_H
+#define _ZIPFILE_ZIPFILE_H
+
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef void* zipfile_t;
+typedef void* zipentry_t;
+
+// Provide a buffer. Returns NULL on failure.
+zipfile_t init_zipfile(const void* data, size_t size);
+
+// Release the zipfile resources.
+void release_zipfile(zipfile_t file);
+
+// Get a named entry object. Returns NULL if it doesn't exist
+// or if we won't be able to decompress it. The zipentry_t is
+// freed by release_zipfile()
+zipentry_t lookup_zipentry(zipfile_t file, const char* entryName);
+
+// Return the size of the entry.
+size_t get_zipentry_size(zipentry_t entry);
+
+// return the filename of this entry, you own the memory returned
+char* get_zipentry_name(zipentry_t entry);
+
+// The buffer must be 1.001 times the buffer size returned
+// by get_zipentry_size. Returns nonzero on failure.
+int decompress_zipentry(zipentry_t entry, void* buf, int bufsize);
+
+// iterate through the entries in the zip file. pass a pointer to
+// a void* initialized to NULL to start. Returns NULL when done
+zipentry_t iterate_zipfile(zipfile_t file, void** cookie);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // _ZIPFILE_ZIPFILE_H
diff --git a/libcutils/Android.mk b/libcutils/Android.mk
new file mode 100644
index 0000000..dec41a5
--- /dev/null
+++ b/libcutils/Android.mk
@@ -0,0 +1,149 @@
+#
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+LOCAL_PATH := $(my-dir)
+include $(CLEAR_VARS)
+
+ifeq ($(TARGET_CPU_SMP),true)
+ targetSmpFlag := -DANDROID_SMP=1
+else
+ targetSmpFlag := -DANDROID_SMP=0
+endif
+hostSmpFlag := -DANDROID_SMP=0
+
+commonSources := \
+ array.c \
+ hashmap.c \
+ atomic.c.arm \
+ native_handle.c \
+ buffer.c \
+ socket_inaddr_any_server.c \
+ socket_local_client.c \
+ socket_local_server.c \
+ socket_loopback_client.c \
+ socket_loopback_server.c \
+ socket_network_client.c \
+ config_utils.c \
+ cpu_info.c \
+ load_file.c \
+ open_memstream.c \
+ strdup16to8.c \
+ strdup8to16.c \
+ record_stream.c \
+ process_name.c \
+ properties.c \
+ threads.c \
+ sched_policy.c \
+ iosched_policy.c
+
+commonHostSources := \
+ ashmem-host.c
+
+# some files must not be compiled when building against Mingw
+# they correspond to features not used by our host development tools
+# which are also hard or even impossible to port to native Win32
+WINDOWS_HOST_ONLY :=
+ifeq ($(HOST_OS),windows)
+ ifeq ($(strip $(USE_CYGWIN)),)
+ WINDOWS_HOST_ONLY := 1
+ endif
+endif
+# USE_MINGW is defined when we build against Mingw on Linux
+ifneq ($(strip $(USE_MINGW)),)
+ WINDOWS_HOST_ONLY := 1
+endif
+
+ifeq ($(WINDOWS_HOST_ONLY),1)
+ commonSources += \
+ uio.c
+else
+ commonSources += \
+ abort_socket.c \
+ mspace.c \
+ selector.c \
+ tztime.c \
+ zygote.c
+
+ commonHostSources += \
+ tzstrftime.c
+endif
+
+
+# Static library for host
+# ========================================================
+LOCAL_MODULE := libcutils
+LOCAL_SRC_FILES := $(commonSources) $(commonHostSources) dlmalloc_stubs.c
+LOCAL_LDLIBS := -lpthread
+LOCAL_STATIC_LIBRARIES := liblog
+LOCAL_CFLAGS += $(hostSmpFlag)
+include $(BUILD_HOST_STATIC_LIBRARY)
+
+
+ifeq ($(TARGET_SIMULATOR),true)
+
+# Shared library for simulator
+# ========================================================
+include $(CLEAR_VARS)
+LOCAL_MODULE := libcutils
+LOCAL_SRC_FILES := $(commonSources) $(commonHostSources) memory.c dlmalloc_stubs.c
+LOCAL_LDLIBS := -lpthread
+LOCAL_SHARED_LIBRARIES := liblog
+LOCAL_CFLAGS += $(targetSmpFlag)
+include $(BUILD_SHARED_LIBRARY)
+
+else #!sim
+
+# Shared and static library for target
+# ========================================================
+
+targetSources := ashmem-dev.c mq.c
+ifeq ($(TARGET_ARCH),arm)
+targetSources += arch-arm/memset32.S
+else # !arm
+ifeq ($(TARGET_ARCH),sh)
+targetSources += memory.c atomic-android-sh.c
+else # !sh
+ifeq ($(TARGET_ARCH_VARIANT),x86-atom)
+LOCAL_CFLAGS += -DHAVE_MEMSET16 -DHAVE_MEMSET32
+LOCAL_SRC_FILES += arch-x86/android_memset16.S arch-x86/android_memset32.S memory.c
+else # !x86-atom
+LOCAL_SRC_FILES += memory.c
+endif # !x86-atom
+endif # !sh
+endif # !arm
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := libcutils
+LOCAL_SRC_FILES := $(commonSources) $(targetSources)
+LOCAL_CFLAGS += $(targetCFLAGS) $(targetSmpFlag)
+
+LOCAL_C_INCLUDES := $(KERNEL_HEADERS)
+LOCAL_STATIC_LIBRARIES := liblog
+include $(BUILD_STATIC_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := libcutils
+ifeq ($(BOARD_NEEDS_CUTILS_LOG),true)
+LOCAL_WHOLE_STATIC_LIBRARIES := libcutils
+else
+LOCAL_SRC_FILES := $(commonSources) $(targetSources)
+endif
+LOCAL_CFLAGS += $(targetCFLAGS) $(targetSmpFlag)
+
+LOCAL_C_INCLUDES := $(KERNEL_HEADERS)
+LOCAL_SHARED_LIBRARIES := liblog
+include $(BUILD_SHARED_LIBRARY)
+
+endif #!sim
diff --git a/libcutils/MODULE_LICENSE_APACHE2 b/libcutils/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/libcutils/MODULE_LICENSE_APACHE2
diff --git a/libcutils/Makefile.am b/libcutils/Makefile.am
new file mode 100644
index 0000000..e08d4dd
--- /dev/null
+++ b/libcutils/Makefile.am
@@ -0,0 +1,49 @@
+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
+
+lib_LTLIBRARIES = libcutils.la
+
+libcutils_la_SOURCES = \
+array.c \
+hashmap.c \
+native_handle.c \
+buffer.c \
+socket_inaddr_any_server.c \
+socket_local_client.c \
+socket_local_server.c \
+socket_loopback_client.c \
+socket_loopback_server.c \
+socket_network_client.c \
+config_utils.c \
+cpu_info.c \
+load_file.c \
+open_memstream.c \
+strdup16to8.c \
+strdup8to16.c \
+record_stream.c \
+process_name.c \
+properties.c \
+threads.c \
+sched_policy.c \
+iosched_policy.c \
+ashmem-host.c \
+abort_socket.c \
+mspace.c \
+selector.c \
+tztime.c \
+tzstrftime.c \
+dlmalloc_stubs.c
+
+libcutils_la_CFLAGS = -DHAVE_PTHREADS -DANDROID_SMP=0
+libcutils_la_LIBADD =
+libcutils_la_LDFLAGS =
+
+docdir = $(datadir)/doc/cutils
+doc_DATA = NOTICE
diff --git a/libcutils/NOTICE b/libcutils/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /dev/null
+++ b/libcutils/NOTICE
@@ -0,0 +1,190 @@
+
+ Copyright (c) 2005-2008, The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
diff --git a/libcutils/abort_socket.c b/libcutils/abort_socket.c
new file mode 100644
index 0000000..6a5e5e4
--- /dev/null
+++ b/libcutils/abort_socket.c
@@ -0,0 +1,293 @@
+/*
+ * Copyright 2009, 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 <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <sys/poll.h>
+
+#include "cutils/abort_socket.h"
+
+struct asocket *asocket_init(int fd) {
+ int abort_fd[2];
+ int flags;
+ struct asocket *s;
+
+ /* set primary socket to non-blocking */
+ flags = fcntl(fd, F_GETFL);
+ if (flags == -1)
+ return NULL;
+ if (fcntl(fd, F_SETFL, flags | O_NONBLOCK))
+ return NULL;
+
+ /* create pipe with non-blocking write, so that asocket_close() cannot
+ block */
+ if (pipe(abort_fd))
+ return NULL;
+ flags = fcntl(abort_fd[1], F_GETFL);
+ if (flags == -1)
+ return NULL;
+ if (fcntl(abort_fd[1], F_SETFL, flags | O_NONBLOCK))
+ return NULL;
+
+ s = malloc(sizeof(struct asocket));
+ if (!s)
+ return NULL;
+
+ s->fd = fd;
+ s->abort_fd[0] = abort_fd[0];
+ s->abort_fd[1] = abort_fd[1];
+
+ return s;
+}
+
+int asocket_connect(struct asocket *s, const struct sockaddr *addr,
+ socklen_t addrlen, int timeout) {
+
+ int ret;
+
+ do {
+ ret = connect(s->fd, addr, addrlen);
+ } while (ret && errno == EINTR);
+
+ if (ret && errno == EINPROGRESS) {
+ /* ready to poll() */
+ socklen_t retlen;
+ struct pollfd pfd[2];
+
+ pfd[0].fd = s->fd;
+ pfd[0].events = POLLOUT;
+ pfd[0].revents = 0;
+ pfd[1].fd = s->abort_fd[0];
+ pfd[1].events = POLLIN;
+ pfd[1].revents = 0;
+
+ do {
+ ret = poll(pfd, 2, timeout);
+ } while (ret < 0 && errno == EINTR);
+
+ if (ret < 0)
+ return -1;
+ else if (ret == 0) {
+ /* timeout */
+ errno = ETIMEDOUT;
+ return -1;
+ }
+
+ if (pfd[1].revents) {
+ /* abort due to asocket_abort() */
+ errno = ECANCELED;
+ return -1;
+ }
+
+ if (pfd[0].revents) {
+ if (pfd[0].revents & POLLOUT) {
+ /* connect call complete, read return code */
+ retlen = sizeof(ret);
+ if (getsockopt(s->fd, SOL_SOCKET, SO_ERROR, &ret, &retlen))
+ return -1;
+ /* got connect() return code */
+ if (ret) {
+ errno = ret;
+ }
+ } else {
+ /* some error event on this fd */
+ errno = ECONNABORTED;
+ return -1;
+ }
+ }
+ }
+
+ return ret;
+}
+
+int asocket_accept(struct asocket *s, struct sockaddr *addr,
+ socklen_t *addrlen, int timeout) {
+
+ int ret;
+ struct pollfd pfd[2];
+
+ pfd[0].fd = s->fd;
+ pfd[0].events = POLLIN;
+ pfd[0].revents = 0;
+ pfd[1].fd = s->abort_fd[0];
+ pfd[1].events = POLLIN;
+ pfd[1].revents = 0;
+
+ do {
+ ret = poll(pfd, 2, timeout);
+ } while (ret < 0 && errno == EINTR);
+
+ if (ret < 0)
+ return -1;
+ else if (ret == 0) {
+ /* timeout */
+ errno = ETIMEDOUT;
+ return -1;
+ }
+
+ if (pfd[1].revents) {
+ /* abort due to asocket_abort() */
+ errno = ECANCELED;
+ return -1;
+ }
+
+ if (pfd[0].revents) {
+ if (pfd[0].revents & POLLIN) {
+ /* ready to accept() without blocking */
+ do {
+ ret = accept(s->fd, addr, addrlen);
+ } while (ret < 0 && errno == EINTR);
+ } else {
+ /* some error event on this fd */
+ errno = ECONNABORTED;
+ return -1;
+ }
+ }
+
+ return ret;
+}
+
+int asocket_read(struct asocket *s, void *buf, size_t count, int timeout) {
+ int ret;
+ struct pollfd pfd[2];
+
+ pfd[0].fd = s->fd;
+ pfd[0].events = POLLIN;
+ pfd[0].revents = 0;
+ pfd[1].fd = s->abort_fd[0];
+ pfd[1].events = POLLIN;
+ pfd[1].revents = 0;
+
+ do {
+ ret = poll(pfd, 2, timeout);
+ } while (ret < 0 && errno == EINTR);
+
+ if (ret < 0)
+ return -1;
+ else if (ret == 0) {
+ /* timeout */
+ errno = ETIMEDOUT;
+ return -1;
+ }
+
+ if (pfd[1].revents) {
+ /* abort due to asocket_abort() */
+ errno = ECANCELED;
+ return -1;
+ }
+
+ if (pfd[0].revents) {
+ if (pfd[0].revents & POLLIN) {
+ /* ready to read() without blocking */
+ do {
+ ret = read(s->fd, buf, count);
+ } while (ret < 0 && errno == EINTR);
+ } else {
+ /* some error event on this fd */
+ errno = ECONNABORTED;
+ return -1;
+ }
+ }
+
+ return ret;
+}
+
+int asocket_write(struct asocket *s, const void *buf, size_t count,
+ int timeout) {
+ int ret;
+ struct pollfd pfd[2];
+
+ pfd[0].fd = s->fd;
+ pfd[0].events = POLLOUT;
+ pfd[0].revents = 0;
+ pfd[1].fd = s->abort_fd[0];
+ pfd[1].events = POLLIN;
+ pfd[1].revents = 0;
+
+ do {
+ ret = poll(pfd, 2, timeout);
+ } while (ret < 0 && errno == EINTR);
+
+ if (ret < 0)
+ return -1;
+ else if (ret == 0) {
+ /* timeout */
+ errno = ETIMEDOUT;
+ return -1;
+ }
+
+ if (pfd[1].revents) {
+ /* abort due to asocket_abort() */
+ errno = ECANCELED;
+ return -1;
+ }
+
+ if (pfd[0].revents) {
+ if (pfd[0].revents & POLLOUT) {
+ /* ready to write() without blocking */
+ do {
+ ret = write(s->fd, buf, count);
+ } while (ret < 0 && errno == EINTR);
+ } else {
+ /* some error event on this fd */
+ errno = ECONNABORTED;
+ return -1;
+ }
+ }
+
+ return ret;
+}
+
+void asocket_abort(struct asocket *s) {
+ int ret;
+ char buf = 0;
+
+ /* Prevent further use of fd, without yet releasing the fd */
+ shutdown(s->fd, SHUT_RDWR);
+
+ /* wake up calls blocked at poll() */
+ do {
+ ret = write(s->abort_fd[1], &buf, 1);
+ } while (ret < 0 && errno == EINTR);
+}
+
+void asocket_destroy(struct asocket *s) {
+ struct asocket s_copy = *s;
+
+ /* Clients should *not* be using these fd's after calling
+ asocket_destroy(), but in case they do, set to -1 so they cannot use a
+ stale fd */
+ s->fd = -1;
+ s->abort_fd[0] = -1;
+ s->abort_fd[1] = -1;
+
+ /* Call asocket_abort() in case there are still threads blocked on this
+ socket. Clients should not rely on this behavior - it is racy because we
+ are about to close() these sockets - clients should instead make sure
+ all threads are done with the socket before calling asocket_destory().
+ */
+ asocket_abort(&s_copy);
+
+ /* enough safety checks, close and release memory */
+ close(s_copy.abort_fd[1]);
+ close(s_copy.abort_fd[0]);
+ close(s_copy.fd);
+
+ free(s);
+}
diff --git a/libcutils/arch-arm/memset32.S b/libcutils/arch-arm/memset32.S
new file mode 100644
index 0000000..4697265
--- /dev/null
+++ b/libcutils/arch-arm/memset32.S
@@ -0,0 +1,93 @@
+/*
+ * 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.
+ */
+/*
+ * memset32.S
+ *
+ */
+
+ .text
+ .align
+
+ .global android_memset32
+ .type android_memset32, %function
+ .global android_memset16
+ .type android_memset16, %function
+
+ /*
+ * Optimized memset32 and memset16 for ARM.
+ *
+ * void android_memset16(uint16_t* dst, uint16_t value, size_t size);
+ * void android_memset32(uint32_t* dst, uint32_t value, size_t size);
+ *
+ */
+
+android_memset16:
+ .fnstart
+ cmp r2, #1
+ bxle lr
+
+ /* expand the data to 32 bits */
+ mov r1, r1, lsl #16
+ orr r1, r1, r1, lsr #16
+
+ /* align to 32 bits */
+ tst r0, #2
+ strneh r1, [r0], #2
+ subne r2, r2, #2
+ .fnend
+
+android_memset32:
+ .fnstart
+ .save {lr}
+ str lr, [sp, #-4]!
+
+ /* align the destination to a cache-line */
+ mov r12, r1
+ mov lr, r1
+ rsb r3, r0, #0
+ ands r3, r3, #0x1C
+ beq .Laligned32
+ cmp r3, r2
+ andhi r3, r2, #0x1C
+ sub r2, r2, r3
+
+ /* conditionally writes 0 to 7 words (length in r3) */
+ movs r3, r3, lsl #28
+ stmcsia r0!, {r1, lr}
+ stmcsia r0!, {r1, lr}
+ stmmiia r0!, {r1, lr}
+ movs r3, r3, lsl #2
+ strcs r1, [r0], #4
+
+.Laligned32:
+ mov r3, r1
+1: subs r2, r2, #32
+ stmhsia r0!, {r1,r3,r12,lr}
+ stmhsia r0!, {r1,r3,r12,lr}
+ bhs 1b
+ add r2, r2, #32
+
+ /* conditionally stores 0 to 30 bytes */
+ movs r2, r2, lsl #28
+ stmcsia r0!, {r1,r3,r12,lr}
+ stmmiia r0!, {r1,lr}
+ movs r2, r2, lsl #2
+ strcs r1, [r0], #4
+ strmih lr, [r0], #2
+
+ ldr lr, [sp], #4
+ bx lr
+ .fnend
diff --git a/libcutils/arch-x86/android_memset16.S b/libcutils/arch-x86/android_memset16.S
new file mode 100644
index 0000000..b1f09cb
--- /dev/null
+++ b/libcutils/arch-x86/android_memset16.S
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+/*
+ * Contributed by: Intel Corporation
+ */
+
+#if defined(USE_SSE2)
+
+# include "cache_wrapper.S"
+# undef __i686
+# define USE_AS_ANDROID
+# define sse2_memset16_atom android_memset16
+# include "sse2-memset16-atom.S"
+
+#else
+
+# include "memset16.S"
+
+#endif
diff --git a/libcutils/arch-x86/android_memset32.S b/libcutils/arch-x86/android_memset32.S
new file mode 100644
index 0000000..1fb2ffe
--- /dev/null
+++ b/libcutils/arch-x86/android_memset32.S
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+/*
+ * Contributed by: Intel Corporation
+ */
+
+#if defined(USE_SSE2)
+
+# include "cache_wrapper.S"
+# undef __i686
+# define USE_AS_ANDROID
+# define sse2_memset32_atom android_memset32
+# include "sse2-memset32-atom.S"
+
+#else
+
+# include "memset32.S"
+
+#endif
+
diff --git a/libcutils/arch-x86/cache_wrapper.S b/libcutils/arch-x86/cache_wrapper.S
new file mode 100644
index 0000000..508fdd3
--- /dev/null
+++ b/libcutils/arch-x86/cache_wrapper.S
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+/*
+ * Contributed by: Intel Corporation
+ */
+
+/* Values are optimized for Atom */
+#define SHARED_CACHE_SIZE (512*1024) /* Atom L2 Cache */
+#define DATA_CACHE_SIZE (24*1024) /* Atom L1 Data Cache */
+#define SHARED_CACHE_SIZE_HALF (SHARED_CACHE_SIZE / 2)
+#define DATA_CACHE_SIZE_HALF (DATA_CACHE_SIZE / 2)
diff --git a/libcutils/arch-x86/sse2-memset16-atom.S b/libcutils/arch-x86/sse2-memset16-atom.S
new file mode 100644
index 0000000..cafec82
--- /dev/null
+++ b/libcutils/arch-x86/sse2-memset16-atom.S
@@ -0,0 +1,722 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+/*
+ * Contributed by: Intel Corporation
+ */
+
+#ifndef L
+# define L(label) .L##label
+#endif
+
+#ifndef ALIGN
+# define ALIGN(n) .p2align n
+#endif
+
+#ifndef cfi_startproc
+# define cfi_startproc .cfi_startproc
+#endif
+
+#ifndef cfi_endproc
+# define cfi_endproc .cfi_endproc
+#endif
+
+#ifndef cfi_rel_offset
+# define cfi_rel_offset(reg, off) .cfi_rel_offset reg, off
+#endif
+
+#ifndef cfi_restore
+# define cfi_restore(reg) .cfi_restore reg
+#endif
+
+#ifndef cfi_adjust_cfa_offset
+# define cfi_adjust_cfa_offset(off) .cfi_adjust_cfa_offset off
+#endif
+
+#ifndef ENTRY
+# define ENTRY(name) \
+ .type name, @function; \
+ .globl name; \
+ .p2align 4; \
+name: \
+ cfi_startproc
+#endif
+
+#ifndef END
+# define END(name) \
+ cfi_endproc; \
+ .size name, .-name
+#endif
+
+#define CFI_PUSH(REG) \
+ cfi_adjust_cfa_offset (4); \
+ cfi_rel_offset (REG, 0)
+
+#define CFI_POP(REG) \
+ cfi_adjust_cfa_offset (-4); \
+ cfi_restore (REG)
+
+#define PUSH(REG) pushl REG; CFI_PUSH (REG)
+#define POP(REG) popl REG; CFI_POP (REG)
+
+#ifdef USE_AS_BZERO16
+# define DEST PARMS
+# define LEN DEST+4
+#else
+# define DEST PARMS
+# define CHR DEST+4
+# define LEN CHR+4
+#endif
+
+#if 1
+# define SETRTNVAL
+#else
+# define SETRTNVAL movl DEST(%esp), %eax
+#endif
+
+#ifdef SHARED
+# define ENTRANCE PUSH (%ebx);
+# define RETURN_END POP (%ebx); ret
+# define RETURN RETURN_END; CFI_PUSH (%ebx)
+# define PARMS 8 /* Preserve EBX. */
+# define JMPTBL(I, B) I - B
+
+/* Load an entry in a jump table into EBX and branch to it. TABLE is a
+ jump table with relative offsets. */
+# define BRANCH_TO_JMPTBL_ENTRY(TABLE) \
+ /* We first load PC into EBX. */ \
+ call __i686.get_pc_thunk.bx; \
+ /* Get the address of the jump table. */ \
+ add $(TABLE - .), %ebx; \
+ /* Get the entry and convert the relative offset to the \
+ absolute address. */ \
+ add (%ebx,%ecx,4), %ebx; \
+ /* We loaded the jump table and adjuested EDX. Go. */ \
+ jmp *%ebx
+
+ .section .gnu.linkonce.t.__i686.get_pc_thunk.bx,"ax",@progbits
+ .globl __i686.get_pc_thunk.bx
+ .hidden __i686.get_pc_thunk.bx
+ ALIGN (4)
+ .type __i686.get_pc_thunk.bx,@function
+__i686.get_pc_thunk.bx:
+ movl (%esp), %ebx
+ ret
+#else
+# define ENTRANCE
+# define RETURN_END ret
+# define RETURN RETURN_END
+# define PARMS 4
+# define JMPTBL(I, B) I
+
+/* Branch to an entry in a jump table. TABLE is a jump table with
+ absolute offsets. */
+# define BRANCH_TO_JMPTBL_ENTRY(TABLE) \
+ jmp *TABLE(,%ecx,4)
+#endif
+
+ .section .text.sse2,"ax",@progbits
+ ALIGN (4)
+ENTRY (sse2_memset16_atom)
+ ENTRANCE
+
+ movl LEN(%esp), %ecx
+#ifdef USE_AS_ANDROID
+ shr $1, %ecx
+#endif
+#ifdef USE_AS_BZERO16
+ xor %eax, %eax
+#else
+ movzwl CHR(%esp), %eax
+ mov %eax, %edx
+ shl $16, %eax
+ or %edx, %eax
+#endif
+ movl DEST(%esp), %edx
+ cmp $32, %ecx
+ jae L(32wordsormore)
+
+L(write_less32words):
+ lea (%edx, %ecx, 2), %edx
+ BRANCH_TO_JMPTBL_ENTRY (L(table_less32words))
+
+
+ .pushsection .rodata.sse2,"a",@progbits
+ ALIGN (2)
+L(table_less32words):
+ .int JMPTBL (L(write_0words), L(table_less32words))
+ .int JMPTBL (L(write_1words), L(table_less32words))
+ .int JMPTBL (L(write_2words), L(table_less32words))
+ .int JMPTBL (L(write_3words), L(table_less32words))
+ .int JMPTBL (L(write_4words), L(table_less32words))
+ .int JMPTBL (L(write_5words), L(table_less32words))
+ .int JMPTBL (L(write_6words), L(table_less32words))
+ .int JMPTBL (L(write_7words), L(table_less32words))
+ .int JMPTBL (L(write_8words), L(table_less32words))
+ .int JMPTBL (L(write_9words), L(table_less32words))
+ .int JMPTBL (L(write_10words), L(table_less32words))
+ .int JMPTBL (L(write_11words), L(table_less32words))
+ .int JMPTBL (L(write_12words), L(table_less32words))
+ .int JMPTBL (L(write_13words), L(table_less32words))
+ .int JMPTBL (L(write_14words), L(table_less32words))
+ .int JMPTBL (L(write_15words), L(table_less32words))
+ .int JMPTBL (L(write_16words), L(table_less32words))
+ .int JMPTBL (L(write_17words), L(table_less32words))
+ .int JMPTBL (L(write_18words), L(table_less32words))
+ .int JMPTBL (L(write_19words), L(table_less32words))
+ .int JMPTBL (L(write_20words), L(table_less32words))
+ .int JMPTBL (L(write_21words), L(table_less32words))
+ .int JMPTBL (L(write_22words), L(table_less32words))
+ .int JMPTBL (L(write_23words), L(table_less32words))
+ .int JMPTBL (L(write_24words), L(table_less32words))
+ .int JMPTBL (L(write_25words), L(table_less32words))
+ .int JMPTBL (L(write_26words), L(table_less32words))
+ .int JMPTBL (L(write_27words), L(table_less32words))
+ .int JMPTBL (L(write_28words), L(table_less32words))
+ .int JMPTBL (L(write_29words), L(table_less32words))
+ .int JMPTBL (L(write_30words), L(table_less32words))
+ .int JMPTBL (L(write_31words), L(table_less32words))
+ .popsection
+
+ ALIGN (4)
+L(write_28words):
+ movl %eax, -56(%edx)
+ movl %eax, -52(%edx)
+L(write_24words):
+ movl %eax, -48(%edx)
+ movl %eax, -44(%edx)
+L(write_20words):
+ movl %eax, -40(%edx)
+ movl %eax, -36(%edx)
+L(write_16words):
+ movl %eax, -32(%edx)
+ movl %eax, -28(%edx)
+L(write_12words):
+ movl %eax, -24(%edx)
+ movl %eax, -20(%edx)
+L(write_8words):
+ movl %eax, -16(%edx)
+ movl %eax, -12(%edx)
+L(write_4words):
+ movl %eax, -8(%edx)
+ movl %eax, -4(%edx)
+L(write_0words):
+ SETRTNVAL
+ RETURN
+
+ ALIGN (4)
+L(write_29words):
+ movl %eax, -58(%edx)
+ movl %eax, -54(%edx)
+L(write_25words):
+ movl %eax, -50(%edx)
+ movl %eax, -46(%edx)
+L(write_21words):
+ movl %eax, -42(%edx)
+ movl %eax, -38(%edx)
+L(write_17words):
+ movl %eax, -34(%edx)
+ movl %eax, -30(%edx)
+L(write_13words):
+ movl %eax, -26(%edx)
+ movl %eax, -22(%edx)
+L(write_9words):
+ movl %eax, -18(%edx)
+ movl %eax, -14(%edx)
+L(write_5words):
+ movl %eax, -10(%edx)
+ movl %eax, -6(%edx)
+L(write_1words):
+ mov %ax, -2(%edx)
+ SETRTNVAL
+ RETURN
+
+ ALIGN (4)
+L(write_30words):
+ movl %eax, -60(%edx)
+ movl %eax, -56(%edx)
+L(write_26words):
+ movl %eax, -52(%edx)
+ movl %eax, -48(%edx)
+L(write_22words):
+ movl %eax, -44(%edx)
+ movl %eax, -40(%edx)
+L(write_18words):
+ movl %eax, -36(%edx)
+ movl %eax, -32(%edx)
+L(write_14words):
+ movl %eax, -28(%edx)
+ movl %eax, -24(%edx)
+L(write_10words):
+ movl %eax, -20(%edx)
+ movl %eax, -16(%edx)
+L(write_6words):
+ movl %eax, -12(%edx)
+ movl %eax, -8(%edx)
+L(write_2words):
+ movl %eax, -4(%edx)
+ SETRTNVAL
+ RETURN
+
+ ALIGN (4)
+L(write_31words):
+ movl %eax, -62(%edx)
+ movl %eax, -58(%edx)
+L(write_27words):
+ movl %eax, -54(%edx)
+ movl %eax, -50(%edx)
+L(write_23words):
+ movl %eax, -46(%edx)
+ movl %eax, -42(%edx)
+L(write_19words):
+ movl %eax, -38(%edx)
+ movl %eax, -34(%edx)
+L(write_15words):
+ movl %eax, -30(%edx)
+ movl %eax, -26(%edx)
+L(write_11words):
+ movl %eax, -22(%edx)
+ movl %eax, -18(%edx)
+L(write_7words):
+ movl %eax, -14(%edx)
+ movl %eax, -10(%edx)
+L(write_3words):
+ movl %eax, -6(%edx)
+ movw %ax, -2(%edx)
+ SETRTNVAL
+ RETURN
+
+ ALIGN (4)
+
+L(32wordsormore):
+ shl $1, %ecx
+ test $0x01, %edx
+ jz L(aligned2bytes)
+ mov %eax, (%edx)
+ mov %eax, -4(%edx, %ecx)
+ sub $2, %ecx
+ add $1, %edx
+ rol $8, %eax
+L(aligned2bytes):
+#ifdef USE_AS_BZERO16
+ pxor %xmm0, %xmm0
+#else
+ movd %eax, %xmm0
+ pshufd $0, %xmm0, %xmm0
+#endif
+ testl $0xf, %edx
+ jz L(aligned_16)
+/* ECX > 32 and EDX is not 16 byte aligned. */
+L(not_aligned_16):
+ movdqu %xmm0, (%edx)
+ movl %edx, %eax
+ and $-16, %edx
+ add $16, %edx
+ sub %edx, %eax
+ add %eax, %ecx
+ movd %xmm0, %eax
+
+ ALIGN (4)
+L(aligned_16):
+ cmp $128, %ecx
+ jae L(128bytesormore)
+
+L(aligned_16_less128bytes):
+ add %ecx, %edx
+ shr $1, %ecx
+ BRANCH_TO_JMPTBL_ENTRY (L(table_16_128bytes))
+
+ ALIGN (4)
+L(128bytesormore):
+#ifdef SHARED_CACHE_SIZE
+ PUSH (%ebx)
+ mov $SHARED_CACHE_SIZE, %ebx
+#else
+# ifdef SHARED
+ call __i686.get_pc_thunk.bx
+ add $_GLOBAL_OFFSET_TABLE_, %ebx
+ mov __x86_shared_cache_size@GOTOFF(%ebx), %ebx
+# else
+ PUSH (%ebx)
+ mov __x86_shared_cache_size, %ebx
+# endif
+#endif
+ cmp %ebx, %ecx
+ jae L(128bytesormore_nt_start)
+
+
+#ifdef DATA_CACHE_SIZE
+ POP (%ebx)
+# define RESTORE_EBX_STATE CFI_PUSH (%ebx)
+ cmp $DATA_CACHE_SIZE, %ecx
+#else
+# ifdef SHARED
+# define RESTORE_EBX_STATE
+ call __i686.get_pc_thunk.bx
+ add $_GLOBAL_OFFSET_TABLE_, %ebx
+ cmp __x86_data_cache_size@GOTOFF(%ebx), %ecx
+# else
+ POP (%ebx)
+# define RESTORE_EBX_STATE CFI_PUSH (%ebx)
+ cmp __x86_data_cache_size, %ecx
+# endif
+#endif
+
+ jae L(128bytes_L2_normal)
+ subl $128, %ecx
+L(128bytesormore_normal):
+ sub $128, %ecx
+ movdqa %xmm0, (%edx)
+ movdqa %xmm0, 0x10(%edx)
+ movdqa %xmm0, 0x20(%edx)
+ movdqa %xmm0, 0x30(%edx)
+ movdqa %xmm0, 0x40(%edx)
+ movdqa %xmm0, 0x50(%edx)
+ movdqa %xmm0, 0x60(%edx)
+ movdqa %xmm0, 0x70(%edx)
+ lea 128(%edx), %edx
+ jb L(128bytesless_normal)
+
+
+ sub $128, %ecx
+ movdqa %xmm0, (%edx)
+ movdqa %xmm0, 0x10(%edx)
+ movdqa %xmm0, 0x20(%edx)
+ movdqa %xmm0, 0x30(%edx)
+ movdqa %xmm0, 0x40(%edx)
+ movdqa %xmm0, 0x50(%edx)
+ movdqa %xmm0, 0x60(%edx)
+ movdqa %xmm0, 0x70(%edx)
+ lea 128(%edx), %edx
+ jae L(128bytesormore_normal)
+
+L(128bytesless_normal):
+ lea 128(%ecx), %ecx
+ add %ecx, %edx
+ shr $1, %ecx
+ BRANCH_TO_JMPTBL_ENTRY (L(table_16_128bytes))
+
+ ALIGN (4)
+L(128bytes_L2_normal):
+ prefetcht0 0x380(%edx)
+ prefetcht0 0x3c0(%edx)
+ sub $128, %ecx
+ movdqa %xmm0, (%edx)
+ movaps %xmm0, 0x10(%edx)
+ movaps %xmm0, 0x20(%edx)
+ movaps %xmm0, 0x30(%edx)
+ movaps %xmm0, 0x40(%edx)
+ movaps %xmm0, 0x50(%edx)
+ movaps %xmm0, 0x60(%edx)
+ movaps %xmm0, 0x70(%edx)
+ add $128, %edx
+ cmp $128, %ecx
+ jae L(128bytes_L2_normal)
+
+L(128bytesless_L2_normal):
+ add %ecx, %edx
+ shr $1, %ecx
+ BRANCH_TO_JMPTBL_ENTRY (L(table_16_128bytes))
+
+ RESTORE_EBX_STATE
+L(128bytesormore_nt_start):
+ sub %ebx, %ecx
+ mov %ebx, %eax
+ and $0x7f, %eax
+ add %eax, %ecx
+ movd %xmm0, %eax
+ ALIGN (4)
+L(128bytesormore_shared_cache_loop):
+ prefetcht0 0x3c0(%edx)
+ prefetcht0 0x380(%edx)
+ sub $0x80, %ebx
+ movdqa %xmm0, (%edx)
+ movdqa %xmm0, 0x10(%edx)
+ movdqa %xmm0, 0x20(%edx)
+ movdqa %xmm0, 0x30(%edx)
+ movdqa %xmm0, 0x40(%edx)
+ movdqa %xmm0, 0x50(%edx)
+ movdqa %xmm0, 0x60(%edx)
+ movdqa %xmm0, 0x70(%edx)
+ add $0x80, %edx
+ cmp $0x80, %ebx
+ jae L(128bytesormore_shared_cache_loop)
+ cmp $0x80, %ecx
+ jb L(shared_cache_loop_end)
+ ALIGN (4)
+L(128bytesormore_nt):
+ sub $0x80, %ecx
+ movntdq %xmm0, (%edx)
+ movntdq %xmm0, 0x10(%edx)
+ movntdq %xmm0, 0x20(%edx)
+ movntdq %xmm0, 0x30(%edx)
+ movntdq %xmm0, 0x40(%edx)
+ movntdq %xmm0, 0x50(%edx)
+ movntdq %xmm0, 0x60(%edx)
+ movntdq %xmm0, 0x70(%edx)
+ add $0x80, %edx
+ cmp $0x80, %ecx
+ jae L(128bytesormore_nt)
+ sfence
+L(shared_cache_loop_end):
+#if defined DATA_CACHE_SIZE || !defined SHARED
+ POP (%ebx)
+#endif
+ add %ecx, %edx
+ shr $1, %ecx
+ BRANCH_TO_JMPTBL_ENTRY (L(table_16_128bytes))
+
+
+ .pushsection .rodata.sse2,"a",@progbits
+ ALIGN (2)
+L(table_16_128bytes):
+ .int JMPTBL (L(aligned_16_0bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_2bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_4bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_6bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_8bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_10bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_12bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_14bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_16bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_18bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_20bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_22bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_24bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_26bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_28bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_30bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_32bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_34bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_36bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_38bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_40bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_42bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_44bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_46bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_48bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_50bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_52bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_54bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_56bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_58bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_60bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_62bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_64bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_66bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_68bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_70bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_72bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_74bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_76bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_78bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_80bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_82bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_84bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_86bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_88bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_90bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_92bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_94bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_96bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_98bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_100bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_102bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_104bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_106bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_108bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_110bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_112bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_114bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_116bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_118bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_120bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_122bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_124bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_126bytes), L(table_16_128bytes))
+ .popsection
+
+
+ ALIGN (4)
+L(aligned_16_112bytes):
+ movdqa %xmm0, -112(%edx)
+L(aligned_16_96bytes):
+ movdqa %xmm0, -96(%edx)
+L(aligned_16_80bytes):
+ movdqa %xmm0, -80(%edx)
+L(aligned_16_64bytes):
+ movdqa %xmm0, -64(%edx)
+L(aligned_16_48bytes):
+ movdqa %xmm0, -48(%edx)
+L(aligned_16_32bytes):
+ movdqa %xmm0, -32(%edx)
+L(aligned_16_16bytes):
+ movdqa %xmm0, -16(%edx)
+L(aligned_16_0bytes):
+ SETRTNVAL
+ RETURN
+
+
+ ALIGN (4)
+L(aligned_16_114bytes):
+ movdqa %xmm0, -114(%edx)
+L(aligned_16_98bytes):
+ movdqa %xmm0, -98(%edx)
+L(aligned_16_82bytes):
+ movdqa %xmm0, -82(%edx)
+L(aligned_16_66bytes):
+ movdqa %xmm0, -66(%edx)
+L(aligned_16_50bytes):
+ movdqa %xmm0, -50(%edx)
+L(aligned_16_34bytes):
+ movdqa %xmm0, -34(%edx)
+L(aligned_16_18bytes):
+ movdqa %xmm0, -18(%edx)
+L(aligned_16_2bytes):
+ movw %ax, -2(%edx)
+ SETRTNVAL
+ RETURN
+
+ ALIGN (4)
+L(aligned_16_116bytes):
+ movdqa %xmm0, -116(%edx)
+L(aligned_16_100bytes):
+ movdqa %xmm0, -100(%edx)
+L(aligned_16_84bytes):
+ movdqa %xmm0, -84(%edx)
+L(aligned_16_68bytes):
+ movdqa %xmm0, -68(%edx)
+L(aligned_16_52bytes):
+ movdqa %xmm0, -52(%edx)
+L(aligned_16_36bytes):
+ movdqa %xmm0, -36(%edx)
+L(aligned_16_20bytes):
+ movdqa %xmm0, -20(%edx)
+L(aligned_16_4bytes):
+ movl %eax, -4(%edx)
+ SETRTNVAL
+ RETURN
+
+
+ ALIGN (4)
+L(aligned_16_118bytes):
+ movdqa %xmm0, -118(%edx)
+L(aligned_16_102bytes):
+ movdqa %xmm0, -102(%edx)
+L(aligned_16_86bytes):
+ movdqa %xmm0, -86(%edx)
+L(aligned_16_70bytes):
+ movdqa %xmm0, -70(%edx)
+L(aligned_16_54bytes):
+ movdqa %xmm0, -54(%edx)
+L(aligned_16_38bytes):
+ movdqa %xmm0, -38(%edx)
+L(aligned_16_22bytes):
+ movdqa %xmm0, -22(%edx)
+L(aligned_16_6bytes):
+ movl %eax, -6(%edx)
+ movw %ax, -2(%edx)
+ SETRTNVAL
+ RETURN
+
+
+ ALIGN (4)
+L(aligned_16_120bytes):
+ movdqa %xmm0, -120(%edx)
+L(aligned_16_104bytes):
+ movdqa %xmm0, -104(%edx)
+L(aligned_16_88bytes):
+ movdqa %xmm0, -88(%edx)
+L(aligned_16_72bytes):
+ movdqa %xmm0, -72(%edx)
+L(aligned_16_56bytes):
+ movdqa %xmm0, -56(%edx)
+L(aligned_16_40bytes):
+ movdqa %xmm0, -40(%edx)
+L(aligned_16_24bytes):
+ movdqa %xmm0, -24(%edx)
+L(aligned_16_8bytes):
+ movq %xmm0, -8(%edx)
+ SETRTNVAL
+ RETURN
+
+
+ ALIGN (4)
+L(aligned_16_122bytes):
+ movdqa %xmm0, -122(%edx)
+L(aligned_16_106bytes):
+ movdqa %xmm0, -106(%edx)
+L(aligned_16_90bytes):
+ movdqa %xmm0, -90(%edx)
+L(aligned_16_74bytes):
+ movdqa %xmm0, -74(%edx)
+L(aligned_16_58bytes):
+ movdqa %xmm0, -58(%edx)
+L(aligned_16_42bytes):
+ movdqa %xmm0, -42(%edx)
+L(aligned_16_26bytes):
+ movdqa %xmm0, -26(%edx)
+L(aligned_16_10bytes):
+ movq %xmm0, -10(%edx)
+ movw %ax, -2(%edx)
+ SETRTNVAL
+ RETURN
+
+
+ ALIGN (4)
+L(aligned_16_124bytes):
+ movdqa %xmm0, -124(%edx)
+L(aligned_16_108bytes):
+ movdqa %xmm0, -108(%edx)
+L(aligned_16_92bytes):
+ movdqa %xmm0, -92(%edx)
+L(aligned_16_76bytes):
+ movdqa %xmm0, -76(%edx)
+L(aligned_16_60bytes):
+ movdqa %xmm0, -60(%edx)
+L(aligned_16_44bytes):
+ movdqa %xmm0, -44(%edx)
+L(aligned_16_28bytes):
+ movdqa %xmm0, -28(%edx)
+L(aligned_16_12bytes):
+ movq %xmm0, -12(%edx)
+ movl %eax, -4(%edx)
+ SETRTNVAL
+ RETURN
+
+
+ ALIGN (4)
+L(aligned_16_126bytes):
+ movdqa %xmm0, -126(%edx)
+L(aligned_16_110bytes):
+ movdqa %xmm0, -110(%edx)
+L(aligned_16_94bytes):
+ movdqa %xmm0, -94(%edx)
+L(aligned_16_78bytes):
+ movdqa %xmm0, -78(%edx)
+L(aligned_16_62bytes):
+ movdqa %xmm0, -62(%edx)
+L(aligned_16_46bytes):
+ movdqa %xmm0, -46(%edx)
+L(aligned_16_30bytes):
+ movdqa %xmm0, -30(%edx)
+L(aligned_16_14bytes):
+ movq %xmm0, -14(%edx)
+ movl %eax, -6(%edx)
+ movw %ax, -2(%edx)
+ SETRTNVAL
+ RETURN
+
+END (sse2_memset16_atom)
diff --git a/libcutils/arch-x86/sse2-memset32-atom.S b/libcutils/arch-x86/sse2-memset32-atom.S
new file mode 100644
index 0000000..4a52484
--- /dev/null
+++ b/libcutils/arch-x86/sse2-memset32-atom.S
@@ -0,0 +1,513 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+/*
+ * Contributed by: Intel Corporation
+ */
+
+#ifndef L
+# define L(label) .L##label
+#endif
+
+#ifndef ALIGN
+# define ALIGN(n) .p2align n
+#endif
+
+#ifndef cfi_startproc
+# define cfi_startproc .cfi_startproc
+#endif
+
+#ifndef cfi_endproc
+# define cfi_endproc .cfi_endproc
+#endif
+
+#ifndef cfi_rel_offset
+# define cfi_rel_offset(reg, off) .cfi_rel_offset reg, off
+#endif
+
+#ifndef cfi_restore
+# define cfi_restore(reg) .cfi_restore reg
+#endif
+
+#ifndef cfi_adjust_cfa_offset
+# define cfi_adjust_cfa_offset(off) .cfi_adjust_cfa_offset off
+#endif
+
+#ifndef ENTRY
+# define ENTRY(name) \
+ .type name, @function; \
+ .globl name; \
+ .p2align 4; \
+name: \
+ cfi_startproc
+#endif
+
+#ifndef END
+# define END(name) \
+ cfi_endproc; \
+ .size name, .-name
+#endif
+
+#define CFI_PUSH(REG) \
+ cfi_adjust_cfa_offset (4); \
+ cfi_rel_offset (REG, 0)
+
+#define CFI_POP(REG) \
+ cfi_adjust_cfa_offset (-4); \
+ cfi_restore (REG)
+
+#define PUSH(REG) pushl REG; CFI_PUSH (REG)
+#define POP(REG) popl REG; CFI_POP (REG)
+
+#ifdef USE_AS_BZERO32
+# define DEST PARMS
+# define LEN DEST+4
+#else
+# define DEST PARMS
+# define DWDS DEST+4
+# define LEN DWDS+4
+#endif
+
+#ifdef USE_AS_WMEMSET32
+# define SETRTNVAL movl DEST(%esp), %eax
+#else
+# define SETRTNVAL
+#endif
+
+#ifdef SHARED
+# define ENTRANCE PUSH (%ebx);
+# define RETURN_END POP (%ebx); ret
+# define RETURN RETURN_END; CFI_PUSH (%ebx)
+# define PARMS 8 /* Preserve EBX. */
+# define JMPTBL(I, B) I - B
+
+/* Load an entry in a jump table into EBX and branch to it. TABLE is a
+ jump table with relative offsets. */
+# define BRANCH_TO_JMPTBL_ENTRY(TABLE) \
+ /* We first load PC into EBX. */ \
+ call __i686.get_pc_thunk.bx; \
+ /* Get the address of the jump table. */ \
+ add $(TABLE - .), %ebx; \
+ /* Get the entry and convert the relative offset to the \
+ absolute address. */ \
+ add (%ebx,%ecx,4), %ebx; \
+ /* We loaded the jump table and adjuested EDX. Go. */ \
+ jmp *%ebx
+
+ .section .gnu.linkonce.t.__i686.get_pc_thunk.bx,"ax",@progbits
+ .globl __i686.get_pc_thunk.bx
+ .hidden __i686.get_pc_thunk.bx
+ ALIGN (4)
+ .type __i686.get_pc_thunk.bx,@function
+__i686.get_pc_thunk.bx:
+ movl (%esp), %ebx
+ ret
+#else
+# define ENTRANCE
+# define RETURN_END ret
+# define RETURN RETURN_END
+# define PARMS 4
+# define JMPTBL(I, B) I
+
+/* Branch to an entry in a jump table. TABLE is a jump table with
+ absolute offsets. */
+# define BRANCH_TO_JMPTBL_ENTRY(TABLE) \
+ jmp *TABLE(,%ecx,4)
+#endif
+
+ .section .text.sse2,"ax",@progbits
+ ALIGN (4)
+ENTRY (sse2_memset32_atom)
+ ENTRANCE
+
+ movl LEN(%esp), %ecx
+#ifdef USE_AS_ANDROID
+ shr $2, %ecx
+#endif
+#ifdef USE_AS_BZERO32
+ xor %eax, %eax
+#else
+ mov DWDS(%esp), %eax
+ mov %eax, %edx
+#endif
+ movl DEST(%esp), %edx
+ cmp $16, %ecx
+ jae L(16dbwordsormore)
+
+L(write_less16dbwords):
+ lea (%edx, %ecx, 4), %edx
+ BRANCH_TO_JMPTBL_ENTRY (L(table_less16dbwords))
+
+ .pushsection .rodata.sse2,"a",@progbits
+ ALIGN (2)
+L(table_less16dbwords):
+ .int JMPTBL (L(write_0dbwords), L(table_less16dbwords))
+ .int JMPTBL (L(write_1dbwords), L(table_less16dbwords))
+ .int JMPTBL (L(write_2dbwords), L(table_less16dbwords))
+ .int JMPTBL (L(write_3dbwords), L(table_less16dbwords))
+ .int JMPTBL (L(write_4dbwords), L(table_less16dbwords))
+ .int JMPTBL (L(write_5dbwords), L(table_less16dbwords))
+ .int JMPTBL (L(write_6dbwords), L(table_less16dbwords))
+ .int JMPTBL (L(write_7dbwords), L(table_less16dbwords))
+ .int JMPTBL (L(write_8dbwords), L(table_less16dbwords))
+ .int JMPTBL (L(write_9dbwords), L(table_less16dbwords))
+ .int JMPTBL (L(write_10dbwords), L(table_less16dbwords))
+ .int JMPTBL (L(write_11dbwords), L(table_less16dbwords))
+ .int JMPTBL (L(write_12dbwords), L(table_less16dbwords))
+ .int JMPTBL (L(write_13dbwords), L(table_less16dbwords))
+ .int JMPTBL (L(write_14dbwords), L(table_less16dbwords))
+ .int JMPTBL (L(write_15dbwords), L(table_less16dbwords))
+ .popsection
+
+ ALIGN (4)
+L(write_15dbwords):
+ movl %eax, -60(%edx)
+L(write_14dbwords):
+ movl %eax, -56(%edx)
+L(write_13dbwords):
+ movl %eax, -52(%edx)
+L(write_12dbwords):
+ movl %eax, -48(%edx)
+L(write_11dbwords):
+ movl %eax, -44(%edx)
+L(write_10dbwords):
+ movl %eax, -40(%edx)
+L(write_9dbwords):
+ movl %eax, -36(%edx)
+L(write_8dbwords):
+ movl %eax, -32(%edx)
+L(write_7dbwords):
+ movl %eax, -28(%edx)
+L(write_6dbwords):
+ movl %eax, -24(%edx)
+L(write_5dbwords):
+ movl %eax, -20(%edx)
+L(write_4dbwords):
+ movl %eax, -16(%edx)
+L(write_3dbwords):
+ movl %eax, -12(%edx)
+L(write_2dbwords):
+ movl %eax, -8(%edx)
+L(write_1dbwords):
+ movl %eax, -4(%edx)
+L(write_0dbwords):
+ SETRTNVAL
+ RETURN
+
+ ALIGN (4)
+L(16dbwordsormore):
+ test $3, %edx
+ jz L(aligned4bytes)
+ mov %eax, (%edx)
+ mov %eax, -4(%edx, %ecx, 4)
+ sub $1, %ecx
+ rol $24, %eax
+ add $1, %edx
+ test $3, %edx
+ jz L(aligned4bytes)
+ ror $8, %eax
+ add $1, %edx
+ test $3, %edx
+ jz L(aligned4bytes)
+ ror $8, %eax
+ add $1, %edx
+L(aligned4bytes):
+ shl $2, %ecx
+
+#ifdef USE_AS_BZERO32
+ pxor %xmm0, %xmm0
+#else
+ movd %eax, %xmm0
+ pshufd $0, %xmm0, %xmm0
+#endif
+ testl $0xf, %edx
+ jz L(aligned_16)
+/* ECX > 32 and EDX is not 16 byte aligned. */
+L(not_aligned_16):
+ movdqu %xmm0, (%edx)
+ movl %edx, %eax
+ and $-16, %edx
+ add $16, %edx
+ sub %edx, %eax
+ add %eax, %ecx
+ movd %xmm0, %eax
+ ALIGN (4)
+L(aligned_16):
+ cmp $128, %ecx
+ jae L(128bytesormore)
+
+L(aligned_16_less128bytes):
+ add %ecx, %edx
+ shr $2, %ecx
+ BRANCH_TO_JMPTBL_ENTRY (L(table_16_128bytes))
+
+ ALIGN (4)
+L(128bytesormore):
+#ifdef SHARED_CACHE_SIZE
+ PUSH (%ebx)
+ mov $SHARED_CACHE_SIZE, %ebx
+#else
+# ifdef SHARED
+ call __i686.get_pc_thunk.bx
+ add $_GLOBAL_OFFSET_TABLE_, %ebx
+ mov __x86_shared_cache_size@GOTOFF(%ebx), %ebx
+# else
+ PUSH (%ebx)
+ mov __x86_shared_cache_size, %ebx
+# endif
+#endif
+ cmp %ebx, %ecx
+ jae L(128bytesormore_nt_start)
+
+#ifdef DATA_CACHE_SIZE
+ POP (%ebx)
+# define RESTORE_EBX_STATE CFI_PUSH (%ebx)
+ cmp $DATA_CACHE_SIZE, %ecx
+#else
+# ifdef SHARED
+# define RESTORE_EBX_STATE
+ call __i686.get_pc_thunk.bx
+ add $_GLOBAL_OFFSET_TABLE_, %ebx
+ cmp __x86_data_cache_size@GOTOFF(%ebx), %ecx
+# else
+ POP (%ebx)
+# define RESTORE_EBX_STATE CFI_PUSH (%ebx)
+ cmp __x86_data_cache_size, %ecx
+# endif
+#endif
+
+ jae L(128bytes_L2_normal)
+ subl $128, %ecx
+L(128bytesormore_normal):
+ sub $128, %ecx
+ movdqa %xmm0, (%edx)
+ movdqa %xmm0, 0x10(%edx)
+ movdqa %xmm0, 0x20(%edx)
+ movdqa %xmm0, 0x30(%edx)
+ movdqa %xmm0, 0x40(%edx)
+ movdqa %xmm0, 0x50(%edx)
+ movdqa %xmm0, 0x60(%edx)
+ movdqa %xmm0, 0x70(%edx)
+ lea 128(%edx), %edx
+ jb L(128bytesless_normal)
+
+
+ sub $128, %ecx
+ movdqa %xmm0, (%edx)
+ movdqa %xmm0, 0x10(%edx)
+ movdqa %xmm0, 0x20(%edx)
+ movdqa %xmm0, 0x30(%edx)
+ movdqa %xmm0, 0x40(%edx)
+ movdqa %xmm0, 0x50(%edx)
+ movdqa %xmm0, 0x60(%edx)
+ movdqa %xmm0, 0x70(%edx)
+ lea 128(%edx), %edx
+ jae L(128bytesormore_normal)
+
+L(128bytesless_normal):
+ lea 128(%ecx), %ecx
+ add %ecx, %edx
+ shr $2, %ecx
+ BRANCH_TO_JMPTBL_ENTRY (L(table_16_128bytes))
+
+ ALIGN (4)
+L(128bytes_L2_normal):
+ prefetcht0 0x380(%edx)
+ prefetcht0 0x3c0(%edx)
+ sub $128, %ecx
+ movdqa %xmm0, (%edx)
+ movaps %xmm0, 0x10(%edx)
+ movaps %xmm0, 0x20(%edx)
+ movaps %xmm0, 0x30(%edx)
+ movaps %xmm0, 0x40(%edx)
+ movaps %xmm0, 0x50(%edx)
+ movaps %xmm0, 0x60(%edx)
+ movaps %xmm0, 0x70(%edx)
+ add $128, %edx
+ cmp $128, %ecx
+ jae L(128bytes_L2_normal)
+
+L(128bytesless_L2_normal):
+ add %ecx, %edx
+ shr $2, %ecx
+ BRANCH_TO_JMPTBL_ENTRY (L(table_16_128bytes))
+
+ RESTORE_EBX_STATE
+L(128bytesormore_nt_start):
+ sub %ebx, %ecx
+ mov %ebx, %eax
+ and $0x7f, %eax
+ add %eax, %ecx
+ movd %xmm0, %eax
+ ALIGN (4)
+L(128bytesormore_shared_cache_loop):
+ prefetcht0 0x3c0(%edx)
+ prefetcht0 0x380(%edx)
+ sub $0x80, %ebx
+ movdqa %xmm0, (%edx)
+ movdqa %xmm0, 0x10(%edx)
+ movdqa %xmm0, 0x20(%edx)
+ movdqa %xmm0, 0x30(%edx)
+ movdqa %xmm0, 0x40(%edx)
+ movdqa %xmm0, 0x50(%edx)
+ movdqa %xmm0, 0x60(%edx)
+ movdqa %xmm0, 0x70(%edx)
+ add $0x80, %edx
+ cmp $0x80, %ebx
+ jae L(128bytesormore_shared_cache_loop)
+ cmp $0x80, %ecx
+ jb L(shared_cache_loop_end)
+
+ ALIGN (4)
+L(128bytesormore_nt):
+ sub $0x80, %ecx
+ movntdq %xmm0, (%edx)
+ movntdq %xmm0, 0x10(%edx)
+ movntdq %xmm0, 0x20(%edx)
+ movntdq %xmm0, 0x30(%edx)
+ movntdq %xmm0, 0x40(%edx)
+ movntdq %xmm0, 0x50(%edx)
+ movntdq %xmm0, 0x60(%edx)
+ movntdq %xmm0, 0x70(%edx)
+ add $0x80, %edx
+ cmp $0x80, %ecx
+ jae L(128bytesormore_nt)
+ sfence
+L(shared_cache_loop_end):
+#if defined DATA_CACHE_SIZE || !defined SHARED
+ POP (%ebx)
+#endif
+ add %ecx, %edx
+ shr $2, %ecx
+ BRANCH_TO_JMPTBL_ENTRY (L(table_16_128bytes))
+
+ .pushsection .rodata.sse2,"a",@progbits
+ ALIGN (2)
+L(table_16_128bytes):
+ .int JMPTBL (L(aligned_16_0bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_4bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_8bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_12bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_16bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_20bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_24bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_28bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_32bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_36bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_40bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_44bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_48bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_52bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_56bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_60bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_64bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_68bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_72bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_76bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_80bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_84bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_88bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_92bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_96bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_100bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_104bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_108bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_112bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_116bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_120bytes), L(table_16_128bytes))
+ .int JMPTBL (L(aligned_16_124bytes), L(table_16_128bytes))
+ .popsection
+
+ ALIGN (4)
+L(aligned_16_112bytes):
+ movdqa %xmm0, -112(%edx)
+L(aligned_16_96bytes):
+ movdqa %xmm0, -96(%edx)
+L(aligned_16_80bytes):
+ movdqa %xmm0, -80(%edx)
+L(aligned_16_64bytes):
+ movdqa %xmm0, -64(%edx)
+L(aligned_16_48bytes):
+ movdqa %xmm0, -48(%edx)
+L(aligned_16_32bytes):
+ movdqa %xmm0, -32(%edx)
+L(aligned_16_16bytes):
+ movdqa %xmm0, -16(%edx)
+L(aligned_16_0bytes):
+ SETRTNVAL
+ RETURN
+
+ ALIGN (4)
+L(aligned_16_116bytes):
+ movdqa %xmm0, -116(%edx)
+L(aligned_16_100bytes):
+ movdqa %xmm0, -100(%edx)
+L(aligned_16_84bytes):
+ movdqa %xmm0, -84(%edx)
+L(aligned_16_68bytes):
+ movdqa %xmm0, -68(%edx)
+L(aligned_16_52bytes):
+ movdqa %xmm0, -52(%edx)
+L(aligned_16_36bytes):
+ movdqa %xmm0, -36(%edx)
+L(aligned_16_20bytes):
+ movdqa %xmm0, -20(%edx)
+L(aligned_16_4bytes):
+ movl %eax, -4(%edx)
+ SETRTNVAL
+ RETURN
+
+ ALIGN (4)
+L(aligned_16_120bytes):
+ movdqa %xmm0, -120(%edx)
+L(aligned_16_104bytes):
+ movdqa %xmm0, -104(%edx)
+L(aligned_16_88bytes):
+ movdqa %xmm0, -88(%edx)
+L(aligned_16_72bytes):
+ movdqa %xmm0, -72(%edx)
+L(aligned_16_56bytes):
+ movdqa %xmm0, -56(%edx)
+L(aligned_16_40bytes):
+ movdqa %xmm0, -40(%edx)
+L(aligned_16_24bytes):
+ movdqa %xmm0, -24(%edx)
+L(aligned_16_8bytes):
+ movq %xmm0, -8(%edx)
+ SETRTNVAL
+ RETURN
+
+ ALIGN (4)
+L(aligned_16_124bytes):
+ movdqa %xmm0, -124(%edx)
+L(aligned_16_108bytes):
+ movdqa %xmm0, -108(%edx)
+L(aligned_16_92bytes):
+ movdqa %xmm0, -92(%edx)
+L(aligned_16_76bytes):
+ movdqa %xmm0, -76(%edx)
+L(aligned_16_60bytes):
+ movdqa %xmm0, -60(%edx)
+L(aligned_16_44bytes):
+ movdqa %xmm0, -44(%edx)
+L(aligned_16_28bytes):
+ movdqa %xmm0, -28(%edx)
+L(aligned_16_12bytes):
+ movq %xmm0, -12(%edx)
+ movl %eax, -4(%edx)
+ SETRTNVAL
+ RETURN
+
+END (sse2_memset32_atom)
diff --git a/libcutils/array.c b/libcutils/array.c
new file mode 100644
index 0000000..55ec055
--- /dev/null
+++ b/libcutils/array.c
@@ -0,0 +1,170 @@
+/*
+ * 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 <cutils/array.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+
+#define INITIAL_CAPACITY (4)
+#define MAX_CAPACITY ((int)(UINT_MAX/sizeof(void*)))
+
+struct Array {
+ void** contents;
+ int size;
+ int capacity;
+};
+
+Array* arrayCreate() {
+ return calloc(1, sizeof(struct Array));
+}
+
+void arrayFree(Array* array) {
+ assert(array != NULL);
+
+ // Free internal array.
+ free(array->contents);
+
+ // Free the Array itself.
+ free(array);
+}
+
+/** Returns 0 if successful, < 0 otherwise.. */
+static int ensureCapacity(Array* array, int capacity) {
+ int oldCapacity = array->capacity;
+ if (capacity > oldCapacity) {
+ int newCapacity = (oldCapacity == 0) ? INITIAL_CAPACITY : oldCapacity;
+
+ // Ensure we're not doing something nasty
+ if (capacity > MAX_CAPACITY)
+ return -1;
+
+ // Keep doubling capacity until we surpass necessary capacity.
+ while (newCapacity < capacity) {
+ int newCap = newCapacity*2;
+ // Handle integer overflows
+ if (newCap < newCapacity || newCap > MAX_CAPACITY) {
+ newCap = MAX_CAPACITY;
+ }
+ newCapacity = newCap;
+ }
+
+ // Should not happen, but better be safe than sorry
+ if (newCapacity < 0 || newCapacity > MAX_CAPACITY)
+ return -1;
+
+ void** newContents;
+ if (array->contents == NULL) {
+ // Allocate new array.
+ newContents = malloc(newCapacity * sizeof(void*));
+ if (newContents == NULL) {
+ return -1;
+ }
+ } else {
+ // Expand existing array.
+ newContents = realloc(array->contents, sizeof(void*) * newCapacity);
+ if (newContents == NULL) {
+ return -1;
+ }
+ }
+
+ array->capacity = newCapacity;
+ array->contents = newContents;
+ }
+
+ return 0;
+}
+
+int arrayAdd(Array* array, void* pointer) {
+ assert(array != NULL);
+ int size = array->size;
+ int result = ensureCapacity(array, size + 1);
+ if (result < 0) {
+ return result;
+ }
+ array->contents[size] = pointer;
+ array->size++;
+ return 0;
+}
+
+static inline void checkBounds(Array* array, int index) {
+ assert(array != NULL);
+ assert(index < array->size);
+ assert(index >= 0);
+}
+
+void* arrayGet(Array* array, int index) {
+ checkBounds(array, index);
+ return array->contents[index];
+}
+
+void* arrayRemove(Array* array, int index) {
+ checkBounds(array, index);
+
+ void* pointer = array->contents[index];
+
+ int newSize = array->size - 1;
+
+ // Shift entries left.
+ if (index != newSize) {
+ memmove(array->contents + index, array->contents + index + 1,
+ (sizeof(void*)) * (newSize - index));
+ }
+
+ array->size = newSize;
+
+ return pointer;
+}
+
+void* arraySet(Array* array, int index, void* pointer) {
+ checkBounds(array, index);
+ void* old = array->contents[index];
+ array->contents[index] = pointer;
+ return old;
+}
+
+int arraySetSize(Array* array, int newSize) {
+ assert(array != NULL);
+ assert(newSize >= 0);
+
+ int oldSize = array->size;
+
+ if (newSize > oldSize) {
+ // Expand.
+ int result = ensureCapacity(array, newSize);
+ if (result < 0) {
+ return result;
+ }
+
+ // Zero out new entries.
+ memset(array->contents + sizeof(void*) * oldSize, 0,
+ sizeof(void*) * (newSize - oldSize));
+ }
+
+ array->size = newSize;
+
+ return 0;
+}
+
+int arraySize(Array* array) {
+ assert(array != NULL);
+ return array->size;
+}
+
+const void** arrayUnwrap(Array* array) {
+ return (const void**)array->contents;
+}
diff --git a/libcutils/ashmem-dev.c b/libcutils/ashmem-dev.c
new file mode 100644
index 0000000..8b71f87
--- /dev/null
+++ b/libcutils/ashmem-dev.c
@@ -0,0 +1,90 @@
+/*
+ * 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.
+ */
+
+/*
+ * Implementation of the user-space ashmem API for devices, which have our
+ * ashmem-enabled kernel. See ashmem-sim.c for the "fake" tmp-based version,
+ * used by the simulator.
+ */
+
+#include <unistd.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+
+#include <linux/ashmem.h>
+#include <cutils/ashmem.h>
+
+#define ASHMEM_DEVICE "/dev/ashmem"
+
+/*
+ * ashmem_create_region - creates a new ashmem region and returns the file
+ * descriptor, or <0 on error
+ *
+ * `name' is an optional label to give the region (visible in /proc/pid/maps)
+ * `size' is the size of the region, in page-aligned bytes
+ */
+int ashmem_create_region(const char *name, size_t size)
+{
+ int fd, ret;
+
+ fd = open(ASHMEM_DEVICE, O_RDWR);
+ if (fd < 0)
+ return fd;
+
+ if (name) {
+ char buf[ASHMEM_NAME_LEN];
+
+ strlcpy(buf, name, sizeof(buf));
+ ret = ioctl(fd, ASHMEM_SET_NAME, buf);
+ if (ret < 0)
+ goto error;
+ }
+
+ ret = ioctl(fd, ASHMEM_SET_SIZE, size);
+ if (ret < 0)
+ goto error;
+
+ return fd;
+
+error:
+ close(fd);
+ return ret;
+}
+
+int ashmem_set_prot_region(int fd, int prot)
+{
+ return ioctl(fd, ASHMEM_SET_PROT_MASK, prot);
+}
+
+int ashmem_pin_region(int fd, size_t offset, size_t len)
+{
+ struct ashmem_pin pin = { offset, len };
+ return ioctl(fd, ASHMEM_PIN, &pin);
+}
+
+int ashmem_unpin_region(int fd, size_t offset, size_t len)
+{
+ struct ashmem_pin pin = { offset, len };
+ return ioctl(fd, ASHMEM_UNPIN, &pin);
+}
+
+int ashmem_get_size_region(int fd)
+{
+ return ioctl(fd, ASHMEM_GET_SIZE, NULL);
+}
diff --git a/libcutils/ashmem-host.c b/libcutils/ashmem-host.c
new file mode 100644
index 0000000..f03e130
--- /dev/null
+++ b/libcutils/ashmem-host.c
@@ -0,0 +1,114 @@
+/*
+ * 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.
+ */
+
+/*
+ * Implementation of the user-space ashmem API for the simulator, which lacks
+ * an ashmem-enabled kernel. See ashmem-dev.c for the real ashmem-based version.
+ */
+
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <errno.h>
+#include <time.h>
+#include <limits.h>
+
+#include <cutils/ashmem.h>
+
+int ashmem_create_region(const char *ignored, size_t size)
+{
+ static const char txt[] = "abcdefghijklmnopqrstuvwxyz"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+ char name[64];
+ unsigned int retries = 0;
+ pid_t pid = getpid();
+ int fd;
+
+ srand(time(NULL) + pid);
+
+retry:
+ /* not beautiful, its just wolf-like loop unrolling */
+ snprintf(name, sizeof(name), "/tmp/android-ashmem-%d-%c%c%c%c%c%c%c%c",
+ pid,
+ txt[(int) ((sizeof(txt) - 1) * (rand() / (RAND_MAX + 1.0)))],
+ txt[(int) ((sizeof(txt) - 1) * (rand() / (RAND_MAX + 1.0)))],
+ txt[(int) ((sizeof(txt) - 1) * (rand() / (RAND_MAX + 1.0)))],
+ txt[(int) ((sizeof(txt) - 1) * (rand() / (RAND_MAX + 1.0)))],
+ txt[(int) ((sizeof(txt) - 1) * (rand() / (RAND_MAX + 1.0)))],
+ txt[(int) ((sizeof(txt) - 1) * (rand() / (RAND_MAX + 1.0)))],
+ txt[(int) ((sizeof(txt) - 1) * (rand() / (RAND_MAX + 1.0)))],
+ txt[(int) ((sizeof(txt) - 1) * (rand() / (RAND_MAX + 1.0)))]);
+
+ /* open O_EXCL & O_CREAT: we are either the sole owner or we fail */
+ fd = open(name, O_RDWR | O_CREAT | O_EXCL, 0600);
+ if (fd == -1) {
+ /* unlikely, but if we failed because `name' exists, retry */
+ if (errno == EEXIST && ++retries < 6)
+ goto retry;
+ return -1;
+ }
+
+ /* truncate the file to `len' bytes */
+ if (ftruncate(fd, size) == -1)
+ goto error;
+
+ if (unlink(name) == -1)
+ goto error;
+
+ return fd;
+error:
+ close(fd);
+ return -1;
+}
+
+int ashmem_set_prot_region(int fd, int prot)
+{
+ return 0;
+}
+
+int ashmem_pin_region(int fd, size_t offset, size_t len)
+{
+ return ASHMEM_NOT_PURGED;
+}
+
+int ashmem_unpin_region(int fd, size_t offset, size_t len)
+{
+ return ASHMEM_IS_UNPINNED;
+}
+
+int ashmem_get_size_region(int fd)
+{
+ struct stat buf;
+ int result;
+
+ result = fstat(fd, &buf);
+ if (result == -1) {
+ return -1;
+ }
+
+ // Check if this is an "ashmem" region.
+ // TODO: This is very hacky, and can easily break. We need some reliable indicator.
+ if (!(buf.st_nlink == 0 && S_ISREG(buf.st_mode))) {
+ errno = ENOTTY;
+ return -1;
+ }
+
+ return (int)buf.st_size; // TODO: care about overflow (> 2GB file)?
+}
diff --git a/libcutils/atomic-android-sh.c b/libcutils/atomic-android-sh.c
new file mode 100644
index 0000000..f8f1f57
--- /dev/null
+++ b/libcutils/atomic-android-sh.c
@@ -0,0 +1,149 @@
+/*
+ * 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 <cutils/atomic.h>
+#ifdef HAVE_WIN32_THREADS
+#include <windows.h>
+#else
+#include <sched.h>
+#endif
+
+/*
+ * Note :
+ *
+ * (1) SuperH does not have CMPXCHG. It has only TAS for atomic
+ * operations. It does not seem a good idea to implement CMPXCHG,
+ * with TAS. So, we choose to implemnt these operations with
+ * posix mutexes. Please be sure that this might cause performance
+ * problem for Android-SH. Using LL/SC instructions supported in SH-X3,
+ * best performnace would be realized.
+ *
+ * (2) Mutex initialization problem happens, which is commented for
+ * ARM implementation, in this file above.
+ * We follow the fact that the initializer for mutex is a simple zero
+ * value.
+ *
+ * (3) These operations are NOT safe for SMP, as there is no currently
+ * no definition for a memory barrier operation.
+ */
+
+#include <pthread.h>
+
+#define SWAP_LOCK_COUNT 32U
+static pthread_mutex_t _swap_locks[SWAP_LOCK_COUNT];
+
+#define SWAP_LOCK(addr) \
+ &_swap_locks[((unsigned)(void*)(addr) >> 3U) % SWAP_LOCK_COUNT]
+
+
+int32_t android_atomic_acquire_load(volatile const int32_t* addr)
+{
+ return *addr;
+}
+
+int32_t android_atomic_release_load(volatile const int32_t* addr)
+{
+ return *addr;
+}
+
+void android_atomic_acquire_store(int32_t value, volatile int32_t* addr) {
+ int32_t oldValue;
+ do {
+ oldValue = *addr;
+ } while (android_atomic_release_cas(oldValue, value, addr));
+}
+
+void android_atomic_release_store(int32_t value, volatile int32_t* addr) {
+ int32_t oldValue;
+ do {
+ oldValue = *addr;
+ } while (android_atomic_release_cas(oldValue, value, addr));
+}
+
+int32_t android_atomic_inc(volatile int32_t* addr) {
+ int32_t oldValue;
+ do {
+ oldValue = *addr;
+ } while (android_atomic_release_cas(oldValue, oldValue+1, addr));
+ return oldValue;
+}
+
+int32_t android_atomic_dec(volatile int32_t* addr) {
+ int32_t oldValue;
+ do {
+ oldValue = *addr;
+ } while (android_atomic_release_cas(oldValue, oldValue-1, addr));
+ return oldValue;
+}
+
+int32_t android_atomic_add(int32_t value, volatile int32_t* addr) {
+ int32_t oldValue;
+ do {
+ oldValue = *addr;
+ } while (android_atomic_release_cas(oldValue, oldValue+value, addr));
+ return oldValue;
+}
+
+int32_t android_atomic_and(int32_t value, volatile int32_t* addr) {
+ int32_t oldValue;
+ do {
+ oldValue = *addr;
+ } while (android_atomic_release_cas(oldValue, oldValue&value, addr));
+ return oldValue;
+}
+
+int32_t android_atomic_or(int32_t value, volatile int32_t* addr) {
+ int32_t oldValue;
+ do {
+ oldValue = *addr;
+ } while (android_atomic_release_cas(oldValue, oldValue|value, addr));
+ return oldValue;
+}
+
+int32_t android_atomic_acquire_swap(int32_t value, volatile int32_t* addr) {
+ return android_atomic_release_swap(value, addr);
+}
+
+int32_t android_atomic_release_swap(int32_t value, volatile int32_t* addr) {
+ int32_t oldValue;
+ do {
+ oldValue = *addr;
+ } while (android_atomic_cmpxchg(oldValue, value, addr));
+ return oldValue;
+}
+
+int android_atomic_acquire_cmpxchg(int32_t oldvalue, int32_t newvalue,
+ volatile int32_t* addr) {
+ return android_atomic_release_cmpxchg(oldValue, newValue, addr);
+}
+
+int android_atomic_release_cmpxchg(int32_t oldvalue, int32_t newvalue,
+ volatile int32_t* addr) {
+ int result;
+ pthread_mutex_t* lock = SWAP_LOCK(addr);
+
+ pthread_mutex_lock(lock);
+
+ if (*addr == oldvalue) {
+ *addr = newvalue;
+ result = 0;
+ } else {
+ result = 1;
+ }
+ pthread_mutex_unlock(lock);
+ return result;
+}
+
diff --git a/libcutils/atomic.c b/libcutils/atomic.c
new file mode 100644
index 0000000..f6cd8b0
--- /dev/null
+++ b/libcutils/atomic.c
@@ -0,0 +1,19 @@
+/*
+ * 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 inline
+
+#include <cutils/atomic-inline.h>
diff --git a/libcutils/buffer.c b/libcutils/buffer.c
new file mode 100644
index 0000000..f34b8f8
--- /dev/null
+++ b/libcutils/buffer.c
@@ -0,0 +1,116 @@
+/*
+ * 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 LOG_TAG "buffer"
+
+#include <assert.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "buffer.h"
+#include "loghack.h"
+
+Buffer* bufferCreate(size_t capacity) {
+ Buffer* buffer = malloc(sizeof(Buffer));
+ if (buffer == NULL) {
+ return NULL;
+ }
+ buffer->capacity = capacity;
+ buffer->expected = 0;
+ buffer->data = malloc(capacity);
+ if (buffer->data == NULL) {
+ free(buffer);
+ return NULL;
+ }
+ return buffer;
+}
+
+void bufferFree(Buffer* buffer) {
+ free(buffer->data);
+ free(buffer);
+}
+
+Buffer* bufferWrap(char* data, size_t capacity, size_t size) {
+ Buffer* buffer = malloc(sizeof(Buffer));
+ if (buffer == NULL) {
+ return NULL;
+ }
+
+ buffer->data = data;
+ buffer->capacity = capacity;
+ buffer->size = size;
+ buffer->expected = 0;
+ return buffer;
+}
+
+int bufferPrepareForRead(Buffer* buffer, size_t expected) {
+ if (expected > buffer->capacity) {
+ // Expand buffer.
+ char* expanded = realloc(buffer->data, expected);
+ if (expanded == NULL) {
+ errno = ENOMEM;
+ return -1;
+ }
+ buffer->capacity = expected;
+ buffer->data = expanded;
+ }
+
+ buffer->size = 0;
+ buffer->expected = expected;
+ return 0;
+}
+
+ssize_t bufferRead(Buffer* buffer, int fd) {
+ assert(buffer->size < buffer->expected);
+
+ ssize_t bytesRead = read(fd,
+ buffer->data + buffer->size,
+ buffer->expected - buffer->size);
+
+ if (bytesRead > 0) {
+ buffer->size += bytesRead;
+ return buffer->size;
+ }
+
+ return bytesRead;
+}
+
+void bufferPrepareForWrite(Buffer* buffer) {
+ buffer->remaining = buffer->size;
+}
+
+ssize_t bufferWrite(Buffer* buffer, int fd) {
+ assert(buffer->remaining > 0);
+ assert(buffer->remaining <= buffer->size);
+
+ ssize_t bytesWritten = write(fd,
+ buffer->data + buffer->size - buffer->remaining,
+ buffer->remaining);
+
+ if (bytesWritten >= 0) {
+ buffer->remaining -= bytesWritten;
+
+ LOGD("Buffer bytes written: %d", (int) bytesWritten);
+ LOGD("Buffer size: %d", (int) buffer->size);
+ LOGD("Buffer remaining: %d", (int) buffer->remaining);
+
+ return buffer->remaining;
+ }
+
+ return bytesWritten;
+}
+
diff --git a/libcutils/buffer.h b/libcutils/buffer.h
new file mode 100644
index 0000000..d8bc108
--- /dev/null
+++ b/libcutils/buffer.h
@@ -0,0 +1,112 @@
+/*
+ * 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.
+ */
+
+/**
+ * Byte buffer utilities.
+ */
+
+#ifndef __BUFFER_H
+#define __BUFFER_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdlib.h>
+
+/**
+ * Byte buffer of known size. Keeps track of how much data has been read
+ * into or written out of the buffer.
+ */
+typedef struct {
+ /** Buffered data. */
+ char* data;
+
+ union {
+ /** For reading. # of bytes we expect. */
+ size_t expected;
+
+ /** For writing. # of bytes to write. */
+ size_t remaining;
+ };
+
+ /** Actual # of bytes in the buffer. */
+ size_t size;
+
+ /** Amount of memory allocated for this buffer. */
+ size_t capacity;
+} Buffer;
+
+/**
+ * Returns true if all data has been read into the buffer.
+ */
+#define bufferReadComplete(buffer) (buffer->expected == buffer->size)
+
+/**
+ * Returns true if the buffer has been completely written.
+ */
+#define bufferWriteComplete(buffer) (buffer->remaining == 0)
+
+/**
+ * Creates a new buffer with the given initial capacity.
+ */
+Buffer* bufferCreate(size_t initialCapacity);
+
+/**
+ * Wraps an existing byte array.
+ */
+Buffer* bufferWrap(char* data, size_t capacity, size_t size);
+
+/**
+ * Frees and its data.
+ */
+void bufferFree(Buffer* buffer);
+
+/**
+ * Prepares buffer to read 'expected' number of bytes. Expands capacity if
+ * necessary. Returns 0 if successful or -1 if an error occurs allocating
+ * memory.
+ */
+int bufferPrepareForRead(Buffer* buffer, size_t expected);
+
+/**
+ * Reads some data into a buffer. Returns -1 in case of an error and sets
+ * errno (see read()). Returns 0 for EOF. Updates buffer->size and returns
+ * the new size after a succesful read.
+ *
+ * Precondition: buffer->size < buffer->expected
+ */
+ssize_t bufferRead(Buffer* buffer, int fd);
+
+/**
+ * Prepares a buffer to be written out.
+ */
+void bufferPrepareForWrite(Buffer* buffer);
+
+/**
+ * Writes data from buffer to the given fd. Returns -1 and sets errno in case
+ * of an error. Updates buffer->remaining and returns the number of remaining
+ * bytes to be written after a successful write.
+ *
+ * Precondition: buffer->remaining > 0
+ */
+ssize_t bufferWrite(Buffer* buffer, int fd);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __BUFFER_H */
diff --git a/libcutils/config_utils.c b/libcutils/config_utils.c
new file mode 100644
index 0000000..75fa6c6
--- /dev/null
+++ b/libcutils/config_utils.c
@@ -0,0 +1,317 @@
+/*
+ * 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 <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <cutils/config_utils.h>
+#include <cutils/misc.h>
+
+cnode* config_node(const char *name, const char *value)
+{
+ cnode *node;
+
+ node = calloc(sizeof(cnode), 1);
+ if(node) {
+ node->name = name ? name : "";
+ node->value = value ? value : "";
+ }
+
+ return node;
+}
+
+cnode* config_find(cnode *root, const char *name)
+{
+ cnode *node, *match = NULL;
+
+ /* we walk the whole list, as we need to return the last (newest) entry */
+ for(node = root->first_child; node; node = node->next)
+ if(!strcmp(node->name, name))
+ match = node;
+
+ return match;
+}
+
+static cnode* _config_create(cnode *root, const char *name)
+{
+ cnode *node;
+
+ node = config_node(name, NULL);
+
+ if(root->last_child)
+ root->last_child->next = node;
+ else
+ root->first_child = node;
+
+ root->last_child = node;
+
+ return node;
+}
+
+int config_bool(cnode *root, const char *name, int _default)
+{
+ cnode *node;
+
+ node = config_find(root, name);
+ if(!node)
+ return _default;
+
+ switch(node->value[0]) {
+ case 'y':
+ case 'Y':
+ case '1':
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+const char* config_str(cnode *root, const char *name, const char *_default)
+{
+ cnode *node;
+
+ node = config_find(root, name);
+ if(!node)
+ return _default;
+ return node->value;
+}
+
+void config_set(cnode *root, const char *name, const char *value)
+{
+ cnode *node;
+
+ node = config_find(root, name);
+ if(node)
+ node->value = value;
+ else {
+ node = _config_create(root, name);
+ node->value = value;
+ }
+}
+
+#define T_EOF 0
+#define T_TEXT 1
+#define T_DOT 2
+#define T_OBRACE 3
+#define T_CBRACE 4
+
+typedef struct
+{
+ char *data;
+ char *text;
+ int len;
+ char next;
+} cstate;
+
+static int _lex(cstate *cs, int value)
+{
+ char c;
+ char *s;
+ char *data;
+
+ data = cs->data;
+
+ if(cs->next != 0) {
+ c = cs->next;
+ cs->next = 0;
+ goto got_c;
+ }
+
+restart:
+ for(;;) {
+ c = *data++;
+ got_c:
+ if(isspace(c))
+ continue;
+
+ switch(c) {
+ case 0:
+ return T_EOF;
+
+ case '#':
+ for(;;) {
+ switch(*data) {
+ case 0:
+ cs->data = data;
+ return T_EOF;
+ case '\n':
+ cs->data = data + 1;
+ goto restart;
+ default:
+ data++;
+ }
+ }
+ break;
+
+ case '.':
+ cs->data = data;
+ return T_DOT;
+
+ case '{':
+ cs->data = data;
+ return T_OBRACE;
+
+ case '}':
+ cs->data = data;
+ return T_CBRACE;
+
+ default:
+ s = data - 1;
+
+ if(value) {
+ for(;;) {
+ if(*data == 0) {
+ cs->data = data;
+ break;
+ }
+ if(*data == '\n') {
+ cs->data = data + 1;
+ *data-- = 0;
+ break;
+ }
+ data++;
+ }
+
+ /* strip trailing whitespace */
+ while(data > s){
+ if(!isspace(*data)) break;
+ *data-- = 0;
+ }
+
+ goto got_text;
+ } else {
+ for(;;) {
+ if(isspace(*data)) {
+ *data = 0;
+ cs->data = data + 1;
+ goto got_text;
+ }
+ switch(*data) {
+ case 0:
+ cs->data = data;
+ goto got_text;
+ case '.':
+ case '{':
+ case '}':
+ cs->next = *data;
+ *data = 0;
+ cs->data = data + 1;
+ goto got_text;
+ default:
+ data++;
+ }
+ }
+ }
+ }
+ }
+
+got_text:
+ cs->text = s;
+ return T_TEXT;
+}
+
+#if 0
+char *TOKENNAMES[] = { "EOF", "TEXT", "DOT", "OBRACE", "CBRACE" };
+
+static int lex(cstate *cs, int value)
+{
+ int tok = _lex(cs, value);
+ printf("TOKEN(%d) %s %s\n", value, TOKENNAMES[tok],
+ tok == T_TEXT ? cs->text : "");
+ return tok;
+}
+#else
+#define lex(cs,v) _lex(cs,v)
+#endif
+
+static int parse_expr(cstate *cs, cnode *node);
+
+static int parse_block(cstate *cs, cnode *node)
+{
+ for(;;){
+ switch(lex(cs, 0)){
+ case T_TEXT:
+ if(parse_expr(cs, node)) return -1;
+ continue;
+
+ case T_CBRACE:
+ return 0;
+
+ default:
+ return -1;
+ }
+ }
+}
+
+static int parse_expr(cstate *cs, cnode *root)
+{
+ cnode *node;
+
+ /* last token was T_TEXT */
+ node = config_find(root, cs->text);
+ if(!node || *node->value)
+ node = _config_create(root, cs->text);
+
+ for(;;) {
+ switch(lex(cs, 1)) {
+ case T_DOT:
+ if(lex(cs, 0) != T_TEXT)
+ return -1;
+ node = _config_create(node, cs->text);
+ continue;
+
+ case T_TEXT:
+ node->value = cs->text;
+ return 0;
+
+ case T_OBRACE:
+ return parse_block(cs, node);
+
+ default:
+ return -1;
+ }
+ }
+}
+
+void config_load(cnode *root, char *data)
+{
+ if(data != 0) {
+ cstate cs;
+ cs.data = data;
+ cs.next = 0;
+
+ for(;;) {
+ switch(lex(&cs, 0)) {
+ case T_TEXT:
+ if(parse_expr(&cs, root))
+ return;
+ break;
+ default:
+ return;
+ }
+ }
+ }
+}
+
+void config_load_file(cnode *root, const char *fn)
+{
+ char *data;
+ data = load_file(fn, 0);
+ config_load(root, data);
+}
diff --git a/libcutils/cpu_info.c b/libcutils/cpu_info.c
new file mode 100644
index 0000000..23dda8a
--- /dev/null
+++ b/libcutils/cpu_info.c
@@ -0,0 +1,83 @@
+/* libs/cutils/cpu_info.c
+**
+** Copyright 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 <cutils/cpu_info.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+// we cache the serial number here.
+// this is also used as a fgets() line buffer when we are reading /proc/cpuinfo
+static char serial_number[100] = { 0 };
+
+extern const char* get_cpu_serial_number(void)
+{
+ if (serial_number[0] == 0)
+ {
+ FILE* file;
+ char* chp, *end;
+ char* whitespace;
+ int length;
+
+ // read serial number from /proc/cpuinfo
+ file = fopen("proc/cpuinfo", "r");
+ if (! file)
+ return NULL;
+
+ while ((chp = fgets(serial_number, sizeof(serial_number), file)) != NULL)
+ {
+ // look for something like "Serial : 999206122a03591c"
+
+ if (strncmp(chp, "Serial", 6) != 0)
+ continue;
+
+ chp = strchr(chp, ':');
+ if (!chp)
+ continue;
+
+ // skip colon and whitespace
+ while ( *(++chp) == ' ') {}
+
+ // truncate trailing whitespace
+ end = chp;
+ while (*end && *end != ' ' && *end != '\t' && *end != '\n' && *end != '\r')
+ ++end;
+ *end = 0;
+
+ whitespace = strchr(chp, ' ');
+ if (whitespace)
+ *whitespace = 0;
+ whitespace = strchr(chp, '\t');
+ if (whitespace)
+ *whitespace = 0;
+ whitespace = strchr(chp, '\r');
+ if (whitespace)
+ *whitespace = 0;
+ whitespace = strchr(chp, '\n');
+ if (whitespace)
+ *whitespace = 0;
+
+ // shift serial number to beginning of the buffer
+ memmove(serial_number, chp, strlen(chp) + 1);
+ break;
+ }
+
+ fclose(file);
+ }
+
+ return (serial_number[0] ? serial_number : NULL);
+}
diff --git a/libcutils/dir_hash.c b/libcutils/dir_hash.c
new file mode 100644
index 0000000..be14af6
--- /dev/null
+++ b/libcutils/dir_hash.c
@@ -0,0 +1,334 @@
+/*
+ * 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 <dirent.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sha1.h>
+#include <unistd.h>
+#include <limits.h>
+
+#include <sys/stat.h>
+
+#include <netinet/in.h>
+#include <resolv.h>
+
+#include <cutils/dir_hash.h>
+
+/**
+ * Copies, if it fits within max_output_string bytes, into output_string
+ * a hash of the contents, size, permissions, uid, and gid of the file
+ * specified by path, using the specified algorithm. Returns the length
+ * of the output string, or a negative number if the buffer is too short.
+ */
+int get_file_hash(HashAlgorithm algorithm, const char *path,
+ char *output_string, size_t max_output_string) {
+ SHA1_CTX context;
+ struct stat sb;
+ unsigned char md[SHA1_DIGEST_LENGTH];
+ int used;
+ size_t n;
+
+ if (algorithm != SHA_1) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (stat(path, &sb) != 0) {
+ return -1;
+ }
+
+ if (S_ISLNK(sb.st_mode)) {
+ char buf[PATH_MAX];
+ int len;
+
+ len = readlink(path, buf, sizeof(buf));
+ if (len < 0) {
+ return -1;
+ }
+
+ SHA1Init(&context);
+ SHA1Update(&context, (unsigned char *) buf, len);
+ SHA1Final(md, &context);
+ } else if (S_ISREG(sb.st_mode)) {
+ char buf[10000];
+ FILE *f = fopen(path, "rb");
+ int len;
+
+ if (f == NULL) {
+ return -1;
+ }
+
+ SHA1Init(&context);
+
+ while ((len = fread(buf, 1, sizeof(buf), f)) > 0) {
+ SHA1Update(&context, (unsigned char *) buf, len);
+ }
+
+ if (ferror(f)) {
+ fclose(f);
+ return -1;
+ }
+
+ fclose(f);
+ SHA1Final(md, &context);
+ }
+
+ if (S_ISLNK(sb.st_mode) || S_ISREG(sb.st_mode)) {
+ used = b64_ntop(md, SHA1_DIGEST_LENGTH,
+ output_string, max_output_string);
+ if (used < 0) {
+ errno = ENOSPC;
+ return -1;
+ }
+
+ n = snprintf(output_string + used, max_output_string - used,
+ " %d 0%o %d %d", (int) sb.st_size, sb.st_mode,
+ (int) sb.st_uid, (int) sb.st_gid);
+ } else {
+ n = snprintf(output_string, max_output_string,
+ "- - 0%o %d %d", sb.st_mode,
+ (int) sb.st_uid, (int) sb.st_gid);
+ }
+
+ if (n >= max_output_string - used) {
+ errno = ENOSPC;
+ return -(used + n);
+ }
+
+ return used + n;
+}
+
+struct list {
+ char *name;
+ struct list *next;
+};
+
+static int cmp(const void *a, const void *b) {
+ struct list *const *ra = a;
+ struct list *const *rb = b;
+
+ return strcmp((*ra)->name, (*rb)->name);
+}
+
+static int recurse(HashAlgorithm algorithm, const char *directory_path,
+ struct list **out) {
+ struct list *list = NULL;
+ struct list *f;
+
+ struct dirent *de;
+ DIR *d = opendir(directory_path);
+
+ if (d == NULL) {
+ return -1;
+ }
+
+ while ((de = readdir(d)) != NULL) {
+ if (strcmp(de->d_name, ".") == 0) {
+ continue;
+ }
+ if (strcmp(de->d_name, "..") == 0) {
+ continue;
+ }
+
+ char *name = malloc(strlen(de->d_name) + 1);
+ struct list *node = malloc(sizeof(struct list));
+
+ if (name == NULL || node == NULL) {
+ struct list *next;
+ for (f = list; f != NULL; f = next) {
+ next = f->next;
+ free(f->name);
+ free(f);
+ }
+
+ free(name);
+ free(node);
+ return -1;
+ }
+
+ strcpy(name, de->d_name);
+
+ node->name = name;
+ node->next = list;
+ list = node;
+ }
+
+ closedir(d);
+
+ for (f = list; f != NULL; f = f->next) {
+ struct stat sb;
+ char *name;
+ char outstr[NAME_MAX + 100];
+ char *keep;
+ struct list *res;
+
+ name = malloc(strlen(f->name) + strlen(directory_path) + 2);
+ if (name == NULL) {
+ struct list *next;
+ for (f = list; f != NULL; f = f->next) {
+ next = f->next;
+ free(f->name);
+ free(f);
+ }
+ for (f = *out; f != NULL; f = f->next) {
+ next = f->next;
+ free(f->name);
+ free(f);
+ }
+ *out = NULL;
+ return -1;
+ }
+
+ sprintf(name, "%s/%s", directory_path, f->name);
+
+ int len = get_file_hash(algorithm, name,
+ outstr, sizeof(outstr));
+ if (len < 0) {
+ // should not happen
+ return -1;
+ }
+
+ keep = malloc(len + strlen(name) + 3);
+ res = malloc(sizeof(struct list));
+
+ if (keep == NULL || res == NULL) {
+ struct list *next;
+ for (f = list; f != NULL; f = f->next) {
+ next = f->next;
+ free(f->name);
+ free(f);
+ }
+ for (f = *out; f != NULL; f = f->next) {
+ next = f->next;
+ free(f->name);
+ free(f);
+ }
+ *out = NULL;
+
+ free(keep);
+ free(res);
+ return -1;
+ }
+
+ sprintf(keep, "%s %s\n", name, outstr);
+
+ res->name = keep;
+ res->next = *out;
+ *out = res;
+
+ if ((stat(name, &sb) == 0) && S_ISDIR(sb.st_mode)) {
+ if (recurse(algorithm, name, out) < 0) {
+ struct list *next;
+ for (f = list; f != NULL; f = next) {
+ next = f->next;
+ free(f->name);
+ free(f);
+ }
+
+ return -1;
+ }
+ }
+ }
+
+ struct list *next;
+ for (f = list; f != NULL; f = next) {
+ next = f->next;
+
+ free(f->name);
+ free(f);
+ }
+}
+
+/**
+ * Allocates a string containing the names and hashes of all files recursively
+ * reached under the specified directory_path, using the specified algorithm.
+ * The string is returned as *output_string; the return value is the length
+ * of the string, or a negative number if there was a failure.
+ */
+int get_recursive_hash_manifest(HashAlgorithm algorithm,
+ const char *directory_path,
+ char **output_string) {
+ struct list *out = NULL;
+ struct list *r;
+ struct list **list;
+ int count = 0;
+ int len = 0;
+ int retlen = 0;
+ int i;
+ char *buf;
+
+ if (recurse(algorithm, directory_path, &out) < 0) {
+ return -1;
+ }
+
+ for (r = out; r != NULL; r = r->next) {
+ count++;
+ len += strlen(r->name);
+ }
+
+ list = malloc(count * sizeof(struct list *));
+ if (list == NULL) {
+ struct list *next;
+ for (r = out; r != NULL; r = next) {
+ next = r->next;
+ free(r->name);
+ free(r);
+ }
+ return -1;
+ }
+
+ count = 0;
+ for (r = out; r != NULL; r = r->next) {
+ list[count++] = r;
+ }
+
+ qsort(list, count, sizeof(struct list *), cmp);
+
+ buf = malloc(len + 1);
+ if (buf == NULL) {
+ struct list *next;
+ for (r = out; r != NULL; r = next) {
+ next = r->next;
+ free(r->name);
+ free(r);
+ }
+ free(list);
+ return -1;
+ }
+
+ for (i = 0; i < count; i++) {
+ int n = strlen(list[i]->name);
+
+ strcpy(buf + retlen, list[i]->name);
+ retlen += n;
+ }
+
+ free(list);
+
+ struct list *next;
+ for (r = out; r != NULL; r = next) {
+ next = r->next;
+
+ free(r->name);
+ free(r);
+ }
+
+ *output_string = buf;
+ return retlen;
+}
diff --git a/libcutils/dlmalloc_stubs.c b/libcutils/dlmalloc_stubs.c
new file mode 100644
index 0000000..1ced147
--- /dev/null
+++ b/libcutils/dlmalloc_stubs.c
@@ -0,0 +1,29 @@
+/*
+ * 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.
+ */
+
+/* No-op stubs for functions defined in system/bionic/bionic/dlmalloc.c.
+ */
+void dlmalloc_walk_free_pages()
+{
+}
+
+void dlmalloc_walk_heap()
+{
+}
+
+void dlmalloc_trim()
+{
+}
diff --git a/libcutils/hashmap.c b/libcutils/hashmap.c
new file mode 100644
index 0000000..e29bc24
--- /dev/null
+++ b/libcutils/hashmap.c
@@ -0,0 +1,350 @@
+/*
+ * 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 <cutils/hashmap.h>
+#include <assert.h>
+#include <errno.h>
+#include <cutils/threads.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdbool.h>
+#include <sys/types.h>
+
+typedef struct Entry Entry;
+struct Entry {
+ void* key;
+ int hash;
+ void* value;
+ Entry* next;
+};
+
+struct Hashmap {
+ Entry** buckets;
+ size_t bucketCount;
+ int (*hash)(void* key);
+ bool (*equals)(void* keyA, void* keyB);
+ mutex_t lock;
+ size_t size;
+};
+
+Hashmap* hashmapCreate(size_t initialCapacity,
+ int (*hash)(void* key), bool (*equals)(void* keyA, void* keyB)) {
+ assert(hash != NULL);
+ assert(equals != NULL);
+
+ Hashmap* map = malloc(sizeof(Hashmap));
+ if (map == NULL) {
+ return NULL;
+ }
+
+ // 0.75 load factor.
+ size_t minimumBucketCount = initialCapacity * 4 / 3;
+ map->bucketCount = 1;
+ while (map->bucketCount <= minimumBucketCount) {
+ // Bucket count must be power of 2.
+ map->bucketCount <<= 1;
+ }
+
+ map->buckets = calloc(map->bucketCount, sizeof(Entry*));
+ if (map->buckets == NULL) {
+ free(map);
+ return NULL;
+ }
+
+ map->size = 0;
+
+ map->hash = hash;
+ map->equals = equals;
+
+ mutex_init(&map->lock);
+
+ return map;
+}
+
+/**
+ * Hashes the given key.
+ */
+static inline int hashKey(Hashmap* map, void* key) {
+ int h = map->hash(key);
+
+ // We apply this secondary hashing discovered by Doug Lea to defend
+ // against bad hashes.
+ h += ~(h << 9);
+ h ^= (((unsigned int) h) >> 14);
+ h += (h << 4);
+ h ^= (((unsigned int) h) >> 10);
+
+ return h;
+}
+
+size_t hashmapSize(Hashmap* map) {
+ return map->size;
+}
+
+static inline size_t calculateIndex(size_t bucketCount, int hash) {
+ return ((size_t) hash) & (bucketCount - 1);
+}
+
+static void expandIfNecessary(Hashmap* map) {
+ // If the load factor exceeds 0.75...
+ if (map->size > (map->bucketCount * 3 / 4)) {
+ // Start off with a 0.33 load factor.
+ size_t newBucketCount = map->bucketCount << 1;
+ Entry** newBuckets = calloc(newBucketCount, sizeof(Entry*));
+ if (newBuckets == NULL) {
+ // Abort expansion.
+ return;
+ }
+
+ // Move over existing entries.
+ size_t i;
+ for (i = 0; i < map->bucketCount; i++) {
+ Entry* entry = map->buckets[i];
+ while (entry != NULL) {
+ Entry* next = entry->next;
+ size_t index = calculateIndex(newBucketCount, entry->hash);
+ entry->next = newBuckets[index];
+ newBuckets[index] = entry;
+ entry = next;
+ }
+ }
+
+ // Copy over internals.
+ free(map->buckets);
+ map->buckets = newBuckets;
+ map->bucketCount = newBucketCount;
+ }
+}
+
+void hashmapLock(Hashmap* map) {
+ mutex_lock(&map->lock);
+}
+
+void hashmapUnlock(Hashmap* map) {
+ mutex_unlock(&map->lock);
+}
+
+void hashmapFree(Hashmap* map) {
+ size_t i;
+ for (i = 0; i < map->bucketCount; i++) {
+ Entry* entry = map->buckets[i];
+ while (entry != NULL) {
+ Entry* next = entry->next;
+ free(entry);
+ entry = next;
+ }
+ }
+ free(map->buckets);
+ mutex_destroy(&map->lock);
+ free(map);
+}
+
+int hashmapHash(void* key, size_t keySize) {
+ int h = keySize;
+ char* data = (char*) key;
+ size_t i;
+ for (i = 0; i < keySize; i++) {
+ h = h * 31 + *data;
+ data++;
+ }
+ return h;
+}
+
+static Entry* createEntry(void* key, int hash, void* value) {
+ Entry* entry = malloc(sizeof(Entry));
+ if (entry == NULL) {
+ return NULL;
+ }
+ entry->key = key;
+ entry->hash = hash;
+ entry->value = value;
+ entry->next = NULL;
+ return entry;
+}
+
+static inline bool equalKeys(void* keyA, int hashA, void* keyB, int hashB,
+ bool (*equals)(void*, void*)) {
+ if (keyA == keyB) {
+ return true;
+ }
+ if (hashA != hashB) {
+ return false;
+ }
+ return equals(keyA, keyB);
+}
+
+void* hashmapPut(Hashmap* map, void* key, void* value) {
+ int hash = hashKey(map, key);
+ size_t index = calculateIndex(map->bucketCount, hash);
+
+ Entry** p = &(map->buckets[index]);
+ while (true) {
+ Entry* current = *p;
+
+ // Add a new entry.
+ if (current == NULL) {
+ *p = createEntry(key, hash, value);
+ if (*p == NULL) {
+ errno = ENOMEM;
+ return NULL;
+ }
+ map->size++;
+ expandIfNecessary(map);
+ return NULL;
+ }
+
+ // Replace existing entry.
+ if (equalKeys(current->key, current->hash, key, hash, map->equals)) {
+ void* oldValue = current->value;
+ current->value = value;
+ return oldValue;
+ }
+
+ // Move to next entry.
+ p = &current->next;
+ }
+}
+
+void* hashmapGet(Hashmap* map, void* key) {
+ int hash = hashKey(map, key);
+ size_t index = calculateIndex(map->bucketCount, hash);
+
+ Entry* entry = map->buckets[index];
+ while (entry != NULL) {
+ if (equalKeys(entry->key, entry->hash, key, hash, map->equals)) {
+ return entry->value;
+ }
+ entry = entry->next;
+ }
+
+ return NULL;
+}
+
+bool hashmapContainsKey(Hashmap* map, void* key) {
+ int hash = hashKey(map, key);
+ size_t index = calculateIndex(map->bucketCount, hash);
+
+ Entry* entry = map->buckets[index];
+ while (entry != NULL) {
+ if (equalKeys(entry->key, entry->hash, key, hash, map->equals)) {
+ return true;
+ }
+ entry = entry->next;
+ }
+
+ return false;
+}
+
+void* hashmapMemoize(Hashmap* map, void* key,
+ void* (*initialValue)(void* key, void* context), void* context) {
+ int hash = hashKey(map, key);
+ size_t index = calculateIndex(map->bucketCount, hash);
+
+ Entry** p = &(map->buckets[index]);
+ while (true) {
+ Entry* current = *p;
+
+ // Add a new entry.
+ if (current == NULL) {
+ *p = createEntry(key, hash, NULL);
+ if (*p == NULL) {
+ errno = ENOMEM;
+ return NULL;
+ }
+ void* value = initialValue(key, context);
+ (*p)->value = value;
+ map->size++;
+ expandIfNecessary(map);
+ return value;
+ }
+
+ // Return existing value.
+ if (equalKeys(current->key, current->hash, key, hash, map->equals)) {
+ return current->value;
+ }
+
+ // Move to next entry.
+ p = &current->next;
+ }
+}
+
+void* hashmapRemove(Hashmap* map, void* key) {
+ int hash = hashKey(map, key);
+ size_t index = calculateIndex(map->bucketCount, hash);
+
+ // Pointer to the current entry.
+ Entry** p = &(map->buckets[index]);
+ Entry* current;
+ while ((current = *p) != NULL) {
+ if (equalKeys(current->key, current->hash, key, hash, map->equals)) {
+ void* value = current->value;
+ *p = current->next;
+ free(current);
+ map->size--;
+ return value;
+ }
+
+ p = &current->next;
+ }
+
+ return NULL;
+}
+
+void hashmapForEach(Hashmap* map,
+ bool (*callback)(void* key, void* value, void* context),
+ void* context) {
+ size_t i;
+ for (i = 0; i < map->bucketCount; i++) {
+ Entry* entry = map->buckets[i];
+ while (entry != NULL) {
+ if (!callback(entry->key, entry->value, context)) {
+ return;
+ }
+ entry = entry->next;
+ }
+ }
+}
+
+size_t hashmapCurrentCapacity(Hashmap* map) {
+ size_t bucketCount = map->bucketCount;
+ return bucketCount * 3 / 4;
+}
+
+size_t hashmapCountCollisions(Hashmap* map) {
+ size_t collisions = 0;
+ size_t i;
+ for (i = 0; i < map->bucketCount; i++) {
+ Entry* entry = map->buckets[i];
+ while (entry != NULL) {
+ if (entry->next != NULL) {
+ collisions++;
+ }
+ entry = entry->next;
+ }
+ }
+ return collisions;
+}
+
+int hashmapIntHash(void* key) {
+ // Return the key value itself.
+ return *((int*) key);
+}
+
+bool hashmapIntEquals(void* keyA, void* keyB) {
+ int a = *((int*) keyA);
+ int b = *((int*) keyB);
+ return a == b;
+}
diff --git a/libcutils/iosched_policy.c b/libcutils/iosched_policy.c
new file mode 100644
index 0000000..f350f58
--- /dev/null
+++ b/libcutils/iosched_policy.c
@@ -0,0 +1,67 @@
+
+/* libs/cutils/iosched_policy.c
+**
+** Copyright 2007, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#ifdef HAVE_SCHED_H
+
+#include <cutils/iosched_policy.h>
+
+extern int ioprio_set(int which, int who, int ioprio);
+
+enum {
+ WHO_PROCESS = 1,
+ WHO_PGRP,
+ WHO_USER,
+};
+
+#define CLASS_SHIFT 13
+#define IOPRIO_NORM 4
+
+int android_set_ioprio(int pid, IoSchedClass clazz, int ioprio) {
+#ifdef HAVE_ANDROID_OS
+ if (ioprio_set(WHO_PROCESS, pid, ioprio | (clazz << CLASS_SHIFT))) {
+ return -1;
+ }
+#endif
+ return 0;
+}
+
+int android_get_ioprio(int pid, IoSchedClass *clazz, int *ioprio) {
+#ifdef HAVE_ANDROID_OS
+ int rc;
+
+ if ((rc = ioprio_get(WHO_PROCESS, pid)) < 0) {
+ return -1;
+ }
+
+ *clazz = (rc >> CLASS_SHIFT);
+ *ioprio = (rc & 0xff);
+#else
+ *clazz = IoSchedClass_NONE;
+ *ioprio = 0;
+#endif
+ return 0;
+}
+
+#endif /* HAVE_SCHED_H */
diff --git a/libcutils/load_file.c b/libcutils/load_file.c
new file mode 100644
index 0000000..99f2965
--- /dev/null
+++ b/libcutils/load_file.c
@@ -0,0 +1,51 @@
+/* libs/cutils/load_file.c
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+void *load_file(const char *fn, unsigned *_sz)
+{
+ char *data;
+ int sz;
+ int fd;
+
+ data = 0;
+ fd = open(fn, O_RDONLY);
+ if(fd < 0) return 0;
+
+ sz = lseek(fd, 0, SEEK_END);
+ if(sz < 0) goto oops;
+
+ if(lseek(fd, 0, SEEK_SET) != 0) goto oops;
+
+ data = (char*) malloc(sz + 1);
+ if(data == 0) goto oops;
+
+ if(read(fd, data, sz) != sz) goto oops;
+ close(fd);
+ data[sz] = 0;
+
+ if(_sz) *_sz = sz;
+ return data;
+
+oops:
+ close(fd);
+ if(data != 0) free(data);
+ return 0;
+}
diff --git a/libcutils/loghack.h b/libcutils/loghack.h
new file mode 100644
index 0000000..2bfffe4
--- /dev/null
+++ b/libcutils/loghack.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * This is a temporary hack to enable logging from cutils.
+ */
+
+#ifndef _CUTILS_LOGHACK_H
+#define _CUTILS_LOGHACK_H
+
+#ifdef HAVE_ANDROID_OS
+#include <cutils/log.h>
+#else
+#include <stdio.h>
+#define LOG(level, ...) \
+ ((void)printf("cutils:" level "/" LOG_TAG ": " __VA_ARGS__))
+#define LOGV(...) LOG("V", __VA_ARGS__)
+#define LOGD(...) LOG("D", __VA_ARGS__)
+#define LOGI(...) LOG("I", __VA_ARGS__)
+#define LOGW(...) LOG("W", __VA_ARGS__)
+#define LOGE(...) LOG("E", __VA_ARGS__)
+#define LOG_ALWAYS_FATAL(...) do { LOGE(__VA_ARGS__); exit(1); } while (0)
+#endif
+
+#endif // _CUTILS_LOGHACK_H
diff --git a/libcutils/memory.c b/libcutils/memory.c
new file mode 100644
index 0000000..6486b45
--- /dev/null
+++ b/libcutils/memory.c
@@ -0,0 +1,91 @@
+/*
+ * 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 <cutils/memory.h>
+
+#if !HAVE_MEMSET16
+void android_memset16(uint16_t* dst, uint16_t value, size_t size)
+{
+ size >>= 1;
+ while (size--) {
+ *dst++ = value;
+ }
+}
+#endif
+
+#if !HAVE_MEMSET32
+void android_memset32(uint32_t* dst, uint32_t value, size_t size)
+{
+ size >>= 2;
+ while (size--) {
+ *dst++ = value;
+ }
+}
+#endif
+
+#if !HAVE_STRLCPY
+/*
+ * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <string.h>
+
+/* Implementation of strlcpy() for platforms that don't already have it. */
+
+/*
+ * Copy src to string dst of size siz. At most siz-1 characters
+ * will be copied. Always NUL terminates (unless siz == 0).
+ * Returns strlen(src); if retval >= siz, truncation occurred.
+ */
+size_t
+strlcpy(char *dst, const char *src, size_t siz)
+{
+ char *d = dst;
+ const char *s = src;
+ size_t n = siz;
+
+ /* Copy as many bytes as will fit */
+ if (n != 0) {
+ while (--n != 0) {
+ if ((*d++ = *s++) == '\0')
+ break;
+ }
+ }
+
+ /* Not enough room in dst, add NUL and traverse rest of src */
+ if (n == 0) {
+ if (siz != 0)
+ *d = '\0'; /* NUL-terminate dst */
+ while (*s++)
+ ;
+ }
+
+ return(s - src - 1); /* count does not include NUL */
+}
+#endif
diff --git a/libcutils/mq.c b/libcutils/mq.c
new file mode 100644
index 0000000..3b65f1f
--- /dev/null
+++ b/libcutils/mq.c
@@ -0,0 +1,1357 @@
+/*
+ * 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 LOG_TAG "mq"
+
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <sys/uio.h>
+
+#include <cutils/array.h>
+#include <cutils/hashmap.h>
+#include <cutils/selector.h>
+
+#include "loghack.h"
+#include "buffer.h"
+
+/** Number of dead peers to remember. */
+#define PEER_HISTORY (16)
+
+typedef struct sockaddr SocketAddress;
+typedef struct sockaddr_un UnixAddress;
+
+/**
+ * Process/user/group ID. We don't use ucred directly because it's only
+ * available on Linux.
+ */
+typedef struct {
+ pid_t pid;
+ uid_t uid;
+ gid_t gid;
+} Credentials;
+
+/** Listens for bytes coming from remote peers. */
+typedef void BytesListener(Credentials credentials, char* bytes, size_t size);
+
+/** Listens for the deaths of remote peers. */
+typedef void DeathListener(pid_t pid);
+
+/** Types of packets. */
+typedef enum {
+ /** Request for a connection to another peer. */
+ CONNECTION_REQUEST,
+
+ /** A connection to another peer. */
+ CONNECTION,
+
+ /** Reports a failed connection attempt. */
+ CONNECTION_ERROR,
+
+ /** A generic packet of bytes. */
+ BYTES,
+} PacketType;
+
+typedef enum {
+ /** Reading a packet header. */
+ READING_HEADER,
+
+ /** Waiting for a connection from the master. */
+ ACCEPTING_CONNECTION,
+
+ /** Reading bytes. */
+ READING_BYTES,
+} InputState;
+
+/** A packet header. */
+// TODO: Use custom headers for master->peer, peer->master, peer->peer.
+typedef struct {
+ PacketType type;
+ union {
+ /** Packet size. Used for BYTES. */
+ size_t size;
+
+ /** Credentials. Used for CONNECTION and CONNECTION_REQUEST. */
+ Credentials credentials;
+ };
+} Header;
+
+/** A packet which will be sent to a peer. */
+typedef struct OutgoingPacket OutgoingPacket;
+struct OutgoingPacket {
+ /** Packet header. */
+ Header header;
+
+ union {
+ /** Connection to peer. Used with CONNECTION. */
+ int socket;
+
+ /** Buffer of bytes. Used with BYTES. */
+ Buffer* bytes;
+ };
+
+ /** Frees all resources associated with this packet. */
+ void (*free)(OutgoingPacket* packet);
+
+ /** Optional context. */
+ void* context;
+
+ /** Next packet in the queue. */
+ OutgoingPacket* nextPacket;
+};
+
+/** Represents a remote peer. */
+typedef struct PeerProxy PeerProxy;
+
+/** Local peer state. You typically have one peer per process. */
+typedef struct {
+ /** This peer's PID. */
+ pid_t pid;
+
+ /**
+ * Map from pid to peer proxy. The peer has a peer proxy for each remote
+ * peer it's connected to.
+ *
+ * Acquire mutex before use.
+ */
+ Hashmap* peerProxies;
+
+ /** Manages I/O. */
+ Selector* selector;
+
+ /** Used to synchronize operations with the selector thread. */
+ pthread_mutex_t mutex;
+
+ /** Is this peer the master? */
+ bool master;
+
+ /** Peer proxy for the master. */
+ PeerProxy* masterProxy;
+
+ /** Listens for packets from remote peers. */
+ BytesListener* onBytes;
+
+ /** Listens for deaths of remote peers. */
+ DeathListener* onDeath;
+
+ /** Keeps track of recently dead peers. Requires mutex. */
+ pid_t deadPeers[PEER_HISTORY];
+ size_t deadPeerCursor;
+} Peer;
+
+struct PeerProxy {
+ /** Credentials of the remote process. */
+ Credentials credentials;
+
+ /** Keeps track of data coming in from the remote peer. */
+ InputState inputState;
+ Buffer* inputBuffer;
+ PeerProxy* connecting;
+
+ /** File descriptor for this peer. */
+ SelectableFd* fd;
+
+ /**
+ * Queue of packets to be written out to the remote peer.
+ *
+ * Requires mutex.
+ */
+ // TODO: Limit queue length.
+ OutgoingPacket* currentPacket;
+ OutgoingPacket* lastPacket;
+
+ /** Used to write outgoing header. */
+ Buffer outgoingHeader;
+
+ /** True if this is the master's proxy. */
+ bool master;
+
+ /** Reference back to the local peer. */
+ Peer* peer;
+
+ /**
+ * Used in master only. Maps this peer proxy to other peer proxies to
+ * which the peer has been connected to. Maps pid to PeerProxy. Helps
+ * keep track of which connections we've sent to whom.
+ */
+ Hashmap* connections;
+};
+
+/** Server socket path. */
+static const char* MASTER_PATH = "/master.peer";
+
+/** Credentials of the master peer. */
+static const Credentials MASTER_CREDENTIALS = {0, 0, 0};
+
+/** Creates a peer proxy and adds it to the peer proxy map. */
+static PeerProxy* peerProxyCreate(Peer* peer, Credentials credentials);
+
+/** Sets the non-blocking flag on a descriptor. */
+static void setNonBlocking(int fd) {
+ int flags;
+ if ((flags = fcntl(fd, F_GETFL, 0)) < 0) {
+ LOG_ALWAYS_FATAL("fcntl() error: %s", strerror(errno));
+ }
+ if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0) {
+ LOG_ALWAYS_FATAL("fcntl() error: %s", strerror(errno));
+ }
+}
+
+/** Closes a fd and logs a warning if the close fails. */
+static void closeWithWarning(int fd) {
+ int result = close(fd);
+ if (result == -1) {
+ LOGW("close() error: %s", strerror(errno));
+ }
+}
+
+/** Hashes pid_t keys. */
+static int pidHash(void* key) {
+ pid_t* pid = (pid_t*) key;
+ return (int) (*pid);
+}
+
+/** Compares pid_t keys. */
+static bool pidEquals(void* keyA, void* keyB) {
+ pid_t* a = (pid_t*) keyA;
+ pid_t* b = (pid_t*) keyB;
+ return *a == *b;
+}
+
+/** Gets the master address. Not thread safe. */
+static UnixAddress* getMasterAddress() {
+ static UnixAddress masterAddress;
+ static bool initialized = false;
+ if (initialized == false) {
+ masterAddress.sun_family = AF_LOCAL;
+ strcpy(masterAddress.sun_path, MASTER_PATH);
+ initialized = true;
+ }
+ return &masterAddress;
+}
+
+/** Gets exclusive access to the peer for this thread. */
+static void peerLock(Peer* peer) {
+ pthread_mutex_lock(&peer->mutex);
+}
+
+/** Releases exclusive access to the peer. */
+static void peerUnlock(Peer* peer) {
+ pthread_mutex_unlock(&peer->mutex);
+}
+
+/** Frees a simple, i.e. header-only, outgoing packet. */
+static void outgoingPacketFree(OutgoingPacket* packet) {
+ LOGD("Freeing outgoing packet.");
+ free(packet);
+}
+
+/**
+ * Prepare to read a new packet from the peer.
+ */
+static void peerProxyExpectHeader(PeerProxy* peerProxy) {
+ peerProxy->inputState = READING_HEADER;
+ bufferPrepareForRead(peerProxy->inputBuffer, sizeof(Header));
+}
+
+/** Sets up the buffer for the outgoing header. */
+static void peerProxyPrepareOutgoingHeader(PeerProxy* peerProxy) {
+ peerProxy->outgoingHeader.data
+ = (char*) &(peerProxy->currentPacket->header);
+ peerProxy->outgoingHeader.size = sizeof(Header);
+ bufferPrepareForWrite(&peerProxy->outgoingHeader);
+}
+
+/** Adds a packet to the end of the queue. Callers must have the mutex. */
+static void peerProxyEnqueueOutgoingPacket(PeerProxy* peerProxy,
+ OutgoingPacket* newPacket) {
+ newPacket->nextPacket = NULL; // Just in case.
+ if (peerProxy->currentPacket == NULL) {
+ // The queue is empty.
+ peerProxy->currentPacket = newPacket;
+ peerProxy->lastPacket = newPacket;
+
+ peerProxyPrepareOutgoingHeader(peerProxy);
+ } else {
+ peerProxy->lastPacket->nextPacket = newPacket;
+ }
+}
+
+/** Takes the peer lock and enqueues the given packet. */
+static void peerProxyLockAndEnqueueOutgoingPacket(PeerProxy* peerProxy,
+ OutgoingPacket* newPacket) {
+ Peer* peer = peerProxy->peer;
+ peerLock(peer);
+ peerProxyEnqueueOutgoingPacket(peerProxy, newPacket);
+ peerUnlock(peer);
+}
+
+/**
+ * Frees current packet and moves to the next one. Returns true if there is
+ * a next packet or false if the queue is empty.
+ */
+static bool peerProxyNextPacket(PeerProxy* peerProxy) {
+ Peer* peer = peerProxy->peer;
+ peerLock(peer);
+
+ OutgoingPacket* current = peerProxy->currentPacket;
+
+ if (current == NULL) {
+ // The queue is already empty.
+ peerUnlock(peer);
+ return false;
+ }
+
+ OutgoingPacket* next = current->nextPacket;
+ peerProxy->currentPacket = next;
+ current->nextPacket = NULL;
+ current->free(current);
+ if (next == NULL) {
+ // The queue is empty.
+ peerProxy->lastPacket = NULL;
+ peerUnlock(peer);
+ return false;
+ } else {
+ peerUnlock(peer);
+ peerProxyPrepareOutgoingHeader(peerProxy);
+
+ // TODO: Start writing next packet? It would reduce the number of
+ // system calls, but we could also starve other peers.
+ return true;
+ }
+}
+
+/**
+ * Checks whether a peer died recently.
+ */
+static bool peerIsDead(Peer* peer, pid_t pid) {
+ size_t i;
+ for (i = 0; i < PEER_HISTORY; i++) {
+ pid_t deadPeer = peer->deadPeers[i];
+ if (deadPeer == 0) {
+ return false;
+ }
+ if (deadPeer == pid) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/**
+ * Cleans up connection information.
+ */
+static bool peerProxyRemoveConnection(void* key, void* value, void* context) {
+ PeerProxy* deadPeer = (PeerProxy*) context;
+ PeerProxy* otherPeer = (PeerProxy*) value;
+ hashmapRemove(otherPeer->connections, &(deadPeer->credentials.pid));
+ return true;
+}
+
+/**
+ * Called when the peer dies.
+ */
+static void peerProxyKill(PeerProxy* peerProxy, bool errnoIsSet) {
+ if (errnoIsSet) {
+ LOGI("Peer %d died. errno: %s", peerProxy->credentials.pid,
+ strerror(errno));
+ } else {
+ LOGI("Peer %d died.", peerProxy->credentials.pid);
+ }
+
+ // If we lost the master, we're up a creek. We can't let this happen.
+ if (peerProxy->master) {
+ LOG_ALWAYS_FATAL("Lost connection to master.");
+ }
+
+ Peer* localPeer = peerProxy->peer;
+ pid_t pid = peerProxy->credentials.pid;
+
+ peerLock(localPeer);
+
+ // Remember for awhile that the peer died.
+ localPeer->deadPeers[localPeer->deadPeerCursor]
+ = peerProxy->credentials.pid;
+ localPeer->deadPeerCursor++;
+ if (localPeer->deadPeerCursor == PEER_HISTORY) {
+ localPeer->deadPeerCursor = 0;
+ }
+
+ // Remove from peer map.
+ hashmapRemove(localPeer->peerProxies, &pid);
+
+ // External threads can no longer get to this peer proxy, so we don't
+ // need the lock anymore.
+ peerUnlock(localPeer);
+
+ // Remove the fd from the selector.
+ if (peerProxy->fd != NULL) {
+ peerProxy->fd->remove = true;
+ }
+
+ // Clear outgoing packet queue.
+ while (peerProxyNextPacket(peerProxy)) {}
+
+ bufferFree(peerProxy->inputBuffer);
+
+ // This only applies to the master.
+ if (peerProxy->connections != NULL) {
+ // We can't leave these other maps pointing to freed memory.
+ hashmapForEach(peerProxy->connections, &peerProxyRemoveConnection,
+ peerProxy);
+ hashmapFree(peerProxy->connections);
+ }
+
+ // Invoke death listener.
+ localPeer->onDeath(pid);
+
+ // Free the peer proxy itself.
+ free(peerProxy);
+}
+
+static void peerProxyHandleError(PeerProxy* peerProxy, char* functionName) {
+ if (errno == EINTR) {
+ // Log interruptions but otherwise ignore them.
+ LOGW("%s() interrupted.", functionName);
+ } else if (errno == EAGAIN) {
+ LOGD("EWOULDBLOCK");
+ // Ignore.
+ } else {
+ LOGW("Error returned by %s().", functionName);
+ peerProxyKill(peerProxy, true);
+ }
+}
+
+/**
+ * Buffers output sent to a peer. May be called multiple times until the entire
+ * buffer is filled. Returns true when the buffer is empty.
+ */
+static bool peerProxyWriteFromBuffer(PeerProxy* peerProxy, Buffer* outgoing) {
+ ssize_t size = bufferWrite(outgoing, peerProxy->fd->fd);
+ if (size < 0) {
+ peerProxyHandleError(peerProxy, "write");
+ return false;
+ } else {
+ return bufferWriteComplete(outgoing);
+ }
+}
+
+/** Writes packet bytes to peer. */
+static void peerProxyWriteBytes(PeerProxy* peerProxy) {
+ Buffer* buffer = peerProxy->currentPacket->bytes;
+ if (peerProxyWriteFromBuffer(peerProxy, buffer)) {
+ LOGD("Bytes written.");
+ peerProxyNextPacket(peerProxy);
+ }
+}
+
+/** Sends a socket to the peer. */
+static void peerProxyWriteConnection(PeerProxy* peerProxy) {
+ int socket = peerProxy->currentPacket->socket;
+
+ // Why does sending and receiving fds have to be such a PITA?
+ struct msghdr msg;
+ struct iovec iov[1];
+
+ union {
+ struct cmsghdr cm;
+ char control[CMSG_SPACE(sizeof(int))];
+ } control_un;
+
+ struct cmsghdr *cmptr;
+
+ msg.msg_control = control_un.control;
+ msg.msg_controllen = sizeof(control_un.control);
+ cmptr = CMSG_FIRSTHDR(&msg);
+ cmptr->cmsg_len = CMSG_LEN(sizeof(int));
+ cmptr->cmsg_level = SOL_SOCKET;
+ cmptr->cmsg_type = SCM_RIGHTS;
+
+ // Store the socket in the message.
+ *((int *) CMSG_DATA(cmptr)) = peerProxy->currentPacket->socket;
+
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+ iov[0].iov_base = "";
+ iov[0].iov_len = 1;
+ msg.msg_iov = iov;
+ msg.msg_iovlen = 1;
+
+ ssize_t result = sendmsg(peerProxy->fd->fd, &msg, 0);
+
+ if (result < 0) {
+ peerProxyHandleError(peerProxy, "sendmsg");
+ } else {
+ // Success. Queue up the next packet.
+ peerProxyNextPacket(peerProxy);
+
+ }
+}
+
+/**
+ * Writes some outgoing data.
+ */
+static void peerProxyWrite(SelectableFd* fd) {
+ // TODO: Try to write header and body with one system call.
+
+ PeerProxy* peerProxy = (PeerProxy*) fd->data;
+ OutgoingPacket* current = peerProxy->currentPacket;
+
+ if (current == NULL) {
+ // We have nothing left to write.
+ return;
+ }
+
+ // Write the header.
+ Buffer* outgoingHeader = &peerProxy->outgoingHeader;
+ bool headerWritten = bufferWriteComplete(outgoingHeader);
+ if (!headerWritten) {
+ LOGD("Writing header...");
+ headerWritten = peerProxyWriteFromBuffer(peerProxy, outgoingHeader);
+ if (headerWritten) {
+ LOGD("Header written.");
+ }
+ }
+
+ // Write body.
+ if (headerWritten) {
+ PacketType type = current->header.type;
+ switch (type) {
+ case CONNECTION:
+ peerProxyWriteConnection(peerProxy);
+ break;
+ case BYTES:
+ peerProxyWriteBytes(peerProxy);
+ break;
+ case CONNECTION_REQUEST:
+ case CONNECTION_ERROR:
+ // These packets consist solely of a header.
+ peerProxyNextPacket(peerProxy);
+ break;
+ default:
+ LOG_ALWAYS_FATAL("Unknown packet type: %d", type);
+ }
+ }
+}
+
+/**
+ * Sets up a peer proxy's fd before we try to select() it.
+ */
+static void peerProxyBeforeSelect(SelectableFd* fd) {
+ LOGD("Before select...");
+
+ PeerProxy* peerProxy = (PeerProxy*) fd->data;
+
+ peerLock(peerProxy->peer);
+ bool hasPackets = peerProxy->currentPacket != NULL;
+ peerUnlock(peerProxy->peer);
+
+ if (hasPackets) {
+ LOGD("Packets found. Setting onWritable().");
+
+ fd->onWritable = &peerProxyWrite;
+ } else {
+ // We have nothing to write.
+ fd->onWritable = NULL;
+ }
+}
+
+/** Prepare to read bytes from the peer. */
+static void peerProxyExpectBytes(PeerProxy* peerProxy, Header* header) {
+ LOGD("Expecting %d bytes.", header->size);
+
+ peerProxy->inputState = READING_BYTES;
+ if (bufferPrepareForRead(peerProxy->inputBuffer, header->size) == -1) {
+ LOGW("Couldn't allocate memory for incoming data. Size: %u",
+ (unsigned int) header->size);
+
+ // TODO: Ignore the packet and log a warning?
+ peerProxyKill(peerProxy, false);
+ }
+}
+
+/**
+ * Gets a peer proxy for the given ID. Creates a peer proxy if necessary.
+ * Sends a connection request to the master if desired.
+ *
+ * Returns NULL if an error occurs. Sets errno to EHOSTDOWN if the peer died
+ * or ENOMEM if memory couldn't be allocated.
+ */
+static PeerProxy* peerProxyGetOrCreate(Peer* peer, pid_t pid,
+ bool requestConnection) {
+ if (pid == peer->pid) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ if (peerIsDead(peer, pid)) {
+ errno = EHOSTDOWN;
+ return NULL;
+ }
+
+ PeerProxy* peerProxy = hashmapGet(peer->peerProxies, &pid);
+ if (peerProxy != NULL) {
+ return peerProxy;
+ }
+
+ // If this is the master peer, we already know about all peers.
+ if (peer->master) {
+ errno = EHOSTDOWN;
+ return NULL;
+ }
+
+ // Try to create a peer proxy.
+ Credentials credentials;
+ credentials.pid = pid;
+
+ // Fake gid and uid until we have the real thing. The real creds are
+ // filled in by masterProxyExpectConnection(). These fake creds will
+ // never be exposed to the user.
+ credentials.uid = 0;
+ credentials.gid = 0;
+
+ // Make sure we can allocate the connection request packet.
+ OutgoingPacket* packet = NULL;
+ if (requestConnection) {
+ packet = calloc(1, sizeof(OutgoingPacket));
+ if (packet == NULL) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ packet->header.type = CONNECTION_REQUEST;
+ packet->header.credentials = credentials;
+ packet->free = &outgoingPacketFree;
+ }
+
+ peerProxy = peerProxyCreate(peer, credentials);
+ if (peerProxy == NULL) {
+ free(packet);
+ errno = ENOMEM;
+ return NULL;
+ } else {
+ // Send a connection request to the master.
+ if (requestConnection) {
+ PeerProxy* masterProxy = peer->masterProxy;
+ peerProxyEnqueueOutgoingPacket(masterProxy, packet);
+ }
+
+ return peerProxy;
+ }
+}
+
+/**
+ * Switches the master peer proxy into a state where it's waiting for a
+ * connection from the master.
+ */
+static void masterProxyExpectConnection(PeerProxy* masterProxy,
+ Header* header) {
+ // TODO: Restructure things so we don't need this check.
+ // Verify that this really is the master.
+ if (!masterProxy->master) {
+ LOGW("Non-master process %d tried to send us a connection.",
+ masterProxy->credentials.pid);
+ // Kill off the evil peer.
+ peerProxyKill(masterProxy, false);
+ return;
+ }
+
+ masterProxy->inputState = ACCEPTING_CONNECTION;
+ Peer* localPeer = masterProxy->peer;
+
+ // Create a peer proxy so we have somewhere to stash the creds.
+ // See if we already have a proxy set up.
+ pid_t pid = header->credentials.pid;
+ peerLock(localPeer);
+ PeerProxy* peerProxy = peerProxyGetOrCreate(localPeer, pid, false);
+ if (peerProxy == NULL) {
+ LOGW("Peer proxy creation failed: %s", strerror(errno));
+ } else {
+ // Fill in full credentials.
+ peerProxy->credentials = header->credentials;
+ }
+ peerUnlock(localPeer);
+
+ // Keep track of which peer proxy we're accepting a connection for.
+ masterProxy->connecting = peerProxy;
+}
+
+/**
+ * Reads input from a peer process.
+ */
+static void peerProxyRead(SelectableFd* fd);
+
+/** Sets up fd callbacks. */
+static void peerProxySetFd(PeerProxy* peerProxy, SelectableFd* fd) {
+ peerProxy->fd = fd;
+ fd->data = peerProxy;
+ fd->onReadable = &peerProxyRead;
+ fd->beforeSelect = &peerProxyBeforeSelect;
+
+ // Make the socket non-blocking.
+ setNonBlocking(fd->fd);
+}
+
+/**
+ * Accepts a connection sent by the master proxy.
+ */
+static void masterProxyAcceptConnection(PeerProxy* masterProxy) {
+ struct msghdr msg;
+ struct iovec iov[1];
+ ssize_t size;
+ char ignored;
+ int incomingFd;
+
+ // TODO: Reuse code which writes the connection. Who the heck designed
+ // this API anyway?
+ union {
+ struct cmsghdr cm;
+ char control[CMSG_SPACE(sizeof(int))];
+ } control_un;
+ struct cmsghdr *cmptr;
+ msg.msg_control = control_un.control;
+ msg.msg_controllen = sizeof(control_un.control);
+
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+
+ // We sent 1 byte of data so we can detect EOF.
+ iov[0].iov_base = &ignored;
+ iov[0].iov_len = 1;
+ msg.msg_iov = iov;
+ msg.msg_iovlen = 1;
+
+ size = recvmsg(masterProxy->fd->fd, &msg, 0);
+ if (size < 0) {
+ if (errno == EINTR) {
+ // Log interruptions but otherwise ignore them.
+ LOGW("recvmsg() interrupted.");
+ return;
+ } else if (errno == EAGAIN) {
+ // Keep waiting for the connection.
+ return;
+ } else {
+ LOG_ALWAYS_FATAL("Error reading connection from master: %s",
+ strerror(errno));
+ }
+ } else if (size == 0) {
+ // EOF.
+ LOG_ALWAYS_FATAL("Received EOF from master.");
+ }
+
+ // Extract fd from message.
+ if ((cmptr = CMSG_FIRSTHDR(&msg)) != NULL
+ && cmptr->cmsg_len == CMSG_LEN(sizeof(int))) {
+ if (cmptr->cmsg_level != SOL_SOCKET) {
+ LOG_ALWAYS_FATAL("Expected SOL_SOCKET.");
+ }
+ if (cmptr->cmsg_type != SCM_RIGHTS) {
+ LOG_ALWAYS_FATAL("Expected SCM_RIGHTS.");
+ }
+ incomingFd = *((int*) CMSG_DATA(cmptr));
+ } else {
+ LOG_ALWAYS_FATAL("Expected fd.");
+ }
+
+ // The peer proxy this connection is for.
+ PeerProxy* peerProxy = masterProxy->connecting;
+ if (peerProxy == NULL) {
+ LOGW("Received connection for unknown peer.");
+ closeWithWarning(incomingFd);
+ } else {
+ Peer* peer = masterProxy->peer;
+
+ SelectableFd* selectableFd = selectorAdd(peer->selector, incomingFd);
+ if (selectableFd == NULL) {
+ LOGW("Error adding fd to selector for %d.",
+ peerProxy->credentials.pid);
+ closeWithWarning(incomingFd);
+ peerProxyKill(peerProxy, false);
+ }
+
+ peerProxySetFd(peerProxy, selectableFd);
+ }
+
+ peerProxyExpectHeader(masterProxy);
+}
+
+/**
+ * Frees an outgoing packet containing a connection.
+ */
+static void outgoingPacketFreeSocket(OutgoingPacket* packet) {
+ closeWithWarning(packet->socket);
+ outgoingPacketFree(packet);
+}
+
+/**
+ * Connects two known peers.
+ */
+static void masterConnectPeers(PeerProxy* peerA, PeerProxy* peerB) {
+ int sockets[2];
+ int result = socketpair(AF_LOCAL, SOCK_STREAM, 0, sockets);
+ if (result == -1) {
+ LOGW("socketpair() error: %s", strerror(errno));
+ // TODO: Send CONNECTION_FAILED packets to peers.
+ return;
+ }
+
+ OutgoingPacket* packetA = calloc(1, sizeof(OutgoingPacket));
+ OutgoingPacket* packetB = calloc(1, sizeof(OutgoingPacket));
+ if (packetA == NULL || packetB == NULL) {
+ free(packetA);
+ free(packetB);
+ LOGW("malloc() error. Failed to tell process %d that process %d is"
+ " dead.", peerA->credentials.pid, peerB->credentials.pid);
+ return;
+ }
+
+ packetA->header.type = CONNECTION;
+ packetB->header.type = CONNECTION;
+
+ packetA->header.credentials = peerB->credentials;
+ packetB->header.credentials = peerA->credentials;
+
+ packetA->socket = sockets[0];
+ packetB->socket = sockets[1];
+
+ packetA->free = &outgoingPacketFreeSocket;
+ packetB->free = &outgoingPacketFreeSocket;
+
+ peerLock(peerA->peer);
+ peerProxyEnqueueOutgoingPacket(peerA, packetA);
+ peerProxyEnqueueOutgoingPacket(peerB, packetB);
+ peerUnlock(peerA->peer);
+}
+
+/**
+ * Informs a peer that the peer they're trying to connect to couldn't be
+ * found.
+ */
+static void masterReportConnectionError(PeerProxy* peerProxy,
+ Credentials credentials) {
+ OutgoingPacket* packet = calloc(1, sizeof(OutgoingPacket));
+ if (packet == NULL) {
+ LOGW("malloc() error. Failed to tell process %d that process %d is"
+ " dead.", peerProxy->credentials.pid, credentials.pid);
+ return;
+ }
+
+ packet->header.type = CONNECTION_ERROR;
+ packet->header.credentials = credentials;
+ packet->free = &outgoingPacketFree;
+
+ peerProxyLockAndEnqueueOutgoingPacket(peerProxy, packet);
+}
+
+/**
+ * Handles a request to be connected to another peer.
+ */
+static void masterHandleConnectionRequest(PeerProxy* peerProxy,
+ Header* header) {
+ Peer* master = peerProxy->peer;
+ pid_t targetPid = header->credentials.pid;
+ if (!hashmapContainsKey(peerProxy->connections, &targetPid)) {
+ // We haven't connected these peers yet.
+ PeerProxy* targetPeer
+ = (PeerProxy*) hashmapGet(master->peerProxies, &targetPid);
+ if (targetPeer == NULL) {
+ // Unknown process.
+ masterReportConnectionError(peerProxy, header->credentials);
+ } else {
+ masterConnectPeers(peerProxy, targetPeer);
+ }
+ }
+
+ // This packet is complete. Get ready for the next one.
+ peerProxyExpectHeader(peerProxy);
+}
+
+/**
+ * The master told us this peer is dead.
+ */
+static void masterProxyHandleConnectionError(PeerProxy* masterProxy,
+ Header* header) {
+ Peer* peer = masterProxy->peer;
+
+ // Look up the peer proxy.
+ pid_t pid = header->credentials.pid;
+ PeerProxy* peerProxy = NULL;
+ peerLock(peer);
+ peerProxy = hashmapGet(peer->peerProxies, &pid);
+ peerUnlock(peer);
+
+ if (peerProxy != NULL) {
+ LOGI("Couldn't connect to %d.", pid);
+ peerProxyKill(peerProxy, false);
+ } else {
+ LOGW("Peer proxy for %d not found. This shouldn't happen.", pid);
+ }
+
+ peerProxyExpectHeader(masterProxy);
+}
+
+/**
+ * Handles a packet header.
+ */
+static void peerProxyHandleHeader(PeerProxy* peerProxy, Header* header) {
+ switch (header->type) {
+ case CONNECTION_REQUEST:
+ masterHandleConnectionRequest(peerProxy, header);
+ break;
+ case CONNECTION:
+ masterProxyExpectConnection(peerProxy, header);
+ break;
+ case CONNECTION_ERROR:
+ masterProxyHandleConnectionError(peerProxy, header);
+ break;
+ case BYTES:
+ peerProxyExpectBytes(peerProxy, header);
+ break;
+ default:
+ LOGW("Invalid packet type from %d: %d", peerProxy->credentials.pid,
+ header->type);
+ peerProxyKill(peerProxy, false);
+ }
+}
+
+/**
+ * Buffers input sent by peer. May be called multiple times until the entire
+ * buffer is filled. Returns true when the buffer is full.
+ */
+static bool peerProxyBufferInput(PeerProxy* peerProxy) {
+ Buffer* in = peerProxy->inputBuffer;
+ ssize_t size = bufferRead(in, peerProxy->fd->fd);
+ if (size < 0) {
+ peerProxyHandleError(peerProxy, "read");
+ return false;
+ } else if (size == 0) {
+ // EOF.
+ LOGI("EOF");
+ peerProxyKill(peerProxy, false);
+ return false;
+ } else if (bufferReadComplete(in)) {
+ // We're done!
+ return true;
+ } else {
+ // Continue reading.
+ return false;
+ }
+}
+
+/**
+ * Reads input from a peer process.
+ */
+static void peerProxyRead(SelectableFd* fd) {
+ LOGD("Reading...");
+ PeerProxy* peerProxy = (PeerProxy*) fd->data;
+ int state = peerProxy->inputState;
+ Buffer* in = peerProxy->inputBuffer;
+ switch (state) {
+ case READING_HEADER:
+ if (peerProxyBufferInput(peerProxy)) {
+ LOGD("Header read.");
+ // We've read the complete header.
+ Header* header = (Header*) in->data;
+ peerProxyHandleHeader(peerProxy, header);
+ }
+ break;
+ case READING_BYTES:
+ LOGD("Reading bytes...");
+ if (peerProxyBufferInput(peerProxy)) {
+ LOGD("Bytes read.");
+ // We have the complete packet. Notify bytes listener.
+ peerProxy->peer->onBytes(peerProxy->credentials,
+ in->data, in->size);
+
+ // Get ready for the next packet.
+ peerProxyExpectHeader(peerProxy);
+ }
+ break;
+ case ACCEPTING_CONNECTION:
+ masterProxyAcceptConnection(peerProxy);
+ break;
+ default:
+ LOG_ALWAYS_FATAL("Unknown state: %d", state);
+ }
+}
+
+static PeerProxy* peerProxyCreate(Peer* peer, Credentials credentials) {
+ PeerProxy* peerProxy = calloc(1, sizeof(PeerProxy));
+ if (peerProxy == NULL) {
+ return NULL;
+ }
+
+ peerProxy->inputBuffer = bufferCreate(sizeof(Header));
+ if (peerProxy->inputBuffer == NULL) {
+ free(peerProxy);
+ return NULL;
+ }
+
+ peerProxy->peer = peer;
+ peerProxy->credentials = credentials;
+
+ // Initial state == expecting a header.
+ peerProxyExpectHeader(peerProxy);
+
+ // Add this proxy to the map. Make sure the key points to the stable memory
+ // inside of the peer proxy itself.
+ pid_t* pid = &(peerProxy->credentials.pid);
+ hashmapPut(peer->peerProxies, pid, peerProxy);
+ return peerProxy;
+}
+
+/** Accepts a connection to the master peer. */
+static void masterAcceptConnection(SelectableFd* listenerFd) {
+ // Accept connection.
+ int socket = accept(listenerFd->fd, NULL, NULL);
+ if (socket == -1) {
+ LOGW("accept() error: %s", strerror(errno));
+ return;
+ }
+
+ LOGD("Accepted connection as fd %d.", socket);
+
+ // Get credentials.
+ Credentials credentials;
+ struct ucred ucredentials;
+ socklen_t credentialsSize = sizeof(struct ucred);
+ int result = getsockopt(socket, SOL_SOCKET, SO_PEERCRED,
+ &ucredentials, &credentialsSize);
+ // We might want to verify credentialsSize.
+ if (result == -1) {
+ LOGW("getsockopt() error: %s", strerror(errno));
+ closeWithWarning(socket);
+ return;
+ }
+
+ // Copy values into our own structure so we know we have the types right.
+ credentials.pid = ucredentials.pid;
+ credentials.uid = ucredentials.uid;
+ credentials.gid = ucredentials.gid;
+
+ LOGI("Accepted connection from process %d.", credentials.pid);
+
+ Peer* masterPeer = (Peer*) listenerFd->data;
+
+ peerLock(masterPeer);
+
+ // Make sure we don't already have a connection from that process.
+ PeerProxy* peerProxy
+ = hashmapGet(masterPeer->peerProxies, &credentials.pid);
+ if (peerProxy != NULL) {
+ peerUnlock(masterPeer);
+ LOGW("Alread connected to process %d.", credentials.pid);
+ closeWithWarning(socket);
+ return;
+ }
+
+ // Add connection to the selector.
+ SelectableFd* socketFd = selectorAdd(masterPeer->selector, socket);
+ if (socketFd == NULL) {
+ peerUnlock(masterPeer);
+ LOGW("malloc() failed.");
+ closeWithWarning(socket);
+ return;
+ }
+
+ // Create a peer proxy.
+ peerProxy = peerProxyCreate(masterPeer, credentials);
+ peerUnlock(masterPeer);
+ if (peerProxy == NULL) {
+ LOGW("malloc() failed.");
+ socketFd->remove = true;
+ closeWithWarning(socket);
+ }
+ peerProxy->connections = hashmapCreate(10, &pidHash, &pidEquals);
+ peerProxySetFd(peerProxy, socketFd);
+}
+
+/**
+ * Creates the local peer.
+ */
+static Peer* peerCreate() {
+ Peer* peer = calloc(1, sizeof(Peer));
+ if (peer == NULL) {
+ LOG_ALWAYS_FATAL("malloc() error.");
+ }
+ peer->peerProxies = hashmapCreate(10, &pidHash, &pidEquals);
+ peer->selector = selectorCreate();
+
+ pthread_mutexattr_t attributes;
+ if (pthread_mutexattr_init(&attributes) != 0) {
+ LOG_ALWAYS_FATAL("pthread_mutexattr_init() error.");
+ }
+ if (pthread_mutexattr_settype(&attributes, PTHREAD_MUTEX_RECURSIVE) != 0) {
+ LOG_ALWAYS_FATAL("pthread_mutexattr_settype() error.");
+ }
+ if (pthread_mutex_init(&peer->mutex, &attributes) != 0) {
+ LOG_ALWAYS_FATAL("pthread_mutex_init() error.");
+ }
+
+ peer->pid = getpid();
+ return peer;
+}
+
+/** The local peer. */
+static Peer* localPeer;
+
+/** Frees a packet of bytes. */
+static void outgoingPacketFreeBytes(OutgoingPacket* packet) {
+ LOGD("Freeing outgoing packet.");
+ bufferFree(packet->bytes);
+ free(packet);
+}
+
+/**
+ * Sends a packet of bytes to a remote peer. Returns 0 on success.
+ *
+ * Returns -1 if an error occurs. Sets errno to ENOMEM if memory couldn't be
+ * allocated. Sets errno to EHOSTDOWN if the peer died recently. Sets errno
+ * to EINVAL if pid is the same as the local pid.
+ */
+int peerSendBytes(pid_t pid, const char* bytes, size_t size) {
+ Peer* peer = localPeer;
+ assert(peer != NULL);
+
+ OutgoingPacket* packet = calloc(1, sizeof(OutgoingPacket));
+ if (packet == NULL) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ Buffer* copy = bufferCreate(size);
+ if (copy == NULL) {
+ free(packet);
+ errno = ENOMEM;
+ return -1;
+ }
+
+ // Copy data.
+ memcpy(copy->data, bytes, size);
+ copy->size = size;
+
+ packet->bytes = copy;
+ packet->header.type = BYTES;
+ packet->header.size = size;
+ packet->free = outgoingPacketFreeBytes;
+ bufferPrepareForWrite(packet->bytes);
+
+ peerLock(peer);
+
+ PeerProxy* peerProxy = peerProxyGetOrCreate(peer, pid, true);
+ if (peerProxy == NULL) {
+ // The peer is already dead or we couldn't alloc memory. Either way,
+ // errno is set.
+ peerUnlock(peer);
+ packet->free(packet);
+ return -1;
+ } else {
+ peerProxyEnqueueOutgoingPacket(peerProxy, packet);
+ peerUnlock(peer);
+ selectorWakeUp(peer->selector);
+ return 0;
+ }
+}
+
+/** Keeps track of how to free shared bytes. */
+typedef struct {
+ void (*free)(void* context);
+ void* context;
+} SharedBytesFreer;
+
+/** Frees shared bytes. */
+static void outgoingPacketFreeSharedBytes(OutgoingPacket* packet) {
+ SharedBytesFreer* sharedBytesFreer
+ = (SharedBytesFreer*) packet->context;
+ sharedBytesFreer->free(sharedBytesFreer->context);
+ free(sharedBytesFreer);
+ free(packet);
+}
+
+/**
+ * Sends a packet of bytes to a remote peer without copying the bytes. Calls
+ * free() with context after the bytes have been sent.
+ *
+ * Returns -1 if an error occurs. Sets errno to ENOMEM if memory couldn't be
+ * allocated. Sets errno to EHOSTDOWN if the peer died recently. Sets errno
+ * to EINVAL if pid is the same as the local pid.
+ */
+int peerSendSharedBytes(pid_t pid, char* bytes, size_t size,
+ void (*free)(void* context), void* context) {
+ Peer* peer = localPeer;
+ assert(peer != NULL);
+
+ OutgoingPacket* packet = calloc(1, sizeof(OutgoingPacket));
+ if (packet == NULL) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ Buffer* wrapper = bufferWrap(bytes, size, size);
+ if (wrapper == NULL) {
+ free(packet);
+ errno = ENOMEM;
+ return -1;
+ }
+
+ SharedBytesFreer* sharedBytesFreer = malloc(sizeof(SharedBytesFreer));
+ if (sharedBytesFreer == NULL) {
+ free(packet);
+ free(wrapper);
+ errno = ENOMEM;
+ return -1;
+ }
+ sharedBytesFreer->free = free;
+ sharedBytesFreer->context = context;
+
+ packet->bytes = wrapper;
+ packet->context = sharedBytesFreer;
+ packet->header.type = BYTES;
+ packet->header.size = size;
+ packet->free = &outgoingPacketFreeSharedBytes;
+ bufferPrepareForWrite(packet->bytes);
+
+ peerLock(peer);
+
+ PeerProxy* peerProxy = peerProxyGetOrCreate(peer, pid, true);
+ if (peerProxy == NULL) {
+ // The peer is already dead or we couldn't alloc memory. Either way,
+ // errno is set.
+ peerUnlock(peer);
+ packet->free(packet);
+ return -1;
+ } else {
+ peerProxyEnqueueOutgoingPacket(peerProxy, packet);
+ peerUnlock(peer);
+ selectorWakeUp(peer->selector);
+ return 0;
+ }
+}
+
+/**
+ * Starts the master peer. The master peer differs from other peers in that
+ * it is responsible for connecting the other peers. You can only have one
+ * master peer.
+ *
+ * Goes into an I/O loop and does not return.
+ */
+void masterPeerInitialize(BytesListener* bytesListener,
+ DeathListener* deathListener) {
+ // Create and bind socket.
+ int listenerSocket = socket(AF_LOCAL, SOCK_STREAM, 0);
+ if (listenerSocket == -1) {
+ LOG_ALWAYS_FATAL("socket() error: %s", strerror(errno));
+ }
+ unlink(MASTER_PATH);
+ int result = bind(listenerSocket, (SocketAddress*) getMasterAddress(),
+ sizeof(UnixAddress));
+ if (result == -1) {
+ LOG_ALWAYS_FATAL("bind() error: %s", strerror(errno));
+ }
+
+ LOGD("Listener socket: %d", listenerSocket);
+
+ // Queue up to 16 connections.
+ result = listen(listenerSocket, 16);
+ if (result != 0) {
+ LOG_ALWAYS_FATAL("listen() error: %s", strerror(errno));
+ }
+
+ // Make socket non-blocking.
+ setNonBlocking(listenerSocket);
+
+ // Create the peer for this process. Fail if we already have one.
+ if (localPeer != NULL) {
+ LOG_ALWAYS_FATAL("Peer is already initialized.");
+ }
+ localPeer = peerCreate();
+ if (localPeer == NULL) {
+ LOG_ALWAYS_FATAL("malloc() failed.");
+ }
+ localPeer->master = true;
+ localPeer->onBytes = bytesListener;
+ localPeer->onDeath = deathListener;
+
+ // Make listener socket selectable.
+ SelectableFd* listenerFd = selectorAdd(localPeer->selector, listenerSocket);
+ if (listenerFd == NULL) {
+ LOG_ALWAYS_FATAL("malloc() error.");
+ }
+ listenerFd->data = localPeer;
+ listenerFd->onReadable = &masterAcceptConnection;
+}
+
+/**
+ * Starts a local peer.
+ *
+ * Goes into an I/O loop and does not return.
+ */
+void peerInitialize(BytesListener* bytesListener,
+ DeathListener* deathListener) {
+ // Connect to master peer.
+ int masterSocket = socket(AF_LOCAL, SOCK_STREAM, 0);
+ if (masterSocket == -1) {
+ LOG_ALWAYS_FATAL("socket() error: %s", strerror(errno));
+ }
+ int result = connect(masterSocket, (SocketAddress*) getMasterAddress(),
+ sizeof(UnixAddress));
+ if (result != 0) {
+ LOG_ALWAYS_FATAL("connect() error: %s", strerror(errno));
+ }
+
+ // Create the peer for this process. Fail if we already have one.
+ if (localPeer != NULL) {
+ LOG_ALWAYS_FATAL("Peer is already initialized.");
+ }
+ localPeer = peerCreate();
+ if (localPeer == NULL) {
+ LOG_ALWAYS_FATAL("malloc() failed.");
+ }
+ localPeer->onBytes = bytesListener;
+ localPeer->onDeath = deathListener;
+
+ // Make connection selectable.
+ SelectableFd* masterFd = selectorAdd(localPeer->selector, masterSocket);
+ if (masterFd == NULL) {
+ LOG_ALWAYS_FATAL("malloc() error.");
+ }
+
+ // Create a peer proxy for the master peer.
+ PeerProxy* masterProxy = peerProxyCreate(localPeer, MASTER_CREDENTIALS);
+ if (masterProxy == NULL) {
+ LOG_ALWAYS_FATAL("malloc() error.");
+ }
+ peerProxySetFd(masterProxy, masterFd);
+ masterProxy->master = true;
+ localPeer->masterProxy = masterProxy;
+}
+
+/** Starts the master peer I/O loop. Doesn't return. */
+void peerLoop() {
+ assert(localPeer != NULL);
+
+ // Start selector.
+ selectorLoop(localPeer->selector);
+}
+
diff --git a/libcutils/mspace.c b/libcutils/mspace.c
new file mode 100644
index 0000000..295e47b
--- /dev/null
+++ b/libcutils/mspace.c
@@ -0,0 +1,286 @@
+/* Copyright 2006 The Android Open Source Project */
+
+/* A wrapper file for dlmalloc.c that compiles in the
+ * mspace_*() functions, which provide an interface for
+ * creating multiple heaps.
+ */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <sys/ioctl.h>
+
+#include <cutils/ashmem.h>
+
+/* It's a pain getting the mallinfo stuff to work
+ * with Linux, OSX, and klibc, so just turn it off
+ * for now.
+ * TODO: make mallinfo work
+ */
+#define NO_MALLINFO 1
+
+/* Allow setting the maximum heap footprint.
+ */
+#define USE_MAX_ALLOWED_FOOTPRINT 1
+
+/* Don't try to trim memory.
+ * TODO: support this.
+ */
+#define MORECORE_CANNOT_TRIM 1
+
+/* Use mmap()d anonymous memory to guarantee
+ * that an mspace is contiguous.
+ *
+ * create_mspace() won't work right if this is
+ * defined, so hide the definition of it and
+ * break any users at build time.
+ */
+#define USE_CONTIGUOUS_MSPACES 1
+#if USE_CONTIGUOUS_MSPACES
+/* This combination of settings forces sys_alloc()
+ * to always use MORECORE(). It won't expect the
+ * results to be contiguous, but we'll guarantee
+ * that they are.
+ */
+#define HAVE_MMAP 0
+#define HAVE_MORECORE 1
+#define MORECORE_CONTIGUOUS 0
+/* m is always the appropriate local when MORECORE() is called. */
+#define MORECORE(S) contiguous_mspace_morecore(m, S)
+#define create_mspace HIDDEN_create_mspace_HIDDEN
+#define destroy_mspace HIDDEN_destroy_mspace_HIDDEN
+typedef struct malloc_state *mstate0;
+static void *contiguous_mspace_morecore(mstate0 m, ssize_t nb);
+#endif
+
+#define MSPACES 1
+#define ONLY_MSPACES 1
+#include "../bionic/dlmalloc.c"
+
+#ifndef PAGESIZE
+#define PAGESIZE mparams.page_size
+#endif
+
+#define ALIGN_UP(p, alignment) \
+ (((uintptr_t)(p) + (alignment)-1) & ~((alignment)-1))
+
+/* A direct copy of dlmalloc_usable_size(),
+ * which isn't compiled in when ONLY_MSPACES is set.
+ * The mspace parameter isn't actually necessary,
+ * but we include it to be consistent with the
+ * rest of the mspace_*() functions.
+ */
+size_t mspace_usable_size(mspace _unused, const void* mem) {
+ if (mem != 0) {
+ const mchunkptr p = mem2chunk(mem);
+ if (cinuse(p))
+ return chunksize(p) - overhead_for(p);
+ }
+ return 0;
+}
+
+#if USE_CONTIGUOUS_MSPACES
+#include <sys/mman.h>
+#include <limits.h>
+
+#define CONTIG_STATE_MAGIC 0xf00dd00d
+struct mspace_contig_state {
+ unsigned int magic;
+ char *brk;
+ char *top;
+ mspace m;
+};
+
+static void *contiguous_mspace_morecore(mstate m, ssize_t nb) {
+ struct mspace_contig_state *cs;
+ char *oldbrk;
+ const unsigned int pagesize = PAGESIZE;
+
+ cs = (struct mspace_contig_state *)((uintptr_t)m & ~(pagesize-1));
+ assert(cs->magic == CONTIG_STATE_MAGIC);
+ assert(cs->m == m);
+assert(nb >= 0); //xxx deal with the trim case
+
+ oldbrk = cs->brk;
+ if (nb > 0) {
+ /* Break to the first page boundary that satisfies the request.
+ */
+ char *newbrk = (char *)ALIGN_UP(oldbrk + nb, pagesize);
+ if (newbrk > cs->top)
+ return CMFAIL;
+
+ /* Update the protection on the underlying memory.
+ * Pages we've given to dlmalloc are read/write, and
+ * pages we haven't are not accessable (read or write
+ * will cause a seg fault).
+ */
+ if (mprotect(cs, newbrk - (char *)cs, PROT_READ | PROT_WRITE) < 0)
+ return CMFAIL;
+ if (newbrk != cs->top) {
+ if (mprotect(newbrk, cs->top - newbrk, PROT_NONE) < 0)
+ return CMFAIL;
+ }
+
+ cs->brk = newbrk;
+
+ /* Make sure that dlmalloc will merge this block with the
+ * initial block that was passed to create_mspace_with_base().
+ * We don't care about extern vs. non-extern, so just clear it.
+ */
+ m->seg.sflags &= ~EXTERN_BIT;
+ }
+
+ return oldbrk;
+}
+
+mspace create_contiguous_mspace_with_base(size_t starting_capacity,
+ size_t max_capacity, int locked, void *base) {
+ struct mspace_contig_state *cs;
+ unsigned int pagesize;
+ mstate m;
+
+ init_mparams();
+ pagesize = PAGESIZE;
+ assert(starting_capacity <= max_capacity);
+ assert(((uintptr_t)base & (pagesize-1)) == 0);
+ assert(((uintptr_t)max_capacity & (pagesize-1)) == 0);
+ starting_capacity = (size_t)ALIGN_UP(starting_capacity, pagesize);
+
+ /* Make the first page read/write. dlmalloc needs to use that page.
+ */
+ if (mprotect(base, starting_capacity, PROT_READ | PROT_WRITE) < 0) {
+ goto error;
+ }
+
+ /* Create the mspace, pointing to the memory given.
+ */
+ m = create_mspace_with_base((char *)base + sizeof(*cs), starting_capacity,
+ locked);
+ if (m == (mspace)0) {
+ goto error;
+ }
+ /* Make sure that m is in the same page as base.
+ */
+ assert(((uintptr_t)m & (uintptr_t)~(pagesize-1)) == (uintptr_t)base);
+ /* Use some space for the information that our MORECORE needs.
+ */
+ cs = (struct mspace_contig_state *)base;
+
+ /* Find out exactly how much of the memory the mspace
+ * is using.
+ */
+ cs->brk = m->seg.base + m->seg.size;
+ cs->top = (char *)base + max_capacity;
+
+ assert((char *)base <= cs->brk);
+ assert(cs->brk <= cs->top);
+ /* Prevent access to the memory we haven't handed out yet.
+ */
+ if (cs->brk != cs->top) {
+ /* mprotect() requires page-aligned arguments, but it's possible
+ * for cs->brk not to be page-aligned at this point.
+ */
+ char *prot_brk = (char *)ALIGN_UP(cs->brk, pagesize);
+ if ((mprotect(base, prot_brk - (char *)base, PROT_READ | PROT_WRITE) < 0) ||
+ (mprotect(prot_brk, cs->top - prot_brk, PROT_NONE) < 0)) {
+ goto error;
+ }
+ }
+
+ cs->m = m;
+ cs->magic = CONTIG_STATE_MAGIC;
+
+ return (mspace)m;
+
+error:
+ return (mspace)0;
+}
+
+
+mspace create_contiguous_mspace_with_name(size_t starting_capacity,
+ size_t max_capacity, int locked, char const *name) {
+ int fd, ret;
+ char buf[ASHMEM_NAME_LEN] = "mspace";
+ void *base;
+ unsigned int pagesize;
+ mstate m;
+
+ if (starting_capacity > max_capacity)
+ return (mspace)0;
+
+ init_mparams();
+ pagesize = PAGESIZE;
+
+ /* Create the anonymous memory that will back the mspace.
+ * This reserves all of the virtual address space we could
+ * ever need. Physical pages will be mapped as the memory
+ * is touched.
+ *
+ * Align max_capacity to a whole page.
+ */
+ max_capacity = (size_t)ALIGN_UP(max_capacity, pagesize);
+
+ if (name)
+ snprintf(buf, sizeof(buf), "mspace/%s", name);
+ fd = ashmem_create_region(buf, max_capacity);
+ if (fd < 0)
+ return (mspace)0;
+
+ base = mmap(NULL, max_capacity, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
+ close(fd);
+ if (base == MAP_FAILED)
+ return (mspace)0;
+
+ /* Make sure that base is at the beginning of a page.
+ */
+ assert(((uintptr_t)base & (pagesize-1)) == 0);
+
+ m = create_contiguous_mspace_with_base(starting_capacity, max_capacity,
+ locked, base);
+ if (m == 0) {
+ munmap(base, max_capacity);
+ }
+ return m;
+}
+
+mspace create_contiguous_mspace(size_t starting_capacity,
+ size_t max_capacity, int locked) {
+ return create_contiguous_mspace_with_name(starting_capacity,
+ max_capacity, locked, NULL);
+}
+
+size_t destroy_contiguous_mspace(mspace msp) {
+ mstate ms = (mstate)msp;
+
+ if (ok_magic(ms)) {
+ struct mspace_contig_state *cs;
+ size_t length;
+ const unsigned int pagesize = PAGESIZE;
+
+ cs = (struct mspace_contig_state *)((uintptr_t)ms & ~(pagesize-1));
+ assert(cs->magic == CONTIG_STATE_MAGIC);
+ assert(cs->m == ms);
+
+ length = cs->top - (char *)cs;
+ if (munmap((char *)cs, length) != 0)
+ return length;
+ }
+ else {
+ USAGE_ERROR_ACTION(ms, ms);
+ }
+ return 0;
+}
+
+void *contiguous_mspace_sbrk0(mspace msp) {
+ struct mspace_contig_state *cs;
+ mstate ms;
+ const unsigned int pagesize = PAGESIZE;
+
+ ms = (mstate)msp;
+ cs = (struct mspace_contig_state *)((uintptr_t)ms & ~(pagesize-1));
+ assert(cs->magic == CONTIG_STATE_MAGIC);
+ assert(cs->m == ms);
+ return cs->brk;
+}
+#endif
diff --git a/libcutils/native_handle.c b/libcutils/native_handle.c
new file mode 100644
index 0000000..4089968
--- /dev/null
+++ b/libcutils/native_handle.c
@@ -0,0 +1,60 @@
+/*
+ * 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 LOG_TAG "NativeHandle"
+
+#include <stdint.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <cutils/log.h>
+#include <cutils/native_handle.h>
+
+native_handle_t* native_handle_create(int numFds, int numInts)
+{
+ native_handle_t* h = malloc(
+ sizeof(native_handle_t) + sizeof(int)*(numFds+numInts));
+
+ h->version = sizeof(native_handle_t);
+ h->numFds = numFds;
+ h->numInts = numInts;
+ return h;
+}
+
+int native_handle_delete(native_handle_t* h)
+{
+ if (h) {
+ if (h->version != sizeof(native_handle_t))
+ return -EINVAL;
+ free(h);
+ }
+ return 0;
+}
+
+int native_handle_close(const native_handle_t* h)
+{
+ if (h->version != sizeof(native_handle_t))
+ return -EINVAL;
+
+ const int numFds = h->numFds;
+ int i;
+ for (i=0 ; i<numFds ; i++) {
+ close(h->data[i]);
+ }
+ return 0;
+}
diff --git a/libcutils/open_memstream.c b/libcutils/open_memstream.c
new file mode 100644
index 0000000..5b4388a
--- /dev/null
+++ b/libcutils/open_memstream.c
@@ -0,0 +1,381 @@
+/*
+ * Copyright (C) 2010 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 HAVE_OPEN_MEMSTREAM
+
+/*
+ * Implementation of the POSIX open_memstream() function, which Linux has
+ * but BSD lacks.
+ *
+ * Summary:
+ * - Works like a file-backed FILE* opened with fopen(name, "w"), but the
+ * backing is a chunk of memory rather than a file.
+ * - The buffer expands as you write more data. Seeking past the end
+ * of the file and then writing to it zero-fills the gap.
+ * - The values at "*bufp" and "*sizep" should be considered read-only,
+ * and are only valid immediately after an fflush() or fclose().
+ * - A '\0' is maintained just past the end of the file. This is not included
+ * in "*sizep". (The behavior w.r.t. fseek() is not clearly defined.
+ * The spec says the null byte is written when a write() advances EOF,
+ * but it looks like glibc ensures the null byte is always found at EOF,
+ * even if you just seeked backwards. The example on the opengroup.org
+ * page suggests that this is the expected behavior. The null must be
+ * present after a no-op fflush(), which we can't see, so we have to save
+ * and restore it. Annoying, but allows file truncation.)
+ * - After fclose(), the caller must eventually free(*bufp).
+ *
+ * This is built out of funopen(), which BSD has but Linux lacks. There is
+ * no flush() operator, so we need to keep the user pointers up to date
+ * after each operation.
+ *
+ * I don't think Windows has any of the above, but we don't need to use
+ * them there, so we just supply a stub.
+ */
+#include <cutils/open_memstream.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+
+#if 0
+# define DBUG(x) printf x
+#else
+# define DBUG(x) ((void)0)
+#endif
+
+#ifdef HAVE_FUNOPEN
+
+/*
+ * Definition of a seekable, write-only memory stream.
+ */
+typedef struct {
+ char** bufp; /* pointer to buffer pointer */
+ size_t* sizep; /* pointer to eof */
+
+ size_t allocSize; /* size of buffer */
+ size_t eof; /* furthest point we've written to */
+ size_t offset; /* current write offset */
+ char saved; /* required by NUL handling */
+} MemStream;
+
+#define kInitialSize 1024
+
+/*
+ * Ensure that we have enough storage to write "size" bytes at the
+ * current offset. We also have to take into account the extra '\0'
+ * that we maintain just past EOF.
+ *
+ * Returns 0 on success.
+ */
+static int ensureCapacity(MemStream* stream, int writeSize)
+{
+ DBUG(("+++ ensureCap off=%d size=%d\n", stream->offset, writeSize));
+
+ size_t neededSize = stream->offset + writeSize + 1;
+ if (neededSize <= stream->allocSize)
+ return 0;
+
+ size_t newSize;
+
+ if (stream->allocSize == 0) {
+ newSize = kInitialSize;
+ } else {
+ newSize = stream->allocSize;
+ newSize += newSize / 2; /* expand by 3/2 */
+ }
+
+ if (newSize < neededSize)
+ newSize = neededSize;
+ DBUG(("+++ realloc %p->%p to size=%d\n",
+ stream->bufp, *stream->bufp, newSize));
+ char* newBuf = (char*) realloc(*stream->bufp, newSize);
+ if (newBuf == NULL)
+ return -1;
+
+ *stream->bufp = newBuf;
+ stream->allocSize = newSize;
+ return 0;
+}
+
+/*
+ * Write data to a memstream, expanding the buffer if necessary.
+ *
+ * If we previously seeked beyond EOF, zero-fill the gap.
+ *
+ * Returns the number of bytes written.
+ */
+static int write_memstream(void* cookie, const char* buf, int size)
+{
+ MemStream* stream = (MemStream*) cookie;
+
+ if (ensureCapacity(stream, size) < 0)
+ return -1;
+
+ /* seeked past EOF earlier? */
+ if (stream->eof < stream->offset) {
+ DBUG(("+++ zero-fill gap from %d to %d\n",
+ stream->eof, stream->offset-1));
+ memset(*stream->bufp + stream->eof, '\0',
+ stream->offset - stream->eof);
+ }
+
+ /* copy data, advance write pointer */
+ memcpy(*stream->bufp + stream->offset, buf, size);
+ stream->offset += size;
+
+ if (stream->offset > stream->eof) {
+ /* EOF has advanced, update it and append null byte */
+ DBUG(("+++ EOF advanced to %d, appending nul\n", stream->offset));
+ assert(stream->offset < stream->allocSize);
+ stream->eof = stream->offset;
+ } else {
+ /* within previously-written area; save char we're about to stomp */
+ DBUG(("+++ within written area, saving '%c' at %d\n",
+ *(*stream->bufp + stream->offset), stream->offset));
+ stream->saved = *(*stream->bufp + stream->offset);
+ }
+ *(*stream->bufp + stream->offset) = '\0';
+ *stream->sizep = stream->offset;
+
+ return size;
+}
+
+/*
+ * Seek within a memstream.
+ *
+ * Returns the new offset, or -1 on failure.
+ */
+static fpos_t seek_memstream(void* cookie, fpos_t offset, int whence)
+{
+ MemStream* stream = (MemStream*) cookie;
+ off_t newPosn = (off_t) offset;
+
+ if (whence == SEEK_CUR) {
+ newPosn += stream->offset;
+ } else if (whence == SEEK_END) {
+ newPosn += stream->eof;
+ }
+
+ if (newPosn < 0 || ((fpos_t)((size_t) newPosn)) != newPosn) {
+ /* bad offset - negative or huge */
+ DBUG(("+++ bogus seek offset %ld\n", (long) newPosn));
+ errno = EINVAL;
+ return (fpos_t) -1;
+ }
+
+ if (stream->offset < stream->eof) {
+ /*
+ * We were pointing to an area we'd already written to, which means
+ * we stomped on a character and must now restore it.
+ */
+ DBUG(("+++ restoring char '%c' at %d\n",
+ stream->saved, stream->offset));
+ *(*stream->bufp + stream->offset) = stream->saved;
+ }
+
+ stream->offset = (size_t) newPosn;
+
+ if (stream->offset < stream->eof) {
+ /*
+ * We're seeked backward into the stream. Preserve the character
+ * at EOF and stomp it with a NUL.
+ */
+ stream->saved = *(*stream->bufp + stream->offset);
+ *(*stream->bufp + stream->offset) = '\0';
+ *stream->sizep = stream->offset;
+ } else {
+ /*
+ * We're positioned at, or possibly beyond, the EOF. We want to
+ * publish the current EOF, not the current position.
+ */
+ *stream->sizep = stream->eof;
+ }
+
+ return newPosn;
+}
+
+/*
+ * Close the memstream. We free everything but the data buffer.
+ */
+static int close_memstream(void* cookie)
+{
+ free(cookie);
+ return 0;
+}
+
+/*
+ * Prepare a memstream.
+ */
+FILE* open_memstream(char** bufp, size_t* sizep)
+{
+ FILE* fp;
+ MemStream* stream;
+
+ if (bufp == NULL || sizep == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ stream = (MemStream*) calloc(1, sizeof(MemStream));
+ if (stream == NULL)
+ return NULL;
+
+ fp = funopen(stream,
+ NULL, write_memstream, seek_memstream, close_memstream);
+ if (fp == NULL) {
+ free(stream);
+ return NULL;
+ }
+
+ *sizep = 0;
+ *bufp = NULL;
+ stream->bufp = bufp;
+ stream->sizep = sizep;
+
+ return fp;
+}
+
+#else /*not HAVE_FUNOPEN*/
+FILE* open_memstream(char** bufp, size_t* sizep)
+{
+ abort();
+}
+#endif /*HAVE_FUNOPEN*/
+
+
+
+#if 0
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/*
+ * Simple regression test.
+ *
+ * To test on desktop Linux with valgrind, it's possible to make a simple
+ * change to open_memstream() to use fopencookie instead:
+ *
+ * cookie_io_functions_t iofuncs =
+ * { NULL, write_memstream, seek_memstream, close_memstream };
+ * fp = fopencookie(stream, "w", iofuncs);
+ *
+ * (Some tweaks to seek_memstream are also required, as that takes a
+ * pointer to an offset rather than an offset, and returns 0 or -1.)
+ */
+int testMemStream(void)
+{
+ FILE *stream;
+ char *buf;
+ size_t len;
+ off_t eob;
+
+ printf("Test1\n");
+
+ /* std example */
+ stream = open_memstream(&buf, &len);
+ fprintf(stream, "hello my world");
+ fflush(stream);
+ printf("buf=%s, len=%zu\n", buf, len);
+ eob = ftello(stream);
+ fseeko(stream, 0, SEEK_SET);
+ fprintf(stream, "good-bye");
+ fseeko(stream, eob, SEEK_SET);
+ fclose(stream);
+ printf("buf=%s, len=%zu\n", buf, len);
+ free(buf);
+
+ printf("Test2\n");
+
+ /* std example without final seek-to-end */
+ stream = open_memstream(&buf, &len);
+ fprintf(stream, "hello my world");
+ fflush(stream);
+ printf("buf=%s, len=%zu\n", buf, len);
+ eob = ftello(stream);
+ fseeko(stream, 0, SEEK_SET);
+ fprintf(stream, "good-bye");
+ //fseeko(stream, eob, SEEK_SET);
+ fclose(stream);
+ printf("buf=%s, len=%zu\n", buf, len);
+ free(buf);
+
+ printf("Test3\n");
+
+ /* fancy example; should expand buffer with writes */
+ static const int kCmpLen = 1024 + 128;
+ char* cmp = malloc(kCmpLen);
+ memset(cmp, 0, 1024);
+ memset(cmp+1024, 0xff, kCmpLen-1024);
+ sprintf(cmp, "This-is-a-tes1234");
+ sprintf(cmp + 1022, "abcdef");
+
+ stream = open_memstream (&buf, &len);
+ setvbuf(stream, NULL, _IONBF, 0); /* note: crashes in glibc with this */
+ fprintf(stream, "This-is-a-test");
+ fseek(stream, -1, SEEK_CUR); /* broken in glibc; can use {13,SEEK_SET} */
+ fprintf(stream, "1234");
+ fseek(stream, 1022, SEEK_SET);
+ fputc('a', stream);
+ fputc('b', stream);
+ fputc('c', stream);
+ fputc('d', stream);
+ fputc('e', stream);
+ fputc('f', stream);
+ fflush(stream);
+
+ if (memcmp(buf, cmp, len+1) != 0) {
+ printf("mismatch\n");
+ } else {
+ printf("match\n");
+ }
+
+ printf("Test4\n");
+ stream = open_memstream (&buf, &len);
+ fseek(stream, 5000, SEEK_SET);
+ fseek(stream, 4096, SEEK_SET);
+ fseek(stream, -1, SEEK_SET); /* should have no effect */
+ fputc('x', stream);
+ if (ftell(stream) == 4097)
+ printf("good\n");
+ else
+ printf("BAD: offset is %ld\n", ftell(stream));
+
+ printf("DONE\n");
+
+ return 0;
+}
+
+/* expected output:
+Test1
+buf=hello my world, len=14
+buf=good-bye world, len=14
+Test2
+buf=hello my world, len=14
+buf=good-bye, len=8
+Test3
+match
+Test4
+good
+DONE
+*/
+
+#endif
+
+#endif /*!HAVE_OPEN_MEMSTREAM*/
diff --git a/libcutils/private.h b/libcutils/private.h
new file mode 100644
index 0000000..2837b70
--- /dev/null
+++ b/libcutils/private.h
@@ -0,0 +1,368 @@
+#ifndef PRIVATE_H
+
+#define PRIVATE_H
+
+/*
+** This file is in the public domain, so clarified as of
+** 1996-06-05 by Arthur David Olson.
+*/
+
+/*
+** This header is for use ONLY with the time conversion code.
+** There is no guarantee that it will remain unchanged,
+** or that it will remain at all.
+** Do NOT copy it to any system include directory.
+** Thank you!
+*/
+
+/*
+** ID
+*/
+
+#ifndef lint
+#ifndef NOID
+static char privatehid[] = "@(#)private.h 8.2";
+#endif /* !defined NOID */
+#endif /* !defined lint */
+
+#define GRANDPARENTED "Local time zone must be set--see zic manual page"
+
+/*
+** Defaults for preprocessor symbols.
+** You can override these in your C compiler options, e.g. `-DHAVE_ADJTIME=0'.
+*/
+
+#ifndef HAVE_ADJTIME
+#define HAVE_ADJTIME 1
+#endif /* !defined HAVE_ADJTIME */
+
+#ifndef HAVE_GETTEXT
+#define HAVE_GETTEXT 0
+#endif /* !defined HAVE_GETTEXT */
+
+#ifndef HAVE_INCOMPATIBLE_CTIME_R
+#define HAVE_INCOMPATIBLE_CTIME_R 0
+#endif /* !defined INCOMPATIBLE_CTIME_R */
+
+#ifndef HAVE_SETTIMEOFDAY
+#define HAVE_SETTIMEOFDAY 3
+#endif /* !defined HAVE_SETTIMEOFDAY */
+
+#ifndef HAVE_STRERROR
+#define HAVE_STRERROR 1
+#endif /* !defined HAVE_STRERROR */
+
+#ifndef HAVE_SYMLINK
+#define HAVE_SYMLINK 1
+#endif /* !defined HAVE_SYMLINK */
+
+#ifndef HAVE_SYS_STAT_H
+#define HAVE_SYS_STAT_H 1
+#endif /* !defined HAVE_SYS_STAT_H */
+
+#ifndef HAVE_SYS_WAIT_H
+#define HAVE_SYS_WAIT_H 1
+#endif /* !defined HAVE_SYS_WAIT_H */
+
+#ifndef HAVE_UNISTD_H
+#define HAVE_UNISTD_H 1
+#endif /* !defined HAVE_UNISTD_H */
+
+#ifndef HAVE_UTMPX_H
+#define HAVE_UTMPX_H 0
+#endif /* !defined HAVE_UTMPX_H */
+
+#ifndef LOCALE_HOME
+#define LOCALE_HOME "/usr/lib/locale"
+#endif /* !defined LOCALE_HOME */
+
+#if HAVE_INCOMPATIBLE_CTIME_R
+#define asctime_r _incompatible_asctime_r
+#define ctime_r _incompatible_ctime_r
+#endif /* HAVE_INCOMPATIBLE_CTIME_R */
+
+/*
+** Nested includes
+*/
+
+#include "sys/types.h" /* for time_t */
+#include "stdio.h"
+#include "errno.h"
+#include "string.h"
+#include "limits.h" /* for CHAR_BIT et al. */
+#include "time.h"
+#include "stdlib.h"
+
+#if HAVE_GETTEXT
+#include "libintl.h"
+#endif /* HAVE_GETTEXT */
+
+#if HAVE_SYS_WAIT_H
+#include <sys/wait.h> /* for WIFEXITED and WEXITSTATUS */
+#endif /* HAVE_SYS_WAIT_H */
+
+#ifndef WIFEXITED
+#define WIFEXITED(status) (((status) & 0xff) == 0)
+#endif /* !defined WIFEXITED */
+#ifndef WEXITSTATUS
+#define WEXITSTATUS(status) (((status) >> 8) & 0xff)
+#endif /* !defined WEXITSTATUS */
+
+#if HAVE_UNISTD_H
+#include "unistd.h" /* for F_OK and R_OK */
+#endif /* HAVE_UNISTD_H */
+
+#if !HAVE_UNISTD_H
+#ifndef F_OK
+#define F_OK 0
+#endif /* !defined F_OK */
+#ifndef R_OK
+#define R_OK 4
+#endif /* !defined R_OK */
+#endif /* !HAVE_UNISTD_H */
+
+/* Unlike <ctype.h>'s isdigit, this also works if c < 0 | c > UCHAR_MAX. */
+#define is_digit(c) ((unsigned)(c) - '0' <= 9)
+
+/*
+** Define HAVE_STDINT_H's default value here, rather than at the
+** start, since __GLIBC__'s value depends on previously-included
+** files.
+** (glibc 2.1 and later have stdint.h, even with pre-C99 compilers.)
+*/
+#ifndef HAVE_STDINT_H
+#define HAVE_STDINT_H \
+ (199901 <= __STDC_VERSION__ || \
+ 2 < (__GLIBC__ + (0 < __GLIBC_MINOR__)))
+#endif /* !defined HAVE_STDINT_H */
+
+#if HAVE_STDINT_H
+#include "stdint.h"
+#endif /* !HAVE_STDINT_H */
+
+#ifndef INT_FAST64_MAX
+/* Pre-C99 GCC compilers define __LONG_LONG_MAX__ instead of LLONG_MAX. */
+#if defined LLONG_MAX || defined __LONG_LONG_MAX__
+typedef long long int_fast64_t;
+#else /* ! (defined LLONG_MAX || defined __LONG_LONG_MAX__) */
+#if (LONG_MAX >> 31) < 0xffffffff
+Please use a compiler that supports a 64-bit integer type (or wider);
+you may need to compile with "-DHAVE_STDINT_H".
+#endif /* (LONG_MAX >> 31) < 0xffffffff */
+typedef long int_fast64_t;
+#endif /* ! (defined LLONG_MAX || defined __LONG_LONG_MAX__) */
+#endif /* !defined INT_FAST64_MAX */
+
+#ifndef INT32_MAX
+#define INT32_MAX 0x7fffffff
+#endif /* !defined INT32_MAX */
+#ifndef INT32_MIN
+#define INT32_MIN (-1 - INT32_MAX)
+#endif /* !defined INT32_MIN */
+
+/*
+** Workarounds for compilers/systems.
+*/
+
+/*
+** If your compiler lacks prototypes, "#define P(x) ()".
+*/
+
+#ifndef P
+#define P(x) x
+#endif /* !defined P */
+
+/*
+** SunOS 4.1.1 headers lack EXIT_SUCCESS.
+*/
+
+#ifndef EXIT_SUCCESS
+#define EXIT_SUCCESS 0
+#endif /* !defined EXIT_SUCCESS */
+
+/*
+** SunOS 4.1.1 headers lack EXIT_FAILURE.
+*/
+
+#ifndef EXIT_FAILURE
+#define EXIT_FAILURE 1
+#endif /* !defined EXIT_FAILURE */
+
+/*
+** SunOS 4.1.1 headers lack FILENAME_MAX.
+*/
+
+#ifndef FILENAME_MAX
+
+#ifndef MAXPATHLEN
+#ifdef unix
+#include "sys/param.h"
+#endif /* defined unix */
+#endif /* !defined MAXPATHLEN */
+
+#ifdef MAXPATHLEN
+#define FILENAME_MAX MAXPATHLEN
+#endif /* defined MAXPATHLEN */
+#ifndef MAXPATHLEN
+#define FILENAME_MAX 1024 /* Pure guesswork */
+#endif /* !defined MAXPATHLEN */
+
+#endif /* !defined FILENAME_MAX */
+
+/*
+** SunOS 4.1.1 libraries lack remove.
+*/
+
+#ifndef remove
+extern int unlink P((const char * filename));
+#define remove unlink
+#endif /* !defined remove */
+
+/*
+** Some ancient errno.h implementations don't declare errno.
+** But some newer errno.h implementations define it as a macro.
+** Fix the former without affecting the latter.
+*/
+
+#ifndef errno
+extern int errno;
+#endif /* !defined errno */
+
+/*
+** Some time.h implementations don't declare asctime_r.
+** Others might define it as a macro.
+** Fix the former without affecting the latter.
+*/
+
+#ifndef asctime_r
+extern char * asctime_r();
+#endif
+
+/*
+** Private function declarations.
+*/
+
+char * icalloc P((int nelem, int elsize));
+char * icatalloc P((char * old, const char * new));
+char * icpyalloc P((const char * string));
+char * imalloc P((int n));
+void * irealloc P((void * pointer, int size));
+void icfree P((char * pointer));
+void ifree P((char * pointer));
+const char * scheck P((const char * string, const char * format));
+
+/*
+** Finally, some convenience items.
+*/
+
+#ifndef TRUE
+#define TRUE 1
+#endif /* !defined TRUE */
+
+#ifndef FALSE
+#define FALSE 0
+#endif /* !defined FALSE */
+
+#ifndef TYPE_BIT
+#define TYPE_BIT(type) (sizeof (type) * CHAR_BIT)
+#endif /* !defined TYPE_BIT */
+
+#ifndef TYPE_SIGNED
+#define TYPE_SIGNED(type) (((type) -1) < 0)
+#endif /* !defined TYPE_SIGNED */
+
+/*
+** Since the definition of TYPE_INTEGRAL contains floating point numbers,
+** it cannot be used in preprocessor directives.
+*/
+
+#ifndef TYPE_INTEGRAL
+#define TYPE_INTEGRAL(type) (((type) 0.5) != 0.5)
+#endif /* !defined TYPE_INTEGRAL */
+
+#ifndef INT_STRLEN_MAXIMUM
+/*
+** 302 / 1000 is log10(2.0) rounded up.
+** Subtract one for the sign bit if the type is signed;
+** add one for integer division truncation;
+** add one more for a minus sign if the type is signed.
+*/
+#define INT_STRLEN_MAXIMUM(type) \
+ ((TYPE_BIT(type) - TYPE_SIGNED(type)) * 302 / 1000 + \
+ 1 + TYPE_SIGNED(type))
+#endif /* !defined INT_STRLEN_MAXIMUM */
+
+/*
+** INITIALIZE(x)
+*/
+
+#ifndef GNUC_or_lint
+#ifdef lint
+#define GNUC_or_lint
+#endif /* defined lint */
+#ifndef lint
+#ifdef __GNUC__
+#define GNUC_or_lint
+#endif /* defined __GNUC__ */
+#endif /* !defined lint */
+#endif /* !defined GNUC_or_lint */
+
+#ifndef INITIALIZE
+#ifdef GNUC_or_lint
+#define INITIALIZE(x) ((x) = 0)
+#endif /* defined GNUC_or_lint */
+#ifndef GNUC_or_lint
+#define INITIALIZE(x)
+#endif /* !defined GNUC_or_lint */
+#endif /* !defined INITIALIZE */
+
+/*
+** For the benefit of GNU folk...
+** `_(MSGID)' uses the current locale's message library string for MSGID.
+** The default is to use gettext if available, and use MSGID otherwise.
+*/
+
+#ifndef _
+#if HAVE_GETTEXT
+#define _(msgid) gettext(msgid)
+#else /* !HAVE_GETTEXT */
+#define _(msgid) msgid
+#endif /* !HAVE_GETTEXT */
+#endif /* !defined _ */
+
+#ifndef TZ_DOMAIN
+#define TZ_DOMAIN "tz"
+#endif /* !defined TZ_DOMAIN */
+
+#if HAVE_INCOMPATIBLE_CTIME_R
+#undef asctime_r
+#undef ctime_r
+char *asctime_r P((struct tm const *, char *));
+char *ctime_r P((time_t const *, char *));
+#endif /* HAVE_INCOMPATIBLE_CTIME_R */
+
+#ifndef YEARSPERREPEAT
+#define YEARSPERREPEAT 400 /* years before a Gregorian repeat */
+#endif /* !defined YEARSPERREPEAT */
+
+/*
+** The Gregorian year averages 365.2425 days, which is 31556952 seconds.
+*/
+
+#ifndef AVGSECSPERYEAR
+#define AVGSECSPERYEAR 31556952L
+#endif /* !defined AVGSECSPERYEAR */
+
+#ifndef SECSPERREPEAT
+#define SECSPERREPEAT ((int_fast64_t) YEARSPERREPEAT * (int_fast64_t) AVGSECSPERYEAR)
+#endif /* !defined SECSPERREPEAT */
+
+#ifndef SECSPERREPEAT_BITS
+#define SECSPERREPEAT_BITS 34 /* ceil(log2(SECSPERREPEAT)) */
+#endif /* !defined SECSPERREPEAT_BITS */
+
+/*
+** UNIX was a registered trademark of The Open Group in 2003.
+*/
+
+#endif /* !defined PRIVATE_H */
diff --git a/libcutils/process_name.c b/libcutils/process_name.c
new file mode 100644
index 0000000..b235429
--- /dev/null
+++ b/libcutils/process_name.c
@@ -0,0 +1,88 @@
+/*
+ * 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 <string.h>
+#include <cutils/process_name.h>
+#include <cutils/properties.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#if defined(HAVE_PRCTL)
+#include <sys/prctl.h>
+#endif
+
+#define PROCESS_NAME_DEVICE "/sys/qemu_trace/process_name"
+
+static const char* process_name = "unknown";
+static int running_in_emulator = -1;
+
+void set_process_name(const char* new_name) {
+ char propBuf[PROPERTY_VALUE_MAX];
+
+ if (new_name == NULL) {
+ return;
+ }
+
+ // We never free the old name. Someone else could be using it.
+ int len = strlen(new_name);
+ char* copy = (char*) malloc(len + 1);
+ strcpy(copy, new_name);
+ process_name = (const char*) copy;
+
+#if defined(HAVE_PRCTL)
+ if (len < 16) {
+ prctl(PR_SET_NAME, (unsigned long) new_name, 0, 0, 0);
+ } else {
+ prctl(PR_SET_NAME, (unsigned long) new_name + len - 15, 0, 0, 0);
+ }
+#endif
+
+ // If we know we are not running in the emulator, then return.
+ if (running_in_emulator == 0) {
+ return;
+ }
+
+ // If the "running_in_emulator" variable has not been initialized,
+ // then do it now.
+ if (running_in_emulator == -1) {
+ property_get("ro.kernel.qemu", propBuf, "");
+ if (propBuf[0] == '1') {
+ running_in_emulator = 1;
+ } else {
+ running_in_emulator = 0;
+ return;
+ }
+ }
+
+ // If the emulator was started with the "-trace file" command line option
+ // then we want to record the process name in the trace even if we are
+ // not currently tracing instructions (so that we will know the process
+ // name when we do start tracing instructions). We do not need to execute
+ // this code if we are just running in the emulator without the "-trace"
+ // command line option, but we don't know that here and this function
+ // isn't called frequently enough to bother optimizing that case.
+ int fd = open(PROCESS_NAME_DEVICE, O_RDWR);
+ if (fd < 0)
+ return;
+ write(fd, process_name, strlen(process_name) + 1);
+ close(fd);
+}
+
+const char* get_process_name(void) {
+ return process_name;
+}
diff --git a/libcutils/properties.c b/libcutils/properties.c
new file mode 100644
index 0000000..f90147d
--- /dev/null
+++ b/libcutils/properties.c
@@ -0,0 +1,392 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "properties"
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <cutils/sockets.h>
+#include <errno.h>
+#include <assert.h>
+
+#include <cutils/properties.h>
+#include "loghack.h"
+
+#ifdef HAVE_LIBC_SYSTEM_PROPERTIES
+
+#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
+#include <sys/_system_properties.h>
+
+static int send_prop_msg(prop_msg *msg, int sync)
+{
+ int s;
+ int r;
+ int dummy;
+
+ s = socket_local_client(PROP_SERVICE_NAME,
+ ANDROID_SOCKET_NAMESPACE_RESERVED,
+ SOCK_STREAM);
+ if(s < 0) return -1;
+
+ while((r = send(s, msg, sizeof(prop_msg), 0)) < 0) {
+ if((errno == EINTR) || (errno == EAGAIN)) continue;
+ break;
+ }
+
+ if(r == sizeof(prop_msg)) {
+ r = 0;
+ } else {
+ r = -1;
+ }
+
+ if(sync == 1)
+ {
+ // wait for server side to close
+ if(recv(s, &dummy, sizeof(dummy), 0) == 0) {
+ } else {
+ // LOGW("unexpected return code %d:%s", errno, strerror(errno));
+ }
+ }
+
+ close(s);
+ return r;
+}
+
+static int property_set_internal(const char *key, const char *value, int sync)
+{
+ prop_msg msg;
+ unsigned resp;
+
+ if(key == 0) return -1;
+ if(value == 0) value = "";
+
+ if(strlen(key) >= PROP_NAME_MAX) return -1;
+ if(strlen(value) >= PROP_VALUE_MAX) return -1;
+
+ if(sync) {
+ msg.cmd = PROP_MSG_SETPROP_SYNC;
+ } else {
+ msg.cmd = PROP_MSG_SETPROP;
+ }
+ strcpy((char*) msg.name, key);
+ strcpy((char*) msg.value, value);
+
+ return send_prop_msg(&msg, sync);
+}
+
+int property_set(const char *key, const char *value)
+{
+ return property_set_internal(key, value, 0);
+}
+
+int property_set_sync(const char *key, const char *value)
+{
+ return property_set_internal(key, value, 1);
+}
+
+int property_get(const char *key, char *value, const char *default_value)
+{
+ int len;
+
+ len = __system_property_get(key, value);
+ if(len > 0) {
+ return len;
+ }
+
+ if(default_value) {
+ len = strlen(default_value);
+ memcpy(value, default_value, len + 1);
+ }
+ return len;
+}
+
+int property_list(void (*propfn)(const char *key, const char *value, void *cookie),
+ void *cookie)
+{
+ char name[PROP_NAME_MAX];
+ char value[PROP_VALUE_MAX];
+ const prop_info *pi;
+ unsigned n;
+
+ for(n = 0; (pi = __system_property_find_nth(n)); n++) {
+ __system_property_read(pi, name, value);
+ propfn(name, value, cookie);
+ }
+ return 0;
+}
+
+#elif defined(HAVE_SYSTEM_PROPERTY_SERVER)
+
+/*
+ * The Linux simulator provides a "system property server" that uses IPC
+ * to set/get/list properties. The file descriptor is shared by all
+ * threads in the process, so we use a mutex to ensure that requests
+ * from multiple threads don't get interleaved.
+ */
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <pthread.h>
+
+static pthread_once_t gInitOnce = PTHREAD_ONCE_INIT;
+static pthread_mutex_t gPropertyFdLock = PTHREAD_MUTEX_INITIALIZER;
+static int gPropFd = -1;
+
+/*
+ * Connect to the properties server.
+ *
+ * Returns the socket descriptor on success.
+ */
+static int connectToServer(const char* fileName)
+{
+ int sock = -1;
+ int cc;
+
+ struct sockaddr_un addr;
+
+ sock = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (sock < 0) {
+ LOGW("UNIX domain socket create failed (errno=%d)\n", errno);
+ return -1;
+ }
+
+ /* connect to socket; fails if file doesn't exist */
+ strcpy(addr.sun_path, fileName); // max 108 bytes
+ addr.sun_family = AF_UNIX;
+ cc = connect(sock, (struct sockaddr*) &addr, SUN_LEN(&addr));
+ if (cc < 0) {
+ // ENOENT means socket file doesn't exist
+ // ECONNREFUSED means socket exists but nobody is listening
+ //LOGW("AF_UNIX connect failed for '%s': %s\n",
+ // fileName, strerror(errno));
+ close(sock);
+ return -1;
+ }
+
+ return sock;
+}
+
+/*
+ * Perform one-time initialization.
+ */
+static void init(void)
+{
+ assert(gPropFd == -1);
+
+ gPropFd = connectToServer(SYSTEM_PROPERTY_PIPE_NAME);
+ if (gPropFd < 0) {
+ //LOGW("not connected to system property server\n");
+ } else {
+ //LOGV("Connected to system property server\n");
+ }
+}
+
+int property_get(const char *key, char *value, const char *default_value)
+{
+ char sendBuf[1+PROPERTY_KEY_MAX];
+ char recvBuf[1+PROPERTY_VALUE_MAX];
+ int len = -1;
+
+ //LOGV("PROPERTY GET [%s]\n", key);
+
+ pthread_once(&gInitOnce, init);
+ if (gPropFd < 0) {
+ /* this mimics the behavior of the device implementation */
+ if (default_value != NULL) {
+ strcpy(value, default_value);
+ len = strlen(value);
+ }
+ return len;
+ }
+
+ if (strlen(key) >= PROPERTY_KEY_MAX) return -1;
+
+ memset(sendBuf, 0xdd, sizeof(sendBuf)); // placate valgrind
+
+ sendBuf[0] = (char) kSystemPropertyGet;
+ strcpy(sendBuf+1, key);
+
+ pthread_mutex_lock(&gPropertyFdLock);
+ if (write(gPropFd, sendBuf, sizeof(sendBuf)) != sizeof(sendBuf)) {
+ pthread_mutex_unlock(&gPropertyFdLock);
+ return -1;
+ }
+ if (read(gPropFd, recvBuf, sizeof(recvBuf)) != sizeof(recvBuf)) {
+ pthread_mutex_unlock(&gPropertyFdLock);
+ return -1;
+ }
+ pthread_mutex_unlock(&gPropertyFdLock);
+
+ /* first byte is 0 if value not defined, 1 if found */
+ if (recvBuf[0] == 0) {
+ if (default_value != NULL) {
+ strcpy(value, default_value);
+ len = strlen(value);
+ } else {
+ /*
+ * If the value isn't defined, hand back an empty string and
+ * a zero length, rather than a failure. This seems wrong,
+ * since you can't tell the difference between "undefined" and
+ * "defined but empty", but it's what the device does.
+ */
+ value[0] = '\0';
+ len = 0;
+ }
+ } else if (recvBuf[0] == 1) {
+ strcpy(value, recvBuf+1);
+ len = strlen(value);
+ } else {
+ LOGE("Got strange response to property_get request (%d)\n",
+ recvBuf[0]);
+ assert(0);
+ return -1;
+ }
+ //LOGV("PROP [found=%d def='%s'] (%d) [%s]: [%s]\n",
+ // recvBuf[0], default_value, len, key, value);
+
+ return len;
+}
+
+
+int property_set(const char *key, const char *value)
+{
+ char sendBuf[1+PROPERTY_KEY_MAX+PROPERTY_VALUE_MAX];
+ char recvBuf[1];
+ int result = -1;
+
+ //LOGV("PROPERTY SET [%s]: [%s]\n", key, value);
+
+ pthread_once(&gInitOnce, init);
+ if (gPropFd < 0)
+ return -1;
+
+ if (strlen(key) >= PROPERTY_KEY_MAX) return -1;
+ if (strlen(value) >= PROPERTY_VALUE_MAX) return -1;
+
+ memset(sendBuf, 0xdd, sizeof(sendBuf)); // placate valgrind
+
+ sendBuf[0] = (char) kSystemPropertySet;
+ strcpy(sendBuf+1, key);
+ strcpy(sendBuf+1+PROPERTY_KEY_MAX, value);
+
+ pthread_mutex_lock(&gPropertyFdLock);
+ if (write(gPropFd, sendBuf, sizeof(sendBuf)) != sizeof(sendBuf)) {
+ pthread_mutex_unlock(&gPropertyFdLock);
+ return -1;
+ }
+ if (read(gPropFd, recvBuf, sizeof(recvBuf)) != sizeof(recvBuf)) {
+ pthread_mutex_unlock(&gPropertyFdLock);
+ return -1;
+ }
+ pthread_mutex_unlock(&gPropertyFdLock);
+
+ if (recvBuf[0] != 1)
+ return -1;
+ return 0;
+}
+
+int property_list(void (*propfn)(const char *key, const char *value, void *cookie),
+ void *cookie)
+{
+ //LOGV("PROPERTY LIST\n");
+ pthread_once(&gInitOnce, init);
+ if (gPropFd < 0)
+ return -1;
+
+ return 0;
+}
+
+#else
+
+/* SUPER-cheesy place-holder implementation for Win32 */
+
+#include <cutils/threads.h>
+
+static mutex_t env_lock = MUTEX_INITIALIZER;
+
+int property_get(const char *key, char *value, const char *default_value)
+{
+ char ename[PROPERTY_KEY_MAX + 6];
+ char *p;
+ int len;
+
+ len = strlen(key);
+ if(len >= PROPERTY_KEY_MAX) return -1;
+ memcpy(ename, "PROP_", 5);
+ memcpy(ename + 5, key, len + 1);
+
+ mutex_lock(&env_lock);
+
+ p = getenv(ename);
+ if(p == 0) p = "";
+ len = strlen(p);
+ if(len >= PROPERTY_VALUE_MAX) {
+ len = PROPERTY_VALUE_MAX - 1;
+ }
+
+ if((len == 0) && default_value) {
+ len = strlen(default_value);
+ memcpy(value, default_value, len + 1);
+ } else {
+ memcpy(value, p, len);
+ value[len] = 0;
+ }
+
+ mutex_unlock(&env_lock);
+
+ return len;
+}
+
+
+int property_set(const char *key, const char *value)
+{
+ char ename[PROPERTY_KEY_MAX + 6];
+ char *p;
+ int len;
+ int r;
+
+ if(strlen(value) >= PROPERTY_VALUE_MAX) return -1;
+
+ len = strlen(key);
+ if(len >= PROPERTY_KEY_MAX) return -1;
+ memcpy(ename, "PROP_", 5);
+ memcpy(ename + 5, key, len + 1);
+
+ mutex_lock(&env_lock);
+#ifdef HAVE_MS_C_RUNTIME
+ {
+ char temp[256];
+ snprintf( temp, sizeof(temp), "%s=%s", ename, value);
+ putenv(temp);
+ r = 0;
+ }
+#else
+ r = setenv(ename, value, 1);
+#endif
+ mutex_unlock(&env_lock);
+
+ return r;
+}
+
+int property_list(void (*propfn)(const char *key, const char *value, void *cookie),
+ void *cookie)
+{
+ return 0;
+}
+
+#endif
diff --git a/libcutils/record_stream.c b/libcutils/record_stream.c
new file mode 100644
index 0000000..274423b
--- /dev/null
+++ b/libcutils/record_stream.c
@@ -0,0 +1,186 @@
+/* libs/cutils/record_stream.c
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <assert.h>
+#include <errno.h>
+#include <cutils/record_stream.h>
+#include <string.h>
+#include <stdint.h>
+#ifdef HAVE_WINSOCK
+#include <winsock2.h> /* for ntohl */
+#else
+#include <netinet/in.h>
+#endif
+
+#define HEADER_SIZE 4
+
+struct RecordStream {
+ int fd;
+ size_t maxRecordLen;
+
+ unsigned char *buffer;
+
+ unsigned char *unconsumed;
+ unsigned char *read_end;
+ unsigned char *buffer_end;
+};
+
+
+extern RecordStream *record_stream_new(int fd, size_t maxRecordLen)
+{
+ RecordStream *ret;
+
+ assert (maxRecordLen <= 0xffff);
+
+ ret = (RecordStream *)calloc(1, sizeof(RecordStream));
+
+ ret->fd = fd;
+ ret->maxRecordLen = maxRecordLen;
+ ret->buffer = (unsigned char *)malloc (maxRecordLen + HEADER_SIZE);
+
+ ret->unconsumed = ret->buffer;
+ ret->read_end = ret->buffer;
+ ret->buffer_end = ret->buffer + maxRecordLen + HEADER_SIZE;
+
+ return ret;
+}
+
+
+extern void record_stream_free(RecordStream *rs)
+{
+ free(rs->buffer);
+ free(rs);
+}
+
+
+/* returns NULL; if there isn't a full record in the buffer */
+static unsigned char * getEndOfRecord (unsigned char *p_begin,
+ unsigned char *p_end)
+{
+ size_t len;
+ unsigned char * p_ret;
+
+ if (p_end < p_begin + HEADER_SIZE) {
+ return NULL;
+ }
+
+ //First four bytes are length
+ len = ntohl(*((uint32_t *)p_begin));
+
+ p_ret = p_begin + HEADER_SIZE + len;
+
+ if (p_end < p_ret) {
+ return NULL;
+ }
+
+ return p_ret;
+}
+
+static void *getNextRecord (RecordStream *p_rs, size_t *p_outRecordLen)
+{
+ unsigned char *record_start, *record_end;
+
+ record_end = getEndOfRecord (p_rs->unconsumed, p_rs->read_end);
+
+ if (record_end != NULL) {
+ /* one full line in the buffer */
+ record_start = p_rs->unconsumed + HEADER_SIZE;
+ p_rs->unconsumed = record_end;
+
+ *p_outRecordLen = record_end - record_start;
+
+ return record_start;
+ }
+
+ return NULL;
+}
+
+/**
+ * Reads the next record from stream fd
+ * Records are prefixed by a 16-bit big endian length value
+ * Records may not be larger than maxRecordLen
+ *
+ * Doesn't guard against EINTR
+ *
+ * p_outRecord and p_outRecordLen may not be NULL
+ *
+ * Return 0 on success, -1 on fail
+ * Returns 0 with *p_outRecord set to NULL on end of stream
+ * Returns -1 / errno = EAGAIN if it needs to read again
+ */
+int record_stream_get_next (RecordStream *p_rs, void ** p_outRecord,
+ size_t *p_outRecordLen)
+{
+ void *ret;
+
+ ssize_t countRead;
+
+ /* is there one record already in the buffer? */
+ ret = getNextRecord (p_rs, p_outRecordLen);
+
+ if (ret != NULL) {
+ *p_outRecord = ret;
+ return 0;
+ }
+
+ // if the buffer is full and we don't have a full record
+ if (p_rs->unconsumed == p_rs->buffer
+ && p_rs->read_end == p_rs->buffer_end
+ ) {
+ // this should never happen
+ //LOGE("max record length exceeded\n");
+ assert (0);
+ errno = EFBIG;
+ return -1;
+ }
+
+ if (p_rs->unconsumed != p_rs->buffer) {
+ // move remainder to the beginning of the buffer
+ size_t toMove;
+
+ toMove = p_rs->read_end - p_rs->unconsumed;
+ if (toMove) {
+ memmove(p_rs->buffer, p_rs->unconsumed, toMove);
+ }
+
+ p_rs->read_end = p_rs->buffer + toMove;
+ p_rs->unconsumed = p_rs->buffer;
+ }
+
+ countRead = read (p_rs->fd, p_rs->read_end, p_rs->buffer_end - p_rs->read_end);
+
+ if (countRead <= 0) {
+ /* note: end-of-stream drops through here too */
+ *p_outRecord = NULL;
+ return countRead;
+ }
+
+ p_rs->read_end += countRead;
+
+ ret = getNextRecord (p_rs, p_outRecordLen);
+
+ if (ret == NULL) {
+ /* not enough of a buffer to for a whole command */
+ errno = EAGAIN;
+ return -1;
+ }
+
+ *p_outRecord = ret;
+ return 0;
+}
diff --git a/libcutils/sched_policy.c b/libcutils/sched_policy.c
new file mode 100644
index 0000000..f9c111e
--- /dev/null
+++ b/libcutils/sched_policy.c
@@ -0,0 +1,277 @@
+
+/* libs/cutils/sched_policy.c
+**
+** Copyright 2007, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#define LOG_TAG "SchedPolicy"
+#include "cutils/log.h"
+
+#ifdef HAVE_SCHED_H
+#ifdef HAVE_PTHREADS
+
+#include <sched.h>
+#include <pthread.h>
+
+#include <cutils/sched_policy.h>
+
+#ifndef SCHED_NORMAL
+ #define SCHED_NORMAL 0
+#endif
+
+#ifndef SCHED_BATCH
+ #define SCHED_BATCH 3
+#endif
+
+#define POLICY_DEBUG 0
+
+static pthread_once_t the_once = PTHREAD_ONCE_INIT;
+
+static int __sys_supports_schedgroups = -1;
+
+// File descriptors open to /dev/cpuctl/../tasks, setup by initialize, or -1 on error.
+static int normal_cgroup_fd = -1;
+static int bg_cgroup_fd = -1;
+
+/* Add tid to the scheduling group defined by the policy */
+static int add_tid_to_cgroup(int tid, SchedPolicy policy)
+{
+ int fd;
+
+ if (policy == SP_BACKGROUND) {
+ fd = bg_cgroup_fd;
+ } else {
+ fd = normal_cgroup_fd;
+ }
+
+ if (fd < 0) {
+ SLOGE("add_tid_to_cgroup failed; background=%d\n",
+ policy == SP_BACKGROUND ? 1 : 0);
+ return -1;
+ }
+
+ // specialized itoa -- works for tid > 0
+ char text[22];
+ char *end = text + sizeof(text) - 1;
+ char *ptr = end;
+ *ptr = '\0';
+ while (tid > 0) {
+ *--ptr = '0' + (tid % 10);
+ tid = tid / 10;
+ }
+
+ if (write(fd, ptr, end - ptr) < 0) {
+ /*
+ * If the thread is in the process of exiting,
+ * don't flag an error
+ */
+ if (errno == ESRCH)
+ return 0;
+ SLOGW("add_tid_to_cgroup failed to write '%s' (%s); background=%d\n",
+ ptr, strerror(errno), policy == SP_BACKGROUND ? 1 : 0);
+ return -1;
+ }
+
+ return 0;
+}
+
+static void __initialize(void) {
+ char* filename;
+ if (!access("/dev/cpuctl/tasks", F_OK)) {
+ __sys_supports_schedgroups = 1;
+
+ filename = "/dev/cpuctl/tasks";
+ normal_cgroup_fd = open(filename, O_WRONLY);
+ if (normal_cgroup_fd < 0) {
+ SLOGE("open of %s failed: %s\n", filename, strerror(errno));
+ }
+
+ filename = "/dev/cpuctl/bg_non_interactive/tasks";
+ bg_cgroup_fd = open(filename, O_WRONLY);
+ if (bg_cgroup_fd < 0) {
+ SLOGE("open of %s failed: %s\n", filename, strerror(errno));
+ }
+ } else {
+ __sys_supports_schedgroups = 0;
+ }
+}
+
+/*
+ * Try to get the scheduler group.
+ *
+ * The data from /proc/<pid>/cgroup looks (something) like:
+ * 2:cpu:/bg_non_interactive
+ * 1:cpuacct:/
+ *
+ * We return the part after the "/", which will be an empty string for
+ * the default cgroup. If the string is longer than "bufLen", the string
+ * will be truncated.
+ */
+static int getSchedulerGroup(int tid, char* buf, size_t bufLen)
+{
+#ifdef HAVE_ANDROID_OS
+ char pathBuf[32];
+ char lineBuf[256];
+ FILE *fp;
+
+ snprintf(pathBuf, sizeof(pathBuf), "/proc/%d/cgroup", tid);
+ if (!(fp = fopen(pathBuf, "r"))) {
+ return -1;
+ }
+
+ while(fgets(lineBuf, sizeof(lineBuf) -1, fp)) {
+ char *next = lineBuf;
+ char *subsys;
+ char *grp;
+ size_t len;
+
+ /* Junk the first field */
+ if (!strsep(&next, ":")) {
+ goto out_bad_data;
+ }
+
+ if (!(subsys = strsep(&next, ":"))) {
+ goto out_bad_data;
+ }
+
+ if (strcmp(subsys, "cpu")) {
+ /* Not the subsys we're looking for */
+ continue;
+ }
+
+ if (!(grp = strsep(&next, ":"))) {
+ goto out_bad_data;
+ }
+ grp++; /* Drop the leading '/' */
+ len = strlen(grp);
+ grp[len-1] = '\0'; /* Drop the trailing '\n' */
+
+ if (bufLen <= len) {
+ len = bufLen - 1;
+ }
+ strncpy(buf, grp, len);
+ buf[len] = '\0';
+ fclose(fp);
+ return 0;
+ }
+
+ SLOGE("Failed to find cpu subsys");
+ fclose(fp);
+ return -1;
+ out_bad_data:
+ SLOGE("Bad cgroup data {%s}", lineBuf);
+ fclose(fp);
+ return -1;
+#else
+ errno = ENOSYS;
+ return -1;
+#endif
+}
+
+int get_sched_policy(int tid, SchedPolicy *policy)
+{
+ pthread_once(&the_once, __initialize);
+
+ if (__sys_supports_schedgroups) {
+ char grpBuf[32];
+ if (getSchedulerGroup(tid, grpBuf, sizeof(grpBuf)) < 0)
+ return -1;
+ if (grpBuf[0] == '\0') {
+ *policy = SP_FOREGROUND;
+ } else if (!strcmp(grpBuf, "bg_non_interactive")) {
+ *policy = SP_BACKGROUND;
+ } else {
+ errno = ERANGE;
+ return -1;
+ }
+ } else {
+ int rc = sched_getscheduler(tid);
+ if (rc < 0)
+ return -1;
+ else if (rc == SCHED_NORMAL)
+ *policy = SP_FOREGROUND;
+ else if (rc == SCHED_BATCH)
+ *policy = SP_BACKGROUND;
+ else {
+ errno = ERANGE;
+ return -1;
+ }
+ }
+ return 0;
+}
+
+int set_sched_policy(int tid, SchedPolicy policy)
+{
+ pthread_once(&the_once, __initialize);
+
+#if POLICY_DEBUG
+ char statfile[64];
+ char statline[1024];
+ char thread_name[255];
+ int fd;
+
+ sprintf(statfile, "/proc/%d/stat", tid);
+ memset(thread_name, 0, sizeof(thread_name));
+
+ fd = open(statfile, O_RDONLY);
+ if (fd >= 0) {
+ int rc = read(fd, statline, 1023);
+ close(fd);
+ statline[rc] = 0;
+ char *p = statline;
+ char *q;
+
+ for (p = statline; *p != '('; p++);
+ p++;
+ for (q = p; *q != ')'; q++);
+
+ strncpy(thread_name, p, (q-p));
+ }
+ if (policy == SP_BACKGROUND) {
+ SLOGD("vvv tid %d (%s)", tid, thread_name);
+ } else if (policy == SP_FOREGROUND) {
+ SLOGD("^^^ tid %d (%s)", tid, thread_name);
+ } else {
+ SLOGD("??? tid %d (%s)", tid, thread_name);
+ }
+#endif
+
+ if (__sys_supports_schedgroups) {
+ if (add_tid_to_cgroup(tid, policy)) {
+ if (errno != ESRCH && errno != ENOENT)
+ return -errno;
+ }
+ } else {
+ struct sched_param param;
+
+ param.sched_priority = 0;
+ sched_setscheduler(tid,
+ (policy == SP_BACKGROUND) ?
+ SCHED_BATCH : SCHED_NORMAL,
+ &param);
+ }
+
+ return 0;
+}
+
+#endif /* HAVE_PTHREADS */
+#endif /* HAVE_SCHED_H */
diff --git a/libcutils/selector.c b/libcutils/selector.c
new file mode 100644
index 0000000..9436393
--- /dev/null
+++ b/libcutils/selector.c
@@ -0,0 +1,263 @@
+/*
+ * 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 LOG_TAG "selector"
+
+#include <assert.h>
+#include <errno.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <cutils/array.h>
+#include <cutils/selector.h>
+
+#include "loghack.h"
+
+struct Selector {
+ Array* selectableFds;
+ bool looping;
+ fd_set readFds;
+ fd_set writeFds;
+ fd_set exceptFds;
+ int maxFd;
+ int wakeupPipe[2];
+ SelectableFd* wakeupFd;
+
+ bool inSelect;
+ pthread_mutex_t inSelectLock;
+};
+
+/** Reads and ignores wake up data. */
+static void eatWakeupData(SelectableFd* wakeupFd) {
+ static char garbage[64];
+ if (read(wakeupFd->fd, garbage, sizeof(garbage)) < 0) {
+ if (errno == EINTR) {
+ LOGI("read() interrupted.");
+ } else {
+ LOG_ALWAYS_FATAL("This should never happen: %s", strerror(errno));
+ }
+ }
+}
+
+static void setInSelect(Selector* selector, bool inSelect) {
+ pthread_mutex_lock(&selector->inSelectLock);
+ selector->inSelect = inSelect;
+ pthread_mutex_unlock(&selector->inSelectLock);
+}
+
+static bool isInSelect(Selector* selector) {
+ pthread_mutex_lock(&selector->inSelectLock);
+ bool inSelect = selector->inSelect;
+ pthread_mutex_unlock(&selector->inSelectLock);
+ return inSelect;
+}
+
+void selectorWakeUp(Selector* selector) {
+ if (!isInSelect(selector)) {
+ // We only need to write wake-up data if we're blocked in select().
+ return;
+ }
+
+ static char garbage[1];
+ if (write(selector->wakeupPipe[1], garbage, sizeof(garbage)) < 0) {
+ if (errno == EINTR) {
+ LOGI("read() interrupted.");
+ } else {
+ LOG_ALWAYS_FATAL("This should never happen: %s", strerror(errno));
+ }
+ }
+}
+
+Selector* selectorCreate(void) {
+ Selector* selector = calloc(1, sizeof(Selector));
+ if (selector == NULL) {
+ LOG_ALWAYS_FATAL("malloc() error.");
+ }
+ selector->selectableFds = arrayCreate();
+
+ // Set up wake-up pipe.
+ if (pipe(selector->wakeupPipe) < 0) {
+ LOG_ALWAYS_FATAL("pipe() error: %s", strerror(errno));
+ }
+
+ LOGD("Wakeup fd: %d", selector->wakeupPipe[0]);
+
+ SelectableFd* wakeupFd = selectorAdd(selector, selector->wakeupPipe[0]);
+ if (wakeupFd == NULL) {
+ LOG_ALWAYS_FATAL("malloc() error.");
+ }
+ wakeupFd->onReadable = &eatWakeupData;
+
+ pthread_mutex_init(&selector->inSelectLock, NULL);
+
+ return selector;
+}
+
+SelectableFd* selectorAdd(Selector* selector, int fd) {
+ assert(selector != NULL);
+
+ SelectableFd* selectableFd = calloc(1, sizeof(SelectableFd));
+ if (selectableFd != NULL) {
+ selectableFd->selector = selector;
+ selectableFd->fd = fd;
+
+ arrayAdd(selector->selectableFds, selectableFd);
+ }
+
+ return selectableFd;
+}
+
+/**
+ * Adds an fd to the given set if the callback is non-null. Returns true
+ * if the fd was added.
+ */
+static inline bool maybeAdd(SelectableFd* selectableFd,
+ void (*callback)(SelectableFd*), fd_set* fdSet) {
+ if (callback != NULL) {
+ FD_SET(selectableFd->fd, fdSet);
+ return true;
+ }
+ return false;
+}
+
+/**
+ * Removes stale file descriptors and initializes file descriptor sets.
+ */
+static void prepareForSelect(Selector* selector) {
+ fd_set* exceptFds = &selector->exceptFds;
+ fd_set* readFds = &selector->readFds;
+ fd_set* writeFds = &selector->writeFds;
+
+ FD_ZERO(exceptFds);
+ FD_ZERO(readFds);
+ FD_ZERO(writeFds);
+
+ Array* selectableFds = selector->selectableFds;
+ int i = 0;
+ selector->maxFd = 0;
+ int size = arraySize(selectableFds);
+ while (i < size) {
+ SelectableFd* selectableFd = arrayGet(selectableFds, i);
+ if (selectableFd->remove) {
+ // This descriptor should be removed.
+ arrayRemove(selectableFds, i);
+ size--;
+ if (selectableFd->onRemove != NULL) {
+ selectableFd->onRemove(selectableFd);
+ }
+ free(selectableFd);
+ } else {
+ if (selectableFd->beforeSelect != NULL) {
+ selectableFd->beforeSelect(selectableFd);
+ }
+
+ bool inSet = false;
+ if (maybeAdd(selectableFd, selectableFd->onExcept, exceptFds)) {
+ LOGD("Selecting fd %d for writing...", selectableFd->fd);
+ inSet = true;
+ }
+ if (maybeAdd(selectableFd, selectableFd->onReadable, readFds)) {
+ LOGD("Selecting fd %d for reading...", selectableFd->fd);
+ inSet = true;
+ }
+ if (maybeAdd(selectableFd, selectableFd->onWritable, writeFds)) {
+ inSet = true;
+ }
+
+ if (inSet) {
+ // If the fd is in a set, check it against max.
+ int fd = selectableFd->fd;
+ if (fd > selector->maxFd) {
+ selector->maxFd = fd;
+ }
+ }
+
+ // Move to next descriptor.
+ i++;
+ }
+ }
+}
+
+/**
+ * Invokes a callback if the callback is non-null and the fd is in the given
+ * set.
+ */
+static inline void maybeInvoke(SelectableFd* selectableFd,
+ void (*callback)(SelectableFd*), fd_set* fdSet) {
+ if (callback != NULL && !selectableFd->remove &&
+ FD_ISSET(selectableFd->fd, fdSet)) {
+ LOGD("Selected fd %d.", selectableFd->fd);
+ callback(selectableFd);
+ }
+}
+
+/**
+ * Notifies user if file descriptors are readable or writable, or if
+ * out-of-band data is present.
+ */
+static void fireEvents(Selector* selector) {
+ Array* selectableFds = selector->selectableFds;
+ int size = arraySize(selectableFds);
+ int i;
+ for (i = 0; i < size; i++) {
+ SelectableFd* selectableFd = arrayGet(selectableFds, i);
+ maybeInvoke(selectableFd, selectableFd->onExcept,
+ &selector->exceptFds);
+ maybeInvoke(selectableFd, selectableFd->onReadable,
+ &selector->readFds);
+ maybeInvoke(selectableFd, selectableFd->onWritable,
+ &selector->writeFds);
+ }
+}
+
+void selectorLoop(Selector* selector) {
+ // Make sure we're not already looping.
+ if (selector->looping) {
+ LOG_ALWAYS_FATAL("Already looping.");
+ }
+ selector->looping = true;
+
+ while (true) {
+ setInSelect(selector, true);
+
+ prepareForSelect(selector);
+
+ LOGD("Entering select().");
+
+ // Select file descriptors.
+ int result = select(selector->maxFd + 1, &selector->readFds,
+ &selector->writeFds, &selector->exceptFds, NULL);
+
+ LOGD("Exiting select().");
+
+ setInSelect(selector, false);
+
+ if (result == -1) {
+ // Abort on everything except EINTR.
+ if (errno == EINTR) {
+ LOGI("select() interrupted.");
+ } else {
+ LOG_ALWAYS_FATAL("select() error: %s",
+ strerror(errno));
+ }
+ } else if (result > 0) {
+ fireEvents(selector);
+ }
+ }
+}
diff --git a/libcutils/socket_inaddr_any_server.c b/libcutils/socket_inaddr_any_server.c
new file mode 100644
index 0000000..7d5dab4
--- /dev/null
+++ b/libcutils/socket_inaddr_any_server.c
@@ -0,0 +1,70 @@
+/* libs/cutils/socket_inaddr_any_server.c
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include <cutils/sockets.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stddef.h>
+
+#ifndef HAVE_WINSOCK
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#endif
+
+#define LISTEN_BACKLOG 4
+
+/* open listen() port on any interface */
+int socket_inaddr_any_server(int port, int type)
+{
+ struct sockaddr_in addr;
+ size_t alen;
+ int s, n;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(port);
+ addr.sin_addr.s_addr = htonl(INADDR_ANY);
+
+ s = socket(AF_INET, type, 0);
+ if(s < 0) return -1;
+
+ n = 1;
+ setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &n, sizeof(n));
+
+ if(bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ close(s);
+ return -1;
+ }
+
+ if (type == SOCK_STREAM) {
+ int ret;
+
+ ret = listen(s, LISTEN_BACKLOG);
+
+ if (ret < 0) {
+ close(s);
+ return -1;
+ }
+ }
+
+ return s;
+}
diff --git a/libcutils/socket_local.h b/libcutils/socket_local.h
new file mode 100644
index 0000000..45b9856
--- /dev/null
+++ b/libcutils/socket_local.h
@@ -0,0 +1,39 @@
+/*
+ * 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 __SOCKET_LOCAL_H
+#define __SOCKET_LOCAL_H
+
+#define FILESYSTEM_SOCKET_PREFIX "/tmp/"
+#define ANDROID_RESERVED_SOCKET_PREFIX "/dev/socket/"
+
+/*
+ * Set up a given sockaddr_un, to have it refer to the given
+ * name in the given namespace. The namespace must be one
+ * of <code>ANDROID_SOCKET_NAMESPACE_ABSTRACT</code>,
+ * <code>ANDROID_SOCKET_NAMESPACE_RESERVED</code>, or
+ * <code>ANDROID_SOCKET_NAMESPACE_FILESYSTEM</code>. Upon success,
+ * the pointed at sockaddr_un is filled in and the pointed at
+ * socklen_t is set to indicate the final length. This function
+ * will fail if the namespace is invalid (not one of the indicated
+ * constants) or if the name is too long.
+ *
+ * @return 0 on success or -1 on failure
+ */
+int socket_make_sockaddr_un(const char *name, int namespaceId,
+ struct sockaddr_un *p_addr, socklen_t *alen);
+
+#endif
diff --git a/libcutils/socket_local_client.c b/libcutils/socket_local_client.c
new file mode 100644
index 0000000..036ce2e
--- /dev/null
+++ b/libcutils/socket_local_client.c
@@ -0,0 +1,167 @@
+/*
+ * 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.
+ */
+
+#include <cutils/sockets.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stddef.h>
+
+#ifdef HAVE_WINSOCK
+
+int socket_local_client(const char *name, int namespaceId, int type)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+#else /* !HAVE_WINSOCK */
+
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/select.h>
+#include <sys/types.h>
+
+#include "socket_local.h"
+
+#define LISTEN_BACKLOG 4
+
+/* Documented in header file. */
+int socket_make_sockaddr_un(const char *name, int namespaceId,
+ struct sockaddr_un *p_addr, socklen_t *alen)
+{
+ memset (p_addr, 0, sizeof (*p_addr));
+ size_t namelen;
+
+ switch (namespaceId) {
+ case ANDROID_SOCKET_NAMESPACE_ABSTRACT:
+#ifdef HAVE_LINUX_LOCAL_SOCKET_NAMESPACE
+ namelen = strlen(name);
+
+ // Test with length +1 for the *initial* '\0'.
+ if ((namelen + 1) > sizeof(p_addr->sun_path)) {
+ goto error;
+ }
+
+ /*
+ * Note: The path in this case is *not* supposed to be
+ * '\0'-terminated. ("man 7 unix" for the gory details.)
+ */
+
+ p_addr->sun_path[0] = 0;
+ memcpy(p_addr->sun_path + 1, name, namelen);
+#else /*HAVE_LINUX_LOCAL_SOCKET_NAMESPACE*/
+ /* this OS doesn't have the Linux abstract namespace */
+
+ namelen = strlen(name) + strlen(FILESYSTEM_SOCKET_PREFIX);
+ /* unix_path_max appears to be missing on linux */
+ if (namelen > sizeof(*p_addr)
+ - offsetof(struct sockaddr_un, sun_path) - 1) {
+ goto error;
+ }
+
+ strcpy(p_addr->sun_path, FILESYSTEM_SOCKET_PREFIX);
+ strcat(p_addr->sun_path, name);
+#endif /*HAVE_LINUX_LOCAL_SOCKET_NAMESPACE*/
+ break;
+
+ case ANDROID_SOCKET_NAMESPACE_RESERVED:
+ namelen = strlen(name) + strlen(ANDROID_RESERVED_SOCKET_PREFIX);
+ /* unix_path_max appears to be missing on linux */
+ if (namelen > sizeof(*p_addr)
+ - offsetof(struct sockaddr_un, sun_path) - 1) {
+ goto error;
+ }
+
+ strcpy(p_addr->sun_path, ANDROID_RESERVED_SOCKET_PREFIX);
+ strcat(p_addr->sun_path, name);
+ break;
+
+ case ANDROID_SOCKET_NAMESPACE_FILESYSTEM:
+ namelen = strlen(name);
+ /* unix_path_max appears to be missing on linux */
+ if (namelen > sizeof(*p_addr)
+ - offsetof(struct sockaddr_un, sun_path) - 1) {
+ goto error;
+ }
+
+ strcpy(p_addr->sun_path, name);
+ break;
+ default:
+ // invalid namespace id
+ return -1;
+ }
+
+ p_addr->sun_family = AF_LOCAL;
+ *alen = namelen + offsetof(struct sockaddr_un, sun_path) + 1;
+ return 0;
+error:
+ return -1;
+}
+
+/**
+ * connect to peer named "name" on fd
+ * returns same fd or -1 on error.
+ * fd is not closed on error. that's your job.
+ *
+ * Used by AndroidSocketImpl
+ */
+int socket_local_client_connect(int fd, const char *name, int namespaceId,
+ int type)
+{
+ struct sockaddr_un addr;
+ socklen_t alen;
+ size_t namelen;
+ int err;
+
+ err = socket_make_sockaddr_un(name, namespaceId, &addr, &alen);
+
+ if (err < 0) {
+ goto error;
+ }
+
+ if(connect(fd, (struct sockaddr *) &addr, alen) < 0) {
+ goto error;
+ }
+
+ return fd;
+
+error:
+ return -1;
+}
+
+/**
+ * connect to peer named "name"
+ * returns fd or -1 on error
+ */
+int socket_local_client(const char *name, int namespaceId, int type)
+{
+ int s;
+
+ s = socket(AF_LOCAL, type, 0);
+ if(s < 0) return -1;
+
+ if ( 0 > socket_local_client_connect(s, name, namespaceId, type)) {
+ close(s);
+ return -1;
+ }
+
+ return s;
+}
+
+#endif /* !HAVE_WINSOCK */
diff --git a/libcutils/socket_local_server.c b/libcutils/socket_local_server.c
new file mode 100644
index 0000000..4971b1b
--- /dev/null
+++ b/libcutils/socket_local_server.c
@@ -0,0 +1,124 @@
+/* libs/cutils/socket_local_server.c
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include <cutils/sockets.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stddef.h>
+
+#ifdef HAVE_WINSOCK
+
+int socket_local_server(const char *name, int namespaceId, int type)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+#else /* !HAVE_WINSOCK */
+
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/select.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+
+#include "socket_local.h"
+
+#define LISTEN_BACKLOG 4
+
+
+/**
+ * Binds a pre-created socket(AF_LOCAL) 's' to 'name'
+ * returns 's' on success, -1 on fail
+ *
+ * Does not call listen()
+ */
+int socket_local_server_bind(int s, const char *name, int namespaceId)
+{
+ struct sockaddr_un addr;
+ socklen_t alen;
+ int n;
+ int err;
+
+ err = socket_make_sockaddr_un(name, namespaceId, &addr, &alen);
+
+ if (err < 0) {
+ return -1;
+ }
+
+ /* basically: if this is a filesystem path, unlink first */
+#ifndef HAVE_LINUX_LOCAL_SOCKET_NAMESPACE
+ if (1) {
+#else
+ if (namespaceId == ANDROID_SOCKET_NAMESPACE_RESERVED
+ || namespaceId == ANDROID_SOCKET_NAMESPACE_FILESYSTEM) {
+#endif
+ /*ignore ENOENT*/
+ unlink(addr.sun_path);
+ }
+
+ n = 1;
+ setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &n, sizeof(n));
+
+ if(bind(s, (struct sockaddr *) &addr, alen) < 0) {
+ return -1;
+ }
+
+ return s;
+
+}
+
+
+/** Open a server-side UNIX domain datagram socket in the Linux non-filesystem
+ * namespace
+ *
+ * Returns fd on success, -1 on fail
+ */
+
+int socket_local_server(const char *name, int namespace, int type)
+{
+ int err;
+ int s;
+
+ s = socket(AF_LOCAL, type, 0);
+ if (s < 0) return -1;
+
+ err = socket_local_server_bind(s, name, namespace);
+
+ if (err < 0) {
+ close(s);
+ return -1;
+ }
+
+ if (type == SOCK_STREAM) {
+ int ret;
+
+ ret = listen(s, LISTEN_BACKLOG);
+
+ if (ret < 0) {
+ close(s);
+ return -1;
+ }
+ }
+
+ return s;
+}
+
+#endif /* !HAVE_WINSOCK */
diff --git a/libcutils/socket_loopback_client.c b/libcutils/socket_loopback_client.c
new file mode 100644
index 0000000..cb82c5e
--- /dev/null
+++ b/libcutils/socket_loopback_client.c
@@ -0,0 +1,59 @@
+/* libs/cutils/socket_loopback_client.c
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include <cutils/sockets.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stddef.h>
+
+#ifndef HAVE_WINSOCK
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#endif
+
+/* Connect to port on the loopback IP interface. type is
+ * SOCK_STREAM or SOCK_DGRAM.
+ * return is a file descriptor or -1 on error
+ */
+int socket_loopback_client(int port, int type)
+{
+ struct sockaddr_in addr;
+ socklen_t alen;
+ int s;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(port);
+ addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+ s = socket(AF_INET, type, 0);
+ if(s < 0) return -1;
+
+ if(connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ close(s);
+ return -1;
+ }
+
+ return s;
+
+}
+
diff --git a/libcutils/socket_loopback_server.c b/libcutils/socket_loopback_server.c
new file mode 100644
index 0000000..3208488
--- /dev/null
+++ b/libcutils/socket_loopback_server.c
@@ -0,0 +1,71 @@
+/* libs/cutils/socket_loopback_server.c
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include <cutils/sockets.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stddef.h>
+
+#define LISTEN_BACKLOG 4
+
+#ifndef HAVE_WINSOCK
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#endif
+
+/* open listen() port on loopback interface */
+int socket_loopback_server(int port, int type)
+{
+ struct sockaddr_in addr;
+ size_t alen;
+ int s, n;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(port);
+ addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+ s = socket(AF_INET, type, 0);
+ if(s < 0) return -1;
+
+ n = 1;
+ setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &n, sizeof(n));
+
+ if(bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ close(s);
+ return -1;
+ }
+
+ if (type == SOCK_STREAM) {
+ int ret;
+
+ ret = listen(s, LISTEN_BACKLOG);
+
+ if (ret < 0) {
+ close(s);
+ return -1;
+ }
+ }
+
+ return s;
+}
+
diff --git a/libcutils/socket_network_client.c b/libcutils/socket_network_client.c
new file mode 100644
index 0000000..a64006c
--- /dev/null
+++ b/libcutils/socket_network_client.c
@@ -0,0 +1,65 @@
+/* libs/cutils/socket_network_client.c
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include <cutils/sockets.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stddef.h>
+
+#ifndef HAVE_WINSOCK
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#endif
+
+
+/* Connect to port on the IP interface. type is
+ * SOCK_STREAM or SOCK_DGRAM.
+ * return is a file descriptor or -1 on error
+ */
+int socket_network_client(const char *host, int port, int type)
+{
+ struct hostent *hp;
+ struct sockaddr_in addr;
+ socklen_t alen;
+ int s;
+
+ hp = gethostbyname(host);
+ if(hp == 0) return -1;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = hp->h_addrtype;
+ addr.sin_port = htons(port);
+ memcpy(&addr.sin_addr, hp->h_addr, hp->h_length);
+
+ s = socket(hp->h_addrtype, type, 0);
+ if(s < 0) return -1;
+
+ if(connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ close(s);
+ return -1;
+ }
+
+ return s;
+
+}
+
diff --git a/libcutils/strdup16to8.c b/libcutils/strdup16to8.c
new file mode 100644
index 0000000..1a8ba86
--- /dev/null
+++ b/libcutils/strdup16to8.c
@@ -0,0 +1,168 @@
+/* libs/cutils/strdup16to8.c
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include <limits.h> /* for SIZE_MAX */
+
+#include <cutils/jstring.h>
+#include <assert.h>
+#include <stdlib.h>
+
+
+/**
+ * Given a UTF-16 string, compute the length of the corresponding UTF-8
+ * string in bytes.
+ */
+extern size_t strnlen16to8(const char16_t* utf16Str, size_t len)
+{
+ size_t utf8Len = 0;
+
+ /* A small note on integer overflow. The result can
+ * potentially be as big as 3*len, which will overflow
+ * for len > SIZE_MAX/3.
+ *
+ * Moreover, the result of a strnlen16to8 is typically used
+ * to allocate a destination buffer to strncpy16to8 which
+ * requires one more byte to terminate the UTF-8 copy, and
+ * this is generally done by careless users by incrementing
+ * the result without checking for integer overflows, e.g.:
+ *
+ * dst = malloc(strnlen16to8(utf16,len)+1)
+ *
+ * Due to this, the following code will try to detect
+ * overflows, and never return more than (SIZE_MAX-1)
+ * when it detects one. A careless user will try to malloc
+ * SIZE_MAX bytes, which will return NULL which can at least
+ * be detected appropriately.
+ *
+ * As far as I know, this function is only used by strndup16(),
+ * but better be safe than sorry.
+ */
+
+ /* Fast path for the usual case where 3*len is < SIZE_MAX-1.
+ */
+ if (len < (SIZE_MAX-1)/3) {
+ while (len--) {
+ unsigned int uic = *utf16Str++;
+
+ if (uic > 0x07ff)
+ utf8Len += 3;
+ else if (uic > 0x7f || uic == 0)
+ utf8Len += 2;
+ else
+ utf8Len++;
+ }
+ return utf8Len;
+ }
+
+ /* The slower but paranoid version */
+ while (len--) {
+ unsigned int uic = *utf16Str++;
+ size_t utf8Cur = utf8Len;
+
+ if (uic > 0x07ff)
+ utf8Len += 3;
+ else if (uic > 0x7f || uic == 0)
+ utf8Len += 2;
+ else
+ utf8Len++;
+
+ if (utf8Len < utf8Cur) /* overflow detected */
+ return SIZE_MAX-1;
+ }
+
+ /* don't return SIZE_MAX to avoid common user bug */
+ if (utf8Len == SIZE_MAX)
+ utf8Len = SIZE_MAX-1;
+
+ return utf8Len;
+}
+
+
+/**
+ * Convert a Java-Style UTF-16 string + length to a JNI-Style UTF-8 string.
+ *
+ * This basically means: embedded \0's in the UTF-16 string are encoded
+ * as "0xc0 0x80"
+ *
+ * Make sure you allocate "utf8Str" with the result of strlen16to8() + 1,
+ * not just "len".
+ *
+ * Please note, a terminated \0 is always added, so your result will always
+ * be "strlen16to8() + 1" bytes long.
+ */
+extern char* strncpy16to8(char* utf8Str, const char16_t* utf16Str, size_t len)
+{
+ char* utf8cur = utf8Str;
+
+ /* Note on overflows: We assume the user did check the result of
+ * strnlen16to8() properly or at a minimum checked the result of
+ * its malloc(SIZE_MAX) in case of overflow.
+ */
+ while (len--) {
+ unsigned int uic = *utf16Str++;
+
+ if (uic > 0x07ff) {
+ *utf8cur++ = (uic >> 12) | 0xe0;
+ *utf8cur++ = ((uic >> 6) & 0x3f) | 0x80;
+ *utf8cur++ = (uic & 0x3f) | 0x80;
+ } else if (uic > 0x7f || uic == 0) {
+ *utf8cur++ = (uic >> 6) | 0xc0;
+ *utf8cur++ = (uic & 0x3f) | 0x80;
+ } else {
+ *utf8cur++ = uic;
+
+ if (uic == 0) {
+ break;
+ }
+ }
+ }
+
+ *utf8cur = '\0';
+
+ return utf8Str;
+}
+
+/**
+ * Convert a UTF-16 string to UTF-8.
+ *
+ */
+char * strndup16to8 (const char16_t* s, size_t n)
+{
+ char* ret;
+ size_t len;
+
+ if (s == NULL) {
+ return NULL;
+ }
+
+ len = strnlen16to8(s, n);
+
+ /* We are paranoid, and we check for SIZE_MAX-1
+ * too since it is an overflow value for our
+ * strnlen16to8 implementation.
+ */
+ if (len >= SIZE_MAX-1)
+ return NULL;
+
+ ret = malloc(len + 1);
+ if (ret == NULL)
+ return NULL;
+
+ strncpy16to8 (ret, s, n);
+
+ return ret;
+}
diff --git a/libcutils/strdup8to16.c b/libcutils/strdup8to16.c
new file mode 100644
index 0000000..63e5ca4
--- /dev/null
+++ b/libcutils/strdup8to16.c
@@ -0,0 +1,214 @@
+/* libs/cutils/strdup8to16.c
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include <cutils/jstring.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <limits.h>
+
+/* See http://www.unicode.org/reports/tr22/ for discussion
+ * on invalid sequences
+ */
+
+#define UTF16_REPLACEMENT_CHAR 0xfffd
+
+/* Clever trick from Dianne that returns 1-4 depending on leading bit sequence*/
+#define UTF8_SEQ_LENGTH(ch) (((0xe5000000 >> ((ch >> 3) & 0x1e)) & 3) + 1)
+
+/* note: macro expands to multiple lines */
+#define UTF8_SHIFT_AND_MASK(unicode, byte) \
+ (unicode)<<=6; (unicode) |= (0x3f & (byte));
+
+#define UNICODE_UPPER_LIMIT 0x10fffd
+
+/**
+ * out_len is an out parameter (which may not be null) containing the
+ * length of the UTF-16 string (which may contain embedded \0's)
+ */
+
+extern char16_t * strdup8to16 (const char* s, size_t *out_len)
+{
+ char16_t *ret;
+ size_t len;
+
+ if (s == NULL) return NULL;
+
+ len = strlen8to16(s);
+
+ // fail on overflow
+ if (len && SIZE_MAX/len < sizeof(char16_t))
+ return NULL;
+
+ // no plus-one here. UTF-16 strings are not null terminated
+ ret = (char16_t *) malloc (sizeof(char16_t) * len);
+
+ return strcpy8to16 (ret, s, out_len);
+}
+
+/**
+ * Like "strlen", but for strings encoded with Java's modified UTF-8.
+ *
+ * The value returned is the number of UTF-16 characters required
+ * to represent this string.
+ */
+extern size_t strlen8to16 (const char* utf8Str)
+{
+ size_t len = 0;
+ int ic;
+ int expected = 0;
+
+ while ((ic = *utf8Str++) != '\0') {
+ /* bytes that start 0? or 11 are lead bytes and count as characters.*/
+ /* bytes that start 10 are extention bytes and are not counted */
+
+ if ((ic & 0xc0) == 0x80) {
+ /* count the 0x80 extention bytes. if we have more than
+ * expected, then start counting them because strcpy8to16
+ * will insert UTF16_REPLACEMENT_CHAR's
+ */
+ expected--;
+ if (expected < 0) {
+ len++;
+ }
+ } else {
+ len++;
+ expected = UTF8_SEQ_LENGTH(ic) - 1;
+
+ /* this will result in a surrogate pair */
+ if (expected == 3) {
+ len++;
+ }
+ }
+ }
+
+ return len;
+}
+
+
+
+/*
+ * Retrieve the next UTF-32 character from a UTF-8 string.
+ *
+ * Stops at inner \0's
+ *
+ * Returns UTF16_REPLACEMENT_CHAR if an invalid sequence is encountered
+ *
+ * Advances "*pUtf8Ptr" to the start of the next character.
+ */
+static inline uint32_t getUtf32FromUtf8(const char** pUtf8Ptr)
+{
+ uint32_t ret;
+ int seq_len;
+ int i;
+
+ /* Mask for leader byte for lengths 1, 2, 3, and 4 respectively*/
+ static const char leaderMask[4] = {0xff, 0x1f, 0x0f, 0x07};
+
+ /* Bytes that start with bits "10" are not leading characters. */
+ if (((**pUtf8Ptr) & 0xc0) == 0x80) {
+ (*pUtf8Ptr)++;
+ return UTF16_REPLACEMENT_CHAR;
+ }
+
+ /* note we tolerate invalid leader 11111xxx here */
+ seq_len = UTF8_SEQ_LENGTH(**pUtf8Ptr);
+
+ ret = (**pUtf8Ptr) & leaderMask [seq_len - 1];
+
+ if (**pUtf8Ptr == '\0') return ret;
+
+ (*pUtf8Ptr)++;
+ for (i = 1; i < seq_len ; i++, (*pUtf8Ptr)++) {
+ if ((**pUtf8Ptr) == '\0') return UTF16_REPLACEMENT_CHAR;
+ if (((**pUtf8Ptr) & 0xc0) != 0x80) return UTF16_REPLACEMENT_CHAR;
+
+ UTF8_SHIFT_AND_MASK(ret, **pUtf8Ptr);
+ }
+
+ return ret;
+}
+
+
+/**
+ * out_len is an out parameter (which may not be null) containing the
+ * length of the UTF-16 string (which may contain embedded \0's)
+ */
+
+extern char16_t * strcpy8to16 (char16_t *utf16Str, const char*utf8Str,
+ size_t *out_len)
+{
+ char16_t *dest = utf16Str;
+
+ while (*utf8Str != '\0') {
+ uint32_t ret;
+
+ ret = getUtf32FromUtf8(&utf8Str);
+
+ if (ret <= 0xffff) {
+ *dest++ = (char16_t) ret;
+ } else if (ret <= UNICODE_UPPER_LIMIT) {
+ /* Create surrogate pairs */
+ /* See http://en.wikipedia.org/wiki/UTF-16/UCS-2#Method_for_code_points_in_Plane_1.2C_Plane_2 */
+
+ *dest++ = 0xd800 | ((ret - 0x10000) >> 10);
+ *dest++ = 0xdc00 | ((ret - 0x10000) & 0x3ff);
+ } else {
+ *dest++ = UTF16_REPLACEMENT_CHAR;
+ }
+ }
+
+ *out_len = dest - utf16Str;
+
+ return utf16Str;
+}
+
+/**
+ * length is the number of characters in the UTF-8 string.
+ * out_len is an out parameter (which may not be null) containing the
+ * length of the UTF-16 string (which may contain embedded \0's)
+ */
+
+extern char16_t * strcpylen8to16 (char16_t *utf16Str, const char*utf8Str,
+ int length, size_t *out_len)
+{
+ /* TODO: Share more of this code with the method above. Only 2 lines changed. */
+
+ char16_t *dest = utf16Str;
+
+ const char *end = utf8Str + length; /* This line */
+ while (utf8Str < end) { /* and this line changed. */
+ uint32_t ret;
+
+ ret = getUtf32FromUtf8(&utf8Str);
+
+ if (ret <= 0xffff) {
+ *dest++ = (char16_t) ret;
+ } else if (ret <= UNICODE_UPPER_LIMIT) {
+ /* Create surrogate pairs */
+ /* See http://en.wikipedia.org/wiki/UTF-16/UCS-2#Method_for_code_points_in_Plane_1.2C_Plane_2 */
+
+ *dest++ = 0xd800 | ((ret - 0x10000) >> 10);
+ *dest++ = 0xdc00 | ((ret - 0x10000) & 0x3ff);
+ } else {
+ *dest++ = UTF16_REPLACEMENT_CHAR;
+ }
+ }
+
+ *out_len = dest - utf16Str;
+
+ return utf16Str;
+}
diff --git a/libcutils/threads.c b/libcutils/threads.c
new file mode 100644
index 0000000..42cc928
--- /dev/null
+++ b/libcutils/threads.c
@@ -0,0 +1,84 @@
+/* libs/cutils/threads.c
+**
+** 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 <cutils/threads.h>
+
+#ifdef HAVE_PTHREADS
+void* thread_store_get( thread_store_t* store )
+{
+ const pthread_key_t k = store->tls;
+
+ if (!store->has_tls)
+ return NULL;
+
+ return pthread_getspecific( store->tls );
+}
+
+extern void thread_store_set( thread_store_t* store,
+ void* value,
+ thread_store_destruct_t destroy)
+{
+ pthread_mutex_lock( &store->lock );
+ if (!store->has_tls) {
+ if (pthread_key_create( &store->tls, destroy) != 0) {
+ pthread_mutex_unlock(&store->lock);
+ return;
+ }
+ store->has_tls = 1;
+ }
+ pthread_mutex_unlock( &store->lock );
+
+ pthread_setspecific( store->tls, value );
+}
+
+#endif
+
+#ifdef HAVE_WIN32_THREADS
+void* thread_store_get( thread_store_t* store )
+{
+ if (!store->has_tls)
+ return NULL;
+
+ return (void*) TlsGetValue( store->tls );
+}
+
+void thread_store_set( thread_store_t* store,
+ void* value,
+ thread_store_destruct_t destroy )
+{
+ /* XXX: can't use destructor on thread exit */
+ if (!store->lock_init) {
+ store->lock_init = -1;
+ InitializeCriticalSection( &store->lock );
+ store->lock_init = -2;
+ } else while (store->lock_init != -2) {
+ Sleep(10); /* 10ms */
+ }
+
+ EnterCriticalSection( &store->lock );
+ if (!store->has_tls) {
+ store->tls = TlsAlloc();
+ if (store->tls == TLS_OUT_OF_INDEXES) {
+ LeaveCriticalSection( &store->lock );
+ return;
+ }
+ store->has_tls = 1;
+ }
+ LeaveCriticalSection( &store->lock );
+
+ TlsSetValue( store->tls, value );
+}
+#endif
diff --git a/libcutils/tzfile.h b/libcutils/tzfile.h
new file mode 100644
index 0000000..8c70375
--- /dev/null
+++ b/libcutils/tzfile.h
@@ -0,0 +1,180 @@
+#ifndef TZFILE_H
+
+#define TZFILE_H
+
+/*
+** This file is in the public domain, so clarified as of
+** 1996-06-05 by Arthur David Olson.
+*/
+
+/*
+** This header is for use ONLY with the time conversion code.
+** There is no guarantee that it will remain unchanged,
+** or that it will remain at all.
+** Do NOT copy it to any system include directory.
+** Thank you!
+*/
+
+/*
+** ID
+*/
+
+#ifndef lint
+#ifndef NOID
+static char tzfilehid[] = "@(#)tzfile.h 8.1";
+#endif /* !defined NOID */
+#endif /* !defined lint */
+
+/*
+** Information about time zone files.
+*/
+
+#ifndef TZDIR
+#define TZDIR "/usr/share/zoneinfo" /* "/android/usr/share/zoneinfo" */ /* Time zone object file directory */
+#endif /* !defined TZDIR */
+
+#ifndef TZDEFAULT
+#define TZDEFAULT "localtime"
+#endif /* !defined TZDEFAULT */
+
+#ifndef TZDEFRULES
+#define TZDEFRULES "posixrules"
+#endif /* !defined TZDEFRULES */
+
+/*
+** Each file begins with. . .
+*/
+
+#define TZ_MAGIC "TZif"
+
+struct tzhead {
+ char tzh_magic[4]; /* TZ_MAGIC */
+ char tzh_version[1]; /* '\0' or '2' as of 2005 */
+ char tzh_reserved[15]; /* reserved--must be zero */
+ char tzh_ttisgmtcnt[4]; /* coded number of trans. time flags */
+ char tzh_ttisstdcnt[4]; /* coded number of trans. time flags */
+ char tzh_leapcnt[4]; /* coded number of leap seconds */
+ char tzh_timecnt[4]; /* coded number of transition times */
+ char tzh_typecnt[4]; /* coded number of local time types */
+ char tzh_charcnt[4]; /* coded number of abbr. chars */
+};
+
+/*
+** . . .followed by. . .
+**
+** tzh_timecnt (char [4])s coded transition times a la time(2)
+** tzh_timecnt (unsigned char)s types of local time starting at above
+** tzh_typecnt repetitions of
+** one (char [4]) coded UTC offset in seconds
+** one (unsigned char) used to set tm_isdst
+** one (unsigned char) that's an abbreviation list index
+** tzh_charcnt (char)s '\0'-terminated zone abbreviations
+** tzh_leapcnt repetitions of
+** one (char [4]) coded leap second transition times
+** one (char [4]) total correction after above
+** tzh_ttisstdcnt (char)s indexed by type; if TRUE, transition
+** time is standard time, if FALSE,
+** transition time is wall clock time
+** if absent, transition times are
+** assumed to be wall clock time
+** tzh_ttisgmtcnt (char)s indexed by type; if TRUE, transition
+** time is UTC, if FALSE,
+** transition time is local time
+** if absent, transition times are
+** assumed to be local time
+*/
+
+/*
+** If tzh_version is '2' or greater, the above is followed by a second instance
+** of tzhead and a second instance of the data in which each coded transition
+** time uses 8 rather than 4 chars,
+** then a POSIX-TZ-environment-variable-style string for use in handling
+** instants after the last transition time stored in the file
+** (with nothing between the newlines if there is no POSIX representation for
+** such instants).
+*/
+
+/*
+** In the current implementation, "tzset()" refuses to deal with files that
+** exceed any of the limits below.
+*/
+
+#ifndef TZ_MAX_TIMES
+#define TZ_MAX_TIMES 1200
+#endif /* !defined TZ_MAX_TIMES */
+
+#ifndef TZ_MAX_TYPES
+#ifndef NOSOLAR
+#define TZ_MAX_TYPES 256 /* Limited by what (unsigned char)'s can hold */
+#endif /* !defined NOSOLAR */
+#ifdef NOSOLAR
+/*
+** Must be at least 14 for Europe/Riga as of Jan 12 1995,
+** as noted by Earl Chew.
+*/
+#define TZ_MAX_TYPES 20 /* Maximum number of local time types */
+#endif /* !defined NOSOLAR */
+#endif /* !defined TZ_MAX_TYPES */
+
+#ifndef TZ_MAX_CHARS
+#define TZ_MAX_CHARS 50 /* Maximum number of abbreviation characters */
+ /* (limited by what unsigned chars can hold) */
+#endif /* !defined TZ_MAX_CHARS */
+
+#ifndef TZ_MAX_LEAPS
+#define TZ_MAX_LEAPS 50 /* Maximum number of leap second corrections */
+#endif /* !defined TZ_MAX_LEAPS */
+
+#define SECSPERMIN 60
+#define MINSPERHOUR 60
+#define HOURSPERDAY 24
+#define DAYSPERWEEK 7
+#define DAYSPERNYEAR 365
+#define DAYSPERLYEAR 366
+#define SECSPERHOUR (SECSPERMIN * MINSPERHOUR)
+#define SECSPERDAY ((long) SECSPERHOUR * HOURSPERDAY)
+#define MONSPERYEAR 12
+
+#define TM_SUNDAY 0
+#define TM_MONDAY 1
+#define TM_TUESDAY 2
+#define TM_WEDNESDAY 3
+#define TM_THURSDAY 4
+#define TM_FRIDAY 5
+#define TM_SATURDAY 6
+
+#define TM_JANUARY 0
+#define TM_FEBRUARY 1
+#define TM_MARCH 2
+#define TM_APRIL 3
+#define TM_MAY 4
+#define TM_JUNE 5
+#define TM_JULY 6
+#define TM_AUGUST 7
+#define TM_SEPTEMBER 8
+#define TM_OCTOBER 9
+#define TM_NOVEMBER 10
+#define TM_DECEMBER 11
+
+#define TM_YEAR_BASE 1900
+
+#define EPOCH_YEAR 1970
+#define EPOCH_WDAY TM_THURSDAY
+
+#define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0))
+
+/*
+** Since everything in isleap is modulo 400 (or a factor of 400), we know that
+** isleap(y) == isleap(y % 400)
+** and so
+** isleap(a + b) == isleap((a + b) % 400)
+** or
+** isleap(a + b) == isleap(a % 400 + b % 400)
+** This is true even if % means modulo rather than Fortran remainder
+** (which is allowed by C89 but not C99).
+** We use this to avoid addition overflow problems.
+*/
+
+#define isleap_sum(a, b) isleap((a) % 400 + (b) % 400)
+
+#endif /* !defined TZFILE_H */
diff --git a/libcutils/tzstrftime.c b/libcutils/tzstrftime.c
new file mode 100644
index 0000000..e37d79a
--- /dev/null
+++ b/libcutils/tzstrftime.c
@@ -0,0 +1,841 @@
+#ifndef lint
+#ifndef NOID
+static char elsieid[] = "@(#)strftime.c 8.1";
+/*
+** Based on the UCB version with the ID appearing below.
+** This is ANSIish only when "multibyte character == plain character".
+*/
+#endif /* !defined NOID */
+#endif /* !defined lint */
+
+#include <time.h>
+#include <tzfile.h>
+#include <limits.h>
+#include <cutils/tztime.h>
+
+/*
+** Copyright (c) 1989 The Regents of the University of California.
+** All rights reserved.
+**
+** Redistribution and use in source and binary forms are permitted
+** provided that the above copyright notice and this paragraph are
+** duplicated in all such forms and that any documentation,
+** advertising materials, and other materials related to such
+** distribution and use acknowledge that the software was developed
+** by the University of California, Berkeley. The name of the
+** University may not be used to endorse or promote products derived
+** from this software without specific prior written permission.
+** THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+** IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+*/
+
+#ifndef LIBC_SCCS
+#ifndef lint
+static const char sccsid[] = "@(#)strftime.c 5.4 (Berkeley) 3/14/89";
+#endif /* !defined lint */
+#endif /* !defined LIBC_SCCS */
+
+#include <ctype.h>
+
+#define P(x) x
+
+static char * _add P((const char *, char *, const char *, int));
+static char * _conv P((int, const char *, char *, const char *));
+static char * _fmt P((const char *, const struct tm *, char *, const char *,
+ int *, const struct strftime_locale *Locale));
+static char * _yconv P((int, int, int, int, char *, const char *, int));
+static char * getformat P((int, char *, char *, char *, char *));
+
+extern char * tzname[];
+
+
+
+
+
+/* from private.h */
+
+#ifndef TYPE_BIT
+#define TYPE_BIT(type) (sizeof (type) * CHAR_BIT)
+#endif /* !defined TYPE_BIT */
+
+#ifndef TYPE_SIGNED
+#define TYPE_SIGNED(type) (((type) -1) < 0)
+#endif /* !defined TYPE_SIGNED */
+
+#ifndef INT_STRLEN_MAXIMUM
+/*
+ * ** 302 / 1000 is log10(2.0) rounded up.
+ * ** Subtract one for the sign bit if the type is signed;
+ * ** add one for integer division truncation;
+ * ** add one more for a minus sign if the type is signed.
+ * */
+#define INT_STRLEN_MAXIMUM(type) \
+ ((TYPE_BIT(type) - TYPE_SIGNED(type)) * 302 / 1000 + \
+ 1 + TYPE_SIGNED(type))
+#endif /* !defined INT_STRLEN_MAXIMUM */
+
+/* end of part from private.h */
+
+
+
+
+#ifndef YEAR_2000_NAME
+#define YEAR_2000_NAME "CHECK_STRFTIME_FORMATS_FOR_TWO_DIGIT_YEARS"
+#endif /* !defined YEAR_2000_NAME */
+
+#define IN_NONE 0
+#define IN_SOME 1
+#define IN_THIS 2
+#define IN_ALL 3
+
+#define FORCE_LOWER_CASE 0x100
+
+size_t
+strftime_tz(s, maxsize, format, t, Locale)
+char * const s;
+const size_t maxsize;
+const char * const format;
+const struct tm * const t;
+const struct strftime_locale *Locale;
+{
+ char * p;
+ int warn;
+
+ warn = IN_NONE;
+ p = _fmt(((format == NULL) ? "%c" : format), t, s, s + maxsize, &warn, Locale);
+#if 0
+ if (warn != IN_NONE && getenv(YEAR_2000_NAME) != NULL) {
+ (void) fprintf(stderr, "\n");
+ if (format == NULL)
+ (void) fprintf(stderr, "NULL strftime format ");
+ else (void) fprintf(stderr, "strftime format \"%s\" ",
+ format);
+ (void) fprintf(stderr, "yields only two digits of years in ");
+ if (warn == IN_SOME)
+ (void) fprintf(stderr, "some locales");
+ else if (warn == IN_THIS)
+ (void) fprintf(stderr, "the current locale");
+ else (void) fprintf(stderr, "all locales");
+ (void) fprintf(stderr, "\n");
+ }
+#endif /* !defined NO_RUN_TIME_WARNINGS_ABOUT_YEAR_2000_PROBLEMS_THANK_YOU */
+ if (p == s + maxsize)
+ return 0;
+ *p = '\0';
+ return p - s;
+}
+
+static char *getformat(int modifier, char *normal, char *underscore,
+ char *dash, char *zero) {
+ switch (modifier) {
+ case '_':
+ return underscore;
+
+ case '-':
+ return dash;
+
+ case '0':
+ return zero;
+ }
+
+ return normal;
+}
+
+static char *
+_fmt(format, t, pt, ptlim, warnp, Locale)
+const char * format;
+const struct tm * const t;
+char * pt;
+const char * const ptlim;
+int * warnp;
+const struct strftime_locale *Locale;
+{
+ for ( ; *format; ++format) {
+ if (*format == '%') {
+ int modifier = 0;
+label:
+ switch (*++format) {
+ case '\0':
+ --format;
+ break;
+ case 'A':
+ pt = _add((t->tm_wday < 0 ||
+ t->tm_wday >= DAYSPERWEEK) ?
+ "?" : Locale->weekday[t->tm_wday],
+ pt, ptlim, modifier);
+ continue;
+ case 'a':
+ pt = _add((t->tm_wday < 0 ||
+ t->tm_wday >= DAYSPERWEEK) ?
+ "?" : Locale->wday[t->tm_wday],
+ pt, ptlim, modifier);
+ continue;
+ case 'B':
+ if (modifier == '-') {
+ pt = _add((t->tm_mon < 0 ||
+ t->tm_mon >= MONSPERYEAR) ?
+ "?" : Locale->standalone_month[t->tm_mon],
+ pt, ptlim, modifier);
+ } else {
+ pt = _add((t->tm_mon < 0 ||
+ t->tm_mon >= MONSPERYEAR) ?
+ "?" : Locale->month[t->tm_mon],
+ pt, ptlim, modifier);
+ }
+ continue;
+ case 'b':
+ case 'h':
+ pt = _add((t->tm_mon < 0 ||
+ t->tm_mon >= MONSPERYEAR) ?
+ "?" : Locale->mon[t->tm_mon],
+ pt, ptlim, modifier);
+ continue;
+ case 'C':
+ /*
+ ** %C used to do a...
+ ** _fmt("%a %b %e %X %Y", t);
+ ** ...whereas now POSIX 1003.2 calls for
+ ** something completely different.
+ ** (ado, 1993-05-24)
+ */
+ pt = _yconv(t->tm_year, TM_YEAR_BASE, 1, 0,
+ pt, ptlim, modifier);
+ continue;
+ case 'c':
+ {
+ int warn2 = IN_SOME;
+
+ pt = _fmt(Locale->c_fmt, t, pt, ptlim, warnp, Locale);
+ if (warn2 == IN_ALL)
+ warn2 = IN_THIS;
+ if (warn2 > *warnp)
+ *warnp = warn2;
+ }
+ continue;
+ case 'D':
+ pt = _fmt("%m/%d/%y", t, pt, ptlim, warnp, Locale);
+ continue;
+ case 'd':
+ pt = _conv(t->tm_mday,
+ getformat(modifier, "%02d",
+ "%2d", "%d", "%02d"),
+ pt, ptlim);
+ continue;
+ case 'E':
+ case 'O':
+ /*
+ ** C99 locale modifiers.
+ ** The sequences
+ ** %Ec %EC %Ex %EX %Ey %EY
+ ** %Od %oe %OH %OI %Om %OM
+ ** %OS %Ou %OU %OV %Ow %OW %Oy
+ ** are supposed to provide alternate
+ ** representations.
+ */
+ goto label;
+ case '_':
+ case '-':
+ case '0':
+ case '^':
+ case '#':
+ modifier = *format;
+ goto label;
+ case 'e':
+ pt = _conv(t->tm_mday,
+ getformat(modifier, "%2d",
+ "%2d", "%d", "%02d"),
+ pt, ptlim);
+ continue;
+ case 'F':
+ pt = _fmt("%Y-%m-%d", t, pt, ptlim, warnp, Locale);
+ continue;
+ case 'H':
+ pt = _conv(t->tm_hour,
+ getformat(modifier, "%02d",
+ "%2d", "%d", "%02d"),
+ pt, ptlim);
+ continue;
+ case 'I':
+ pt = _conv((t->tm_hour % 12) ?
+ (t->tm_hour % 12) : 12,
+ getformat(modifier, "%02d",
+ "%2d", "%d", "%02d"),
+ pt, ptlim);
+ continue;
+ case 'j':
+ pt = _conv(t->tm_yday + 1,
+ getformat(modifier, "%03d", "%3d", "%d", "%03d"),
+ pt, ptlim);
+ continue;
+ case 'k':
+ /*
+ ** This used to be...
+ ** _conv(t->tm_hour % 12 ?
+ ** t->tm_hour % 12 : 12, 2, ' ');
+ ** ...and has been changed to the below to
+ ** match SunOS 4.1.1 and Arnold Robbins'
+ ** strftime version 3.0. That is, "%k" and
+ ** "%l" have been swapped.
+ ** (ado, 1993-05-24)
+ */
+ pt = _conv(t->tm_hour,
+ getformat(modifier, "%2d",
+ "%2d", "%d", "%02d"),
+ pt, ptlim);
+ continue;
+#ifdef KITCHEN_SINK
+ case 'K':
+ /*
+ ** After all this time, still unclaimed!
+ */
+ pt = _add("kitchen sink", pt, ptlim, modifier);
+ continue;
+#endif /* defined KITCHEN_SINK */
+ case 'l':
+ /*
+ ** This used to be...
+ ** _conv(t->tm_hour, 2, ' ');
+ ** ...and has been changed to the below to
+ ** match SunOS 4.1.1 and Arnold Robbin's
+ ** strftime version 3.0. That is, "%k" and
+ ** "%l" have been swapped.
+ ** (ado, 1993-05-24)
+ */
+ pt = _conv((t->tm_hour % 12) ?
+ (t->tm_hour % 12) : 12,
+ getformat(modifier, "%2d",
+ "%2d", "%d", "%02d"),
+ pt, ptlim);
+ continue;
+ case 'M':
+ pt = _conv(t->tm_min,
+ getformat(modifier, "%02d",
+ "%2d", "%d", "%02d"),
+ pt, ptlim);
+ continue;
+ case 'm':
+ pt = _conv(t->tm_mon + 1,
+ getformat(modifier, "%02d",
+ "%2d", "%d", "%02d"),
+ pt, ptlim);
+ continue;
+ case 'n':
+ pt = _add("\n", pt, ptlim, modifier);
+ continue;
+ case 'p':
+ pt = _add((t->tm_hour >= (HOURSPERDAY / 2)) ?
+ Locale->pm :
+ Locale->am,
+ pt, ptlim, modifier);
+ continue;
+ case 'P':
+ pt = _add((t->tm_hour >= (HOURSPERDAY / 2)) ?
+ Locale->pm :
+ Locale->am,
+ pt, ptlim, FORCE_LOWER_CASE);
+ continue;
+ case 'R':
+ pt = _fmt("%H:%M", t, pt, ptlim, warnp, Locale);
+ continue;
+ case 'r':
+ pt = _fmt("%I:%M:%S %p", t, pt, ptlim, warnp, Locale);
+ continue;
+ case 'S':
+ pt = _conv(t->tm_sec,
+ getformat(modifier, "%02d",
+ "%2d", "%d", "%02d"),
+ pt, ptlim);
+ continue;
+ case 's':
+ {
+ struct tm tm;
+ char buf[INT_STRLEN_MAXIMUM(
+ time_t) + 1];
+ time_t mkt;
+
+ tm = *t;
+ mkt = mktime(&tm);
+ if (TYPE_SIGNED(time_t))
+ (void) sprintf(buf, "%ld",
+ (long) mkt);
+ else (void) sprintf(buf, "%lu",
+ (unsigned long) mkt);
+ pt = _add(buf, pt, ptlim, modifier);
+ }
+ continue;
+ case 'T':
+ pt = _fmt("%H:%M:%S", t, pt, ptlim, warnp, Locale);
+ continue;
+ case 't':
+ pt = _add("\t", pt, ptlim, modifier);
+ continue;
+ case 'U':
+ pt = _conv((t->tm_yday + DAYSPERWEEK -
+ t->tm_wday) / DAYSPERWEEK,
+ getformat(modifier, "%02d",
+ "%2d", "%d", "%02d"),
+ pt, ptlim);
+ continue;
+ case 'u':
+ /*
+ ** From Arnold Robbins' strftime version 3.0:
+ ** "ISO 8601: Weekday as a decimal number
+ ** [1 (Monday) - 7]"
+ ** (ado, 1993-05-24)
+ */
+ pt = _conv((t->tm_wday == 0) ?
+ DAYSPERWEEK : t->tm_wday, "%d", pt, ptlim);
+ continue;
+ case 'V': /* ISO 8601 week number */
+ case 'G': /* ISO 8601 year (four digits) */
+ case 'g': /* ISO 8601 year (two digits) */
+/*
+** From Arnold Robbins' strftime version 3.0: "the week number of the
+** year (the first Monday as the first day of week 1) as a decimal number
+** (01-53)."
+** (ado, 1993-05-24)
+**
+** From "http://www.ft.uni-erlangen.de/~mskuhn/iso-time.html" by Markus Kuhn:
+** "Week 01 of a year is per definition the first week which has the
+** Thursday in this year, which is equivalent to the week which contains
+** the fourth day of January. In other words, the first week of a new year
+** is the week which has the majority of its days in the new year. Week 01
+** might also contain days from the previous year and the week before week
+** 01 of a year is the last week (52 or 53) of the previous year even if
+** it contains days from the new year. A week starts with Monday (day 1)
+** and ends with Sunday (day 7). For example, the first week of the year
+** 1997 lasts from 1996-12-30 to 1997-01-05..."
+** (ado, 1996-01-02)
+*/
+ {
+ int year;
+ int base;
+ int yday;
+ int wday;
+ int w;
+
+ year = t->tm_year;
+ base = TM_YEAR_BASE;
+ yday = t->tm_yday;
+ wday = t->tm_wday;
+ for ( ; ; ) {
+ int len;
+ int bot;
+ int top;
+
+ len = isleap_sum(year, base) ?
+ DAYSPERLYEAR :
+ DAYSPERNYEAR;
+ /*
+ ** What yday (-3 ... 3) does
+ ** the ISO year begin on?
+ */
+ bot = ((yday + 11 - wday) %
+ DAYSPERWEEK) - 3;
+ /*
+ ** What yday does the NEXT
+ ** ISO year begin on?
+ */
+ top = bot -
+ (len % DAYSPERWEEK);
+ if (top < -3)
+ top += DAYSPERWEEK;
+ top += len;
+ if (yday >= top) {
+ ++base;
+ w = 1;
+ break;
+ }
+ if (yday >= bot) {
+ w = 1 + ((yday - bot) /
+ DAYSPERWEEK);
+ break;
+ }
+ --base;
+ yday += isleap_sum(year, base) ?
+ DAYSPERLYEAR :
+ DAYSPERNYEAR;
+ }
+#ifdef XPG4_1994_04_09
+ if ((w == 52 &&
+ t->tm_mon == TM_JANUARY) ||
+ (w == 1 &&
+ t->tm_mon == TM_DECEMBER))
+ w = 53;
+#endif /* defined XPG4_1994_04_09 */
+ if (*format == 'V')
+ pt = _conv(w,
+ getformat(modifier,
+ "%02d",
+ "%2d",
+ "%d",
+ "%02d"),
+ pt, ptlim);
+ else if (*format == 'g') {
+ *warnp = IN_ALL;
+ pt = _yconv(year, base, 0, 1,
+ pt, ptlim, modifier);
+ } else pt = _yconv(year, base, 1, 1,
+ pt, ptlim, modifier);
+ }
+ continue;
+ case 'v':
+ /*
+ ** From Arnold Robbins' strftime version 3.0:
+ ** "date as dd-bbb-YYYY"
+ ** (ado, 1993-05-24)
+ */
+ pt = _fmt("%e-%b-%Y", t, pt, ptlim, warnp, Locale);
+ continue;
+ case 'W':
+ pt = _conv((t->tm_yday + DAYSPERWEEK -
+ (t->tm_wday ?
+ (t->tm_wday - 1) :
+ (DAYSPERWEEK - 1))) / DAYSPERWEEK,
+ getformat(modifier, "%02d",
+ "%2d", "%d", "%02d"),
+ pt, ptlim);
+ continue;
+ case 'w':
+ pt = _conv(t->tm_wday, "%d", pt, ptlim);
+ continue;
+ case 'X':
+ pt = _fmt(Locale->X_fmt, t, pt, ptlim, warnp, Locale);
+ continue;
+ case 'x':
+ {
+ int warn2 = IN_SOME;
+
+ pt = _fmt(Locale->x_fmt, t, pt, ptlim, &warn2, Locale);
+ if (warn2 == IN_ALL)
+ warn2 = IN_THIS;
+ if (warn2 > *warnp)
+ *warnp = warn2;
+ }
+ continue;
+ case 'y':
+ *warnp = IN_ALL;
+ pt = _yconv(t->tm_year, TM_YEAR_BASE, 0, 1,
+ pt, ptlim, modifier);
+ continue;
+ case 'Y':
+ pt = _yconv(t->tm_year, TM_YEAR_BASE, 1, 1,
+ pt, ptlim, modifier);
+ continue;
+ case 'Z':
+#ifdef TM_ZONE
+ if (t->TM_ZONE != NULL)
+ pt = _add(t->TM_ZONE, pt, ptlim,
+ modifier);
+ else
+#endif /* defined TM_ZONE */
+ if (t->tm_isdst >= 0)
+ pt = _add(tzname[t->tm_isdst != 0],
+ pt, ptlim, modifier);
+ /*
+ ** C99 says that %Z must be replaced by the
+ ** empty string if the time zone is not
+ ** determinable.
+ */
+ continue;
+ case 'z':
+ {
+ int diff;
+ char const * sign;
+
+ if (t->tm_isdst < 0)
+ continue;
+#ifdef TM_GMTOFF
+ diff = t->TM_GMTOFF;
+#else /* !defined TM_GMTOFF */
+ /*
+ ** C99 says that the UTC offset must
+ ** be computed by looking only at
+ ** tm_isdst. This requirement is
+ ** incorrect, since it means the code
+ ** must rely on magic (in this case
+ ** altzone and timezone), and the
+ ** magic might not have the correct
+ ** offset. Doing things correctly is
+ ** tricky and requires disobeying C99;
+ ** see GNU C strftime for details.
+ ** For now, punt and conform to the
+ ** standard, even though it's incorrect.
+ **
+ ** C99 says that %z must be replaced by the
+ ** empty string if the time zone is not
+ ** determinable, so output nothing if the
+ ** appropriate variables are not available.
+ */
+ if (t->tm_isdst == 0)
+#ifdef USG_COMPAT
+ diff = -timezone;
+#else /* !defined USG_COMPAT */
+ continue;
+#endif /* !defined USG_COMPAT */
+ else
+#ifdef ALTZONE
+ diff = -altzone;
+#else /* !defined ALTZONE */
+ continue;
+#endif /* !defined ALTZONE */
+#endif /* !defined TM_GMTOFF */
+ if (diff < 0) {
+ sign = "-";
+ diff = -diff;
+ } else sign = "+";
+ pt = _add(sign, pt, ptlim, modifier);
+ diff /= SECSPERMIN;
+ diff = (diff / MINSPERHOUR) * 100 +
+ (diff % MINSPERHOUR);
+ pt = _conv(diff,
+ getformat(modifier, "%04d",
+ "%4d", "%d", "%04d"),
+ pt, ptlim);
+ }
+ continue;
+ case '+':
+ pt = _fmt(Locale->date_fmt, t, pt, ptlim,
+ warnp, Locale);
+ continue;
+ case '%':
+ /*
+ ** X311J/88-090 (4.12.3.5): if conversion char is
+ ** undefined, behavior is undefined. Print out the
+ ** character itself as printf(3) also does.
+ */
+ default:
+ break;
+ }
+ }
+ if (pt == ptlim)
+ break;
+ *pt++ = *format;
+ }
+ return pt;
+}
+
+static char *
+_conv(n, format, pt, ptlim)
+const int n;
+const char * const format;
+char * const pt;
+const char * const ptlim;
+{
+ char buf[INT_STRLEN_MAXIMUM(int) + 1];
+
+ (void) sprintf(buf, format, n);
+ return _add(buf, pt, ptlim, 0);
+}
+
+static char *
+_add(str, pt, ptlim, modifier)
+const char * str;
+char * pt;
+const char * const ptlim;
+int modifier;
+{
+ int c;
+
+ switch (modifier) {
+ case FORCE_LOWER_CASE:
+ while (pt < ptlim && (*pt = tolower(*str++)) != '\0') {
+ ++pt;
+ }
+ break;
+
+ case '^':
+ while (pt < ptlim && (*pt = toupper(*str++)) != '\0') {
+ ++pt;
+ }
+ break;
+
+ case '#':
+ while (pt < ptlim && (c = *str++) != '\0') {
+ if (isupper(c)) {
+ c = tolower(c);
+ } else if (islower(c)) {
+ c = toupper(c);
+ }
+ *pt = c;
+ ++pt;
+ }
+
+ break;
+
+ default:
+ while (pt < ptlim && (*pt = *str++) != '\0') {
+ ++pt;
+ }
+ }
+
+ return pt;
+}
+
+/*
+** POSIX and the C Standard are unclear or inconsistent about
+** what %C and %y do if the year is negative or exceeds 9999.
+** Use the convention that %C concatenated with %y yields the
+** same output as %Y, and that %Y contains at least 4 bytes,
+** with more only if necessary.
+*/
+
+static char *
+_yconv(a, b, convert_top, convert_yy, pt, ptlim, modifier)
+const int a;
+const int b;
+const int convert_top;
+const int convert_yy;
+char * pt;
+const char * const ptlim;
+int modifier;
+{
+ register int lead;
+ register int trail;
+
+#define DIVISOR 100
+ trail = a % DIVISOR + b % DIVISOR;
+ lead = a / DIVISOR + b / DIVISOR + trail / DIVISOR;
+ trail %= DIVISOR;
+ if (trail < 0 && lead > 0) {
+ trail += DIVISOR;
+ --lead;
+ } else if (lead < 0 && trail > 0) {
+ trail -= DIVISOR;
+ ++lead;
+ }
+ if (convert_top) {
+ if (lead == 0 && trail < 0)
+ pt = _add("-0", pt, ptlim, modifier);
+ else pt = _conv(lead, getformat(modifier, "%02d",
+ "%2d", "%d", "%02d"),
+ pt, ptlim);
+ }
+ if (convert_yy)
+ pt = _conv(((trail < 0) ? -trail : trail),
+ getformat(modifier, "%02d", "%2d", "%d", "%02d"),
+ pt, ptlim);
+ return pt;
+}
+
+#ifdef LOCALE_HOME
+static struct lc_time_T *
+_loc P((void))
+{
+ static const char locale_home[] = LOCALE_HOME;
+ static const char lc_time[] = "LC_TIME";
+ static char * locale_buf;
+
+ int fd;
+ int oldsun; /* "...ain't got nothin' to do..." */
+ char * lbuf;
+ char * name;
+ char * p;
+ const char ** ap;
+ const char * plim;
+ char filename[FILENAME_MAX];
+ struct stat st;
+ size_t namesize;
+ size_t bufsize;
+
+ /*
+ ** Use localebuf.mon[0] to signal whether locale is already set up.
+ */
+ if (localebuf.mon[0])
+ return &localebuf;
+ name = setlocale(LC_TIME, (char *) NULL);
+ if (name == NULL || *name == '\0')
+ goto no_locale;
+ /*
+ ** If the locale name is the same as our cache, use the cache.
+ */
+ lbuf = locale_buf;
+ if (lbuf != NULL && strcmp(name, lbuf) == 0) {
+ p = lbuf;
+ for (ap = (const char **) &localebuf;
+ ap < (const char **) (&localebuf + 1);
+ ++ap)
+ *ap = p += strlen(p) + 1;
+ return &localebuf;
+ }
+ /*
+ ** Slurp the locale file into the cache.
+ */
+ namesize = strlen(name) + 1;
+ if (sizeof filename <
+ ((sizeof locale_home) + namesize + (sizeof lc_time)))
+ goto no_locale;
+ oldsun = 0;
+ (void) sprintf(filename, "%s/%s/%s", locale_home, name, lc_time);
+ fd = open(filename, O_RDONLY);
+ if (fd < 0) {
+ /*
+ ** Old Sun systems have a different naming and data convention.
+ */
+ oldsun = 1;
+ (void) sprintf(filename, "%s/%s/%s", locale_home,
+ lc_time, name);
+ fd = open(filename, O_RDONLY);
+ if (fd < 0)
+ goto no_locale;
+ }
+ if (fstat(fd, &st) != 0)
+ goto bad_locale;
+ if (st.st_size <= 0)
+ goto bad_locale;
+ bufsize = namesize + st.st_size;
+ locale_buf = NULL;
+ lbuf = (lbuf == NULL) ? malloc(bufsize) : realloc(lbuf, bufsize);
+ if (lbuf == NULL)
+ goto bad_locale;
+ (void) strcpy(lbuf, name);
+ p = lbuf + namesize;
+ plim = p + st.st_size;
+ if (read(fd, p, (size_t) st.st_size) != st.st_size)
+ goto bad_lbuf;
+ if (close(fd) != 0)
+ goto bad_lbuf;
+ /*
+ ** Parse the locale file into localebuf.
+ */
+ if (plim[-1] != '\n')
+ goto bad_lbuf;
+ for (ap = (const char **) &localebuf;
+ ap < (const char **) (&localebuf + 1);
+ ++ap) {
+ if (p == plim)
+ goto bad_lbuf;
+ *ap = p;
+ while (*p != '\n')
+ ++p;
+ *p++ = '\0';
+ }
+ if (oldsun) {
+ /*
+ ** SunOS 4 used an obsolescent format; see localdtconv(3).
+ ** c_fmt had the ``short format for dates and times together''
+ ** (SunOS 4 date, "%a %b %e %T %Z %Y" in the C locale);
+ ** date_fmt had the ``long format for dates''
+ ** (SunOS 4 strftime %C, "%A, %B %e, %Y" in the C locale).
+ ** Discard the latter in favor of the former.
+ */
+ localebuf.date_fmt = localebuf.c_fmt;
+ }
+ /*
+ ** Record the successful parse in the cache.
+ */
+ locale_buf = lbuf;
+
+ return &localebuf;
+
+bad_lbuf:
+ free(lbuf);
+bad_locale:
+ (void) close(fd);
+no_locale:
+ localebuf = C_time_locale;
+ locale_buf = NULL;
+ return &localebuf;
+}
+#endif /* defined LOCALE_HOME */
diff --git a/libcutils/tztime.c b/libcutils/tztime.c
new file mode 100644
index 0000000..d6448a1
--- /dev/null
+++ b/libcutils/tztime.c
@@ -0,0 +1,1950 @@
+/*
+** This file is in the public domain, so clarified as of
+** 1996-06-05 by Arthur David Olson.
+*/
+
+#include <stdio.h>
+
+#ifndef lint
+#ifndef NOID
+static char elsieid[] = "@(#)localtime.c 8.3";
+#endif /* !defined NOID */
+#endif /* !defined lint */
+
+/*
+** Leap second handling from Bradley White.
+** POSIX-style TZ environment variable handling from Guy Harris.
+*/
+
+/*LINTLIBRARY*/
+
+#include "private.h"
+#include "tzfile.h"
+#include "fcntl.h"
+#include "float.h" /* for FLT_MAX and DBL_MAX */
+
+#ifndef TZ_ABBR_MAX_LEN
+#define TZ_ABBR_MAX_LEN 16
+#endif /* !defined TZ_ABBR_MAX_LEN */
+
+#ifndef TZ_ABBR_CHAR_SET
+#define TZ_ABBR_CHAR_SET \
+ "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 :+-._"
+#endif /* !defined TZ_ABBR_CHAR_SET */
+
+#ifndef TZ_ABBR_ERR_CHAR
+#define TZ_ABBR_ERR_CHAR '_'
+#endif /* !defined TZ_ABBR_ERR_CHAR */
+
+#define INDEXFILE "/system/usr/share/zoneinfo/zoneinfo.idx"
+#define DATAFILE "/system/usr/share/zoneinfo/zoneinfo.dat"
+#define NAMELEN 40
+#define INTLEN 4
+#define READLEN (NAMELEN + 3 * INTLEN)
+
+/*
+** SunOS 4.1.1 headers lack O_BINARY.
+*/
+
+#ifdef O_BINARY
+#define OPEN_MODE (O_RDONLY | O_BINARY)
+#endif /* defined O_BINARY */
+#ifndef O_BINARY
+#define OPEN_MODE O_RDONLY
+#endif /* !defined O_BINARY */
+
+/* Complex computations to determine the min/max of time_t depending
+ * on TYPE_BIT / TYPE_SIGNED / TYPE_INTEGRAL.
+ * These macros cannot be used in pre-processor directives, so we
+ * let the C compiler do the work, which makes things a bit funky.
+ */
+static const time_t TIME_T_MAX =
+ TYPE_INTEGRAL(time_t) ?
+ ( TYPE_SIGNED(time_t) ?
+ ~((time_t)1 << (TYPE_BIT(time_t)-1))
+ :
+ ~(time_t)0
+ )
+ : /* if time_t is a floating point number */
+ ( sizeof(time_t) > sizeof(float) ? (time_t)DBL_MAX : (time_t)FLT_MAX );
+
+static const time_t TIME_T_MIN =
+ TYPE_INTEGRAL(time_t) ?
+ ( TYPE_SIGNED(time_t) ?
+ ((time_t)1 << (TYPE_BIT(time_t)-1))
+ :
+ 0
+ )
+ :
+ ( sizeof(time_t) > sizeof(float) ? (time_t)DBL_MIN : (time_t)FLT_MIN );
+
+#ifndef WILDABBR
+/*
+** Someone might make incorrect use of a time zone abbreviation:
+** 1. They might reference tzname[0] before calling tzset (explicitly
+** or implicitly).
+** 2. They might reference tzname[1] before calling tzset (explicitly
+** or implicitly).
+** 3. They might reference tzname[1] after setting to a time zone
+** in which Daylight Saving Time is never observed.
+** 4. They might reference tzname[0] after setting to a time zone
+** in which Standard Time is never observed.
+** 5. They might reference tm.TM_ZONE after calling offtime.
+** What's best to do in the above cases is open to debate;
+** for now, we just set things up so that in any of the five cases
+** WILDABBR is used. Another possibility: initialize tzname[0] to the
+** string "tzname[0] used before set", and similarly for the other cases.
+** And another: initialize tzname[0] to "ERA", with an explanation in the
+** manual page of what this "time zone abbreviation" means (doing this so
+** that tzname[0] has the "normal" length of three characters).
+*/
+#define WILDABBR " "
+#endif /* !defined WILDABBR */
+
+static char wildabbr[] = WILDABBR;
+
+static const char gmt[] = "GMT";
+
+/*
+** The DST rules to use if TZ has no rules and we can't load TZDEFRULES.
+** We default to US rules as of 1999-08-17.
+** POSIX 1003.1 section 8.1.1 says that the default DST rules are
+** implementation dependent; for historical reasons, US rules are a
+** common default.
+*/
+#ifndef TZDEFRULESTRING
+#define TZDEFRULESTRING ",M4.1.0,M10.5.0"
+#endif /* !defined TZDEFDST */
+
+struct ttinfo { /* time type information */
+ long tt_gmtoff; /* UTC offset in seconds */
+ int tt_isdst; /* used to set tm_isdst */
+ int tt_abbrind; /* abbreviation list index */
+ int tt_ttisstd; /* TRUE if transition is std time */
+ int tt_ttisgmt; /* TRUE if transition is UTC */
+};
+
+struct lsinfo { /* leap second information */
+ time_t ls_trans; /* transition time */
+ long ls_corr; /* correction to apply */
+};
+
+#define BIGGEST(a, b) (((a) > (b)) ? (a) : (b))
+
+#ifdef TZNAME_MAX
+#define MY_TZNAME_MAX TZNAME_MAX
+#endif /* defined TZNAME_MAX */
+#ifndef TZNAME_MAX
+#define MY_TZNAME_MAX 255
+#endif /* !defined TZNAME_MAX */
+
+struct state {
+ int leapcnt;
+ int timecnt;
+ int typecnt;
+ int charcnt;
+ int goback;
+ int goahead;
+ time_t ats[TZ_MAX_TIMES];
+ unsigned char types[TZ_MAX_TIMES];
+ struct ttinfo ttis[TZ_MAX_TYPES];
+ char chars[BIGGEST(BIGGEST(TZ_MAX_CHARS + 1, sizeof gmt),
+ (2 * (MY_TZNAME_MAX + 1)))];
+ struct lsinfo lsis[TZ_MAX_LEAPS];
+};
+
+struct rule {
+ int r_type; /* type of rule--see below */
+ int r_day; /* day number of rule */
+ int r_week; /* week number of rule */
+ int r_mon; /* month number of rule */
+ long r_time; /* transition time of rule */
+};
+
+#define JULIAN_DAY 0 /* Jn - Julian day */
+#define DAY_OF_YEAR 1 /* n - day of year */
+#define MONTH_NTH_DAY_OF_WEEK 2 /* Mm.n.d - month, week, day of week */
+
+/*
+** Prototypes for static functions.
+*/
+
+static long detzcode P((const char * codep));
+static time_t detzcode64 P((const char * codep));
+static int differ_by_repeat P((time_t t1, time_t t0));
+static const char * getzname P((const char * strp));
+static const char * getqzname P((const char * strp, const int delim));
+static const char * getnum P((const char * strp, int * nump, int min,
+ int max));
+static const char * getsecs P((const char * strp, long * secsp));
+static const char * getoffset P((const char * strp, long * offsetp));
+static const char * getrule P((const char * strp, struct rule * rulep));
+static void gmtload P((struct state * sp));
+static struct tm * gmtsub P((const time_t * timep, long offset,
+ struct tm * tmp));
+static struct tm * localsub P((const time_t * timep, long offset,
+ struct tm * tmp, const struct state *sp));
+static int increment_overflow P((int * number, int delta));
+static int leaps_thru_end_of P((int y));
+static int long_increment_overflow P((long * number, int delta));
+static int long_normalize_overflow P((long * tensptr,
+ int * unitsptr, int base));
+static int normalize_overflow P((int * tensptr, int * unitsptr,
+ int base));
+static void settzname P((void));
+static time_t time1 P((struct tm * tmp,
+ struct tm * (*funcp) P((const time_t *,
+ long, struct tm *, const struct state* sp)),
+ long offset, const struct state * sp));
+static time_t time2 P((struct tm *tmp,
+ struct tm * (*funcp) P((const time_t *,
+ long, struct tm*, const struct state* sp)),
+ long offset, int * okayp, const struct state * sp));
+static time_t time2sub P((struct tm *tmp,
+ struct tm * (*funcp) P((const time_t*, long, struct tm*,const struct state *sp)),
+ long offset, int * okayp, int do_norm_secs,
+ const struct state *sp));
+static struct tm * timesub P((const time_t * timep, long offset,
+ const struct state * sp, struct tm * tmp));
+static int tmcomp P((const struct tm * atmp,
+ const struct tm * btmp));
+static time_t transtime P((time_t janfirst, int year,
+ const struct rule * rulep, long offset));
+static int tzload P((const char * name, struct state * sp,
+ int doextend));
+static int tzload_uncached P((const char * name, struct state * sp,
+ int doextend));
+static int tzparse P((const char * name, struct state * sp,
+ int lastditch));
+
+#ifdef ALL_STATE
+static struct state * gmtptr;
+#endif /* defined ALL_STATE */
+
+#ifndef ALL_STATE
+static struct state gmtmem;
+#define gmtptr (&gmtmem)
+#endif /* State Farm */
+
+#define CACHE_COUNT 4
+static char * g_cacheNames[CACHE_COUNT] = {0,0};
+static struct state g_cacheStates[CACHE_COUNT];
+static int g_lastCache = 0;
+static struct state g_utc;
+unsigned char g_utcSet = 0;
+
+
+#ifndef TZ_STRLEN_MAX
+#define TZ_STRLEN_MAX 255
+#endif /* !defined TZ_STRLEN_MAX */
+
+static char lcl_TZname[TZ_STRLEN_MAX + 1];
+static int lcl_is_set;
+static int gmt_is_set;
+
+char * tzname[2] = {
+ wildabbr,
+ wildabbr
+};
+
+/*
+** Section 4.12.3 of X3.159-1989 requires that
+** Except for the strftime function, these functions [asctime,
+** ctime, gmtime, localtime] return values in one of two static
+** objects: a broken-down time structure and an array of char.
+** Thanks to Paul Eggert for noting this.
+*/
+
+static struct tm tm;
+
+#ifdef USG_COMPAT
+time_t timezone = 0;
+int daylight = 0;
+#endif /* defined USG_COMPAT */
+
+#ifdef ALTZONE
+time_t altzone = 0;
+#endif /* defined ALTZONE */
+
+static long
+detzcode(codep)
+const char * const codep;
+{
+ register long result;
+ register int i;
+
+ result = (codep[0] & 0x80) ? ~0L : 0;
+ for (i = 0; i < 4; ++i)
+ result = (result << 8) | (codep[i] & 0xff);
+ return result;
+}
+
+static time_t
+detzcode64(codep)
+const char * const codep;
+{
+ register time_t result;
+ register int i;
+
+ result = (codep[0] & 0x80) ? (~(int_fast64_t) 0) : 0;
+ for (i = 0; i < 8; ++i)
+ result = result * 256 + (codep[i] & 0xff);
+ return result;
+}
+
+static int
+differ_by_repeat(t1, t0)
+const time_t t1;
+const time_t t0;
+{
+ if (TYPE_INTEGRAL(time_t) &&
+ TYPE_BIT(time_t) - TYPE_SIGNED(time_t) < SECSPERREPEAT_BITS)
+ return 0;
+ return t1 - t0 == SECSPERREPEAT;
+}
+
+static int toint(unsigned char *s) {
+ return (s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3];
+}
+
+static int
+tzload(const char *name, struct state * const sp, const int doextend)
+{
+ if (name) {
+ int i, err;
+ if (0 == strcmp(name, "UTC")) {
+ if (!g_utcSet) {
+ tzload_uncached(name, &g_utc, 1);
+ g_utcSet = 1;
+ }
+ //printf("tzload: utc\n");
+ *sp = g_utc;
+ return 0;
+ }
+ for (i=0; i<CACHE_COUNT; i++) {
+ if (g_cacheNames[i] && 0 == strcmp(name, g_cacheNames[i])) {
+ *sp = g_cacheStates[i];
+ //printf("tzload: hit: %s\n", name);
+ return 0;
+ }
+ }
+ //printf("tzload: miss: %s\n", name);
+ g_lastCache++;
+ if (g_lastCache >= CACHE_COUNT) {
+ g_lastCache = 0;
+ }
+ i = g_lastCache;
+ if (g_cacheNames[i]) {
+ free(g_cacheNames[i]);
+ }
+ err = tzload_uncached(name, &(g_cacheStates[i]), 1);
+ if (err == 0) {
+ g_cacheNames[i] = strdup(name);
+ *sp = g_cacheStates[i];
+ return 0;
+ } else {
+ g_cacheNames[i] = NULL;
+ return err;
+ }
+ }
+ return tzload_uncached(name, sp, doextend);
+}
+
+static int
+tzload_uncached(name, sp, doextend)
+register const char * name;
+register struct state * const sp;
+register const int doextend;
+{
+ register const char * p;
+ register int i;
+ register int fid;
+ register int stored;
+ register int nread;
+ union {
+ struct tzhead tzhead;
+ char buf[2 * sizeof(struct tzhead) +
+ 2 * sizeof *sp +
+ 4 * TZ_MAX_TIMES];
+ } u;
+ int toread = sizeof u.buf;
+
+ if (name == NULL && (name = TZDEFAULT) == NULL)
+ return -1;
+ {
+ register int doaccess;
+ /*
+ ** Section 4.9.1 of the C standard says that
+ ** "FILENAME_MAX expands to an integral constant expression
+ ** that is the size needed for an array of char large enough
+ ** to hold the longest file name string that the implementation
+ ** guarantees can be opened."
+ */
+ char fullname[FILENAME_MAX + 1];
+ const char *origname = name;
+
+ if (name[0] == ':')
+ ++name;
+ doaccess = name[0] == '/';
+ if (!doaccess) {
+ if ((p = TZDIR) == NULL)
+ return -1;
+ if ((strlen(p) + strlen(name) + 1) >= sizeof fullname)
+ return -1;
+ (void) strcpy(fullname, p);
+ (void) strcat(fullname, "/");
+ (void) strcat(fullname, name);
+ /*
+ ** Set doaccess if '.' (as in "../") shows up in name.
+ */
+ if (strchr(name, '.') != NULL)
+ doaccess = TRUE;
+ name = fullname;
+ }
+ if (doaccess && access(name, R_OK) != 0)
+ return -1;
+ if ((fid = open(name, OPEN_MODE)) == -1) {
+ char buf[READLEN];
+ char name[NAMELEN + 1];
+ int fidix = open(INDEXFILE, OPEN_MODE);
+ int off = -1;
+
+ if (fidix < 0) {
+ return -1;
+ }
+
+ while (read(fidix, buf, sizeof(buf)) == sizeof(buf)) {
+ memcpy(name, buf, NAMELEN);
+ name[NAMELEN] = '\0';
+
+ if (strcmp(name, origname) == 0) {
+ off = toint((unsigned char *) buf + NAMELEN);
+ toread = toint((unsigned char *) buf + NAMELEN + INTLEN);
+ break;
+ }
+ }
+
+ close(fidix);
+
+ if (off < 0)
+ return -1;
+
+ fid = open(DATAFILE, OPEN_MODE);
+
+ if (fid < 0) {
+ return -1;
+ }
+
+ if (lseek(fid, off, SEEK_SET) < 0) {
+ return -1;
+ }
+ }
+ }
+ nread = read(fid, u.buf, toread);
+ if (close(fid) < 0 || nread <= 0)
+ return -1;
+ for (stored = 4; stored <= 8; stored *= 2) {
+ int ttisstdcnt;
+ int ttisgmtcnt;
+
+ ttisstdcnt = (int) detzcode(u.tzhead.tzh_ttisstdcnt);
+ ttisgmtcnt = (int) detzcode(u.tzhead.tzh_ttisgmtcnt);
+ sp->leapcnt = (int) detzcode(u.tzhead.tzh_leapcnt);
+ sp->timecnt = (int) detzcode(u.tzhead.tzh_timecnt);
+ sp->typecnt = (int) detzcode(u.tzhead.tzh_typecnt);
+ sp->charcnt = (int) detzcode(u.tzhead.tzh_charcnt);
+ p = u.tzhead.tzh_charcnt + sizeof u.tzhead.tzh_charcnt;
+ if (sp->leapcnt < 0 || sp->leapcnt > TZ_MAX_LEAPS ||
+ sp->typecnt <= 0 || sp->typecnt > TZ_MAX_TYPES ||
+ sp->timecnt < 0 || sp->timecnt > TZ_MAX_TIMES ||
+ sp->charcnt < 0 || sp->charcnt > TZ_MAX_CHARS ||
+ (ttisstdcnt != sp->typecnt && ttisstdcnt != 0) ||
+ (ttisgmtcnt != sp->typecnt && ttisgmtcnt != 0))
+ return -1;
+ if (nread - (p - u.buf) <
+ sp->timecnt * stored + /* ats */
+ sp->timecnt + /* types */
+ sp->typecnt * 6 + /* ttinfos */
+ sp->charcnt + /* chars */
+ sp->leapcnt * (stored + 4) + /* lsinfos */
+ ttisstdcnt + /* ttisstds */
+ ttisgmtcnt) /* ttisgmts */
+ return -1;
+ for (i = 0; i < sp->timecnt; ++i) {
+ sp->ats[i] = (stored == 4) ?
+ detzcode(p) : detzcode64(p);
+ p += stored;
+ }
+ for (i = 0; i < sp->timecnt; ++i) {
+ sp->types[i] = (unsigned char) *p++;
+ if (sp->types[i] >= sp->typecnt)
+ return -1;
+ }
+ for (i = 0; i < sp->typecnt; ++i) {
+ register struct ttinfo * ttisp;
+
+ ttisp = &sp->ttis[i];
+ ttisp->tt_gmtoff = detzcode(p);
+ p += 4;
+ ttisp->tt_isdst = (unsigned char) *p++;
+ if (ttisp->tt_isdst != 0 && ttisp->tt_isdst != 1)
+ return -1;
+ ttisp->tt_abbrind = (unsigned char) *p++;
+ if (ttisp->tt_abbrind < 0 ||
+ ttisp->tt_abbrind > sp->charcnt)
+ return -1;
+ }
+ for (i = 0; i < sp->charcnt; ++i)
+ sp->chars[i] = *p++;
+ sp->chars[i] = '\0'; /* ensure '\0' at end */
+ for (i = 0; i < sp->leapcnt; ++i) {
+ register struct lsinfo * lsisp;
+
+ lsisp = &sp->lsis[i];
+ lsisp->ls_trans = (stored == 4) ?
+ detzcode(p) : detzcode64(p);
+ p += stored;
+ lsisp->ls_corr = detzcode(p);
+ p += 4;
+ }
+ for (i = 0; i < sp->typecnt; ++i) {
+ register struct ttinfo * ttisp;
+
+ ttisp = &sp->ttis[i];
+ if (ttisstdcnt == 0)
+ ttisp->tt_ttisstd = FALSE;
+ else {
+ ttisp->tt_ttisstd = *p++;
+ if (ttisp->tt_ttisstd != TRUE &&
+ ttisp->tt_ttisstd != FALSE)
+ return -1;
+ }
+ }
+ for (i = 0; i < sp->typecnt; ++i) {
+ register struct ttinfo * ttisp;
+
+ ttisp = &sp->ttis[i];
+ if (ttisgmtcnt == 0)
+ ttisp->tt_ttisgmt = FALSE;
+ else {
+ ttisp->tt_ttisgmt = *p++;
+ if (ttisp->tt_ttisgmt != TRUE &&
+ ttisp->tt_ttisgmt != FALSE)
+ return -1;
+ }
+ }
+ /*
+ ** Out-of-sort ats should mean we're running on a
+ ** signed time_t system but using a data file with
+ ** unsigned values (or vice versa).
+ */
+ for (i = 0; i < sp->timecnt - 2; ++i)
+ if (sp->ats[i] > sp->ats[i + 1]) {
+ ++i;
+ if (TYPE_SIGNED(time_t)) {
+ /*
+ ** Ignore the end (easy).
+ */
+ sp->timecnt = i;
+ } else {
+ /*
+ ** Ignore the beginning (harder).
+ */
+ register int j;
+
+ for (j = 0; j + i < sp->timecnt; ++j) {
+ sp->ats[j] = sp->ats[j + i];
+ sp->types[j] = sp->types[j + i];
+ }
+ sp->timecnt = j;
+ }
+ break;
+ }
+ /*
+ ** If this is an old file, we're done.
+ */
+ if (u.tzhead.tzh_version[0] == '\0')
+ break;
+ nread -= p - u.buf;
+ for (i = 0; i < nread; ++i)
+ u.buf[i] = p[i];
+ /*
+ ** If this is a narrow integer time_t system, we're done.
+ */
+ if (stored >= (int) sizeof(time_t) && TYPE_INTEGRAL(time_t))
+ break;
+ }
+ if (doextend && nread > 2 &&
+ u.buf[0] == '\n' && u.buf[nread - 1] == '\n' &&
+ sp->typecnt + 2 <= TZ_MAX_TYPES) {
+ struct state ts;
+ register int result;
+
+ u.buf[nread - 1] = '\0';
+ result = tzparse(&u.buf[1], &ts, FALSE);
+ if (result == 0 && ts.typecnt == 2 &&
+ sp->charcnt + ts.charcnt <= TZ_MAX_CHARS) {
+ for (i = 0; i < 2; ++i)
+ ts.ttis[i].tt_abbrind +=
+ sp->charcnt;
+ for (i = 0; i < ts.charcnt; ++i)
+ sp->chars[sp->charcnt++] =
+ ts.chars[i];
+ i = 0;
+ while (i < ts.timecnt &&
+ ts.ats[i] <=
+ sp->ats[sp->timecnt - 1])
+ ++i;
+ while (i < ts.timecnt &&
+ sp->timecnt < TZ_MAX_TIMES) {
+ sp->ats[sp->timecnt] =
+ ts.ats[i];
+ sp->types[sp->timecnt] =
+ sp->typecnt +
+ ts.types[i];
+ ++sp->timecnt;
+ ++i;
+ }
+ sp->ttis[sp->typecnt++] = ts.ttis[0];
+ sp->ttis[sp->typecnt++] = ts.ttis[1];
+ }
+ }
+ i = 2 * YEARSPERREPEAT;
+ sp->goback = sp->goahead = sp->timecnt > i;
+ sp->goback &= sp->types[i] == sp->types[0] &&
+ differ_by_repeat(sp->ats[i], sp->ats[0]);
+ sp->goahead &=
+ sp->types[sp->timecnt - 1] == sp->types[sp->timecnt - 1 - i] &&
+ differ_by_repeat(sp->ats[sp->timecnt - 1],
+ sp->ats[sp->timecnt - 1 - i]);
+ return 0;
+}
+
+static const int mon_lengths[2][MONSPERYEAR] = {
+ { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
+ { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
+};
+
+static const int year_lengths[2] = {
+ DAYSPERNYEAR, DAYSPERLYEAR
+};
+
+/*
+** Given a pointer into a time zone string, scan until a character that is not
+** a valid character in a zone name is found. Return a pointer to that
+** character.
+*/
+
+static const char *
+getzname(strp)
+register const char * strp;
+{
+ register char c;
+
+ while ((c = *strp) != '\0' && !is_digit(c) && c != ',' && c != '-' &&
+ c != '+')
+ ++strp;
+ return strp;
+}
+
+/*
+** Given a pointer into an extended time zone string, scan until the ending
+** delimiter of the zone name is located. Return a pointer to the delimiter.
+**
+** As with getzname above, the legal character set is actually quite
+** restricted, with other characters producing undefined results.
+** We don't do any checking here; checking is done later in common-case code.
+*/
+
+static const char *
+getqzname(register const char *strp, const int delim)
+{
+ register int c;
+
+ while ((c = *strp) != '\0' && c != delim)
+ ++strp;
+ return strp;
+}
+
+/*
+** Given a pointer into a time zone string, extract a number from that string.
+** Check that the number is within a specified range; if it is not, return
+** NULL.
+** Otherwise, return a pointer to the first character not part of the number.
+*/
+
+static const char *
+getnum(strp, nump, min, max)
+register const char * strp;
+int * const nump;
+const int min;
+const int max;
+{
+ register char c;
+ register int num;
+
+ if (strp == NULL || !is_digit(c = *strp))
+ return NULL;
+ num = 0;
+ do {
+ num = num * 10 + (c - '0');
+ if (num > max)
+ return NULL; /* illegal value */
+ c = *++strp;
+ } while (is_digit(c));
+ if (num < min)
+ return NULL; /* illegal value */
+ *nump = num;
+ return strp;
+}
+
+/*
+** Given a pointer into a time zone string, extract a number of seconds,
+** in hh[:mm[:ss]] form, from the string.
+** If any error occurs, return NULL.
+** Otherwise, return a pointer to the first character not part of the number
+** of seconds.
+*/
+
+static const char *
+getsecs(strp, secsp)
+register const char * strp;
+long * const secsp;
+{
+ int num;
+
+ /*
+ ** `HOURSPERDAY * DAYSPERWEEK - 1' allows quasi-Posix rules like
+ ** "M10.4.6/26", which does not conform to Posix,
+ ** but which specifies the equivalent of
+ ** ``02:00 on the first Sunday on or after 23 Oct''.
+ */
+ strp = getnum(strp, &num, 0, HOURSPERDAY * DAYSPERWEEK - 1);
+ if (strp == NULL)
+ return NULL;
+ *secsp = num * (long) SECSPERHOUR;
+ if (*strp == ':') {
+ ++strp;
+ strp = getnum(strp, &num, 0, MINSPERHOUR - 1);
+ if (strp == NULL)
+ return NULL;
+ *secsp += num * SECSPERMIN;
+ if (*strp == ':') {
+ ++strp;
+ /* `SECSPERMIN' allows for leap seconds. */
+ strp = getnum(strp, &num, 0, SECSPERMIN);
+ if (strp == NULL)
+ return NULL;
+ *secsp += num;
+ }
+ }
+ return strp;
+}
+
+/*
+** Given a pointer into a time zone string, extract an offset, in
+** [+-]hh[:mm[:ss]] form, from the string.
+** If any error occurs, return NULL.
+** Otherwise, return a pointer to the first character not part of the time.
+*/
+
+static const char *
+getoffset(strp, offsetp)
+register const char * strp;
+long * const offsetp;
+{
+ register int neg = 0;
+
+ if (*strp == '-') {
+ neg = 1;
+ ++strp;
+ } else if (*strp == '+')
+ ++strp;
+ strp = getsecs(strp, offsetp);
+ if (strp == NULL)
+ return NULL; /* illegal time */
+ if (neg)
+ *offsetp = -*offsetp;
+ return strp;
+}
+
+/*
+** Given a pointer into a time zone string, extract a rule in the form
+** date[/time]. See POSIX section 8 for the format of "date" and "time".
+** If a valid rule is not found, return NULL.
+** Otherwise, return a pointer to the first character not part of the rule.
+*/
+
+static const char *
+getrule(strp, rulep)
+const char * strp;
+register struct rule * const rulep;
+{
+ if (*strp == 'J') {
+ /*
+ ** Julian day.
+ */
+ rulep->r_type = JULIAN_DAY;
+ ++strp;
+ strp = getnum(strp, &rulep->r_day, 1, DAYSPERNYEAR);
+ } else if (*strp == 'M') {
+ /*
+ ** Month, week, day.
+ */
+ rulep->r_type = MONTH_NTH_DAY_OF_WEEK;
+ ++strp;
+ strp = getnum(strp, &rulep->r_mon, 1, MONSPERYEAR);
+ if (strp == NULL)
+ return NULL;
+ if (*strp++ != '.')
+ return NULL;
+ strp = getnum(strp, &rulep->r_week, 1, 5);
+ if (strp == NULL)
+ return NULL;
+ if (*strp++ != '.')
+ return NULL;
+ strp = getnum(strp, &rulep->r_day, 0, DAYSPERWEEK - 1);
+ } else if (is_digit(*strp)) {
+ /*
+ ** Day of year.
+ */
+ rulep->r_type = DAY_OF_YEAR;
+ strp = getnum(strp, &rulep->r_day, 0, DAYSPERLYEAR - 1);
+ } else return NULL; /* invalid format */
+ if (strp == NULL)
+ return NULL;
+ if (*strp == '/') {
+ /*
+ ** Time specified.
+ */
+ ++strp;
+ strp = getsecs(strp, &rulep->r_time);
+ } else rulep->r_time = 2 * SECSPERHOUR; /* default = 2:00:00 */
+ return strp;
+}
+
+/*
+** Given the Epoch-relative time of January 1, 00:00:00 UTC, in a year, the
+** year, a rule, and the offset from UTC at the time that rule takes effect,
+** calculate the Epoch-relative time that rule takes effect.
+*/
+
+static time_t
+transtime(janfirst, year, rulep, offset)
+const time_t janfirst;
+const int year;
+register const struct rule * const rulep;
+const long offset;
+{
+ register int leapyear;
+ register time_t value;
+ register int i;
+ int d, m1, yy0, yy1, yy2, dow;
+
+ INITIALIZE(value);
+ leapyear = isleap(year);
+ switch (rulep->r_type) {
+
+ case JULIAN_DAY:
+ /*
+ ** Jn - Julian day, 1 == January 1, 60 == March 1 even in leap
+ ** years.
+ ** In non-leap years, or if the day number is 59 or less, just
+ ** add SECSPERDAY times the day number-1 to the time of
+ ** January 1, midnight, to get the day.
+ */
+ value = janfirst + (rulep->r_day - 1) * SECSPERDAY;
+ if (leapyear && rulep->r_day >= 60)
+ value += SECSPERDAY;
+ break;
+
+ case DAY_OF_YEAR:
+ /*
+ ** n - day of year.
+ ** Just add SECSPERDAY times the day number to the time of
+ ** January 1, midnight, to get the day.
+ */
+ value = janfirst + rulep->r_day * SECSPERDAY;
+ break;
+
+ case MONTH_NTH_DAY_OF_WEEK:
+ /*
+ ** Mm.n.d - nth "dth day" of month m.
+ */
+ value = janfirst;
+ for (i = 0; i < rulep->r_mon - 1; ++i)
+ value += mon_lengths[leapyear][i] * SECSPERDAY;
+
+ /*
+ ** Use Zeller's Congruence to get day-of-week of first day of
+ ** month.
+ */
+ m1 = (rulep->r_mon + 9) % 12 + 1;
+ yy0 = (rulep->r_mon <= 2) ? (year - 1) : year;
+ yy1 = yy0 / 100;
+ yy2 = yy0 % 100;
+ dow = ((26 * m1 - 2) / 10 +
+ 1 + yy2 + yy2 / 4 + yy1 / 4 - 2 * yy1) % 7;
+ if (dow < 0)
+ dow += DAYSPERWEEK;
+
+ /*
+ ** "dow" is the day-of-week of the first day of the month. Get
+ ** the day-of-month (zero-origin) of the first "dow" day of the
+ ** month.
+ */
+ d = rulep->r_day - dow;
+ if (d < 0)
+ d += DAYSPERWEEK;
+ for (i = 1; i < rulep->r_week; ++i) {
+ if (d + DAYSPERWEEK >=
+ mon_lengths[leapyear][rulep->r_mon - 1])
+ break;
+ d += DAYSPERWEEK;
+ }
+
+ /*
+ ** "d" is the day-of-month (zero-origin) of the day we want.
+ */
+ value += d * SECSPERDAY;
+ break;
+ }
+
+ /*
+ ** "value" is the Epoch-relative time of 00:00:00 UTC on the day in
+ ** question. To get the Epoch-relative time of the specified local
+ ** time on that day, add the transition time and the current offset
+ ** from UTC.
+ */
+ return value + rulep->r_time + offset;
+}
+
+/*
+** Given a POSIX section 8-style TZ string, fill in the rule tables as
+** appropriate.
+*/
+
+static int
+tzparse(name, sp, lastditch)
+const char * name;
+register struct state * const sp;
+const int lastditch;
+{
+ const char * stdname;
+ const char * dstname;
+ size_t stdlen;
+ size_t dstlen;
+ long stdoffset;
+ long dstoffset;
+ register time_t * atp;
+ register unsigned char * typep;
+ register char * cp;
+ register int load_result;
+
+ INITIALIZE(dstname);
+ stdname = name;
+ if (lastditch) {
+ stdlen = strlen(name); /* length of standard zone name */
+ name += stdlen;
+ if (stdlen >= sizeof sp->chars)
+ stdlen = (sizeof sp->chars) - 1;
+ stdoffset = 0;
+ } else {
+ if (*name == '<') {
+ name++;
+ stdname = name;
+ name = getqzname(name, '>');
+ if (*name != '>')
+ return (-1);
+ stdlen = name - stdname;
+ name++;
+ } else {
+ name = getzname(name);
+ stdlen = name - stdname;
+ }
+ if (*name == '\0')
+ return -1;
+ name = getoffset(name, &stdoffset);
+ if (name == NULL)
+ return -1;
+ }
+ load_result = tzload(TZDEFRULES, sp, FALSE);
+ if (load_result != 0)
+ sp->leapcnt = 0; /* so, we're off a little */
+ sp->timecnt = 0;
+ if (*name != '\0') {
+ if (*name == '<') {
+ dstname = ++name;
+ name = getqzname(name, '>');
+ if (*name != '>')
+ return -1;
+ dstlen = name - dstname;
+ name++;
+ } else {
+ dstname = name;
+ name = getzname(name);
+ dstlen = name - dstname; /* length of DST zone name */
+ }
+ if (*name != '\0' && *name != ',' && *name != ';') {
+ name = getoffset(name, &dstoffset);
+ if (name == NULL)
+ return -1;
+ } else dstoffset = stdoffset - SECSPERHOUR;
+ if (*name == '\0' && load_result != 0)
+ name = TZDEFRULESTRING;
+ if (*name == ',' || *name == ';') {
+ struct rule start;
+ struct rule end;
+ register int year;
+ register time_t janfirst;
+ time_t starttime;
+ time_t endtime;
+
+ ++name;
+ if ((name = getrule(name, &start)) == NULL)
+ return -1;
+ if (*name++ != ',')
+ return -1;
+ if ((name = getrule(name, &end)) == NULL)
+ return -1;
+ if (*name != '\0')
+ return -1;
+ sp->typecnt = 2; /* standard time and DST */
+ /*
+ ** Two transitions per year, from EPOCH_YEAR forward.
+ */
+ sp->ttis[0].tt_gmtoff = -dstoffset;
+ sp->ttis[0].tt_isdst = 1;
+ sp->ttis[0].tt_abbrind = stdlen + 1;
+ sp->ttis[1].tt_gmtoff = -stdoffset;
+ sp->ttis[1].tt_isdst = 0;
+ sp->ttis[1].tt_abbrind = 0;
+ atp = sp->ats;
+ typep = sp->types;
+ janfirst = 0;
+ for (year = EPOCH_YEAR;
+ sp->timecnt + 2 <= TZ_MAX_TIMES;
+ ++year) {
+ time_t newfirst;
+
+ starttime = transtime(janfirst, year, &start,
+ stdoffset);
+ endtime = transtime(janfirst, year, &end,
+ dstoffset);
+ if (starttime > endtime) {
+ *atp++ = endtime;
+ *typep++ = 1; /* DST ends */
+ *atp++ = starttime;
+ *typep++ = 0; /* DST begins */
+ } else {
+ *atp++ = starttime;
+ *typep++ = 0; /* DST begins */
+ *atp++ = endtime;
+ *typep++ = 1; /* DST ends */
+ }
+ sp->timecnt += 2;
+ newfirst = janfirst;
+ newfirst += year_lengths[isleap(year)] *
+ SECSPERDAY;
+ if (newfirst <= janfirst)
+ break;
+ janfirst = newfirst;
+ }
+ } else {
+ register long theirstdoffset;
+ register long theirdstoffset;
+ register long theiroffset;
+ register int isdst;
+ register int i;
+ register int j;
+
+ if (*name != '\0')
+ return -1;
+ /*
+ ** Initial values of theirstdoffset and theirdstoffset.
+ */
+ theirstdoffset = 0;
+ for (i = 0; i < sp->timecnt; ++i) {
+ j = sp->types[i];
+ if (!sp->ttis[j].tt_isdst) {
+ theirstdoffset =
+ -sp->ttis[j].tt_gmtoff;
+ break;
+ }
+ }
+ theirdstoffset = 0;
+ for (i = 0; i < sp->timecnt; ++i) {
+ j = sp->types[i];
+ if (sp->ttis[j].tt_isdst) {
+ theirdstoffset =
+ -sp->ttis[j].tt_gmtoff;
+ break;
+ }
+ }
+ /*
+ ** Initially we're assumed to be in standard time.
+ */
+ isdst = FALSE;
+ theiroffset = theirstdoffset;
+ /*
+ ** Now juggle transition times and types
+ ** tracking offsets as you do.
+ */
+ for (i = 0; i < sp->timecnt; ++i) {
+ j = sp->types[i];
+ sp->types[i] = sp->ttis[j].tt_isdst;
+ if (sp->ttis[j].tt_ttisgmt) {
+ /* No adjustment to transition time */
+ } else {
+ /*
+ ** If summer time is in effect, and the
+ ** transition time was not specified as
+ ** standard time, add the summer time
+ ** offset to the transition time;
+ ** otherwise, add the standard time
+ ** offset to the transition time.
+ */
+ /*
+ ** Transitions from DST to DDST
+ ** will effectively disappear since
+ ** POSIX provides for only one DST
+ ** offset.
+ */
+ if (isdst && !sp->ttis[j].tt_ttisstd) {
+ sp->ats[i] += dstoffset -
+ theirdstoffset;
+ } else {
+ sp->ats[i] += stdoffset -
+ theirstdoffset;
+ }
+ }
+ theiroffset = -sp->ttis[j].tt_gmtoff;
+ if (sp->ttis[j].tt_isdst)
+ theirdstoffset = theiroffset;
+ else theirstdoffset = theiroffset;
+ }
+ /*
+ ** Finally, fill in ttis.
+ ** ttisstd and ttisgmt need not be handled.
+ */
+ sp->ttis[0].tt_gmtoff = -stdoffset;
+ sp->ttis[0].tt_isdst = FALSE;
+ sp->ttis[0].tt_abbrind = 0;
+ sp->ttis[1].tt_gmtoff = -dstoffset;
+ sp->ttis[1].tt_isdst = TRUE;
+ sp->ttis[1].tt_abbrind = stdlen + 1;
+ sp->typecnt = 2;
+ }
+ } else {
+ dstlen = 0;
+ sp->typecnt = 1; /* only standard time */
+ sp->timecnt = 0;
+ sp->ttis[0].tt_gmtoff = -stdoffset;
+ sp->ttis[0].tt_isdst = 0;
+ sp->ttis[0].tt_abbrind = 0;
+ }
+ sp->charcnt = stdlen + 1;
+ if (dstlen != 0)
+ sp->charcnt += dstlen + 1;
+ if ((size_t) sp->charcnt > sizeof sp->chars)
+ return -1;
+ cp = sp->chars;
+ (void) strncpy(cp, stdname, stdlen);
+ cp += stdlen;
+ *cp++ = '\0';
+ if (dstlen != 0) {
+ (void) strncpy(cp, dstname, dstlen);
+ *(cp + dstlen) = '\0';
+ }
+ return 0;
+}
+
+static void
+gmtload(sp)
+struct state * const sp;
+{
+ if (tzload(gmt, sp, TRUE) != 0)
+ (void) tzparse(gmt, sp, TRUE);
+}
+
+/*
+** The easy way to behave "as if no library function calls" localtime
+** is to not call it--so we drop its guts into "localsub", which can be
+** freely called. (And no, the PANS doesn't require the above behavior--
+** but it *is* desirable.)
+**
+** The unused offset argument is for the benefit of mktime variants.
+*/
+
+/*ARGSUSED*/
+static struct tm *
+localsub(timep, offset, tmp, sp)
+const time_t * const timep;
+const long offset;
+struct tm * const tmp;
+const struct state * sp;
+{
+ register const struct ttinfo * ttisp;
+ register int i;
+ register struct tm * result;
+ const time_t t = *timep;
+
+#ifdef ALL_STATE
+ if (sp == NULL)
+ return gmtsub(timep, offset, tmp);
+#endif /* defined ALL_STATE */
+ if ((sp->goback && t < sp->ats[0]) ||
+ (sp->goahead && t > sp->ats[sp->timecnt - 1])) {
+ time_t newt = t;
+ register time_t seconds;
+ register time_t tcycles;
+ register int_fast64_t icycles;
+
+ if (t < sp->ats[0])
+ seconds = sp->ats[0] - t;
+ else seconds = t - sp->ats[sp->timecnt - 1];
+ --seconds;
+ tcycles = seconds / YEARSPERREPEAT / AVGSECSPERYEAR;
+ ++tcycles;
+ icycles = tcycles;
+ if (tcycles - icycles >= 1 || icycles - tcycles >= 1)
+ return NULL;
+ seconds = icycles;
+ seconds *= YEARSPERREPEAT;
+ seconds *= AVGSECSPERYEAR;
+ if (t < sp->ats[0])
+ newt += seconds;
+ else newt -= seconds;
+ if (newt < sp->ats[0] ||
+ newt > sp->ats[sp->timecnt - 1])
+ return NULL; /* "cannot happen" */
+ result = localsub(&newt, offset, tmp, sp);
+ if (result == tmp) {
+ register time_t newy;
+
+ newy = tmp->tm_year;
+ if (t < sp->ats[0])
+ newy -= icycles * YEARSPERREPEAT;
+ else newy += icycles * YEARSPERREPEAT;
+ tmp->tm_year = newy;
+ if (tmp->tm_year != newy)
+ return NULL;
+ }
+ return result;
+ }
+ if (sp->timecnt == 0 || t < sp->ats[0]) {
+ i = 0;
+ while (sp->ttis[i].tt_isdst)
+ if (++i >= sp->typecnt) {
+ i = 0;
+ break;
+ }
+ } else {
+ register int lo = 1;
+ register int hi = sp->timecnt;
+
+ while (lo < hi) {
+ register int mid = (lo + hi) >> 1;
+
+ if (t < sp->ats[mid])
+ hi = mid;
+ else lo = mid + 1;
+ }
+ i = (int) sp->types[lo - 1];
+ }
+ ttisp = &sp->ttis[i];
+ /*
+ ** To get (wrong) behavior that's compatible with System V Release 2.0
+ ** you'd replace the statement below with
+ ** t += ttisp->tt_gmtoff;
+ ** timesub(&t, 0L, sp, tmp);
+ */
+ result = timesub(&t, ttisp->tt_gmtoff, sp, tmp);
+ tmp->tm_isdst = ttisp->tt_isdst;
+#ifdef HAVE_TM_GMTOFF
+ tmp->tm_gmtoff = ttisp->tt_gmtoff;
+#endif
+ tzname[tmp->tm_isdst] = &sp->chars[ttisp->tt_abbrind];
+#ifdef TM_ZONE
+ tmp->TM_ZONE = &sp->chars[ttisp->tt_abbrind];
+#endif /* defined TM_ZONE */
+ return result;
+}
+
+
+// ============================================================================
+#if 0
+struct tm *
+localtime(timep)
+const time_t * const timep;
+{
+ tzset();
+ return localsub(timep, 0L, &tm);
+}
+#endif
+
+/*
+** Re-entrant version of localtime.
+*/
+
+// ============================================================================
+void
+localtime_tz(const time_t * const timep, struct tm * tmp, const char* tz)
+{
+ struct state st;
+ if (tzload(tz, &st, TRUE) != 0) {
+ // not sure what's best here, but for now, we fall back to gmt
+ gmtload(&st);
+ }
+
+ localsub(timep, 0L, tmp, &st);
+}
+
+/*
+** gmtsub is to gmtime as localsub is to localtime.
+*/
+
+static struct tm *
+gmtsub(timep, offset, tmp)
+const time_t * const timep;
+const long offset;
+struct tm * const tmp;
+{
+ register struct tm * result;
+
+ if (!gmt_is_set) {
+ gmt_is_set = TRUE;
+#ifdef ALL_STATE
+ gmtptr = (struct state *) malloc(sizeof *gmtptr);
+ if (gmtptr != NULL)
+#endif /* defined ALL_STATE */
+ gmtload(gmtptr);
+ }
+ result = timesub(timep, offset, gmtptr, tmp);
+#ifdef TM_ZONE
+ /*
+ ** Could get fancy here and deliver something such as
+ ** "UTC+xxxx" or "UTC-xxxx" if offset is non-zero,
+ ** but this is no time for a treasure hunt.
+ */
+ if (offset != 0)
+ tmp->TM_ZONE = wildabbr;
+ else {
+#ifdef ALL_STATE
+ if (gmtptr == NULL)
+ tmp->TM_ZONE = gmt;
+ else tmp->TM_ZONE = gmtptr->chars;
+#endif /* defined ALL_STATE */
+#ifndef ALL_STATE
+ tmp->TM_ZONE = gmtptr->chars;
+#endif /* State Farm */
+ }
+#endif /* defined TM_ZONE */
+ return result;
+}
+
+// ============================================================================
+#if 0
+struct tm *
+gmtime(timep)
+const time_t * const timep;
+{
+ return gmtsub(timep, 0L, &tm);
+}
+#endif
+
+/*
+* Re-entrant version of gmtime.
+*/
+
+// ============================================================================
+#if 0
+struct tm *
+gmtime_r(timep, tmp)
+const time_t * const timep;
+struct tm * tmp;
+{
+ return gmtsub(timep, 0L, tmp);
+}
+#endif
+
+#ifdef STD_INSPIRED
+
+// ============================================================================
+#if 0
+struct tm *
+offtime(timep, offset)
+const time_t * const timep;
+const long offset;
+{
+ return gmtsub(timep, offset, &tm);
+}
+#endif
+
+#endif /* defined STD_INSPIRED */
+
+/*
+** Return the number of leap years through the end of the given year
+** where, to make the math easy, the answer for year zero is defined as zero.
+*/
+
+static int
+leaps_thru_end_of(y)
+register const int y;
+{
+ return (y >= 0) ? (y / 4 - y / 100 + y / 400) :
+ -(leaps_thru_end_of(-(y + 1)) + 1);
+}
+
+static struct tm *
+timesub(timep, offset, sp, tmp)
+const time_t * const timep;
+const long offset;
+register const struct state * const sp;
+register struct tm * const tmp;
+{
+ register const struct lsinfo * lp;
+ register time_t tdays;
+ register int idays; /* unsigned would be so 2003 */
+ register long rem;
+ int y;
+ register const int * ip;
+ register long corr;
+ register int hit;
+ register int i;
+
+ corr = 0;
+ hit = 0;
+#ifdef ALL_STATE
+ i = (sp == NULL) ? 0 : sp->leapcnt;
+#endif /* defined ALL_STATE */
+#ifndef ALL_STATE
+ i = sp->leapcnt;
+#endif /* State Farm */
+ while (--i >= 0) {
+ lp = &sp->lsis[i];
+ if (*timep >= lp->ls_trans) {
+ if (*timep == lp->ls_trans) {
+ hit = ((i == 0 && lp->ls_corr > 0) ||
+ lp->ls_corr > sp->lsis[i - 1].ls_corr);
+ if (hit)
+ while (i > 0 &&
+ sp->lsis[i].ls_trans ==
+ sp->lsis[i - 1].ls_trans + 1 &&
+ sp->lsis[i].ls_corr ==
+ sp->lsis[i - 1].ls_corr + 1) {
+ ++hit;
+ --i;
+ }
+ }
+ corr = lp->ls_corr;
+ break;
+ }
+ }
+ y = EPOCH_YEAR;
+ tdays = *timep / SECSPERDAY;
+ rem = *timep - tdays * SECSPERDAY;
+ while (tdays < 0 || tdays >= year_lengths[isleap(y)]) {
+ int newy;
+ register time_t tdelta;
+ register int idelta;
+ register int leapdays;
+
+ tdelta = tdays / DAYSPERLYEAR;
+ idelta = tdelta;
+ if (tdelta - idelta >= 1 || idelta - tdelta >= 1)
+ return NULL;
+ if (idelta == 0)
+ idelta = (tdays < 0) ? -1 : 1;
+ newy = y;
+ if (increment_overflow(&newy, idelta))
+ return NULL;
+ leapdays = leaps_thru_end_of(newy - 1) -
+ leaps_thru_end_of(y - 1);
+ tdays -= ((time_t) newy - y) * DAYSPERNYEAR;
+ tdays -= leapdays;
+ y = newy;
+ }
+ {
+ register long seconds;
+
+ seconds = tdays * SECSPERDAY + 0.5;
+ tdays = seconds / SECSPERDAY;
+ rem += seconds - tdays * SECSPERDAY;
+ }
+ /*
+ ** Given the range, we can now fearlessly cast...
+ */
+ idays = tdays;
+ rem += offset - corr;
+ while (rem < 0) {
+ rem += SECSPERDAY;
+ --idays;
+ }
+ while (rem >= SECSPERDAY) {
+ rem -= SECSPERDAY;
+ ++idays;
+ }
+ while (idays < 0) {
+ if (increment_overflow(&y, -1))
+ return NULL;
+ idays += year_lengths[isleap(y)];
+ }
+ while (idays >= year_lengths[isleap(y)]) {
+ idays -= year_lengths[isleap(y)];
+ if (increment_overflow(&y, 1))
+ return NULL;
+ }
+ tmp->tm_year = y;
+ if (increment_overflow(&tmp->tm_year, -TM_YEAR_BASE))
+ return NULL;
+ tmp->tm_yday = idays;
+ /*
+ ** The "extra" mods below avoid overflow problems.
+ */
+ tmp->tm_wday = EPOCH_WDAY +
+ ((y - EPOCH_YEAR) % DAYSPERWEEK) *
+ (DAYSPERNYEAR % DAYSPERWEEK) +
+ leaps_thru_end_of(y - 1) -
+ leaps_thru_end_of(EPOCH_YEAR - 1) +
+ idays;
+ tmp->tm_wday %= DAYSPERWEEK;
+ if (tmp->tm_wday < 0)
+ tmp->tm_wday += DAYSPERWEEK;
+ tmp->tm_hour = (int) (rem / SECSPERHOUR);
+ rem %= SECSPERHOUR;
+ tmp->tm_min = (int) (rem / SECSPERMIN);
+ /*
+ ** A positive leap second requires a special
+ ** representation. This uses "... ??:59:60" et seq.
+ */
+ tmp->tm_sec = (int) (rem % SECSPERMIN) + hit;
+ ip = mon_lengths[isleap(y)];
+ for (tmp->tm_mon = 0; idays >= ip[tmp->tm_mon]; ++(tmp->tm_mon))
+ idays -= ip[tmp->tm_mon];
+ tmp->tm_mday = (int) (idays + 1);
+ tmp->tm_isdst = 0;
+#ifdef TM_GMTOFF
+ tmp->TM_GMTOFF = offset;
+#endif /* defined TM_GMTOFF */
+ return tmp;
+}
+
+// ============================================================================
+#if 0
+char *
+ctime(timep)
+const time_t * const timep;
+{
+/*
+** Section 4.12.3.2 of X3.159-1989 requires that
+** The ctime function converts the calendar time pointed to by timer
+** to local time in the form of a string. It is equivalent to
+** asctime(localtime(timer))
+*/
+ return asctime(localtime(timep));
+}
+#endif
+
+// ============================================================================
+#if 0
+char *
+ctime_r(timep, buf)
+const time_t * const timep;
+char * buf;
+{
+ struct tm mytm;
+
+ return asctime_r(localtime_r(timep, &mytm), buf);
+}
+#endif
+
+/*
+** Adapted from code provided by Robert Elz, who writes:
+** The "best" way to do mktime I think is based on an idea of Bob
+** Kridle's (so its said...) from a long time ago.
+** It does a binary search of the time_t space. Since time_t's are
+** just 32 bits, its a max of 32 iterations (even at 64 bits it
+** would still be very reasonable).
+*/
+
+#ifndef WRONG
+#define WRONG (-1)
+#endif /* !defined WRONG */
+
+/*
+** Simplified normalize logic courtesy Paul Eggert.
+*/
+
+static int
+increment_overflow(number, delta)
+int * number;
+int delta;
+{
+ unsigned number0 = (unsigned)*number;
+ unsigned number1 = (unsigned)(number0 + delta);
+
+ *number = (int)number1;
+
+ if (delta >= 0) {
+ return ((int)number1 < (int)number0);
+ } else {
+ return ((int)number1 > (int)number0);
+ }
+}
+
+static int
+long_increment_overflow(number, delta)
+long * number;
+int delta;
+{
+ unsigned long number0 = (unsigned long)*number;
+ unsigned long number1 = (unsigned long)(number0 + delta);
+
+ *number = (long)number1;
+
+ if (delta >= 0) {
+ return ((long)number1 < (long)number0);
+ } else {
+ return ((long)number1 > (long)number0);
+ }
+}
+
+static int
+normalize_overflow(tensptr, unitsptr, base)
+int * const tensptr;
+int * const unitsptr;
+const int base;
+{
+ register int tensdelta;
+
+ tensdelta = (*unitsptr >= 0) ?
+ (*unitsptr / base) :
+ (-1 - (-1 - *unitsptr) / base);
+ *unitsptr -= tensdelta * base;
+ return increment_overflow(tensptr, tensdelta);
+}
+
+static int
+long_normalize_overflow(tensptr, unitsptr, base)
+long * const tensptr;
+int * const unitsptr;
+const int base;
+{
+ register int tensdelta;
+
+ tensdelta = (*unitsptr >= 0) ?
+ (*unitsptr / base) :
+ (-1 - (-1 - *unitsptr) / base);
+ *unitsptr -= tensdelta * base;
+ return long_increment_overflow(tensptr, tensdelta);
+}
+
+static int
+tmcomp(atmp, btmp)
+register const struct tm * const atmp;
+register const struct tm * const btmp;
+{
+ register int result;
+
+ if ((result = (atmp->tm_year - btmp->tm_year)) == 0 &&
+ (result = (atmp->tm_mon - btmp->tm_mon)) == 0 &&
+ (result = (atmp->tm_mday - btmp->tm_mday)) == 0 &&
+ (result = (atmp->tm_hour - btmp->tm_hour)) == 0 &&
+ (result = (atmp->tm_min - btmp->tm_min)) == 0)
+ result = atmp->tm_sec - btmp->tm_sec;
+ return result;
+}
+
+static time_t
+time2sub(tmp, funcp, offset, okayp, do_norm_secs, sp)
+struct tm * const tmp;
+struct tm * (* const funcp) P((const time_t*, long, struct tm*,const struct state *sp));
+const long offset;
+int * const okayp;
+const int do_norm_secs;
+const struct state * sp;
+{
+ register int dir;
+ register int i, j;
+ register int saved_seconds;
+ register long li;
+ register time_t lo;
+ register time_t hi;
+ long y;
+ time_t newt;
+ time_t t;
+ struct tm yourtm, mytm;
+
+ *okayp = FALSE;
+ yourtm = *tmp;
+ if (do_norm_secs) {
+ if (normalize_overflow(&yourtm.tm_min, &yourtm.tm_sec,
+ SECSPERMIN))
+ return WRONG;
+ }
+ if (normalize_overflow(&yourtm.tm_hour, &yourtm.tm_min, MINSPERHOUR))
+ return WRONG;
+ if (normalize_overflow(&yourtm.tm_mday, &yourtm.tm_hour, HOURSPERDAY))
+ return WRONG;
+ y = yourtm.tm_year;
+ if (long_normalize_overflow(&y, &yourtm.tm_mon, MONSPERYEAR))
+ return WRONG;
+ /*
+ ** Turn y into an actual year number for now.
+ ** It is converted back to an offset from TM_YEAR_BASE later.
+ */
+ if (long_increment_overflow(&y, TM_YEAR_BASE))
+ return WRONG;
+ while (yourtm.tm_mday <= 0) {
+ if (long_increment_overflow(&y, -1))
+ return WRONG;
+ li = y + (1 < yourtm.tm_mon);
+ yourtm.tm_mday += year_lengths[isleap(li)];
+ }
+ while (yourtm.tm_mday > DAYSPERLYEAR) {
+ li = y + (1 < yourtm.tm_mon);
+ yourtm.tm_mday -= year_lengths[isleap(li)];
+ if (long_increment_overflow(&y, 1))
+ return WRONG;
+ }
+ for ( ; ; ) {
+ i = mon_lengths[isleap(y)][yourtm.tm_mon];
+ if (yourtm.tm_mday <= i)
+ break;
+ yourtm.tm_mday -= i;
+ if (++yourtm.tm_mon >= MONSPERYEAR) {
+ yourtm.tm_mon = 0;
+ if (long_increment_overflow(&y, 1))
+ return WRONG;
+ }
+ }
+ if (long_increment_overflow(&y, -TM_YEAR_BASE))
+ return WRONG;
+ yourtm.tm_year = y;
+ if (yourtm.tm_year != y)
+ return WRONG;
+ if (yourtm.tm_sec >= 0 && yourtm.tm_sec < SECSPERMIN)
+ saved_seconds = 0;
+ else if (y + TM_YEAR_BASE < EPOCH_YEAR) {
+ /*
+ ** We can't set tm_sec to 0, because that might push the
+ ** time below the minimum representable time.
+ ** Set tm_sec to 59 instead.
+ ** This assumes that the minimum representable time is
+ ** not in the same minute that a leap second was deleted from,
+ ** which is a safer assumption than using 58 would be.
+ */
+ if (increment_overflow(&yourtm.tm_sec, 1 - SECSPERMIN))
+ return WRONG;
+ saved_seconds = yourtm.tm_sec;
+ yourtm.tm_sec = SECSPERMIN - 1;
+ } else {
+ saved_seconds = yourtm.tm_sec;
+ yourtm.tm_sec = 0;
+ }
+ /*
+ ** Do a binary search (this works whatever time_t's type is).
+ */
+ if (!TYPE_SIGNED(time_t)) {
+ lo = 0;
+ hi = lo - 1;
+ } else if (!TYPE_INTEGRAL(time_t)) {
+ if (sizeof(time_t) > sizeof(float))
+ hi = (time_t) DBL_MAX;
+ else hi = (time_t) FLT_MAX;
+ lo = -hi;
+ } else {
+ lo = 1;
+ for (i = 0; i < (int) TYPE_BIT(time_t) - 1; ++i)
+ lo *= 2;
+ hi = -(lo + 1);
+ }
+ for ( ; ; ) {
+ t = lo / 2 + hi / 2;
+ if (t < lo)
+ t = lo;
+ else if (t > hi)
+ t = hi;
+ if ((*funcp)(&t, offset, &mytm, sp) == NULL) {
+ /*
+ ** Assume that t is too extreme to be represented in
+ ** a struct tm; arrange things so that it is less
+ ** extreme on the next pass.
+ */
+ dir = (t > 0) ? 1 : -1;
+ } else dir = tmcomp(&mytm, &yourtm);
+ if (dir != 0) {
+ if (t == lo) {
+ if (t == TIME_T_MAX)
+ return WRONG;
+ ++t;
+ ++lo;
+ } else if (t == hi) {
+ if (t == TIME_T_MIN)
+ return WRONG;
+ --t;
+ --hi;
+ }
+ if (lo > hi)
+ return WRONG;
+ if (dir > 0)
+ hi = t;
+ else lo = t;
+ continue;
+ }
+ if (yourtm.tm_isdst < 0 || mytm.tm_isdst == yourtm.tm_isdst)
+ break;
+ /*
+ ** Right time, wrong type.
+ ** Hunt for right time, right type.
+ ** It's okay to guess wrong since the guess
+ ** gets checked.
+ */
+ /*
+ ** The (void *) casts are the benefit of SunOS 3.3 on Sun 2's.
+ */
+#ifdef ALL_STATE
+ if (sp == NULL)
+ return WRONG;
+#endif /* defined ALL_STATE */
+ for (i = sp->typecnt - 1; i >= 0; --i) {
+ if (sp->ttis[i].tt_isdst != yourtm.tm_isdst)
+ continue;
+ for (j = sp->typecnt - 1; j >= 0; --j) {
+ if (sp->ttis[j].tt_isdst == yourtm.tm_isdst)
+ continue;
+ newt = t + sp->ttis[j].tt_gmtoff -
+ sp->ttis[i].tt_gmtoff;
+ if ((*funcp)(&newt, offset, &mytm, sp) == NULL)
+ continue;
+ if (tmcomp(&mytm, &yourtm) != 0)
+ continue;
+ if (mytm.tm_isdst != yourtm.tm_isdst)
+ continue;
+ /*
+ ** We have a match.
+ */
+ t = newt;
+ goto label;
+ }
+ }
+ return WRONG;
+ }
+label:
+ newt = t + saved_seconds;
+ if ((newt < t) != (saved_seconds < 0))
+ return WRONG;
+ t = newt;
+ if ((*funcp)(&t, offset, tmp, sp))
+ *okayp = TRUE;
+ return t;
+}
+
+static time_t
+time2(tmp, funcp, offset, okayp, sp)
+struct tm * const tmp;
+struct tm * (* const funcp) P((const time_t*, long, struct tm*,
+ const struct state* sp));
+const long offset;
+int * const okayp;
+const struct state * sp;
+{
+ time_t t;
+
+ /*
+ ** First try without normalization of seconds
+ ** (in case tm_sec contains a value associated with a leap second).
+ ** If that fails, try with normalization of seconds.
+ */
+ t = time2sub(tmp, funcp, offset, okayp, FALSE, sp);
+ return *okayp ? t : time2sub(tmp, funcp, offset, okayp, TRUE, sp);
+}
+
+static time_t
+time1(tmp, funcp, offset, sp)
+struct tm * const tmp;
+struct tm * (* const funcp) P((const time_t *, long, struct tm *, const struct state* sp));
+const long offset;
+const struct state * sp;
+{
+ register time_t t;
+ register int samei, otheri;
+ register int sameind, otherind;
+ register int i;
+ register int nseen;
+ int seen[TZ_MAX_TYPES];
+ int types[TZ_MAX_TYPES];
+ int okay;
+
+ if (tmp->tm_isdst > 1)
+ tmp->tm_isdst = 1;
+ t = time2(tmp, funcp, offset, &okay, sp);
+#define PCTS 1
+#ifdef PCTS
+ /*
+ ** PCTS code courtesy Grant Sullivan.
+ */
+ if (okay)
+ return t;
+ if (tmp->tm_isdst < 0)
+ tmp->tm_isdst = 0; /* reset to std and try again */
+#endif /* defined PCTS */
+#ifndef PCTS
+ if (okay || tmp->tm_isdst < 0)
+ return t;
+#endif /* !defined PCTS */
+ /*
+ ** We're supposed to assume that somebody took a time of one type
+ ** and did some math on it that yielded a "struct tm" that's bad.
+ ** We try to divine the type they started from and adjust to the
+ ** type they need.
+ */
+ /*
+ ** The (void *) casts are the benefit of SunOS 3.3 on Sun 2's.
+ */
+#ifdef ALL_STATE
+ if (sp == NULL)
+ return WRONG;
+#endif /* defined ALL_STATE */
+ for (i = 0; i < sp->typecnt; ++i)
+ seen[i] = FALSE;
+ nseen = 0;
+ for (i = sp->timecnt - 1; i >= 0; --i)
+ if (!seen[sp->types[i]]) {
+ seen[sp->types[i]] = TRUE;
+ types[nseen++] = sp->types[i];
+ }
+ for (sameind = 0; sameind < nseen; ++sameind) {
+ samei = types[sameind];
+ if (sp->ttis[samei].tt_isdst != tmp->tm_isdst)
+ continue;
+ for (otherind = 0; otherind < nseen; ++otherind) {
+ otheri = types[otherind];
+ if (sp->ttis[otheri].tt_isdst == tmp->tm_isdst)
+ continue;
+ tmp->tm_sec += sp->ttis[otheri].tt_gmtoff -
+ sp->ttis[samei].tt_gmtoff;
+ tmp->tm_isdst = !tmp->tm_isdst;
+ t = time2(tmp, funcp, offset, &okay, sp);
+ if (okay)
+ return t;
+ tmp->tm_sec -= sp->ttis[otheri].tt_gmtoff -
+ sp->ttis[samei].tt_gmtoff;
+ tmp->tm_isdst = !tmp->tm_isdst;
+ }
+ }
+ return WRONG;
+}
+
+// ============================================================================
+time_t
+mktime_tz(struct tm * const tmp, char const * tz)
+{
+ struct state st;
+ if (tzload(tz, &st, TRUE) != 0) {
+ // not sure what's best here, but for now, we fall back to gmt
+ gmtload(&st);
+ }
+ return time1(tmp, localsub, 0L, &st);
+}
diff --git a/libcutils/uio.c b/libcutils/uio.c
new file mode 100644
index 0000000..baa8051
--- /dev/null
+++ b/libcutils/uio.c
@@ -0,0 +1,76 @@
+/*
+ * 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 HAVE_SYS_UIO_H
+
+#include <cutils/uio.h>
+#include <unistd.h>
+
+int readv( int fd, struct iovec* vecs, int count )
+{
+ int total = 0;
+
+ for ( ; count > 0; count--, vecs++ ) {
+ const char* buf = vecs->iov_base;
+ int len = vecs->iov_len;
+
+ while (len > 0) {
+ int ret = read( fd, buf, len );
+ if (ret < 0) {
+ if (total == 0)
+ total = -1;
+ goto Exit;
+ }
+ if (ret == 0)
+ goto Exit;
+
+ total += ret;
+ buf += ret;
+ len -= ret;
+ }
+ }
+Exit:
+ return total;
+}
+
+int writev( int fd, const struct iovec* vecs, int count )
+{
+ int total = 0;
+
+ for ( ; count > 0; count--, vecs++ ) {
+ const char* buf = (const char*)vecs->iov_base;
+ int len = (int)vecs->iov_len;
+
+ while (len > 0) {
+ int ret = write( fd, buf, len );
+ if (ret < 0) {
+ if (total == 0)
+ total = -1;
+ goto Exit;
+ }
+ if (ret == 0)
+ goto Exit;
+
+ total += ret;
+ buf += ret;
+ len -= ret;
+ }
+ }
+Exit:
+ return total;
+}
+
+#endif /* !HAVE_SYS_UIO_H */
diff --git a/libcutils/zygote.c b/libcutils/zygote.c
new file mode 100644
index 0000000..aa060c0
--- /dev/null
+++ b/libcutils/zygote.c
@@ -0,0 +1,267 @@
+/*
+ * 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 LOG_TAG "Zygote"
+
+#include <cutils/sockets.h>
+#include <cutils/zygote.h>
+#include <cutils/log.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#define ZYGOTE_SOCKET "zygote"
+
+#define ZYGOTE_RETRY_COUNT 1000
+#define ZYGOTE_RETRY_MILLIS 500
+
+static void replace_nl(char *str);
+
+/*
+ * If sendStdio is non-zero, the current process's stdio file descriptors
+ * will be sent and inherited by the spawned process.
+ */
+static int send_request(int fd, int sendStdio, int argc, const char **argv)
+{
+#ifndef HAVE_ANDROID_OS
+ // not supported on simulator targets
+ //LOGE("zygote_* not supported on simulator targets");
+ return -1;
+#else /* HAVE_ANDROID_OS */
+ uint32_t pid;
+ int i;
+ struct iovec ivs[2];
+ struct msghdr msg;
+ char argc_buffer[12];
+ const char *newline_string = "\n";
+ struct cmsghdr *cmsg;
+ char msgbuf[CMSG_SPACE(sizeof(int) * 3)];
+ int *cmsg_payload;
+ ssize_t ret;
+
+ memset(&msg, 0, sizeof(msg));
+ memset(&ivs, 0, sizeof(ivs));
+
+ // First line is arg count
+ snprintf(argc_buffer, sizeof(argc_buffer), "%d\n", argc);
+
+ ivs[0].iov_base = argc_buffer;
+ ivs[0].iov_len = strlen(argc_buffer);
+
+ msg.msg_iov = ivs;
+ msg.msg_iovlen = 1;
+
+ if (sendStdio != 0) {
+ // Pass the file descriptors with the first write
+ msg.msg_control = msgbuf;
+ msg.msg_controllen = sizeof msgbuf;
+
+ cmsg = CMSG_FIRSTHDR(&msg);
+
+ cmsg->cmsg_len = CMSG_LEN(3 * sizeof(int));
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+
+ cmsg_payload = (int *)CMSG_DATA(cmsg);
+ cmsg_payload[0] = STDIN_FILENO;
+ cmsg_payload[1] = STDOUT_FILENO;
+ cmsg_payload[2] = STDERR_FILENO;
+ }
+
+ do {
+ ret = sendmsg(fd, &msg, MSG_NOSIGNAL);
+ } while (ret < 0 && errno == EINTR);
+
+ if (ret < 0) {
+ return -1;
+ }
+
+ // Only send the fd's once
+ msg.msg_control = NULL;
+ msg.msg_controllen = 0;
+
+ // replace any newlines with spaces and send the args
+ for (i = 0; i < argc; i++) {
+ char *tofree = NULL;
+ const char *toprint;
+
+ toprint = argv[i];
+
+ if (strchr(toprint, '\n') != NULL) {
+ tofree = strdup(toprint);
+ toprint = tofree;
+ replace_nl(tofree);
+ }
+
+ ivs[0].iov_base = (char *)toprint;
+ ivs[0].iov_len = strlen(toprint);
+ ivs[1].iov_base = (char *)newline_string;
+ ivs[1].iov_len = 1;
+
+ msg.msg_iovlen = 2;
+
+ do {
+ ret = sendmsg(fd, &msg, MSG_NOSIGNAL);
+ } while (ret < 0 && errno == EINTR);
+
+ if (tofree != NULL) {
+ free(tofree);
+ }
+
+ if (ret < 0) {
+ return -1;
+ }
+ }
+
+ // Read the pid, as a 4-byte network-order integer
+
+ ivs[0].iov_base = &pid;
+ ivs[0].iov_len = sizeof(pid);
+ msg.msg_iovlen = 1;
+
+ do {
+ do {
+ ret = recvmsg(fd, &msg, MSG_NOSIGNAL | MSG_WAITALL);
+ } while (ret < 0 && errno == EINTR);
+
+ if (ret < 0) {
+ return -1;
+ }
+
+ ivs[0].iov_len -= ret;
+ ivs[0].iov_base += ret;
+ } while (ivs[0].iov_len > 0);
+
+ pid = ntohl(pid);
+
+ return pid;
+#endif /* HAVE_ANDROID_OS */
+}
+
+int zygote_run_wait(int argc, const char **argv, void (*post_run_func)(int))
+{
+ int fd;
+ int pid;
+ int err;
+ const char *newargv[argc + 1];
+
+ fd = socket_local_client(ZYGOTE_SOCKET,
+ ANDROID_SOCKET_NAMESPACE_RESERVED, AF_LOCAL);
+
+ if (fd < 0) {
+ return -1;
+ }
+
+ // The command socket is passed to the peer as close-on-exec
+ // and will close when the peer dies
+ newargv[0] = "--peer-wait";
+ memcpy(newargv + 1, argv, argc * sizeof(*argv));
+
+ pid = send_request(fd, 1, argc + 1, newargv);
+
+ if (pid > 0 && post_run_func != NULL) {
+ post_run_func(pid);
+ }
+
+ // Wait for socket to close
+ do {
+ int dummy;
+ err = read(fd, &dummy, sizeof(dummy));
+ } while ((err < 0 && errno == EINTR) || err != 0);
+
+ do {
+ err = close(fd);
+ } while (err < 0 && errno == EINTR);
+
+ return 0;
+}
+
+/**
+ * Spawns a new dalvik instance via the Zygote process. The non-zygote
+ * arguments are passed to com.android.internal.os.RuntimeInit(). The
+ * first non-option argument should be a class name in the system class path.
+ *
+ * The arg list may start with zygote params such as --set-uid.
+ *
+ * If sendStdio is non-zero, the current process's stdio file descriptors
+ * will be sent and inherited by the spawned process.
+ *
+ * The pid of the child process is returned, or -1 if an error was
+ * encountered.
+ *
+ * zygote_run_oneshot waits up to ZYGOTE_RETRY_COUNT *
+ * ZYGOTE_RETRY_MILLIS for the zygote socket to be available.
+ */
+int zygote_run_oneshot(int sendStdio, int argc, const char **argv)
+{
+ int fd = -1;
+ int err;
+ int i;
+ int retries;
+ int pid;
+ const char **newargv = argv;
+ const int newargc = argc;
+
+ for (retries = 0; (fd < 0) && (retries < ZYGOTE_RETRY_COUNT); retries++) {
+ if (retries > 0) {
+ struct timespec ts;
+
+ memset(&ts, 0, sizeof(ts));
+ ts.tv_nsec = ZYGOTE_RETRY_MILLIS * 1000 * 1000;
+
+ do {
+ err = nanosleep (&ts, &ts);
+ } while (err < 0 && errno == EINTR);
+ }
+ fd = socket_local_client(ZYGOTE_SOCKET, AF_LOCAL,
+ ANDROID_SOCKET_NAMESPACE_RESERVED);
+ }
+
+ if (fd < 0) {
+ return -1;
+ }
+
+ pid = send_request(fd, 0, newargc, newargv);
+
+ do {
+ err = close(fd);
+ } while (err < 0 && errno == EINTR);
+
+ return pid;
+}
+
+/**
+ * Replaces all occurrances of newline with space.
+ */
+static void replace_nl(char *str)
+{
+ for(; *str; str++) {
+ if (*str == '\n') {
+ *str = ' ';
+ }
+ }
+}
+
+
+
diff --git a/libzipfile/Android.mk b/libzipfile/Android.mk
new file mode 100644
index 0000000..d2d758c
--- /dev/null
+++ b/libzipfile/Android.mk
@@ -0,0 +1,48 @@
+LOCAL_PATH:= $(call my-dir)
+
+# build host static library
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ centraldir.c \
+ zipfile.c
+
+LOCAL_STATIC_LIBRARIES := \
+ libunz
+
+LOCAL_MODULE:= libzipfile
+
+LOCAL_C_INCLUDES += external/zlib
+
+include $(BUILD_HOST_STATIC_LIBRARY)
+
+# build device static library
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ centraldir.c \
+ zipfile.c
+
+LOCAL_STATIC_LIBRARIES := \
+ libunz
+
+LOCAL_MODULE:= libzipfile
+
+LOCAL_C_INCLUDES += external/zlib
+
+include $(BUILD_STATIC_LIBRARY)
+
+
+# build test_zipfile
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ test_zipfile.c
+
+LOCAL_STATIC_LIBRARIES := libzipfile libunz
+
+LOCAL_MODULE := test_zipfile
+
+LOCAL_C_INCLUDES += external/zlib
+
+include $(BUILD_HOST_EXECUTABLE)
diff --git a/libzipfile/MODULE_LICENSE_APACHE2 b/libzipfile/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/libzipfile/MODULE_LICENSE_APACHE2
diff --git a/libzipfile/Makefile.am b/libzipfile/Makefile.am
new file mode 100644
index 0000000..6479658
--- /dev/null
+++ b/libzipfile/Makefile.am
@@ -0,0 +1,25 @@
+AUTOMAKE_OPTIONS = 1.4 foreign
+MAINTAINERCLEANFILES = Makefile.in
+
+AM_CPPFLAGS = \
+-I. \
+-I$(top_builddir) \
+-I$(top_srcdir) \
+-I$(top_srcdir)/src/lib \
+-I$(top_builddir)/src/lib \
+-I$(top_srcdir)/include \
+-I$(top_builddir)/include
+
+lib_LTLIBRARIES = libzipfile.la
+
+libzipfile_la_SOURCES = \
+private.h \
+centraldir.c \
+zipfile.c
+
+libzipfile_la_CFLAGS =
+libzipfile_la_LIBADD =
+libzipfile_la_LDFLAGS =
+
+docdir = $(datadir)/doc/zipfile
+doc_DATA = NOTICE \ No newline at end of file
diff --git a/libzipfile/NOTICE b/libzipfile/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /dev/null
+++ b/libzipfile/NOTICE
@@ -0,0 +1,190 @@
+
+ Copyright (c) 2005-2008, The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
diff --git a/libzipfile/centraldir.c b/libzipfile/centraldir.c
new file mode 100644
index 0000000..0e264a3
--- /dev/null
+++ b/libzipfile/centraldir.c
@@ -0,0 +1,253 @@
+#include "private.h"
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+enum {
+ // finding the directory
+ CD_SIGNATURE = 0x06054b50,
+ EOCD_LEN = 22, // EndOfCentralDir len, excl. comment
+ MAX_COMMENT_LEN = 65535,
+ MAX_EOCD_SEARCH = MAX_COMMENT_LEN + EOCD_LEN,
+
+ // central directory entries
+ ENTRY_SIGNATURE = 0x02014b50,
+ ENTRY_LEN = 46, // CentralDirEnt len, excl. var fields
+
+ // local file header
+ LFH_SIZE = 30,
+};
+
+unsigned int
+read_le_int(const unsigned char* buf)
+{
+ return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
+}
+
+unsigned int
+read_le_short(const unsigned char* buf)
+{
+ return buf[0] | (buf[1] << 8);
+}
+
+static int
+read_central_dir_values(Zipfile* file, const unsigned char* buf, int len)
+{
+ if (len < EOCD_LEN) {
+ // looks like ZIP file got truncated
+ fprintf(stderr, " Zip EOCD: expected >= %d bytes, found %d\n",
+ EOCD_LEN, len);
+ return -1;
+ }
+
+ file->disknum = read_le_short(&buf[0x04]);
+ file->diskWithCentralDir = read_le_short(&buf[0x06]);
+ file->entryCount = read_le_short(&buf[0x08]);
+ file->totalEntryCount = read_le_short(&buf[0x0a]);
+ file->centralDirSize = read_le_int(&buf[0x0c]);
+ file->centralDirOffest = read_le_int(&buf[0x10]);
+ file->commentLen = read_le_short(&buf[0x14]);
+
+ if (file->commentLen > 0) {
+ if (EOCD_LEN + file->commentLen > len) {
+ fprintf(stderr, "EOCD(%d) + comment(%d) exceeds len (%d)\n",
+ EOCD_LEN, file->commentLen, len);
+ return -1;
+ }
+ file->comment = buf + EOCD_LEN;
+ }
+
+ return 0;
+}
+
+static int
+read_central_directory_entry(Zipfile* file, Zipentry* entry,
+ const unsigned char** buf, ssize_t* len)
+{
+ const unsigned char* p;
+
+ unsigned short versionMadeBy;
+ unsigned short versionToExtract;
+ unsigned short gpBitFlag;
+ unsigned short compressionMethod;
+ unsigned short lastModFileTime;
+ unsigned short lastModFileDate;
+ unsigned long crc32;
+ unsigned short extraFieldLength;
+ unsigned short fileCommentLength;
+ unsigned short diskNumberStart;
+ unsigned short internalAttrs;
+ unsigned long externalAttrs;
+ unsigned long localHeaderRelOffset;
+ const unsigned char* extraField;
+ const unsigned char* fileComment;
+ unsigned int dataOffset;
+ unsigned short lfhExtraFieldSize;
+
+
+ p = *buf;
+
+ if (*len < ENTRY_LEN) {
+ fprintf(stderr, "cde entry not large enough\n");
+ return -1;
+ }
+
+ if (read_le_int(&p[0x00]) != ENTRY_SIGNATURE) {
+ fprintf(stderr, "Whoops: didn't find expected signature\n");
+ return -1;
+ }
+
+ versionMadeBy = read_le_short(&p[0x04]);
+ versionToExtract = read_le_short(&p[0x06]);
+ gpBitFlag = read_le_short(&p[0x08]);
+ entry->compressionMethod = read_le_short(&p[0x0a]);
+ lastModFileTime = read_le_short(&p[0x0c]);
+ lastModFileDate = read_le_short(&p[0x0e]);
+ crc32 = read_le_int(&p[0x10]);
+ entry->compressedSize = read_le_int(&p[0x14]);
+ entry->uncompressedSize = read_le_int(&p[0x18]);
+ entry->fileNameLength = read_le_short(&p[0x1c]);
+ extraFieldLength = read_le_short(&p[0x1e]);
+ fileCommentLength = read_le_short(&p[0x20]);
+ diskNumberStart = read_le_short(&p[0x22]);
+ internalAttrs = read_le_short(&p[0x24]);
+ externalAttrs = read_le_int(&p[0x26]);
+ localHeaderRelOffset = read_le_int(&p[0x2a]);
+
+ p += ENTRY_LEN;
+
+ // filename
+ if (entry->fileNameLength != 0) {
+ entry->fileName = p;
+ } else {
+ entry->fileName = NULL;
+ }
+ p += entry->fileNameLength;
+
+ // extra field
+ if (extraFieldLength != 0) {
+ extraField = p;
+ } else {
+ extraField = NULL;
+ }
+ p += extraFieldLength;
+
+ // comment, if any
+ if (fileCommentLength != 0) {
+ fileComment = p;
+ } else {
+ fileComment = NULL;
+ }
+ p += fileCommentLength;
+
+ *buf = p;
+
+ // the size of the extraField in the central dir is how much data there is,
+ // but the one in the local file header also contains some padding.
+ p = file->buf + localHeaderRelOffset;
+ extraFieldLength = read_le_short(&p[0x1c]);
+
+ dataOffset = localHeaderRelOffset + LFH_SIZE
+ + entry->fileNameLength + extraFieldLength;
+ entry->data = file->buf + dataOffset;
+#if 0
+ printf("file->buf=%p entry->data=%p dataOffset=%x localHeaderRelOffset=%d "
+ "entry->fileNameLength=%d extraFieldLength=%d\n",
+ file->buf, entry->data, dataOffset, localHeaderRelOffset,
+ entry->fileNameLength, extraFieldLength);
+#endif
+ return 0;
+}
+
+/*
+ * Find the central directory and read the contents.
+ *
+ * The fun thing about ZIP archives is that they may or may not be
+ * readable from start to end. In some cases, notably for archives
+ * that were written to stdout, the only length information is in the
+ * central directory at the end of the file.
+ *
+ * Of course, the central directory can be followed by a variable-length
+ * comment field, so we have to scan through it backwards. The comment
+ * is at most 64K, plus we have 18 bytes for the end-of-central-dir stuff
+ * itself, plus apparently sometimes people throw random junk on the end
+ * just for the fun of it.
+ *
+ * This is all a little wobbly. If the wrong value ends up in the EOCD
+ * area, we're hosed. This appears to be the way that everbody handles
+ * it though, so we're in pretty good company if this fails.
+ */
+int
+read_central_dir(Zipfile *file)
+{
+ int err;
+
+ const unsigned char* buf = file->buf;
+ ssize_t bufsize = file->bufsize;
+ const unsigned char* eocd;
+ const unsigned char* p;
+ const unsigned char* start;
+ ssize_t len;
+ int i;
+
+ // too small to be a ZIP archive?
+ if (bufsize < EOCD_LEN) {
+ fprintf(stderr, "Length is %d -- too small\n", bufsize);
+ goto bail;
+ }
+
+ // find the end-of-central-dir magic
+ if (bufsize > MAX_EOCD_SEARCH) {
+ start = buf + bufsize - MAX_EOCD_SEARCH;
+ } else {
+ start = buf;
+ }
+ p = buf + bufsize - 4;
+ while (p >= start) {
+ if (*p == 0x50 && read_le_int(p) == CD_SIGNATURE) {
+ eocd = p;
+ break;
+ }
+ p--;
+ }
+ if (p < start) {
+ fprintf(stderr, "EOCD not found, not Zip\n");
+ goto bail;
+ }
+
+ // extract eocd values
+ err = read_central_dir_values(file, eocd, (buf+bufsize)-eocd);
+ if (err != 0) {
+ goto bail;
+ }
+
+ if (file->disknum != 0
+ || file->diskWithCentralDir != 0
+ || file->entryCount != file->totalEntryCount) {
+ fprintf(stderr, "Archive spanning not supported\n");
+ goto bail;
+ }
+
+ // Loop through and read the central dir entries.
+ p = buf + file->centralDirOffest;
+ len = (buf+bufsize)-p;
+ for (i=0; i < file->totalEntryCount; i++) {
+ Zipentry* entry = malloc(sizeof(Zipentry));
+ memset(entry, 0, sizeof(Zipentry));
+
+ err = read_central_directory_entry(file, entry, &p, &len);
+ if (err != 0) {
+ fprintf(stderr, "read_central_directory_entry failed\n");
+ free(entry);
+ goto bail;
+ }
+
+ // add it to our list
+ entry->next = file->entries;
+ file->entries = entry;
+ }
+
+ return 0;
+bail:
+ return -1;
+}
diff --git a/libzipfile/private.h b/libzipfile/private.h
new file mode 100644
index 0000000..06f788d
--- /dev/null
+++ b/libzipfile/private.h
@@ -0,0 +1,45 @@
+#ifndef PRIVATE_H
+#define PRIVATE_H
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+typedef struct Zipentry {
+ unsigned long fileNameLength;
+ const unsigned char* fileName;
+ unsigned short compressionMethod;
+ unsigned int uncompressedSize;
+ unsigned int compressedSize;
+ const unsigned char* data;
+
+ struct Zipentry* next;
+} Zipentry;
+
+typedef struct Zipfile
+{
+ const unsigned char *buf;
+ ssize_t bufsize;
+
+ // Central directory
+ unsigned short disknum; //mDiskNumber;
+ unsigned short diskWithCentralDir; //mDiskWithCentralDir;
+ unsigned short entryCount; //mNumEntries;
+ unsigned short totalEntryCount; //mTotalNumEntries;
+ unsigned int centralDirSize; //mCentralDirSize;
+ unsigned int centralDirOffest; // offset from first disk //mCentralDirOffset;
+ unsigned short commentLen; //mCommentLen;
+ const unsigned char* comment; //mComment;
+
+ Zipentry* entries;
+} Zipfile;
+
+int read_central_dir(Zipfile* file);
+
+unsigned int read_le_int(const unsigned char* buf);
+unsigned int read_le_short(const unsigned char* buf);
+
+#endif // PRIVATE_H
+
diff --git a/libzipfile/test_zipfile.c b/libzipfile/test_zipfile.c
new file mode 100644
index 0000000..40840ec
--- /dev/null
+++ b/libzipfile/test_zipfile.c
@@ -0,0 +1,92 @@
+#include <zipfile/zipfile.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+void dump_zipfile(FILE* to, zipfile_t file);
+
+int
+main(int argc, char** argv)
+{
+ FILE* f;
+ size_t size, unsize;
+ void* buf;
+ void* scratch;
+ zipfile_t zip;
+ zipentry_t entry;
+ int err;
+ enum { HUH, LIST, UNZIP } what = HUH;
+
+ if (strcmp(argv[2], "-l") == 0 && argc == 3) {
+ what = LIST;
+ }
+ else if (strcmp(argv[2], "-u") == 0 && argc == 5) {
+ what = UNZIP;
+ }
+ else {
+ fprintf(stderr, "usage: test_zipfile ZIPFILE -l\n"
+ " lists the files in the zipfile\n"
+ " test_zipfile ZIPFILE -u FILENAME SAVETO\n"
+ " saves FILENAME from the zip file into SAVETO\n");
+ return 1;
+ }
+
+ f = fopen(argv[1], "r");
+ if (f == NULL) {
+ fprintf(stderr, "couldn't open %s\n", argv[1]);
+ return 1;
+ }
+
+ fseek(f, 0, SEEK_END);
+ size = ftell(f);
+ rewind(f);
+
+ buf = malloc(size);
+ fread(buf, 1, size, f);
+
+ zip = init_zipfile(buf, size);
+ if (zip == NULL) {
+ fprintf(stderr, "inti_zipfile failed\n");
+ return 1;
+ }
+
+ fclose(f);
+
+
+ switch (what)
+ {
+ case LIST:
+ dump_zipfile(stdout, zip);
+ break;
+ case UNZIP:
+ entry = lookup_zipentry(zip, argv[3]);
+ if (entry == NULL) {
+ fprintf(stderr, "zip file '%s' does not contain file '%s'\n",
+ argv[1], argv[1]);
+ return 1;
+ }
+ f = fopen(argv[4], "w");
+ if (f == NULL) {
+ fprintf(stderr, "can't open file for writing '%s'\n", argv[4]);
+ return 1;
+ }
+ unsize = get_zipentry_size(entry);
+ size = unsize * 1.001;
+ scratch = malloc(size);
+ printf("scratch=%p\n", scratch);
+ err = decompress_zipentry(entry, scratch, size);
+ if (err != 0) {
+ fprintf(stderr, "error decompressing file\n");
+ return 1;
+ }
+ fwrite(scratch, unsize, 1, f);
+ free(scratch);
+ fclose(f);
+ break;
+ }
+
+ free(buf);
+
+ return 0;
+}
+
diff --git a/libzipfile/zipfile.c b/libzipfile/zipfile.c
new file mode 100644
index 0000000..a401a9b
--- /dev/null
+++ b/libzipfile/zipfile.c
@@ -0,0 +1,160 @@
+#include <zipfile/zipfile.h>
+
+#include "private.h"
+#include <stdlib.h>
+#include <string.h>
+#include <zlib.h>
+#define DEF_MEM_LEVEL 8 // normally in zutil.h?
+
+zipfile_t
+init_zipfile(const void* data, size_t size)
+{
+ int err;
+
+ Zipfile *file = malloc(sizeof(Zipfile));
+ if (file == NULL) return NULL;
+ memset(file, 0, sizeof(Zipfile));
+ file->buf = data;
+ file->bufsize = size;
+
+ err = read_central_dir(file);
+ if (err != 0) goto fail;
+
+ return file;
+fail:
+ free(file);
+ return NULL;
+}
+
+void
+release_zipfile(zipfile_t f)
+{
+ Zipfile* file = (Zipfile*)f;
+ Zipentry* entry = file->entries;
+ while (entry) {
+ Zipentry* next = entry->next;
+ free(entry);
+ entry = next;
+ }
+ free(file);
+}
+
+zipentry_t
+lookup_zipentry(zipfile_t f, const char* entryName)
+{
+ Zipfile* file = (Zipfile*)f;
+ Zipentry* entry = file->entries;
+ while (entry) {
+ if (0 == memcmp(entryName, entry->fileName, entry->fileNameLength)) {
+ return entry;
+ }
+ entry = entry->next;
+ }
+ return NULL;
+}
+
+size_t
+get_zipentry_size(zipentry_t entry)
+{
+ return ((Zipentry*)entry)->uncompressedSize;
+}
+
+char*
+get_zipentry_name(zipentry_t entry)
+{
+ Zipentry* e = (Zipentry*)entry;
+ int l = e->fileNameLength;
+ char* s = malloc(l+1);
+ memcpy(s, e->fileName, l);
+ s[l] = '\0';
+ return s;
+}
+
+enum {
+ STORED = 0,
+ DEFLATED = 8
+};
+
+static int
+uninflate(unsigned char* out, int unlen, const unsigned char* in, int clen)
+{
+ z_stream zstream;
+ unsigned long crc;
+ int err = 0;
+ int zerr;
+
+ memset(&zstream, 0, sizeof(zstream));
+ zstream.zalloc = Z_NULL;
+ zstream.zfree = Z_NULL;
+ zstream.opaque = Z_NULL;
+ zstream.next_in = (void*)in;
+ zstream.avail_in = clen;
+ zstream.next_out = (Bytef*) out;
+ zstream.avail_out = unlen;
+ zstream.data_type = Z_UNKNOWN;
+
+ // Use the undocumented "negative window bits" feature to tell zlib
+ // that there's no zlib header waiting for it.
+ zerr = inflateInit2(&zstream, -MAX_WBITS);
+ if (zerr != Z_OK) {
+ return -1;
+ }
+
+ // uncompress the data
+ zerr = inflate(&zstream, Z_FINISH);
+ if (zerr != Z_STREAM_END) {
+ fprintf(stderr, "zerr=%d Z_STREAM_END=%d total_out=%lu\n", zerr, Z_STREAM_END,
+ zstream.total_out);
+ err = -1;
+ }
+
+ inflateEnd(&zstream);
+ return err;
+}
+
+int
+decompress_zipentry(zipentry_t e, void* buf, int bufsize)
+{
+ Zipentry* entry = (Zipentry*)e;
+ switch (entry->compressionMethod)
+ {
+ case STORED:
+ memcpy(buf, entry->data, entry->uncompressedSize);
+ return 0;
+ case DEFLATED:
+ return uninflate(buf, bufsize, entry->data, entry->compressedSize);
+ default:
+ return -1;
+ }
+}
+
+void
+dump_zipfile(FILE* to, zipfile_t file)
+{
+ Zipfile* zip = (Zipfile*)file;
+ Zipentry* entry = zip->entries;
+ int i;
+
+ fprintf(to, "entryCount=%d\n", zip->entryCount);
+ for (i=0; i<zip->entryCount; i++) {
+ fprintf(to, " file \"");
+ fwrite(entry->fileName, entry->fileNameLength, 1, to);
+ fprintf(to, "\"\n");
+ entry = entry->next;
+ }
+}
+
+zipentry_t
+iterate_zipfile(zipfile_t file, void** cookie)
+{
+ Zipentry* entry = (Zipentry*)*cookie;
+ if (entry == NULL) {
+ Zipfile* zip = (Zipfile*)file;
+ *cookie = zip->entries;
+ return *cookie;
+ } else {
+ entry = entry->next;
+ *cookie = entry;
+ return entry;
+ }
+}