auto import from //depot/cupcake/@135843
diff --git a/libcutils/Android.mk b/libcutils/Android.mk
new file mode 100644
index 0000000..a43f7e3
--- /dev/null
+++ b/libcutils/Android.mk
@@ -0,0 +1,113 @@
+#
+# 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)
+
+commonSources := \
+	array.c \
+	hashmap.c \
+	atomic.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 \
+	strdup16to8.c \
+	strdup8to16.c \
+	record_stream.c \
+	process_name.c \
+	properties.c \
+	threads.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
+WITH_MINGW :=
+ifeq ($(HOST_OS),windows)
+    ifeq ($(strip $(USE_CYGWIN)),)
+        WITH_MINGW := 1
+    endif
+endif
+# USE_MINGW is defined when we build against Mingw on Linux
+ifneq ($(strip $(USE_MINGW)),)
+    WITH_MINGW := 1
+endif
+
+ifeq ($(WITH_MINGW),1)
+    commonSources += \
+        uio.c
+else
+    commonSources += \
+        mspace.c \
+        selector.c \
+        fdevent.c \
+        tztime.c \
+        tzstrftime.c \
+        adb_networking.c \
+	zygote.c
+endif
+
+
+# Static library for host
+# ========================================================
+LOCAL_MODULE := libcutils
+LOCAL_SRC_FILES := $(commonSources) ashmem-host.c
+LOCAL_LDLIBS := -lpthread
+LOCAL_STATIC_LIBRARIES := liblog
+include $(BUILD_HOST_STATIC_LIBRARY)
+
+
+ifeq ($(TARGET_SIMULATOR),true)
+
+# Shared library for simulator
+# ========================================================
+include $(CLEAR_VARS)
+LOCAL_MODULE := libcutils
+LOCAL_SRC_FILES := $(commonSources) memory.c dlmalloc_stubs.c ashmem-host.c
+LOCAL_LDLIBS := -lpthread
+LOCAL_SHARED_LIBRARIES := liblog
+include $(BUILD_SHARED_LIBRARY)
+
+else #!sim
+
+# Shared and static library for target
+# ========================================================
+include $(CLEAR_VARS)
+LOCAL_MODULE := libcutils
+LOCAL_SRC_FILES := $(commonSources) ashmem-dev.c mq.c
+
+ifeq ($(TARGET_ARCH),arm)
+LOCAL_SRC_FILES += memset32.S atomic-android-arm.S
+else  # !arm
+LOCAL_SRC_FILES += memory.c
+endif # !arm
+
+LOCAL_C_INCLUDES := $(KERNEL_HEADERS)
+LOCAL_STATIC_LIBRARIES := liblog
+include $(BUILD_STATIC_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := libcutils
+LOCAL_WHOLE_STATIC_LIBRARIES := libcutils
+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/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/adb_networking.c b/libcutils/adb_networking.c
new file mode 100644
index 0000000..d819d44
--- /dev/null
+++ b/libcutils/adb_networking.c
@@ -0,0 +1,172 @@
+/* libs/utils/adb_networking.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.
+*/
+
+#define ADB_PORT 5037
+
+#define _GNU_SOURCE     /* for asprintf */
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string.h>
+
+#include <cutils/adb_networking.h>
+#include <cutils/sockets.h>
+#include <cutils/properties.h>
+
+#define ADB_RESPONSE_SIZE 4
+
+/**
+ * Unfortunately, java.net.Socket wants to create it's filedescriptor early
+ * So, this function takes an fd that must be an unconnected
+ * PF_LOCAL SOCK_STREAM
+ */
+int adb_networking_connect_fd(int fd, struct sockaddr_in *p_address)
+{
+    struct sockaddr_in local_addr;
+    socklen_t alen;
+    char *cmd;
+    char buf[ADB_RESPONSE_SIZE + 1];
+    ssize_t count_read;
+    int ret;
+    int err;
+    /* for impl of inet_ntoa below*/
+    union {
+        uint8_t  b[4];
+        uint32_t l;
+    } a;
+
+    /* First, connect to adb */
+   
+    memset(&local_addr, 0, sizeof(local_addr));
+    local_addr.sin_family = AF_INET;
+    local_addr.sin_port = htons(ADB_PORT);
+    local_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+    do {
+        err = connect(fd, (struct sockaddr *) &local_addr, sizeof(local_addr));
+    } while (err < 0 && errno == EINTR);
+
+    if (err < 0) {
+        return -1;
+    }
+
+    a.l = p_address->sin_addr.s_addr;
+
+    // compose the command
+    asprintf(&cmd, "tcp:%u:%u.%u.%u.%u", 
+                (unsigned int)ntohs(p_address->sin_port), 
+                a.b[0],a.b[1],a.b[2],a.b[3]);
+
+    // buf is now the ascii hex length of cmd
+    snprintf(buf, sizeof(buf), "%04X", strlen(cmd));
+
+    // write the 4-byte length
+    do {
+        err = write(fd, buf, 4);        
+    } while (err < 0 && errno == EINTR);
+
+    // write the command
+    do {
+        err = write(fd, cmd, strlen(cmd));        
+    } while (err < 0 && errno == EINTR);
+
+    // read the result
+    do {
+        count_read = read(fd, buf, sizeof(buf) - 1);
+    } while (count_read < 0 && errno != EINTR);
+
+    if (count_read == ADB_RESPONSE_SIZE 
+            && 0 == strncmp(buf, "OKAY", ADB_RESPONSE_SIZE)) {
+        ret = 0;
+    } else {
+        /* what errno here? <shrug? */
+        errno = ENETUNREACH;
+        ret = -1;
+    }
+
+    free(cmd);
+    
+    return ret;
+}
+
+/**
+ * Fills in *p_out_addr and returns 0 on success
+ * Memset's *p_out_addr and returns -1 on fail
+ */
+
+int adb_networking_gethostbyname(const char *name, struct in_addr *p_out_addr)
+{
+    int fd;
+    char *cmd = NULL;
+    char buf[ADB_RESPONSE_SIZE + 1];
+    int err;
+    ssize_t count_read;
+    
+    fd = socket_loopback_client(ADB_PORT, SOCK_STREAM);
+
+    if (fd < 0) {
+        return -1;
+    }
+
+    // compose the command
+    asprintf(&cmd, "dns:%s", name);
+
+    // buf is now the ascii hex length of cmd
+    snprintf(buf, sizeof(buf), "%04X", strlen(cmd));
+
+    // write the 4-byte length
+    do {
+        err = write(fd, buf, 4);        
+    } while (err < 0 && errno == EINTR);
+
+    // write the command
+    do {
+        err = write(fd, cmd, strlen(cmd));        
+    } while (err < 0 && errno == EINTR);
+
+    // read the result
+    do {
+        count_read = read(fd, buf, ADB_RESPONSE_SIZE);
+    } while (count_read < 0 && errno != EINTR);
+
+    if (count_read != ADB_RESPONSE_SIZE 
+            || 0 != strncmp(buf, "OKAY", ADB_RESPONSE_SIZE)) {
+        goto error;
+    }
+
+    // read the actual IP address
+    do {
+        count_read = read(fd, &(p_out_addr->s_addr), sizeof(p_out_addr->s_addr));
+    } while (count_read < 0 && errno != EINTR);
+
+    if (count_read != 4) {
+        goto error;
+    }
+
+    free(cmd);
+    close(fd);
+    return 0;
+error:
+    free(cmd);
+    close(fd);
+    memset(p_out_addr, 0, sizeof(struct in_addr));
+    return -1;
+}
+
diff --git a/libcutils/array.c b/libcutils/array.c
new file mode 100644
index 0000000..ff2c8ff
--- /dev/null
+++ b/libcutils/array.c
@@ -0,0 +1,155 @@
+/*
+ * 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>
+
+#define INITIAL_CAPACITY (4)
+
+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 * 2;
+    
+        // Keep doubling capacity until we surpass necessary capacity. 
+        while (newCapacity < capacity) {
+            newCapacity *= 2;
+        }
+    
+        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 array->contents;
+}
diff --git a/libcutils/ashmem-dev.c b/libcutils/ashmem-dev.c
new file mode 100644
index 0000000..5e158af
--- /dev/null
+++ b/libcutils/ashmem-dev.c
@@ -0,0 +1,85 @@
+/*
+ * 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);
+}
diff --git a/libcutils/ashmem-host.c b/libcutils/ashmem-host.c
new file mode 100644
index 0000000..dbb52bc
--- /dev/null
+++ b/libcutils/ashmem-host.c
@@ -0,0 +1,94 @@
+/*
+ * 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;
+}
diff --git a/libcutils/atomic-android-arm.S b/libcutils/atomic-android-arm.S
new file mode 100644
index 0000000..c56ec5d
--- /dev/null
+++ b/libcutils/atomic-android-arm.S
@@ -0,0 +1,274 @@
+/*
+ * 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.
+ */
+
+#include <machine/cpu-features.h>
+
+/*
+ * NOTE: these atomic operations are SMP safe on all architectures, 
+ * except swap(), see below.
+ */
+
+	.text
+	.align
+	
+    .global android_atomic_write
+
+	.global android_atomic_inc
+	.global android_atomic_dec
+    
+	.global android_atomic_add
+	.global android_atomic_and
+	.global android_atomic_or
+    
+    .global android_atomic_swap
+	
+	.global android_atomic_cmpxchg
+
+/*
+ * ----------------------------------------------------------------------------
+ * int __kernel_cmpxchg(int oldval, int newval, int *ptr)
+ * clobbered: r3, ip, flags
+ * return 0 if a swap was made, non-zero otherwise.
+ */ 
+
+   .equ     kernel_cmpxchg, 0xFFFF0FC0
+   .equ     kernel_atomic_base, 0xFFFF0FFF
+
+/*
+ * ----------------------------------------------------------------------------
+ * android_atomic_write
+ * input: r0=value, r1=address
+ * output: void
+ */
+ 
+android_atomic_write:
+    stmdb   sp!, {r4, lr}
+    mov     r2, r1
+    mov     r1, r0
+1: @ android_atomic_write
+    ldr     r0, [r2]
+    mov     r3, #kernel_atomic_base
+#ifdef __ARM_HAVE_PC_INTERWORK
+    add     lr, pc, #4
+    add     pc, r3, #(kernel_cmpxchg - kernel_atomic_base)
+#else
+    add     r3, r3, #(kernel_cmpxchg - kernel_atomic_base)
+    mov     lr, pc
+    bx      r3
+#endif
+    bcc     1b
+    ldmia   sp!, {r4, lr}
+    bx      lr
+
+/*
+ * ----------------------------------------------------------------------------
+ * android_atomic_inc
+ * input: r0 = address
+ * output: r0 = old value
+ */
+ 
+android_atomic_inc:
+    stmdb   sp!, {r4, lr}
+    mov     r2, r0
+1: @ android_atomic_inc
+    ldr     r0, [r2]
+    mov     r3, #kernel_atomic_base
+#ifdef __ARM_HAVE_PC_INTERWORK
+    add     lr, pc, #4
+    add     r1, r0, #1
+    add     pc, r3, #(kernel_cmpxchg - kernel_atomic_base)
+#else
+    add     r1, r0, #1
+    add     r3, r3, #(kernel_cmpxchg - kernel_atomic_base)
+    mov     lr, pc
+    bx      r3
+#endif
+    bcc     1b
+    sub     r0, r1, #1
+    ldmia   sp!, {r4, lr}
+    bx      lr
+  
+/*
+ * ----------------------------------------------------------------------------
+ * android_atomic_dec
+ * input: r0=address
+ * output: r0 = old value
+ */
+ 
+android_atomic_dec:
+    stmdb   sp!, {r4, lr}
+    mov     r2, r0
+1: @ android_atomic_dec
+    ldr     r0, [r2]
+    mov     r3, #kernel_atomic_base
+#ifdef __ARM_HAVE_PC_INTERWORK
+    add     lr, pc, #4
+    sub     r1, r0, #1
+    add     pc, r3, #(kernel_cmpxchg - kernel_atomic_base)
+#else
+    sub     r1, r0, #1
+    add     r3, r3, #(kernel_cmpxchg - kernel_atomic_base)
+    mov     lr, pc
+    bx      r3
+#endif
+    bcc     1b
+    add     r0, r1, #1
+    ldmia   sp!, {r4, lr}
+    bx      lr
+    
+/*
+ * ----------------------------------------------------------------------------
+ * android_atomic_add
+ * input: r0=value, r1=address
+ * output: r0 = old value
+ */
+
+android_atomic_add:
+    stmdb   sp!, {r4, lr}
+    mov     r2, r1
+    mov     r4, r0
+1: @ android_atomic_add
+    ldr     r0, [r2]
+    mov     r3, #kernel_atomic_base
+#ifdef __ARM_HAVE_PC_INTERWORK
+    add     lr, pc, #4
+    add     r1, r0, r4
+    add     pc, r3, #(kernel_cmpxchg - kernel_atomic_base)
+#else
+    add     r1, r0, r4
+    add     r3, r3, #(kernel_cmpxchg - kernel_atomic_base)
+    mov     lr, pc
+    bx      r3
+#endif
+    bcc     1b
+    sub     r0, r1, r4
+    ldmia   sp!, {r4, lr}
+    bx      lr
+    
+    
+/*
+ * ----------------------------------------------------------------------------
+ * android_atomic_and
+ * input: r0=value, r1=address
+ * output: r0 = old value
+ */
+
+android_atomic_and:
+    stmdb   sp!, {r4, r5, lr}   
+    mov     r2, r1              /* r2 = address */
+    mov     r4, r0              /* r4 = the value */
+1: @ android_atomic_and
+    ldr     r0, [r2]            /* r0 = address[0] */
+    mov     r3, #kernel_atomic_base
+#ifdef __ARM_HAVE_PC_INTERWORK
+    add     lr, pc, #8
+    mov     r5, r0              /* r5 = save address[0] */
+    and     r1, r0, r4          /* r1 = new value */
+    add     pc, r3, #(kernel_cmpxchg - kernel_atomic_base)  /* call cmpxchg() */
+#else
+    mov     r5, r0              /* r5 = save address[0] */
+    and     r1, r0, r4          /* r1 = new value */
+    add     r3, r3, #(kernel_cmpxchg - kernel_atomic_base)  /* call cmpxchg() */
+    mov     lr, pc
+    bx      r3
+#endif
+    bcc     1b
+    mov     r0, r5
+    ldmia   sp!, {r4, r5, lr}
+    bx      lr
+    
+/*
+ * ----------------------------------------------------------------------------
+ * android_atomic_or
+ * input: r0=value, r1=address
+ * output: r0 = old value
+ */
+
+android_atomic_or:
+    stmdb   sp!, {r4, r5, lr}   
+    mov     r2, r1              /* r2 = address */
+    mov     r4, r0              /* r4 = the value */
+1: @ android_atomic_or
+    ldr     r0, [r2]            /* r0 = address[0] */
+    mov     r3, #kernel_atomic_base
+#ifdef __ARM_HAVE_PC_INTERWORK
+    add     lr, pc, #8
+    mov     r5, r0              /* r5 = save address[0] */
+    orr     r1, r0, r4          /* r1 = new value */
+    add     pc, r3, #(kernel_cmpxchg - kernel_atomic_base)  /* call cmpxchg() */
+#else
+    mov     r5, r0              /* r5 = save address[0] */
+    orr     r1, r0, r4          /* r1 = new value */
+    add     r3, r3, #(kernel_cmpxchg - kernel_atomic_base)  /* call cmpxchg() */
+    mov     lr, pc
+    bx      r3
+#endif
+    bcc     1b
+    mov     r0, r5
+    ldmia   sp!, {r4, r5, lr}
+    bx      lr
+
+/*
+ * ----------------------------------------------------------------------------
+ * android_atomic_swap
+ * input: r0=value, r1=address
+ * output: r0 = old value
+ */
+
+/* FIXME: this is not safe on SMP systems 
+ * a general way to do it is to use kernel_cmpxchg */
+
+android_atomic_swap:
+    swp     r0, r0, [r1]
+    bx      lr
+
+/*
+ * ----------------------------------------------------------------------------
+ * android_atomic_cmpxchg
+ * input: r0=oldvalue, r1=newvalue, r2=address
+ * output: r0 = 0 (xchg done) or non-zero (xchg not done)
+ */
+
+android_atomic_cmpxchg:
+    stmdb   sp!, {r4, lr}
+    mov     r4, r0          /* r4 = save oldvalue */
+1: @ android_atomic_cmpxchg
+    mov     r3, #kernel_atomic_base
+#ifdef __ARM_HAVE_PC_INTERWORK
+    add     lr, pc, #4
+    mov     r0, r4          /* r0 = oldvalue */
+    add     pc, r3, #(kernel_cmpxchg - kernel_atomic_base)
+#else
+    mov     r0, r4          /* r0 = oldvalue */
+    add     r3, r3, #(kernel_cmpxchg - kernel_atomic_base)
+    mov     lr, pc
+    bx      r3
+#endif
+    bcs     2f              /* swap was made. we're good, return. */
+    ldr     r3, [r2]        /* swap not made, see if it's because *ptr!=oldvalue */
+    cmp     r3, r4
+    beq     1b
+2: @ android_atomic_cmpxchg
+    ldmia   sp!, {r4, lr}
+    bx      lr
+
+/*
+ * ----------------------------------------------------------------------------
+ * android_atomic_cmpxchg_64
+ * input: r0-r1=oldvalue, r2-r3=newvalue, arg4 (on stack)=address
+ * output: r0 = 0 (xchg done) or non-zero (xchg not done)
+ */
+/* TODO: NEED IMPLEMENTATION FOR THIS ARCHITECTURE */
diff --git a/libcutils/atomic-android-armv6.S b/libcutils/atomic-android-armv6.S
new file mode 100644
index 0000000..64146c1
--- /dev/null
+++ b/libcutils/atomic-android-armv6.S
@@ -0,0 +1,169 @@
+/*
+ * 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.
+ */
+
+
+    .text
+    .align
+    
+    .global android_atomic_write
+    
+    .global android_atomic_inc
+    .global android_atomic_dec
+    
+    .global android_atomic_add
+    .global android_atomic_and
+    .global android_atomic_or
+    
+    .global android_atomic_swap
+    
+    .global android_atomic_cmpxchg
+    
+
+
+/* FIXME: On SMP systems memory barriers may be needed */
+#warning  "this file is not safe with SMP systems"
+
+
+/*
+ * ----------------------------------------------------------------------------
+ * android_atomic_write
+ * input: r0=value, r1=address
+ * output: void
+ */
+
+android_atomic_write:
+1:  ldrex   r12, [r1]
+    strex   r12, r0, [r1]
+    cmp     r12, #0
+    bne     1b
+    bx      lr
+
+/*
+ * ----------------------------------------------------------------------------
+ * android_atomic_inc
+ * input: r0 = address
+ * output: r0 = old value
+ */
+ 
+android_atomic_inc:
+    mov     r12, r0
+1:  ldrex   r0, [r12]
+    add     r2, r0, #1
+    strex   r1, r2, [r12]
+    cmp     r1, #0
+    bxeq    lr
+    b       1b
+
+/*
+ * ----------------------------------------------------------------------------
+ * android_atomic_dec
+ * input: r0=address
+ * output: r0 = old value
+ */
+ 
+android_atomic_dec:
+    mov     r12, r0
+1:  ldrex   r0, [r12]
+    sub     r2, r0, #1
+    strex   r1, r2, [r12]
+    cmp     r1, #0
+    bxeq    lr
+    b       1b
+
+    
+/*
+ * ----------------------------------------------------------------------------
+ * android_atomic_add
+ * input: r0=value, r1=address
+ * output: r0 = old value
+ */
+
+android_atomic_add:
+    mov     r12, r0
+1:  ldrex   r0, [r1]
+    add     r2, r0, r12
+    strex   r3, r2, [r1]
+    cmp     r3, #0
+    bxeq    lr
+    b       1b
+    
+/*
+ * ----------------------------------------------------------------------------
+ * android_atomic_and
+ * input: r0=value, r1=address
+ * output: r0 = old value
+ */
+
+android_atomic_and:
+    mov     r12, r0
+1:  ldrex   r0, [r1]
+    and     r2, r0, r12
+    strex   r3, r2, [r1]
+    cmp     r3, #0
+    bxeq    lr
+    b       1b
+
+    
+/*
+ * ----------------------------------------------------------------------------
+ * android_atomic_or
+ * input: r0=value, r1=address
+ * output: r0 = old value
+ */
+
+android_atomic_or:
+    mov     r12, r0
+1:  ldrex   r0, [r1]
+    orr     r2, r0, r12
+    strex   r3, r2, [r1]
+    cmp     r3, #0
+    bxeq    lr
+    b       1b
+
+/*
+ * ----------------------------------------------------------------------------
+ * android_atomic_swap
+ * input: r0=value, r1=address
+ * output: r0 = old value
+ */
+
+android_atomic_swap:
+    swp     r0, r0, [r1]
+    bx      lr
+
+/*
+ * ----------------------------------------------------------------------------
+ * android_atomic_cmpxchg
+ * input: r0=oldvalue, r1=newvalue, r2=address
+ * output: r0 = 0 (xchg done) or non-zero (xchg not done)
+ */
+
+android_atomic_cmpxchg:
+    mov     r12, r1
+    ldrex   r3, [r2]
+    eors    r0, r0, r3
+    strexeq r0, r12, [r2]
+    bx      lr
+
+
+
+/*
+ * ----------------------------------------------------------------------------
+ * android_atomic_cmpxchg_64
+ * input: r0-r1=oldvalue, r2-r3=newvalue, arg4 (on stack)=address
+ * output: r0 = 0 (xchg done) or non-zero (xchg not done)
+ */
+/* TODO: NEED IMPLEMENTATION FOR THIS ARCHITECTURE */
diff --git a/libcutils/atomic.c b/libcutils/atomic.c
new file mode 100644
index 0000000..65d7af0
--- /dev/null
+++ b/libcutils/atomic.c
@@ -0,0 +1,335 @@
+/*
+ * 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
+
+/*****************************************************************************/
+#if defined(HAVE_MACOSX_IPC)
+
+#include <libkern/OSAtomic.h>
+
+void android_atomic_write(int32_t value, volatile int32_t* addr) {
+    int32_t oldValue;
+    do {
+        oldValue = *addr;
+    } while (OSAtomicCompareAndSwap32Barrier(oldValue, value, (int32_t*)addr) == 0);
+}
+
+int32_t android_atomic_inc(volatile int32_t* addr) {
+    return OSAtomicIncrement32Barrier((int32_t*)addr)-1;
+}
+
+int32_t android_atomic_dec(volatile int32_t* addr) {
+    return OSAtomicDecrement32Barrier((int32_t*)addr)+1;
+}
+
+int32_t android_atomic_add(int32_t value, volatile int32_t* addr) {
+    return OSAtomicAdd32Barrier(value, (int32_t*)addr)-value;
+}
+
+int32_t android_atomic_and(int32_t value, volatile int32_t* addr) {
+    int32_t oldValue;
+    do {
+        oldValue = *addr;
+    } while (OSAtomicCompareAndSwap32Barrier(oldValue, oldValue&value, (int32_t*)addr) == 0);
+    return oldValue;
+}
+
+int32_t android_atomic_or(int32_t value, volatile int32_t* addr) {
+    int32_t oldValue;
+    do {
+        oldValue = *addr;
+    } while (OSAtomicCompareAndSwap32Barrier(oldValue, oldValue|value, (int32_t*)addr) == 0);
+    return oldValue;
+}
+
+int32_t android_atomic_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_cmpxchg(int32_t oldvalue, int32_t newvalue, volatile int32_t* addr) {
+    return OSAtomicCompareAndSwap32Barrier(oldvalue, newvalue, (int32_t*)addr) == 0;
+}
+
+#if defined(__ppc__)        \
+    || defined(__PPC__)     \
+    || defined(__powerpc__) \
+    || defined(__powerpc)   \
+    || defined(__POWERPC__) \
+    || defined(_M_PPC)      \
+    || defined(__PPC)
+#define NEED_QUASIATOMICS 1
+#else
+
+int android_quasiatomic_cmpxchg_64(int64_t oldvalue, int64_t newvalue,
+        volatile int64_t* addr) {
+    return OSAtomicCompareAndSwap64Barrier(oldvalue, newvalue,
+            (int64_t*)addr) == 0;
+}
+
+int64_t android_quasiatomic_swap_64(int64_t value, volatile int64_t* addr) {
+    int64_t oldValue;
+    do {
+        oldValue = *addr;
+    } while (android_quasiatomic_cmpxchg_64(oldValue, value, addr));
+    return oldValue;
+}
+
+int64_t android_quasiatomic_read_64(volatile int64_t* addr) {
+    return OSAtomicAdd64Barrier(0, addr);
+}    
+
+#endif
+
+
+/*****************************************************************************/
+#elif defined(__i386__) || defined(__x86_64__)
+
+void android_atomic_write(int32_t value, volatile int32_t* addr) {
+    int32_t oldValue;
+    do {
+        oldValue = *addr;
+    } while (android_atomic_cmpxchg(oldValue, value, addr));
+}
+
+int32_t android_atomic_inc(volatile int32_t* addr) {
+    int32_t oldValue;
+    do {
+        oldValue = *addr;
+    } while (android_atomic_cmpxchg(oldValue, oldValue+1, addr));
+    return oldValue;
+}
+
+int32_t android_atomic_dec(volatile int32_t* addr) {
+    int32_t oldValue;
+    do {
+        oldValue = *addr;
+    } while (android_atomic_cmpxchg(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_cmpxchg(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_cmpxchg(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_cmpxchg(oldValue, oldValue|value, addr));
+    return oldValue;
+}
+
+int32_t android_atomic_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_cmpxchg(int32_t oldvalue, int32_t newvalue, volatile int32_t* addr) {
+    int xchg;
+    asm volatile
+    (
+    "   lock; cmpxchg %%ecx, (%%edx);"
+    "   setne %%al;"
+    "   andl $1, %%eax"
+    : "=a" (xchg)
+    : "a" (oldvalue), "c" (newvalue), "d" (addr)
+    );
+    return xchg;
+}
+
+#define NEED_QUASIATOMICS 1
+
+/*****************************************************************************/
+#elif __arm__
+// Most of the implementation is in atomic-android-arm.s.
+
+// on the device, we implement the 64-bit atomic operations through
+// mutex locking. normally, this is bad because we must initialize
+// a pthread_mutex_t before being able to use it, and this means
+// having to do an initialization check on each function call, and
+// that's where really ugly things begin...
+//
+// BUT, as a special twist, we take advantage of the fact that in our
+// pthread library, a mutex is simply a volatile word whose value is always
+// initialized to 0. In other words, simply declaring a static mutex
+// object initializes it !
+//
+// another twist is that we use a small array of mutexes to dispatch
+// the contention locks from different memory addresses
+//
+
+#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]
+
+
+int64_t android_quasiatomic_swap_64(int64_t value, volatile int64_t* addr) {
+    int64_t oldValue;
+    pthread_mutex_t*  lock = SWAP_LOCK(addr);
+
+    pthread_mutex_lock(lock);
+
+    oldValue = *addr;
+    *addr    = value;
+
+    pthread_mutex_unlock(lock);
+    return oldValue;
+}
+
+int android_quasiatomic_cmpxchg_64(int64_t oldvalue, int64_t newvalue,
+        volatile int64_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;
+}
+
+int64_t android_quasiatomic_read_64(volatile int64_t* addr) {
+    int64_t result;
+    pthread_mutex_t*  lock = SWAP_LOCK(addr);
+
+    pthread_mutex_lock(lock);
+    result = *addr;
+    pthread_mutex_unlock(lock);
+    return result;
+}    
+
+#else
+
+#error "Unsupported atomic operations for this platform"
+
+#endif
+
+
+
+#if NEED_QUASIATOMICS
+
+/* Note that a spinlock is *not* a good idea in general
+ * since they can introduce subtle issues. For example,
+ * a real-time thread trying to acquire a spinlock already
+ * acquired by another thread will never yeld, making the
+ * CPU loop endlessly!
+ *
+ * However, this code is only used on the Linux simulator
+ * so it's probably ok for us.
+ *
+ * The alternative is to use a pthread mutex, but
+ * these must be initialized before being used, and
+ * then you have the problem of lazily initializing
+ * a mutex without any other synchronization primitive.
+ */
+
+/* global spinlock for all 64-bit quasiatomic operations */
+static int32_t quasiatomic_spinlock = 0;
+
+int android_quasiatomic_cmpxchg_64(int64_t oldvalue, int64_t newvalue,
+        volatile int64_t* addr) {
+    int result;
+    
+    while (android_atomic_cmpxchg(0, 1, &quasiatomic_spinlock)) {
+#ifdef HAVE_WIN32_THREADS
+        Sleep(0);
+#else        
+        sched_yield();
+#endif        
+    }
+
+    if (*addr == oldvalue) {
+        *addr = newvalue;
+        result = 0;
+    } else {
+        result = 1;
+    }
+
+    android_atomic_swap(0, &quasiatomic_spinlock);
+
+    return result;
+}
+
+int64_t android_quasiatomic_read_64(volatile int64_t* addr) {
+    int64_t result;
+    
+    while (android_atomic_cmpxchg(0, 1, &quasiatomic_spinlock)) {
+#ifdef HAVE_WIN32_THREADS
+        Sleep(0);
+#else
+        sched_yield();
+#endif
+    }
+
+    result = *addr;
+    android_atomic_swap(0, &quasiatomic_spinlock);
+
+    return result;
+}
+
+int64_t android_quasiatomic_swap_64(int64_t value, volatile int64_t* addr) {
+    int64_t result;
+    
+    while (android_atomic_cmpxchg(0, 1, &quasiatomic_spinlock)) {
+#ifdef HAVE_WIN32_THREADS
+        Sleep(0);
+#else
+        sched_yield();
+#endif
+    }
+
+    result = *addr;
+    *addr = value;
+    android_atomic_swap(0, &quasiatomic_spinlock);
+
+    return result;
+}
+
+#endif
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/fdevent.c b/libcutils/fdevent.c
new file mode 100644
index 0000000..4cf46fa
--- /dev/null
+++ b/libcutils/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 <cutils/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/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/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..ef6c7e6
--- /dev/null
+++ b/libcutils/memory.c
@@ -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.
+ */
+
+#include <cutils/memory.h>
+
+void android_memset16(uint16_t* dst, uint16_t value, size_t size)
+{
+    size >>= 1;
+    while (size--) {
+        *dst++ = value;
+    }
+}
+
+void android_memset32(uint32_t* dst, uint32_t value, size_t size)
+{
+    size >>= 2;
+    while (size--) {
+        *dst++ = value;
+    }
+}
+
+#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/memset32.S b/libcutils/memset32.S
new file mode 100644
index 0000000..4697265
--- /dev/null
+++ b/libcutils/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/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..8fd5de7
--- /dev/null
+++ b/libcutils/mspace.c
@@ -0,0 +1,246 @@
+/* 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/libc/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_name(size_t starting_capacity,
+    size_t max_capacity, int locked, char const * name) {
+  int fd, ret;
+  struct mspace_contig_state *cs;
+  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);
+
+  /* Reserve some space for the information that our MORECORE needs.
+   */
+  cs = base;
+
+  /* Create the mspace, pointing to the memory we just reserved.
+   */
+  m = create_mspace_with_base(base + sizeof(*cs), starting_capacity, locked);
+  if (m == (mspace)0)
+    goto error;
+
+  /* Make sure that m is in the same page as cs.
+   */
+  assert(((uintptr_t)m & (uintptr_t)~(pagesize-1)) == (uintptr_t)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(prot_brk, cs->top - prot_brk, PROT_NONE) < 0)
+      goto error;
+  }
+
+  cs->m = m;
+  cs->magic = CONTIG_STATE_MAGIC;
+
+  return (mspace)m;
+
+error:
+  munmap(base, max_capacity);
+  return (mspace)0;
+}
+
+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;
+}
+#endif
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..17f52e2
--- /dev/null
+++ b/libcutils/process_name.c
@@ -0,0 +1,75 @@
+/*
+ * 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>
+
+#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.
+    char* copy = (char*) malloc(strlen(new_name) + 1);
+    strcpy(copy, new_name);
+    process_name = (const char*) copy;
+
+    // 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..547cc6d
--- /dev/null
+++ b/libcutils/properties.c
@@ -0,0 +1,368 @@
+/*
+ * 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 s;
+    int r;
+    
+    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;
+    }
+
+    close(s);
+    return r;
+}
+
+int property_set(const char *key, const char *value)
+{
+    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;
+    
+    msg.cmd = PROP_MSG_SETPROP;
+    strcpy((char*) msg.name, key);
+    strcpy((char*) msg.value, value);
+
+    return send_prop_msg(&msg);
+}
+
+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/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..fadaabe
--- /dev/null
+++ b/libcutils/strdup16to8.c
@@ -0,0 +1,104 @@
+/* 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 <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;
+
+   while (len--) {
+       unsigned int uic = *utf16Str++;
+
+       if (uic > 0x07ff)
+           utf8Len += 3;
+       else if (uic > 0x7f || uic == 0)
+           utf8Len += 2;
+       else
+           utf8Len++;
+   }
+   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;
+
+    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.
+ *
+ * Make sure you allocate "dest" with the result of strblen16to8(),
+ * not just "strlen16()".
+ */
+char * strndup16to8 (const char16_t* s, size_t n)
+{
+    char *ret;
+
+    if (s == NULL) {
+        return NULL;
+    }
+
+    ret = malloc(strnlen16to8(s, n) + 1);
+
+    strncpy16to8 (ret, s, n);
+    
+    return ret;    
+}
diff --git a/libcutils/strdup8to16.c b/libcutils/strdup8to16.c
new file mode 100644
index 0000000..8654b04
--- /dev/null
+++ b/libcutils/strdup8to16.c
@@ -0,0 +1,209 @@
+/* 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>
+
+/* 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);
+
+    // 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..29c5015
--- /dev/null
+++ b/libcutils/tzstrftime.c
@@ -0,0 +1,834 @@
+#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':
+				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..93bbb29
--- /dev/null
+++ b/libcutils/tztime.c
@@ -0,0 +1,1915 @@
+/*
+** 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 */
+
+#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, 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;
+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;
+{
+	int	number0;
+
+	number0 = *number;
+	*number += delta;
+	return (*number < number0) != (delta < 0);
+}
+
+static int
+long_increment_overflow(number, delta)
+long *	number;
+int	delta;
+{
+	long	number0;
+
+	number0 = *number;
+	*number += delta;
+	return (*number < number0) != (delta < 0);
+}
+
+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) {
+				++t;
+				if (t <= lo)
+					return WRONG;
+				++lo;
+			} else if (t == hi) {
+				--t;
+				if (t >= hi)
+					return WRONG;
+				--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 = ' ';
+        }
+    }
+}
+
+
+