am 723b410f: am 48d59acc: init.usb.rc: Add new system property for configfs

* commit '723b410ff50920cea270b282c19edcfaf6f444e2':
  init.usb.rc: Add new system property for configfs
diff --git a/.gitignore b/.gitignore
index b25c15b..2f836aa 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,2 @@
 *~
+*.pyc
diff --git a/adb/Android.mk b/adb/Android.mk
index 425bf9b..bbdc2ea 100644
--- a/adb/Android.mk
+++ b/adb/Android.mk
@@ -5,19 +5,24 @@
 
 LOCAL_PATH:= $(call my-dir)
 
-ifeq ($(HOST_OS),windows)
-  adb_host_clang := false  # libc++ for mingw not ready yet.
-else
-  adb_host_clang := true
-endif
+adb_host_sanitize :=
+adb_target_sanitize :=
 
 adb_version := $(shell git -C $(LOCAL_PATH) rev-parse --short=12 HEAD 2>/dev/null)-android
 
 ADB_COMMON_CFLAGS := \
-    -Wall -Werror \
+    -Wall -Wextra -Werror \
     -Wno-unused-parameter \
+    -Wno-missing-field-initializers \
     -DADB_REVISION='"$(adb_version)"' \
 
+# Define windows.h and tchar.h Unicode preprocessor symbols so that
+# CreateFile(), _tfopen(), etc. map to versions that take wchar_t*, breaking the
+# build if you accidentally pass char*. Fix by calling like:
+# CreateFileW(widen(utf8).c_str()).
+ADB_COMMON_windows_CFLAGS := \
+    -DUNICODE=1 -D_UNICODE=1 \
+
 # libadb
 # =========================================================
 
@@ -45,9 +50,14 @@
 
 LIBADB_CFLAGS := \
     $(ADB_COMMON_CFLAGS) \
-    -Wno-missing-field-initializers \
     -fvisibility=hidden \
 
+LIBADB_linux_CFLAGS := \
+    -std=c++14 \
+
+LIBADB_windows_CFLAGS := \
+    $(ADB_COMMON_windows_CFLAGS) \
+
 LIBADB_darwin_SRC_FILES := \
     fdevent.cpp \
     get_my_path_darwin.cpp \
@@ -59,10 +69,15 @@
     usb_linux.cpp \
 
 LIBADB_windows_SRC_FILES := \
-    get_my_path_windows.cpp \
     sysdeps_win32.cpp \
     usb_windows.cpp \
 
+LIBADB_TEST_linux_SRCS := \
+    fdevent_test.cpp \
+
+LIBADB_TEST_darwin_SRCS := \
+    fdevent_test.cpp \
+
 include $(CLEAR_VARS)
 LOCAL_CLANG := true
 LOCAL_MODULE := libadbd
@@ -72,30 +87,39 @@
     adb_auth_client.cpp \
     fdevent.cpp \
     jdwp_service.cpp \
-    qemu_tracing.cpp \
     usb_linux_client.cpp \
 
-LOCAL_SHARED_LIBRARIES := libbase
+LOCAL_SANITIZE := $(adb_target_sanitize)
+
+# Even though we're building a static library (and thus there's no link step for
+# this to take effect), this adds the includes to our path.
+LOCAL_STATIC_LIBRARIES := libbase
 
 include $(BUILD_STATIC_LIBRARY)
 
 include $(CLEAR_VARS)
-LOCAL_CLANG := $(adb_host_clang)
 LOCAL_MODULE := libadb
+LOCAL_MODULE_HOST_OS := darwin linux windows
 LOCAL_CFLAGS := $(LIBADB_CFLAGS) -DADB_HOST=1
+LOCAL_CFLAGS_windows := $(LIBADB_windows_CFLAGS)
+LOCAL_CFLAGS_linux := $(LIBADB_linux_CFLAGS)
 LOCAL_SRC_FILES := \
     $(LIBADB_SRC_FILES) \
-    $(LIBADB_$(HOST_OS)_SRC_FILES) \
     adb_auth_host.cpp \
 
-LOCAL_SHARED_LIBRARIES := libbase
+LOCAL_SRC_FILES_darwin := $(LIBADB_darwin_SRC_FILES)
+LOCAL_SRC_FILES_linux := $(LIBADB_linux_SRC_FILES)
+LOCAL_SRC_FILES_windows := $(LIBADB_windows_SRC_FILES)
+
+LOCAL_SANITIZE := $(adb_host_sanitize)
 
 # Even though we're building a static library (and thus there's no link step for
-# this to take effect), this adds the SSL includes to our path.
-LOCAL_STATIC_LIBRARIES := libcrypto_static
+# this to take effect), this adds the includes to our path.
+LOCAL_STATIC_LIBRARIES := libcrypto_static libbase
 
-ifeq ($(HOST_OS),windows)
-    LOCAL_C_INCLUDES += development/host/windows/usb/api/
+LOCAL_C_INCLUDES_windows := development/host/windows/usb/api/
+ifneq ($(HOST_OS),windows)
+    LOCAL_MULTILIB := 64
 endif
 
 include $(BUILD_HOST_STATIC_LIBRARY)
@@ -104,29 +128,37 @@
 LOCAL_CLANG := true
 LOCAL_MODULE := adbd_test
 LOCAL_CFLAGS := -DADB_HOST=0 $(LIBADB_CFLAGS)
-LOCAL_SRC_FILES := $(LIBADB_TEST_SRCS)
+LOCAL_SRC_FILES := \
+    $(LIBADB_TEST_SRCS) \
+    $(LIBADB_TEST_linux_SRCS) \
+
+LOCAL_SANITIZE := $(adb_target_sanitize)
 LOCAL_STATIC_LIBRARIES := libadbd
 LOCAL_SHARED_LIBRARIES := liblog libbase libcutils
 include $(BUILD_NATIVE_TEST)
 
+# adb_test
+# =========================================================
+
 include $(CLEAR_VARS)
-LOCAL_CLANG := $(adb_host_clang)
 LOCAL_MODULE := adb_test
 LOCAL_CFLAGS := -DADB_HOST=1 $(LIBADB_CFLAGS)
+LOCAL_CFLAGS_windows := $(LIBADB_windows_CFLAGS)
+LOCAL_CFLAGS_linux := $(LIBADB_linux_CFLAGS)
 LOCAL_SRC_FILES := $(LIBADB_TEST_SRCS) services.cpp
+LOCAL_SRC_FILES_linux := $(LIBADB_TEST_linux_SRCS)
+LOCAL_SRC_FILES_darwin := $(LIBADB_TEST_darwin_SRCS)
+LOCAL_SANITIZE := $(adb_host_sanitize)
 LOCAL_SHARED_LIBRARIES := liblog libbase
 LOCAL_STATIC_LIBRARIES := \
     libadb \
     libcrypto_static \
     libcutils \
 
-ifeq ($(HOST_OS),linux)
-  LOCAL_LDLIBS += -lrt -ldl -lpthread
-endif
-
-ifeq ($(HOST_OS),darwin)
-  LOCAL_LDLIBS += -framework CoreFoundation -framework IOKit
-endif
+LOCAL_LDLIBS_linux := -lrt -ldl -lpthread
+LOCAL_LDLIBS_darwin := -framework CoreFoundation -framework IOKit
+LOCAL_LDLIBS_windows := -lws2_32 -luserenv
+LOCAL_STATIC_LIBRARIES_windows := AdbWinApi
 
 include $(BUILD_HOST_NATIVE_TEST)
 
@@ -135,10 +167,12 @@
 
 ifeq ($(HOST_OS),linux)
 include $(CLEAR_VARS)
-LOCAL_CLANG := $(adb_host_clang)
 LOCAL_MODULE := adb_device_tracker_test
 LOCAL_CFLAGS := -DADB_HOST=1 $(LIBADB_CFLAGS)
+LOCAL_CFLAGS_windows := $(LIBADB_windows_CFLAGS)
+LOCAL_CFLAGS_linux := $(LIBADB_linux_CFLAGS)
 LOCAL_SRC_FILES := test_track_devices.cpp
+LOCAL_SANITIZE := $(adb_host_sanitize)
 LOCAL_SHARED_LIBRARIES := liblog libbase
 LOCAL_STATIC_LIBRARIES := libadb libcrypto_static libcutils
 LOCAL_LDLIBS += -lrt -ldl -lpthread
@@ -149,25 +183,19 @@
 # =========================================================
 include $(CLEAR_VARS)
 
-ifeq ($(HOST_OS),linux)
-  LOCAL_LDLIBS += -lrt -ldl -lpthread
-  LOCAL_CFLAGS += -DWORKAROUND_BUG6558362
-endif
+LOCAL_LDLIBS_linux := -lrt -ldl -lpthread
 
-ifeq ($(HOST_OS),darwin)
-  LOCAL_LDLIBS += -lpthread -framework CoreFoundation -framework IOKit -framework Carbon
-  LOCAL_CFLAGS += -Wno-sizeof-pointer-memaccess -Wno-unused-parameter
-endif
+LOCAL_LDLIBS_darwin := -lpthread -framework CoreFoundation -framework IOKit -framework Carbon
+LOCAL_CFLAGS_darwin := -Wno-sizeof-pointer-memaccess -Wno-unused-parameter
 
-ifeq ($(HOST_OS),windows)
-  LOCAL_LDLIBS += -lws2_32 -lgdi32
-  EXTRA_STATIC_LIBS := AdbWinApi
-endif
-
-LOCAL_CLANG := $(adb_host_clang)
+# Use wmain instead of main
+LOCAL_LDFLAGS_windows := -municode
+LOCAL_LDLIBS_windows := -lws2_32 -lgdi32
+LOCAL_STATIC_LIBRARIES_windows := AdbWinApi
+LOCAL_REQUIRED_MODULES_windows := AdbWinApi AdbWinUsbApi
 
 LOCAL_SRC_FILES := \
-    adb_main.cpp \
+    client/main.cpp \
     console.cpp \
     commandline.cpp \
     adb_client.cpp \
@@ -179,21 +207,22 @@
     -D_GNU_SOURCE \
     -DADB_HOST=1 \
 
+LOCAL_CFLAGS_windows := \
+    $(ADB_COMMON_windows_CFLAGS)
+
 LOCAL_MODULE := adb
 LOCAL_MODULE_TAGS := debug
+LOCAL_MODULE_HOST_OS := darwin linux windows
 
+LOCAL_SANITIZE := $(adb_host_sanitize)
 LOCAL_STATIC_LIBRARIES := \
     libadb \
     libbase \
     libcrypto_static \
     libcutils \
     liblog \
-    $(EXTRA_STATIC_LIBS) \
 
-# libc++ not available on windows yet
-ifneq ($(HOST_OS),windows)
-    LOCAL_CXX_STL := libc++_static
-endif
+LOCAL_CXX_STL := libc++_static
 
 # Don't add anything here, we don't want additional shared dependencies
 # on the host adb tool, and shared libraries that link against libc++
@@ -204,12 +233,6 @@
 
 $(call dist-for-goals,dist_files sdk,$(LOCAL_BUILT_MODULE))
 
-ifeq ($(HOST_OS),windows)
-$(LOCAL_INSTALLED_MODULE): \
-    $(HOST_OUT_EXECUTABLES)/AdbWinApi.dll \
-    $(HOST_OUT_EXECUTABLES)/AdbWinUsbApi.dll
-endif
-
 
 # adbd device daemon
 # =========================================================
@@ -219,7 +242,7 @@
 LOCAL_CLANG := true
 
 LOCAL_SRC_FILES := \
-    adb_main.cpp \
+    daemon/main.cpp \
     services.cpp \
     file_sync_service.cpp \
     framebuffer_service.cpp \
@@ -241,20 +264,23 @@
 
 LOCAL_MODULE := adbd
 
+LOCAL_INIT_RC := adbd.rc
+
 LOCAL_FORCE_STATIC_EXECUTABLE := true
 LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT_SBIN)
 LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_SBIN_UNSTRIPPED)
 LOCAL_C_INCLUDES += system/extras/ext4_utils
 
+LOCAL_SANITIZE := $(adb_target_sanitize)
 LOCAL_STATIC_LIBRARIES := \
     libadbd \
     libbase \
     libfs_mgr \
     liblog \
-    libcutils \
-    libc \
     libmincrypt \
     libselinux \
     libext4_utils_static \
+    libcutils \
+    libbase \
 
 include $(BUILD_EXECUTABLE)
diff --git a/adb/CPPLINT.cfg b/adb/CPPLINT.cfg
index 9b906e8..f496490 100644
--- a/adb/CPPLINT.cfg
+++ b/adb/CPPLINT.cfg
@@ -1,2 +1,2 @@
 set noparent
-filter=-build/header_guard,-build/include,-readability/function
+filter=-build/header_guard,-build/include,-readability/function,-whitespace/indent
diff --git a/adb/SERVICES.TXT b/adb/SERVICES.TXT
index 63000f2..30c21f7 100644
--- a/adb/SERVICES.TXT
+++ b/adb/SERVICES.TXT
@@ -237,7 +237,7 @@
     Note that there is no single-shot service to retrieve the list only once.
 
 sync:
-    This starts the file synchronisation service, used to implement "adb push"
+    This starts the file synchronization service, used to implement "adb push"
     and "adb pull". Since this service is pretty complex, it will be detailed
     in a companion document named SYNC.TXT
 
diff --git a/adb/SYNC.TXT b/adb/SYNC.TXT
index e74d217..06d7804 100644
--- a/adb/SYNC.TXT
+++ b/adb/SYNC.TXT
@@ -25,12 +25,9 @@
 
 The following sync requests are accepted:
 LIST - List the files in a folder
+RECV - Retrieve a file from device
 SEND - Send a file to device
-RECV - Retreive a file from device
-
-Not yet documented:
 STAT - Stat a file
-ULNK - Unlink (remove) a file. (Not currently supported)
 
 For all of the sync request above the must be followed by length number of
 bytes containing an utf-8 string with a remote filename.
@@ -40,7 +37,7 @@
 respond with zero or more directory entries or "dents".
 
 The directory entries will be returned in the following form
-1. A four-byte sync response id beeing "DENT"
+1. A four-byte sync response id "DENT"
 2. A four-byte integer representing file mode.
 3. A four-byte integer representing file size.
 4. A four-byte integer representing last modified time.
@@ -60,13 +57,13 @@
   adb push disk_image /some_block_device
 to work.
 
-After this the actual file is sent in chunks. Each chucks has the following
+After this the actual file is sent in chunks. Each chunk has the following
 format.
 A sync request with id "DATA" and length equal to the chunk size. After
 follows chunk size number of bytes. This is repeated until the file is
-transfered. Each chunk must not be larger than 64k.
+transferred. Each chunk must not be larger than 64k.
 
-When the file is tranfered a sync request "DONE" is sent, where length is set
+When the file is transferred a sync request "DONE" is sent, where length is set
 to the last modified time for the file. The server responds to this last
 request (but not to chuck requests) with an "OKAY" sync response (length can
 be ignored).
@@ -77,8 +74,8 @@
 the file that will be returned. Just as for the SEND sync request the file
 received is split up into chunks. The sync response id is "DATA" and length is
 the chuck size. After follows chunk size number of bytes. This is repeated
-until the file is transfered. Each chuck will not be larger than 64k.
+until the file is transferred. Each chuck will not be larger than 64k.
 
-When the file is transfered a sync resopnse "DONE" is retrieved where the
+When the file is transferred a sync response "DONE" is retrieved where the
 length can be ignored.
 
diff --git a/adb/__init__.py b/adb/__init__.py
new file mode 100644
index 0000000..6b509c6
--- /dev/null
+++ b/adb/__init__.py
@@ -0,0 +1,17 @@
+#
+# Copyright (C) 2015 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.
+#
+from __future__ import absolute_import
+from .device import *  # pylint: disable=wildcard-import
diff --git a/adb/adb.cpp b/adb/adb.cpp
index c09aee3..eb01da8 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -31,13 +31,18 @@
 #include <time.h>
 
 #include <string>
+#include <vector>
+#include <unordered_map>
 
+#include <base/logging.h>
+#include <base/macros.h>
 #include <base/stringprintf.h>
 #include <base/strings.h>
 
 #include "adb_auth.h"
 #include "adb_io.h"
 #include "adb_listeners.h"
+#include "adb_utils.h"
 #include "transport.h"
 
 #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
@@ -48,16 +53,33 @@
 #include <sys/mount.h>
 #endif
 
-ADB_MUTEX_DEFINE( D_lock );
-
-int HOST = 0;
+ADB_MUTEX_DEFINE(D_lock);
 
 #if !ADB_HOST
-const char *adb_device_banner = "device";
+const char* adb_device_banner = "device";
+static android::base::LogdLogger gLogdLogger;
+#else
+const char* adb_device_banner = "host";
 #endif
 
-void fatal(const char *fmt, ...)
-{
+void AdbLogger(android::base::LogId id, android::base::LogSeverity severity,
+               const char* tag, const char* file, unsigned int line,
+               const char* message) {
+    android::base::StderrLogger(id, severity, tag, file, line, message);
+#if !ADB_HOST
+    gLogdLogger(id, severity, tag, file, line, message);
+#endif
+}
+
+std::string adb_version() {
+    // Don't change the format of this --- it's parsed by ddmlib.
+    return android::base::StringPrintf("Android Debug Bridge version %d.%d.%d\n"
+                                       "Revision %s\n",
+                                       ADB_VERSION_MAJOR, ADB_VERSION_MINOR, ADB_SERVER_VERSION,
+                                       ADB_REVISION);
+}
+
+void fatal(const char *fmt, ...) {
     va_list ap;
     va_start(ap, fmt);
     fprintf(stderr, "error: ");
@@ -67,8 +89,7 @@
     exit(-1);
 }
 
-void fatal_errno(const char *fmt, ...)
-{
+void fatal_errno(const char* fmt, ...) {
     va_list ap;
     va_start(ap, fmt);
     fprintf(stderr, "error: %s: ", strerror(errno));
@@ -79,7 +100,7 @@
 }
 
 #if !ADB_HOST
-void start_device_log(void) {
+static std::string get_log_file_name() {
     struct tm now;
     time_t t;
     tzset();
@@ -89,17 +110,22 @@
     char timestamp[PATH_MAX];
     strftime(timestamp, sizeof(timestamp), "%Y-%m-%d-%H-%M-%S", &now);
 
-    std::string path = android::base::StringPrintf("/data/adb/adb-%s-%d", timestamp, getpid());
-    int fd = unix_open(path.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0640);
+    return android::base::StringPrintf("/data/adb/adb-%s-%d", timestamp,
+                                       getpid());
+}
+
+void start_device_log(void) {
+    int fd = unix_open(get_log_file_name().c_str(),
+                       O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0640);
     if (fd == -1) {
         return;
     }
 
-    // redirect stdout and stderr to the log file
+    // Redirect stdout and stderr to the log file.
     dup2(fd, STDOUT_FILENO);
     dup2(fd, STDERR_FILENO);
     fprintf(stderr, "--- adb starting (pid %d) ---\n", getpid());
-    adb_close(fd);
+    unix_close(fd);
 }
 #endif
 
@@ -130,71 +156,64 @@
 #endif
 }
 
-// Split the comma/space/colum/semi-column separated list of tags from the trace
-// setting and build the trace mask from it. note that '1' and 'all' are special
-// cases to enable all tracing.
+// Split the space separated list of tags from the trace setting and build the
+// trace mask from it. note that '1' and 'all' are special cases to enable all
+// tracing.
 //
 // adb's trace setting comes from the ADB_TRACE environment variable, whereas
 // adbd's comes from the system property persist.adb.trace_mask.
-void adb_trace_init() {
+static void setup_trace_mask() {
     const std::string trace_setting = get_trace_setting();
 
-    static const struct {
-        const char*  tag;
-        int           flag;
-    } tags[] = {
-        { "1", 0 },
-        { "all", 0 },
-        { "adb", TRACE_ADB },
-        { "sockets", TRACE_SOCKETS },
-        { "packets", TRACE_PACKETS },
-        { "rwx", TRACE_RWX },
-        { "usb", TRACE_USB },
-        { "sync", TRACE_SYNC },
-        { "sysdeps", TRACE_SYSDEPS },
-        { "transport", TRACE_TRANSPORT },
-        { "jdwp", TRACE_JDWP },
-        { "services", TRACE_SERVICES },
-        { "auth", TRACE_AUTH },
-        { NULL, 0 }
-    };
+    std::unordered_map<std::string, int> trace_flags = {
+        {"1", 0},
+        {"all", 0},
+        {"adb", TRACE_ADB},
+        {"sockets", TRACE_SOCKETS},
+        {"packets", TRACE_PACKETS},
+        {"rwx", TRACE_RWX},
+        {"usb", TRACE_USB},
+        {"sync", TRACE_SYNC},
+        {"sysdeps", TRACE_SYSDEPS},
+        {"transport", TRACE_TRANSPORT},
+        {"jdwp", TRACE_JDWP},
+        {"services", TRACE_SERVICES},
+        {"auth", TRACE_AUTH}};
 
-    if (trace_setting.empty()) {
-        return;
-    }
-
-    // Use a comma/colon/semi-colon/space separated list
-    const char* p = trace_setting.c_str();
-    while (*p) {
-        int  len, tagn;
-
-        const char* q = strpbrk(p, " ,:;");
-        if (q == NULL) {
-            q = p + strlen(p);
+    std::vector<std::string> elements = android::base::Split(trace_setting, " ");
+    for (const auto& elem : elements) {
+        const auto& flag = trace_flags.find(elem);
+        if (flag == trace_flags.end()) {
+            D("Unknown trace flag: %s\n", flag->first.c_str());
+            continue;
         }
-        len = q - p;
 
-        for (tagn = 0; tags[tagn].tag != NULL; tagn++) {
-            int  taglen = strlen(tags[tagn].tag);
-
-            if (len == taglen && !memcmp(tags[tagn].tag, p, len)) {
-                int  flag = tags[tagn].flag;
-                if (flag == 0) {
-                    adb_trace_mask = ~0;
-                    return;
-                }
-                adb_trace_mask |= (1 << flag);
-                break;
-            }
+        if (flag->second == 0) {
+            // 0 is used for the special values "1" and "all" that enable all
+            // tracing.
+            adb_trace_mask = ~0;
+            return;
+        } else {
+            adb_trace_mask |= 1 << flag->second;
         }
-        p = q;
-        if (*p)
-            p++;
     }
+}
 
+void adb_trace_init(char** argv) {
 #if !ADB_HOST
-    start_device_log();
+    // Don't open log file if no tracing, since this will block
+    // the crypto unmount of /data
+    if (!get_trace_setting().empty()) {
+        if (isatty(STDOUT_FILENO) == 0) {
+            start_device_log();
+        }
+    }
 #endif
+
+    setup_trace_mask();
+    android::base::InitLogging(argv, AdbLogger);
+
+    D("%s", adb_version().c_str());
 }
 
 apacket* get_apacket(void)
@@ -224,7 +243,12 @@
     D("adb: offline\n");
     //Close the associated usb
     t->online = 0;
-    run_transport_disconnects(t);
+
+    // This is necessary to avoid a race condition that occurred when a transport closes
+    // while a client socket is still active.
+    close_all_sockets(t);
+
+    t->RunDisconnects();
 }
 
 #if DEBUG_PACKETS
@@ -288,45 +312,50 @@
     send_packet(p, t);
 }
 
-static size_t fill_connect_data(char *buf, size_t bufsize)
-{
-#if ADB_HOST
-    return snprintf(buf, bufsize, "host::") + 1;
-#else
-    static const char *cnxn_props[] = {
+std::string get_connection_string() {
+    std::vector<std::string> connection_properties;
+
+#if !ADB_HOST
+    static const char* cnxn_props[] = {
         "ro.product.name",
         "ro.product.model",
         "ro.product.device",
     };
-    static const int num_cnxn_props = ARRAY_SIZE(cnxn_props);
-    int i;
-    size_t remaining = bufsize;
-    size_t len;
 
-    len = snprintf(buf, remaining, "%s::", adb_device_banner);
-    remaining -= len;
-    buf += len;
-    for (i = 0; i < num_cnxn_props; i++) {
+    for (const auto& prop_name : cnxn_props) {
         char value[PROPERTY_VALUE_MAX];
-        property_get(cnxn_props[i], value, "");
-        len = snprintf(buf, remaining, "%s=%s;", cnxn_props[i], value);
-        remaining -= len;
-        buf += len;
+        property_get(prop_name, value, "");
+        connection_properties.push_back(
+            android::base::StringPrintf("%s=%s", prop_name, value));
     }
-
-    return bufsize - remaining + 1;
 #endif
+
+    connection_properties.push_back(android::base::StringPrintf(
+        "features=%s", android::base::Join(supported_features(), ',').c_str()));
+
+    return android::base::StringPrintf(
+        "%s::%s", adb_device_banner,
+        android::base::Join(connection_properties, ';').c_str());
 }
 
-void send_connect(atransport *t)
-{
+void send_connect(atransport* t) {
     D("Calling send_connect \n");
-    apacket *cp = get_apacket();
+    apacket* cp = get_apacket();
     cp->msg.command = A_CNXN;
-    cp->msg.arg0 = A_VERSION;
-    cp->msg.arg1 = MAX_PAYLOAD;
-    cp->msg.data_length = fill_connect_data((char *)cp->data,
-                                            sizeof(cp->data));
+    cp->msg.arg0 = t->get_protocol_version();
+    cp->msg.arg1 = t->get_max_payload();
+
+    std::string connection_str = get_connection_string();
+    // Connect and auth packets are limited to MAX_PAYLOAD_V1 because we don't
+    // yet know how much data the other size is willing to accept.
+    if (connection_str.length() > MAX_PAYLOAD_V1) {
+        LOG(FATAL) << "Connection banner is too long (length = "
+                   << connection_str.length() << ")";
+    }
+
+    memcpy(cp->data, connection_str.c_str(), connection_str.length());
+    cp->msg.data_length = connection_str.length();
+
     send_packet(cp, t);
 }
 
@@ -339,8 +368,8 @@
     *dst = strdup(src.c_str());
 }
 
-void parse_banner(const char* banner, atransport* t) {
-    D("parse_banner: %s\n", banner);
+void parse_banner(const std::string& banner, atransport* t) {
+    D("parse_banner: %s\n", banner.c_str());
 
     // The format is something like:
     // "device::ro.product.name=x;ro.product.model=y;ro.product.device=z;".
@@ -363,33 +392,60 @@
                 qual_overwrite(&t->model, value);
             } else if (key == "ro.product.device") {
                 qual_overwrite(&t->device, value);
+            } else if (key == "features") {
+                for (const auto& feature : android::base::Split(value, ",")) {
+                    t->add_feature(feature);
+                }
             }
         }
     }
 
     const std::string& type = pieces[0];
     if (type == "bootloader") {
-        D("setting connection_state to CS_BOOTLOADER\n");
-        t->connection_state = CS_BOOTLOADER;
+        D("setting connection_state to kCsBootloader\n");
+        t->connection_state = kCsBootloader;
         update_transports();
     } else if (type == "device") {
-        D("setting connection_state to CS_DEVICE\n");
-        t->connection_state = CS_DEVICE;
+        D("setting connection_state to kCsDevice\n");
+        t->connection_state = kCsDevice;
         update_transports();
     } else if (type == "recovery") {
-        D("setting connection_state to CS_RECOVERY\n");
-        t->connection_state = CS_RECOVERY;
+        D("setting connection_state to kCsRecovery\n");
+        t->connection_state = kCsRecovery;
         update_transports();
     } else if (type == "sideload") {
-        D("setting connection_state to CS_SIDELOAD\n");
-        t->connection_state = CS_SIDELOAD;
+        D("setting connection_state to kCsSideload\n");
+        t->connection_state = kCsSideload;
         update_transports();
     } else {
-        D("setting connection_state to CS_HOST\n");
-        t->connection_state = CS_HOST;
+        D("setting connection_state to kCsHost\n");
+        t->connection_state = kCsHost;
     }
 }
 
+static void handle_new_connection(atransport* t, apacket* p) {
+    if (t->connection_state != kCsOffline) {
+        t->connection_state = kCsOffline;
+        handle_offline(t);
+    }
+
+    t->update_version(p->msg.arg0, p->msg.arg1);
+    std::string banner(reinterpret_cast<const char*>(p->data),
+                       p->msg.data_length);
+    parse_banner(banner, t);
+
+#if ADB_HOST
+    handle_online(t);
+#else
+    if (!auth_required) {
+        handle_online(t);
+        send_connect(t);
+    } else {
+        send_auth_request(t);
+    }
+#endif
+}
+
 void handle_packet(apacket *p, atransport *t)
 {
     asocket *s;
@@ -404,34 +460,23 @@
     case A_SYNC:
         if(p->msg.arg0){
             send_packet(p, t);
-            if(HOST) send_connect(t);
+#if ADB_HOST
+            send_connect(t);
+#endif
         } else {
-            t->connection_state = CS_OFFLINE;
+            t->connection_state = kCsOffline;
             handle_offline(t);
             send_packet(p, t);
         }
         return;
 
-    case A_CNXN: /* CONNECT(version, maxdata, "system-id-string") */
-            /* XXX verify version, etc */
-        if(t->connection_state != CS_OFFLINE) {
-            t->connection_state = CS_OFFLINE;
-            handle_offline(t);
-        }
-
-        parse_banner(reinterpret_cast<const char*>(p->data), t);
-
-        if (HOST || !auth_required) {
-            handle_online(t);
-            if (!HOST) send_connect(t);
-        } else {
-            send_auth_request(t);
-        }
+    case A_CNXN:  // CONNECT(version, maxdata, "system-id-string")
+        handle_new_connection(t, p);
         break;
 
     case A_AUTH:
         if (p->msg.arg0 == ADB_AUTH_TOKEN) {
-            t->connection_state = CS_UNAUTHORIZED;
+            t->connection_state = kCsUnauthorized;
             t->key = adb_auth_nextkey(t->key);
             if (t->key) {
                 send_auth_response(p->data, p->msg.data_length, t);
@@ -535,6 +580,107 @@
 
 #if ADB_HOST
 
+#ifdef _WIN32
+
+static bool _make_handle_noninheritable(HANDLE h) {
+    if (h != INVALID_HANDLE_VALUE && h != NULL) {
+        if (!SetHandleInformation(h, HANDLE_FLAG_INHERIT, 0)) {
+            // Show the handle value to give us a clue in case we have problems
+            // with pseudo-handle values.
+            fprintf(stderr, "Cannot make handle 0x%p non-inheritable: %s\n",
+                    h, SystemErrorCodeToString(GetLastError()).c_str());
+            return false;
+        }
+    }
+
+    return true;
+}
+
+// Create anonymous pipe, preventing inheritance of the read pipe and setting
+// security of the write pipe to sa.
+static bool _create_anonymous_pipe(unique_handle* pipe_read_out,
+                                   unique_handle* pipe_write_out,
+                                   SECURITY_ATTRIBUTES* sa) {
+    HANDLE pipe_read_raw = NULL;
+    HANDLE pipe_write_raw = NULL;
+    if (!CreatePipe(&pipe_read_raw, &pipe_write_raw, sa, 0)) {
+        fprintf(stderr, "Cannot create pipe: %s\n",
+                SystemErrorCodeToString(GetLastError()).c_str());
+        return false;
+    }
+
+    unique_handle pipe_read(pipe_read_raw);
+    pipe_read_raw = NULL;
+    unique_handle pipe_write(pipe_write_raw);
+    pipe_write_raw = NULL;
+
+    if (!_make_handle_noninheritable(pipe_read.get())) {
+        return false;
+    }
+
+    *pipe_read_out = std::move(pipe_read);
+    *pipe_write_out = std::move(pipe_write);
+
+    return true;
+}
+
+// Read from a pipe (that we take ownership of) and write what is returned to
+// GetStdHandle(nStdHandle). Return on error or when the pipe is closed.
+static unsigned _redirect_pipe_thread(HANDLE h, DWORD nStdHandle) {
+    // Take ownership of the HANDLE and close when we're done.
+    unique_handle   read_pipe(h);
+    const HANDLE    write_handle = GetStdHandle(nStdHandle);
+    const char*     output_name = nStdHandle == STD_OUTPUT_HANDLE ?
+                                      "stdout" : "stderr";
+
+    while (true) {
+        char    buf[64 * 1024];
+        DWORD   bytes_read = 0;
+        if (!ReadFile(read_pipe.get(), buf, sizeof(buf), &bytes_read, NULL)) {
+            const DWORD err = GetLastError();
+            // ERROR_BROKEN_PIPE is expected when the subprocess closes
+            // the other end of the pipe.
+            if (err == ERROR_BROKEN_PIPE) {
+                return EXIT_SUCCESS;
+            } else {
+                fprintf(stderr, "Failed to read from %s: %s\n", output_name,
+                        SystemErrorCodeToString(err).c_str());
+                return EXIT_FAILURE;
+            }
+        }
+
+        // Don't try to write if our stdout/stderr was not setup by the
+        // parent process.
+        if (write_handle != NULL && write_handle != INVALID_HANDLE_VALUE) {
+            DWORD   bytes_written = 0;
+            if (!WriteFile(write_handle, buf, bytes_read, &bytes_written,
+                           NULL)) {
+                fprintf(stderr, "Failed to write to %s: %s\n", output_name,
+                        SystemErrorCodeToString(GetLastError()).c_str());
+                return EXIT_FAILURE;
+            }
+
+            if (bytes_written != bytes_read) {
+                fprintf(stderr, "Only wrote %lu of %lu bytes to %s\n",
+                        bytes_written, bytes_read, output_name);
+                return EXIT_FAILURE;
+            }
+        }
+    }
+}
+
+static unsigned __stdcall _redirect_stdout_thread(HANDLE h) {
+    adb_thread_setname("stdout redirect");
+    return _redirect_pipe_thread(h, STD_OUTPUT_HANDLE);
+}
+
+static unsigned __stdcall _redirect_stderr_thread(HANDLE h) {
+    adb_thread_setname("stderr redirect");
+    return _redirect_pipe_thread(h, STD_ERROR_HANDLE);
+}
+
+#endif
+
 int launch_server(int server_port)
 {
 #if defined(_WIN32)
@@ -542,26 +688,42 @@
     /* we create a PIPE that will be used to wait for the server's "OK" */
     /* message since the pipe handles must be inheritable, we use a     */
     /* security attribute                                               */
-    HANDLE                pipe_read, pipe_write;
-    HANDLE                stdout_handle, stderr_handle;
     SECURITY_ATTRIBUTES   sa;
-    STARTUPINFO           startup;
-    PROCESS_INFORMATION   pinfo;
-    char                  program_path[ MAX_PATH ];
-    int                   ret;
-
     sa.nLength = sizeof(sa);
     sa.lpSecurityDescriptor = NULL;
     sa.bInheritHandle = TRUE;
 
-    /* create pipe, and ensure its read handle isn't inheritable */
-    ret = CreatePipe( &pipe_read, &pipe_write, &sa, 0 );
-    if (!ret) {
-        fprintf(stderr, "CreatePipe() failure, error %ld\n", GetLastError() );
+    // Redirect stdin to Windows /dev/null. If we instead pass an original
+    // stdin/stdout/stderr handle and it is a console handle, when the adb
+    // server starts up, the C Runtime will see a console handle for a process
+    // that isn't connected to a console and it will configure
+    // stdin/stdout/stderr to be closed. At that point, freopen() could be used
+    // to reopen stderr/out, but it would take more massaging to fixup the file
+    // descriptor number that freopen() uses. It's simplest to avoid all of this
+    // complexity by just redirecting stdin to `nul' and then the C Runtime acts
+    // as expected.
+    unique_handle   nul_read(CreateFileW(L"nul", GENERIC_READ,
+            FILE_SHARE_READ | FILE_SHARE_WRITE, &sa, OPEN_EXISTING,
+            FILE_ATTRIBUTE_NORMAL, NULL));
+    if (nul_read.get() == INVALID_HANDLE_VALUE) {
+        fprintf(stderr, "Cannot open 'nul': %s\n",
+                SystemErrorCodeToString(GetLastError()).c_str());
         return -1;
     }
 
-    SetHandleInformation( pipe_read, HANDLE_FLAG_INHERIT, 0 );
+    // create pipes with non-inheritable read handle, inheritable write handle
+    unique_handle   ack_read, ack_write;
+    if (!_create_anonymous_pipe(&ack_read, &ack_write, &sa)) {
+        return -1;
+    }
+    unique_handle   stdout_read, stdout_write;
+    if (!_create_anonymous_pipe(&stdout_read, &stdout_write, &sa)) {
+        return -1;
+    }
+    unique_handle   stderr_read, stderr_write;
+    if (!_create_anonymous_pipe(&stderr_read, &stderr_write, &sa)) {
+        return -1;
+    }
 
     /* Some programs want to launch an adb command and collect its output by
      * calling CreateProcess with inheritable stdout/stderr handles, then
@@ -573,30 +735,64 @@
      * the calling process is stuck while read()-ing from the stdout/stderr
      * descriptors, because they're connected to corresponding handles in the
      * adb server process (even if the latter never uses/writes to them).
+     * Note that even if we don't pass these handles in the STARTUPINFO struct,
+     * if they're marked inheritable, they're still inherited, requiring us to
+     * deal with this.
+     *
+     * If we're still having problems with inheriting random handles in the
+     * future, consider using PROC_THREAD_ATTRIBUTE_HANDLE_LIST to explicitly
+     * specify which handles should be inherited: http://blogs.msdn.com/b/oldnewthing/archive/2011/12/16/10248328.aspx
      */
-    stdout_handle = GetStdHandle( STD_OUTPUT_HANDLE );
-    stderr_handle = GetStdHandle( STD_ERROR_HANDLE );
-    if (stdout_handle != INVALID_HANDLE_VALUE) {
-        SetHandleInformation( stdout_handle, HANDLE_FLAG_INHERIT, 0 );
+    if (!_make_handle_noninheritable(GetStdHandle(STD_INPUT_HANDLE))) {
+        return -1;
     }
-    if (stderr_handle != INVALID_HANDLE_VALUE) {
-        SetHandleInformation( stderr_handle, HANDLE_FLAG_INHERIT, 0 );
+    if (!_make_handle_noninheritable(GetStdHandle(STD_OUTPUT_HANDLE))) {
+        return -1;
+    }
+    if (!_make_handle_noninheritable(GetStdHandle(STD_ERROR_HANDLE))) {
+        return -1;
     }
 
+    STARTUPINFOW    startup;
     ZeroMemory( &startup, sizeof(startup) );
     startup.cb = sizeof(startup);
-    startup.hStdInput  = GetStdHandle( STD_INPUT_HANDLE );
-    startup.hStdOutput = pipe_write;
-    startup.hStdError  = GetStdHandle( STD_ERROR_HANDLE );
+    startup.hStdInput  = nul_read.get();
+    startup.hStdOutput = stdout_write.get();
+    startup.hStdError  = stderr_write.get();
     startup.dwFlags    = STARTF_USESTDHANDLES;
 
-    ZeroMemory( &pinfo, sizeof(pinfo) );
+    // Verify that the pipe_write handle value can be passed on the command line
+    // as %d and that the rest of adb code can pass it around in an int.
+    const int ack_write_as_int = cast_handle_to_int(ack_write.get());
+    if (cast_int_to_handle(ack_write_as_int) != ack_write.get()) {
+        // If this fires, either handle values are larger than 32-bits or else
+        // there is a bug in our casting.
+        // https://msdn.microsoft.com/en-us/library/windows/desktop/aa384203%28v=vs.85%29.aspx
+        fprintf(stderr, "Cannot fit pipe handle value into 32-bits: 0x%p\n",
+                ack_write.get());
+        return -1;
+    }
 
-    /* get path of current program */
-    GetModuleFileName( NULL, program_path, sizeof(program_path) );
-    char args[64];
-    snprintf(args, sizeof(args), "adb -P %d fork-server server",  server_port);
-    ret = CreateProcess(
+    // get path of current program
+    WCHAR       program_path[MAX_PATH];
+    const DWORD module_result = GetModuleFileNameW(NULL, program_path,
+                                                   arraysize(program_path));
+    if ((module_result >= arraysize(program_path)) || (module_result == 0)) {
+        // String truncation or some other error.
+        fprintf(stderr, "Cannot get executable path: %s\n",
+                SystemErrorCodeToString(GetLastError()).c_str());
+        return -1;
+    }
+
+    WCHAR   args[64];
+    snwprintf(args, arraysize(args),
+              L"adb -P %d fork-server server --reply-fd %d", server_port,
+              ack_write_as_int);
+
+    PROCESS_INFORMATION   pinfo;
+    ZeroMemory(&pinfo, sizeof(pinfo));
+
+    if (!CreateProcessW(
             program_path,                              /* program path  */
             args,
                                     /* the fork-server argument will set the
@@ -608,41 +804,120 @@
             NULL,                     /* use parent's environment block */
             NULL,                    /* use parent's starting directory */
             &startup,                 /* startup info, i.e. std handles */
-            &pinfo );
-
-    CloseHandle( pipe_write );
-
-    if (!ret) {
-        fprintf(stderr, "CreateProcess failure, error %ld\n", GetLastError() );
-        CloseHandle( pipe_read );
+            &pinfo )) {
+        fprintf(stderr, "Cannot create process: %s\n",
+                SystemErrorCodeToString(GetLastError()).c_str());
         return -1;
     }
 
-    CloseHandle( pinfo.hProcess );
-    CloseHandle( pinfo.hThread );
+    unique_handle   process_handle(pinfo.hProcess);
+    pinfo.hProcess = NULL;
 
-    /* wait for the "OK\n" message */
+    // Close handles that we no longer need to complete the rest.
+    CloseHandle(pinfo.hThread);
+    pinfo.hThread = NULL;
+
+    nul_read.reset();
+    ack_write.reset();
+    stdout_write.reset();
+    stderr_write.reset();
+
+    // Start threads to read from subprocess stdout/stderr and write to ours
+    // to make subprocess errors easier to diagnose.
+
+    // In the past, reading from a pipe before the child process's C Runtime
+    // started up and called GetFileType() caused a hang: http://blogs.msdn.com/b/oldnewthing/archive/2011/12/02/10243553.aspx#10244216
+    // This is reportedly fixed in Windows Vista: https://support.microsoft.com/en-us/kb/2009703
+    // I was unable to reproduce the problem on Windows XP. It sounds like a
+    // Windows Update may have fixed this: https://www.duckware.com/tech/peeknamedpipe.html
+    unique_handle   stdout_thread(reinterpret_cast<HANDLE>(
+            _beginthreadex(NULL, 0, _redirect_stdout_thread, stdout_read.get(),
+                           0, NULL)));
+    if (stdout_thread.get() == nullptr) {
+        fprintf(stderr, "Cannot create thread: %s\n", strerror(errno));
+        return -1;
+    }
+    stdout_read.release();  // Transfer ownership to new thread
+
+    unique_handle   stderr_thread(reinterpret_cast<HANDLE>(
+            _beginthreadex(NULL, 0, _redirect_stderr_thread, stderr_read.get(),
+                           0, NULL)));
+    if (stderr_thread.get() == nullptr) {
+        fprintf(stderr, "Cannot create thread: %s\n", strerror(errno));
+        return -1;
+    }
+    stderr_read.release();  // Transfer ownership to new thread
+
+    bool    got_ack = false;
+
+    // Wait for the "OK\n" message, for the pipe to be closed, or other error.
     {
-        char  temp[3];
-        DWORD  count;
+        char    temp[3];
+        DWORD   count = 0;
 
-        ret = ReadFile( pipe_read, temp, 3, &count, NULL );
-        CloseHandle( pipe_read );
-        if ( !ret ) {
-            fprintf(stderr, "could not read ok from ADB Server, error = %ld\n", GetLastError() );
-            return -1;
+        if (ReadFile(ack_read.get(), temp, sizeof(temp), &count, NULL)) {
+            const CHAR  expected[] = "OK\n";
+            const DWORD expected_length = arraysize(expected) - 1;
+            if (count == expected_length &&
+                memcmp(temp, expected, expected_length) == 0) {
+                got_ack = true;
+            } else {
+                fprintf(stderr, "ADB server didn't ACK\n");
+            }
+        } else {
+            const DWORD err = GetLastError();
+            // If the ACK was not written and the process exited, GetLastError()
+            // is probably ERROR_BROKEN_PIPE, in which case that info is not
+            // useful to the user.
+            fprintf(stderr, "could not read ok from ADB Server%s\n",
+                    err == ERROR_BROKEN_PIPE ? "" :
+                    android::base::StringPrintf(": %s",
+                            SystemErrorCodeToString(err).c_str()).c_str());
         }
-        if (count != 3 || temp[0] != 'O' || temp[1] != 'K' || temp[2] != '\n') {
-            fprintf(stderr, "ADB server didn't ACK\n" );
-            return -1;
+    }
+
+    // Always try to wait a bit for threads reading stdout/stderr to finish.
+    // If the process started ok, it should close the pipes causing the threads
+    // to finish. If the process had an error, it should exit, also causing
+    // the pipes to be closed. In that case we want to read all of the output
+    // and write it out so that the user can diagnose failures.
+    const DWORD     thread_timeout_ms = 15 * 1000;
+    const HANDLE    threads[] = { stdout_thread.get(), stderr_thread.get() };
+    const DWORD     wait_result = WaitForMultipleObjects(arraysize(threads),
+            threads, TRUE, thread_timeout_ms);
+    if (wait_result == WAIT_TIMEOUT) {
+        // Threads did not finish after waiting a little while. Perhaps the
+        // server didn't close pipes, or it is hung.
+        fprintf(stderr, "Timed-out waiting for threads to finish reading from "
+                "ADB Server\n");
+        // Process handles are signaled when the process exits, so if we wait
+        // on the handle for 0 seconds and it returns 'timeout', that means that
+        // the process is still running.
+        if (WaitForSingleObject(process_handle.get(), 0) == WAIT_TIMEOUT) {
+            // We could TerminateProcess(), but that seems somewhat presumptive.
+            fprintf(stderr, "ADB Server is running: process id %lu\n",
+                    pinfo.dwProcessId);
         }
+        return -1;
+    }
+
+    if (wait_result != WAIT_OBJECT_0) {
+        fprintf(stderr, "Unexpected result waiting for threads: %lu: %s\n",
+                wait_result, SystemErrorCodeToString(GetLastError()).c_str());
+        return -1;
+    }
+
+    // For now ignore the thread exit codes and assume they worked properly.
+
+    if (!got_ack) {
+        return -1;
     }
 #else /* !defined(_WIN32) */
     char    path[PATH_MAX];
     int     fd[2];
 
     // set up a pipe so the child can tell us when it is ready.
-    // fd[0] will be parent's end, and fd[1] will get mapped to stderr in the child.
+    // fd[0] will be parent's end, and the child will write on fd[1]
     if (pipe(fd)) {
         fprintf(stderr, "pipe failed in launch_server, errno: %d\n", errno);
         return -1;
@@ -654,16 +929,14 @@
     if (pid == 0) {
         // child side of the fork
 
-        // redirect stderr to the pipe
-        // we use stderr instead of stdout due to stdout's buffering behavior.
         adb_close(fd[0]);
-        dup2(fd[1], STDERR_FILENO);
-        adb_close(fd[1]);
 
         char str_port[30];
-        snprintf(str_port, sizeof(str_port), "%d",  server_port);
+        snprintf(str_port, sizeof(str_port), "%d", server_port);
+        char reply_fd[30];
+        snprintf(reply_fd, sizeof(reply_fd), "%d", fd[1]);
         // child process
-        int result = execl(path, "adb", "-P", str_port, "fork-server", "server", NULL);
+        int result = execl(path, "adb", "-P", str_port, "fork-server", "server", "--reply-fd", reply_fd, NULL);
         // this should not return
         fprintf(stderr, "OOPS! execl returned %d, errno: %d\n", result, errno);
     } else  {
@@ -696,7 +969,7 @@
 // Try to handle a network forwarding request.
 // This returns 1 on success, 0 on failure, and -1 to indicate this is not
 // a forwarding-related request.
-int handle_forward_request(const char* service, transport_type ttype, char* serial, int reply_fd)
+int handle_forward_request(const char* service, TransportType type, const char* serial, int reply_fd)
 {
     if (!strcmp(service, "list-forward")) {
         // Create the list of forward redirections.
@@ -718,56 +991,52 @@
         return 1;
     }
 
-    if (!strncmp(service, "forward:",8) ||
-        !strncmp(service, "killforward:",12)) {
-        char *local, *remote;
-        atransport *transport;
-
-        int createForward = strncmp(service, "kill", 4);
-        int no_rebind = 0;
-
-        local = strchr(service, ':') + 1;
-
-        // Handle forward:norebind:<local>... here
-        if (createForward && !strncmp(local, "norebind:", 9)) {
-            no_rebind = 1;
-            local = strchr(local, ':') + 1;
+    if (!strncmp(service, "forward:", 8) || !strncmp(service, "killforward:", 12)) {
+        // killforward:local
+        // forward:(norebind:)?local;remote
+        bool kill_forward = false;
+        bool no_rebind = false;
+        if (android::base::StartsWith(service, "killforward:")) {
+            kill_forward = true;
+            service += 12;
+        } else {
+            service += 8;   // skip past "forward:"
+            if (android::base::StartsWith(service, "norebind:")) {
+                no_rebind = true;
+                service += 9;
+            }
         }
 
-        remote = strchr(local,';');
+        std::vector<std::string> pieces = android::base::Split(service, ";");
 
-        if (createForward) {
-            // Check forward: parameter format: '<local>;<remote>'
-            if(remote == 0) {
-                SendFail(reply_fd, "malformed forward spec");
-                return 1;
-            }
-
-            *remote++ = 0;
-            if((local[0] == 0) || (remote[0] == 0) || (remote[0] == '*')) {
-                SendFail(reply_fd, "malformed forward spec");
+        if (kill_forward) {
+            // Check killforward: parameter format: '<local>'
+            if (pieces.size() != 1 || pieces[0].empty()) {
+                SendFail(reply_fd, android::base::StringPrintf("bad killforward: %s", service));
                 return 1;
             }
         } else {
-            // Check killforward: parameter format: '<local>'
-            if (local[0] == 0) {
-                SendFail(reply_fd, "malformed forward spec");
+            // Check forward: parameter format: '<local>;<remote>'
+            if (pieces.size() != 2 || pieces[0].empty() || pieces[1].empty() || pieces[1][0] == '*') {
+                SendFail(reply_fd, android::base::StringPrintf("bad forward: %s", service));
                 return 1;
             }
         }
 
         std::string error_msg;
-        transport = acquire_one_transport(CS_ANY, ttype, serial, &error_msg);
+        atransport* transport = acquire_one_transport(kCsAny, type, serial, &error_msg);
         if (!transport) {
             SendFail(reply_fd, error_msg);
             return 1;
         }
 
-        install_status_t r;
-        if (createForward) {
-            r = install_listener(local, remote, transport, no_rebind);
+        std::string error;
+        InstallStatus r;
+        if (kill_forward) {
+            r = remove_listener(pieces[0].c_str(), transport);
         } else {
-            r = remove_listener(local, transport);
+            r = install_listener(pieces[0], pieces[1].c_str(), transport,
+                                 no_rebind, &error);
         }
         if (r == INSTALL_STATUS_OK) {
 #if ADB_HOST
@@ -780,15 +1049,18 @@
 
         std::string message;
         switch (r) {
-          case INSTALL_STATUS_OK: message = " "; break;
+          case INSTALL_STATUS_OK: message = "success (!)"; break;
           case INSTALL_STATUS_INTERNAL_ERROR: message = "internal error"; break;
           case INSTALL_STATUS_CANNOT_BIND:
-            message = android::base::StringPrintf("cannot bind to socket: %s", strerror(errno));
+            message = android::base::StringPrintf("cannot bind listener: %s",
+                                                  error.c_str());
             break;
           case INSTALL_STATUS_CANNOT_REBIND:
-            message = android::base::StringPrintf("cannot rebind existing socket: %s", strerror(errno));
+            message = android::base::StringPrintf("cannot rebind existing socket");
             break;
-          case INSTALL_STATUS_LISTENER_NOT_FOUND: message = "listener not found"; break;
+          case INSTALL_STATUS_LISTENER_NOT_FOUND:
+            message = android::base::StringPrintf("listener '%s' not found", service);
+            break;
         }
         SendFail(reply_fd, message);
         return 1;
@@ -796,24 +1068,36 @@
     return 0;
 }
 
-int handle_host_request(char *service, transport_type ttype, char* serial, int reply_fd, asocket *s)
-{
-    if(!strcmp(service, "kill")) {
-        fprintf(stderr,"adb server killed by remote request\n");
+#if ADB_HOST
+static int SendOkay(int fd, const std::string& s) {
+    SendOkay(fd);
+    SendProtocolString(fd, s);
+    return 0;
+}
+#endif
+
+int handle_host_request(const char* service, TransportType type,
+                        const char* serial, int reply_fd, asocket* s) {
+    if (strcmp(service, "kill") == 0) {
+        fprintf(stderr, "adb server killed by remote request\n");
         fflush(stdout);
         SendOkay(reply_fd);
-        usb_cleanup();
+
+        // At least on Windows, if we exit() without shutdown(SD_SEND) or
+        // closesocket(), the client's next recv() will error-out with
+        // WSAECONNRESET and they'll never read the OKAY.
+        adb_shutdown(reply_fd);
+
         exit(0);
     }
 
 #if ADB_HOST
-    atransport *transport = NULL;
     // "transport:" is used for switching transport with a specified serial number
     // "transport-usb:" is used for switching transport to the only USB transport
     // "transport-local:" is used for switching transport to the only local transport
     // "transport-any:" is used for switching transport to the only transport
     if (!strncmp(service, "transport", strlen("transport"))) {
-        transport_type type = kTransportAny;
+        TransportType type = kTransportAny;
 
         if (!strncmp(service, "transport-usb", strlen("transport-usb"))) {
             type = kTransportUsb;
@@ -826,11 +1110,10 @@
             serial = service;
         }
 
-        std::string error_msg = "unknown failure";
-        transport = acquire_one_transport(CS_ANY, type, serial, &error_msg);
-
-        if (transport) {
-            s->transport = transport;
+        std::string error_msg;
+        atransport* t = acquire_one_transport(kCsAny, type, serial, &error_msg);
+        if (t != nullptr) {
+            s->transport = t;
             SendOkay(reply_fd);
         } else {
             SendFail(reply_fd, error_msg);
@@ -845,86 +1128,75 @@
             D("Getting device list...\n");
             std::string device_list = list_transports(long_listing);
             D("Sending device list...\n");
-            SendOkay(reply_fd);
-            SendProtocolString(reply_fd, device_list);
-            return 0;
+            return SendOkay(reply_fd, device_list);
         }
         return 1;
     }
 
+    if (!strcmp(service, "features")) {
+        SendOkay(reply_fd);
+        SendProtocolString(
+            reply_fd, android::base::Join(supported_features(), '\n'));
+        return 0;
+    }
+
     // remove TCP transport
     if (!strncmp(service, "disconnect:", 11)) {
-        char buffer[4096];
-        memset(buffer, 0, sizeof(buffer));
-        char* serial = service + 11;
-        if (serial[0] == 0) {
-            // disconnect from all TCP devices
-            unregister_all_tcp_transports();
-        } else {
-            char hostbuf[100];
-            // assume port 5555 if no port is specified
-            if (!strchr(serial, ':')) {
-                snprintf(hostbuf, sizeof(hostbuf) - 1, "%s:5555", serial);
-                serial = hostbuf;
-            }
-            atransport *t = find_transport(serial);
-
-            if (t) {
-                unregister_transport(t);
-            } else {
-                snprintf(buffer, sizeof(buffer), "No such device %s", serial);
-            }
+        const std::string address(service + 11);
+        if (address.empty()) {
+            kick_all_tcp_devices();
+            return SendOkay(reply_fd, "disconnected everything");
         }
 
-        SendOkay(reply_fd);
-        SendProtocolString(reply_fd, buffer);
-        return 0;
+        std::string serial;
+        std::string host;
+        int port = DEFAULT_ADB_LOCAL_TRANSPORT_PORT;
+        std::string error;
+        if (!parse_host_and_port(address, &serial, &host, &port, &error)) {
+            return SendFail(reply_fd, android::base::StringPrintf("couldn't parse '%s': %s",
+                                                                  address.c_str(), error.c_str()));
+        }
+        atransport* t = find_transport(serial.c_str());
+        if (t == nullptr) {
+            return SendFail(reply_fd, android::base::StringPrintf("no such device '%s'",
+                                                                  serial.c_str()));
+        }
+        kick_transport(t);
+        return SendOkay(reply_fd, android::base::StringPrintf("disconnected %s", address.c_str()));
     }
 
     // returns our value for ADB_SERVER_VERSION
     if (!strcmp(service, "version")) {
-        SendOkay(reply_fd);
-        SendProtocolString(reply_fd, android::base::StringPrintf("%04x", ADB_SERVER_VERSION));
-        return 0;
+        return SendOkay(reply_fd, android::base::StringPrintf("%04x", ADB_SERVER_VERSION));
     }
 
-    if(!strncmp(service,"get-serialno",strlen("get-serialno"))) {
-        const char *out = "unknown";
-        transport = acquire_one_transport(CS_ANY, ttype, serial, NULL);
-        if (transport && transport->serial) {
-            out = transport->serial;
-        }
-        SendOkay(reply_fd);
-        SendProtocolString(reply_fd, out);
-        return 0;
+    // These always report "unknown" rather than the actual error, for scripts.
+    if (!strcmp(service, "get-serialno")) {
+        std::string ignored;
+        atransport* t = acquire_one_transport(kCsAny, type, serial, &ignored);
+        return SendOkay(reply_fd, (t && t->serial) ? t->serial : "unknown");
     }
-    if(!strncmp(service,"get-devpath",strlen("get-devpath"))) {
-        const char *out = "unknown";
-        transport = acquire_one_transport(CS_ANY, ttype, serial, NULL);
-        if (transport && transport->devpath) {
-            out = transport->devpath;
-        }
-        SendOkay(reply_fd);
-        SendProtocolString(reply_fd, out);
-        return 0;
+    if (!strcmp(service, "get-devpath")) {
+        std::string ignored;
+        atransport* t = acquire_one_transport(kCsAny, type, serial, &ignored);
+        return SendOkay(reply_fd, (t && t->devpath) ? t->devpath : "unknown");
     }
+    if (!strcmp(service, "get-state")) {
+        std::string ignored;
+        atransport* t = acquire_one_transport(kCsAny, type, serial, &ignored);
+        return SendOkay(reply_fd, t ? t->connection_state_name() : "unknown");
+    }
+
     // indicates a new emulator instance has started
-    if (!strncmp(service,"emulator:",9)) {
+    if (!strncmp(service, "emulator:", 9)) {
         int  port = atoi(service+9);
         local_connect(port);
         /* we don't even need to send a reply */
         return 0;
     }
-
-    if(!strncmp(service,"get-state",strlen("get-state"))) {
-        transport = acquire_one_transport(CS_ANY, ttype, serial, NULL);
-        SendOkay(reply_fd);
-        SendProtocolString(reply_fd, transport ? transport->connection_state_name() : "unknown");
-        return 0;
-    }
 #endif // ADB_HOST
 
-    int ret = handle_forward_request(service, ttype, serial, reply_fd);
+    int ret = handle_forward_request(service, type, serial, reply_fd);
     if (ret >= 0)
       return ret - 1;
     return -1;
diff --git a/adb/adb.h b/adb/adb.h
index fd9d0e6..8b3359e 100644
--- a/adb/adb.h
+++ b/adb/adb.h
@@ -20,10 +20,16 @@
 #include <limits.h>
 #include <sys/types.h>
 
+#include <string>
+
+#include <base/macros.h>
+
 #include "adb_trace.h"
 #include "fdevent.h"
 
-#define MAX_PAYLOAD 4096
+constexpr size_t MAX_PAYLOAD_V1 = 4 * 1024;
+constexpr size_t MAX_PAYLOAD_V2 = 256 * 1024;
+constexpr size_t MAX_PAYLOAD = MAX_PAYLOAD_V2;
 
 #define A_SYNC 0x434e5953
 #define A_CNXN 0x4e584e43
@@ -40,10 +46,12 @@
 #define ADB_VERSION_MAJOR 1
 #define ADB_VERSION_MINOR 0
 
+std::string adb_version();
+
 // Increment this when we want to force users to start a new adb server.
 #define ADB_SERVER_VERSION 32
 
-struct atransport;
+class atransport;
 struct usb_handle;
 
 struct amessage {
@@ -135,6 +143,8 @@
 
         /* A socket is bound to atransport */
     atransport *transport;
+
+    size_t get_max_payload() const;
 };
 
 
@@ -147,73 +157,38 @@
 {
     void        (*func)(void*  opaque, atransport*  t);
     void*         opaque;
-    adisconnect*  next;
-    adisconnect*  prev;
 };
 
 
-/* a transport object models the connection to a remote device or emulator
-** there is one transport per connected device/emulator. a "local transport"
-** connects through TCP (for the emulator), while a "usb transport" through
-** USB (for real devices)
-**
-** note that kTransportHost doesn't really correspond to a real transport
-** object, it's a special value used to indicate that a client wants to
-** connect to a service implemented within the ADB server itself.
-*/
-enum transport_type {
-        kTransportUsb,
-        kTransportLocal,
-        kTransportAny,
-        kTransportHost,
+// A transport object models the connection to a remote device or emulator there
+// is one transport per connected device/emulator. A "local transport" connects
+// through TCP (for the emulator), while a "usb transport" through USB (for real
+// devices).
+//
+// Note that kTransportHost doesn't really correspond to a real transport
+// object, it's a special value used to indicate that a client wants to connect
+// to a service implemented within the ADB server itself.
+enum TransportType {
+    kTransportUsb,
+    kTransportLocal,
+    kTransportAny,
+    kTransportHost,
 };
 
 #define TOKEN_SIZE 20
 
-struct atransport
-{
-    atransport *next;
-    atransport *prev;
-
-    int (*read_from_remote)(apacket *p, atransport *t);
-    int (*write_to_remote)(apacket *p, atransport *t);
-    void (*close)(atransport *t);
-    void (*kick)(atransport *t);
-
-    int fd;
-    int transport_socket;
-    fdevent transport_fde;
-    int ref_count;
-    unsigned sync_token;
-    int connection_state;
-    int online;
-    transport_type type;
-
-        /* usb handle or socket fd as needed */
-    usb_handle *usb;
-    int sfd;
-
-        /* used to identify transports for clients */
-    char *serial;
-    char *product;
-    char *model;
-    char *device;
-    char *devpath;
-    int adb_port; // Use for emulators (local transport)
-
-        /* a list of adisconnect callbacks called when the transport is kicked */
-    int          kicked;
-    adisconnect  disconnects;
-
-    void *key;
-    unsigned char token[TOKEN_SIZE];
-    fdevent auth_fde;
-    unsigned failed_auth_attempts;
-
-    const char* connection_state_name() const;
+enum ConnectionState {
+    kCsAny = -1,
+    kCsOffline = 0,
+    kCsBootloader,
+    kCsDevice,
+    kCsHost,
+    kCsRecovery,
+    kCsNoPerm,  // Insufficient permissions to communicate with the device.
+    kCsSideload,
+    kCsUnauthorized,
 };
 
-
 /* A listener is an entity which binds to a local port
 ** and, upon receiving a connection on that port, creates
 ** an asocket to connect the new local connection to a
@@ -252,21 +227,21 @@
 void connect_to_remote(asocket *s, const char *destination);
 void connect_to_smartsocket(asocket *s);
 
-void fatal(const char *fmt, ...);
-void fatal_errno(const char *fmt, ...);
+void fatal(const char *fmt, ...) __attribute__((noreturn));
+void fatal_errno(const char *fmt, ...) __attribute__((noreturn));
 
 void handle_packet(apacket *p, atransport *t);
 
 void get_my_path(char *s, size_t maxLen);
 int launch_server(int server_port);
-int adb_main(int is_daemon, int server_port);
+int adb_main(int is_daemon, int server_port, int ack_reply_fd);
 
 /* initialize a transport object's func pointers and state */
 #if ADB_HOST
 int get_available_local_transport_index();
 #endif
 int  init_socket_transport(atransport *t, int s, int port, int local);
-void init_usb_transport(atransport *t, usb_handle *usb, int state);
+void init_usb_transport(atransport *t, usb_handle *usb, ConnectionState state);
 
 #if ADB_HOST
 atransport* find_emulator_transport_by_adb_port(int adb_port);
@@ -284,7 +259,7 @@
 int       create_jdwp_connection_fd(int  jdwp_pid);
 #endif
 
-int handle_forward_request(const char* service, transport_type ttype, char* serial, int reply_fd);
+int handle_forward_request(const char* service, TransportType type, const char* serial, int reply_fd);
 
 #if !ADB_HOST
 void framebuffer_service(int fd, void *cookie);
@@ -319,12 +294,11 @@
 
 
 void local_init(int port);
-int  local_connect(int  port);
-int  local_connect_arbitrary_ports(int console_port, int adb_port);
+void local_connect(int port);
+int  local_connect_arbitrary_ports(int console_port, int adb_port, std::string* error);
 
 /* usb host/client interface */
 void usb_init();
-void usb_cleanup();
 int usb_write(usb_handle *h, const void *data, int len);
 int usb_read(usb_handle *h, void *data, int len);
 int usb_close(usb_handle *h);
@@ -337,26 +311,13 @@
 
 int adb_commandline(int argc, const char **argv);
 
-int connection_state(atransport *t);
+ConnectionState connection_state(atransport *t);
 
-#define CS_ANY       -1
-#define CS_OFFLINE    0
-#define CS_BOOTLOADER 1
-#define CS_DEVICE     2
-#define CS_HOST       3
-#define CS_RECOVERY   4
-#define CS_NOPERM     5 /* Insufficient permissions to communicate with the device */
-#define CS_SIDELOAD   6
-#define CS_UNAUTHORIZED 7
+extern const char* adb_device_banner;
 
-extern const char *adb_device_banner;
-extern int HOST;
+#if !ADB_HOST
 extern int SHELL_EXIT_NOTIFY_FD;
-
-enum subproc_mode {
-    SUBPROC_PTY = 0,
-    SUBPROC_RAW = 1,
-} ;
+#endif // !ADB_HOST
 
 #define CHUNK_SIZE (64*1024)
 
@@ -371,11 +332,13 @@
 #define USB_FFS_ADB_IN    USB_FFS_ADB_EP(ep2)
 #endif
 
-int handle_host_request(char *service, transport_type ttype, char* serial, int reply_fd, asocket *s);
+int handle_host_request(const char* service, TransportType type, const char* serial, int reply_fd, asocket *s);
 
 void handle_online(atransport *t);
 void handle_offline(atransport *t);
 
 void send_connect(atransport *t);
 
+void parse_banner(const std::string&, atransport* t);
+
 #endif
diff --git a/adb/adb_auth.cpp b/adb/adb_auth.cpp
index cff26d6..8a6b156 100644
--- a/adb/adb_auth.cpp
+++ b/adb/adb_auth.cpp
@@ -75,7 +75,7 @@
     apacket *p = get_apacket();
     int ret;
 
-    ret = adb_auth_get_userkey(p->data, sizeof(p->data));
+    ret = adb_auth_get_userkey(p->data, MAX_PAYLOAD_V1);
     if (!ret) {
         D("Failed to get user public key\n");
         put_apacket(p);
diff --git a/adb/adb_auth_client.cpp b/adb/adb_auth_client.cpp
index 8e7d38b..c3af024 100644
--- a/adb/adb_auth_client.cpp
+++ b/adb/adb_auth_client.cpp
@@ -47,14 +47,14 @@
 static int framework_fd = -1;
 
 static void usb_disconnected(void* unused, atransport* t);
-static struct adisconnect usb_disconnect = { usb_disconnected, 0, 0, 0 };
+static struct adisconnect usb_disconnect = { usb_disconnected, nullptr};
 static atransport* usb_transport;
 static bool needs_retry = false;
 
 static void read_keys(const char *file, struct listnode *list)
 {
     FILE *f;
-    char buf[MAX_PAYLOAD];
+    char buf[MAX_PAYLOAD_V1];
     char *sep;
     int ret;
 
@@ -164,7 +164,6 @@
 static void usb_disconnected(void* unused, atransport* t)
 {
     D("USB disconnect\n");
-    remove_transport_disconnect(usb_transport, &usb_disconnect);
     usb_transport = NULL;
     needs_retry = false;
 }
@@ -191,12 +190,12 @@
 
 void adb_auth_confirm_key(unsigned char *key, size_t len, atransport *t)
 {
-    char msg[MAX_PAYLOAD];
+    char msg[MAX_PAYLOAD_V1];
     int ret;
 
     if (!usb_transport) {
         usb_transport = t;
-        add_transport_disconnect(t, &usb_disconnect);
+        t->AddDisconnect(&usb_disconnect);
     }
 
     if (framework_fd < 0) {
@@ -212,7 +211,7 @@
 
     ret = snprintf(msg, sizeof(msg), "PK%s", key);
     if (ret >= (signed)sizeof(msg)) {
-        D("Key too long. ret=%d", ret);
+        D("Key too long. ret=%d\n", ret);
         return;
     }
     D("Sending '%s'\n", msg);
diff --git a/adb/adb_auth_host.cpp b/adb/adb_auth_host.cpp
index 61a3777..e7f82a9 100644
--- a/adb/adb_auth_host.cpp
+++ b/adb/adb_auth_host.cpp
@@ -157,7 +157,7 @@
 {
     RSAPublicKey pkey;
     FILE *outfile = NULL;
-    char path[PATH_MAX], info[MAX_PAYLOAD];
+    char path[PATH_MAX], info[MAX_PAYLOAD_V1];
     uint8_t* encoded = nullptr;
     size_t encoded_length;
     int ret = 0;
@@ -183,7 +183,7 @@
 
 #if defined(OPENSSL_IS_BORINGSSL)
     if (!EVP_EncodedLength(&encoded_length, sizeof(pkey))) {
-        D("Public key too large to base64 encode");
+        D("Public key too large to base64 encode\n");
         goto out;
     }
 #else
@@ -194,7 +194,7 @@
 
     encoded = new uint8_t[encoded_length];
     if (encoded == nullptr) {
-        D("Allocation failure");
+        D("Allocation failure\n");
         goto out;
     }
 
@@ -203,7 +203,7 @@
 
     if (fwrite(encoded, encoded_length, 1, outfile) != 1 ||
         fwrite(info, strlen(info), 1, outfile) != 1) {
-        D("Write error while writing public key");
+        D("Write error while writing public key\n");
         goto out;
     }
 
@@ -301,11 +301,18 @@
     char android_dir[PATH_MAX];
     struct stat buf;
 #ifdef _WIN32
-    char path[PATH_MAX];
+    std::string home_str;
     home = getenv("ANDROID_SDK_HOME");
     if (!home) {
-        SHGetFolderPath(NULL, CSIDL_PROFILE, NULL, 0, path);
-        home = path;
+        WCHAR path[MAX_PATH];
+        const HRESULT hr = SHGetFolderPathW(NULL, CSIDL_PROFILE, NULL, 0, path);
+        if (FAILED(hr)) {
+            D("SHGetFolderPathW failed: %s\n",
+              SystemErrorCodeToString(hr).c_str());
+            return -1;
+        }
+        home_str = narrow(path);
+        home = home_str.c_str();
     }
     format = "%s\\%s";
 #else
@@ -323,7 +330,7 @@
 
     if (stat(android_dir, &buf)) {
         if (adb_mkdir(android_dir, 0750) < 0) {
-            D("Cannot mkdir '%s'", android_dir);
+            D("Cannot mkdir '%s'\n", android_dir);
             return -1;
         }
     }
@@ -339,7 +346,7 @@
 
     ret = get_user_keyfilepath(path, sizeof(path));
     if (ret < 0 || ret >= (signed)sizeof(path)) {
-        D("Error getting user key filename");
+        D("Error getting user key filename\n");
         return 0;
     }
 
@@ -414,12 +421,15 @@
     char path[PATH_MAX];
     int ret = get_user_keyfilepath(path, sizeof(path) - 4);
     if (ret < 0 || ret >= (signed)(sizeof(path) - 4)) {
-        D("Error getting user key filename");
+        D("Error getting user key filename\n");
         return 0;
     }
     strcat(path, ".pub");
 
     // TODO(danalbert): ReadFileToString
+    // Note that on Windows, load_file() does not do CR/LF translation, but
+    // ReadFileToString() uses the C Runtime which uses CR/LF translation by
+    // default (by is overridable with _setmode()).
     unsigned size;
     char* file_data = reinterpret_cast<char*>(load_file(path, &size));
     if (file_data == nullptr) {
diff --git a/adb/adb_client.cpp b/adb/adb_client.cpp
index 7bb8e4a..a1bbb78 100644
--- a/adb/adb_client.cpp
+++ b/adb/adb_client.cpp
@@ -33,38 +33,18 @@
 
 #include <base/stringprintf.h>
 #include <base/strings.h>
+#include <cutils/sockets.h>
 
 #include "adb_io.h"
+#include "adb_utils.h"
 
-static transport_type __adb_transport = kTransportAny;
+static TransportType __adb_transport = kTransportAny;
 static const char* __adb_serial = NULL;
 
 static int __adb_server_port = DEFAULT_ADB_PORT;
 static const char* __adb_server_name = NULL;
 
-static std::string perror_str(const char* msg) {
-    return android::base::StringPrintf("%s: %s", msg, strerror(errno));
-}
-
-static bool ReadProtocolString(int fd, std::string* s, std::string* error) {
-    char buf[5];
-    if (!ReadFdExactly(fd, buf, 4)) {
-        *error = perror_str("protocol fault (couldn't read status length)");
-        return false;
-    }
-    buf[4] = 0;
-
-    unsigned long len = strtoul(buf, 0, 16);
-    s->resize(len, '\0');
-    if (!ReadFdExactly(fd, &(*s)[0], len)) {
-        *error = perror_str("protocol fault (couldn't read status message)");
-        return false;
-    }
-
-    return true;
-}
-
-void adb_set_transport(transport_type type, const char* serial)
+void adb_set_transport(TransportType type, const char* serial)
 {
     __adb_transport = type;
     __adb_serial = serial;
@@ -80,36 +60,6 @@
     __adb_server_name = hostname;
 }
 
-int adb_get_emulator_console_port() {
-    if (__adb_serial) {
-        // The user specified a serial number; is it an emulator?
-        int port;
-        return (sscanf(__adb_serial, "emulator-%d", &port) == 1) ? port : -1;
-    }
-
-    // No specific device was given, so get the list of connected
-    // devices and search for emulators. If there's one, we'll
-    // take it. If there are more than one, that's an error.
-    std::string devices;
-    std::string error;
-    if (!adb_query("host:devices", &devices, &error)) {
-        printf("no emulator connected: %s\n", error.c_str());
-        return -1;
-    }
-
-    int port;
-    size_t emulator_count = 0;
-    for (auto& device : android::base::Split(devices, "\n")) {
-        if (sscanf(device.c_str(), "emulator-%d", &port) == 1) {
-            if (++emulator_count > 1) {
-                return -2;
-            }
-        }
-    }
-    if (emulator_count == 0) return -1;
-    return port;
-}
-
 static int switch_socket_transport(int fd, std::string* error) {
     std::string service;
     if (__adb_serial) {
@@ -175,27 +125,35 @@
 int _adb_connect(const std::string& service, std::string* error) {
     D("_adb_connect: %s\n", service.c_str());
     if (service.empty() || service.size() > 1024) {
-        *error = android::base::StringPrintf("bad service name length (%d)",
-                                             static_cast<int>(service.size()));
+        *error = android::base::StringPrintf("bad service name length (%zd)",
+                                             service.size());
         return -1;
     }
 
     int fd;
+    std::string reason;
     if (__adb_server_name) {
-        fd = socket_network_client(__adb_server_name, __adb_server_port, SOCK_STREAM);
+        fd = network_connect(__adb_server_name, __adb_server_port, SOCK_STREAM, 0, &reason);
+        if (fd == -1) {
+            *error = android::base::StringPrintf("can't connect to %s:%d: %s",
+                                                 __adb_server_name, __adb_server_port,
+                                                 reason.c_str());
+            return -2;
+        }
     } else {
-        fd = socket_loopback_client(__adb_server_port, SOCK_STREAM);
-    }
-    if (fd < 0) {
-        *error = perror_str("cannot connect to daemon");
-        return -2;
+        fd = network_loopback_client(__adb_server_port, SOCK_STREAM, &reason);
+        if (fd == -1) {
+            *error = android::base::StringPrintf("cannot connect to daemon: %s",
+                                                 reason.c_str());
+            return -2;
+        }
     }
 
     if (memcmp(&service[0],"host",4) != 0 && switch_socket_transport(fd, error)) {
         return -1;
     }
 
-    if(!SendProtocolString(fd, service)) {
+    if (!SendProtocolString(fd, service)) {
         *error = perror_str("write failure during connection");
         adb_close(fd);
         return -1;
@@ -217,6 +175,7 @@
     D("adb_connect: service %s\n", service.c_str());
     if (fd == -2 && __adb_server_name) {
         fprintf(stderr,"** Cannot start server on remote host\n");
+        // error is the original network connection error
         return fd;
     } else if (fd == -2) {
         fprintf(stdout,"* daemon not running. starting it now on port %d *\n",
@@ -224,6 +183,10 @@
     start_server:
         if (launch_server(__adb_server_port)) {
             fprintf(stderr,"* failed to start daemon *\n");
+            // launch_server() has already printed detailed error info, so just
+            // return a generic error string about the overall adb_connect()
+            // that the caller requested.
+            *error = "cannot connect to daemon";
             return -1;
         } else {
             fprintf(stdout,"* daemon started successfully *\n");
@@ -245,12 +208,16 @@
             adb_close(fd);
 
             if (sscanf(&version_string[0], "%04x", &version) != 1) {
-                goto error;
+                *error = android::base::StringPrintf(
+                        "cannot parse version string: %s",
+                        version_string.c_str());
+                return -1;
             }
         } else {
             // if fd is -1, then check for "unknown host service",
-            // which would indicate a version of adb that does not support the version command
-            if (*error == "unknown host service") {
+            // which would indicate a version of adb that does not support the
+            // version command, in which case we should fall-through to kill it.
+            if (*error != "unknown host service") {
                 return fd;
             }
         }
@@ -258,7 +225,13 @@
         if (version != ADB_SERVER_VERSION) {
             printf("adb server is out of date.  killing...\n");
             fd = _adb_connect("host:kill", error);
-            adb_close(fd);
+            if (fd >= 0) {
+                adb_close(fd);
+            } else {
+                // If we couldn't connect to the server or had some other error,
+                // report it, but still try to start the server.
+                fprintf(stderr, "error: %s\n", error->c_str());
+            }
 
             /* XXX can we better detect its death? */
             adb_sleep_ms(2000);
@@ -273,7 +246,7 @@
 
     fd = _adb_connect(service, error);
     if (fd == -1) {
-        D("_adb_connect error: %s", error->c_str());
+        D("_adb_connect error: %s\n", error->c_str());
     } else if(fd == -2) {
         fprintf(stderr,"** daemon still not running\n");
     }
@@ -286,27 +259,28 @@
 }
 
 
-int adb_command(const std::string& service, std::string* error) {
-    int fd = adb_connect(service, error);
+bool adb_command(const std::string& service) {
+    std::string error;
+    int fd = adb_connect(service, &error);
     if (fd < 0) {
-        fprintf(stderr, "error: %s\n", error->c_str());
-        return -1;
+        fprintf(stderr, "error: %s\n", error.c_str());
+        return false;
     }
 
-    if (!adb_status(fd, error)) {
+    if (!adb_status(fd, &error)) {
+        fprintf(stderr, "error: %s\n", error.c_str());
         adb_close(fd);
-        return -1;
+        return false;
     }
 
-    return 0;
+    return true;
 }
 
 bool adb_query(const std::string& service, std::string* result, std::string* error) {
     D("adb_query: %s\n", service.c_str());
     int fd = adb_connect(service, error);
     if (fd < 0) {
-        fprintf(stderr,"error: %s\n", error->c_str());
-        return 0;
+        return false;
     }
 
     result->clear();
diff --git a/adb/adb_client.h b/adb/adb_client.h
index 96416f5..5de0638 100644
--- a/adb/adb_client.h
+++ b/adb/adb_client.h
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 #ifndef _ADB_CLIENT_H_
 #define _ADB_CLIENT_H_
 
@@ -5,49 +21,35 @@
 
 #include <string>
 
-/* connect to adb, connect to the named service, and return
-** a valid fd for interacting with that service upon success
-** or a negative number on failure
-*/
+// Connect to adb, connect to the named service, and return a valid fd for
+// interacting with that service upon success or a negative number on failure.
 int adb_connect(const std::string& service, std::string* error);
 int _adb_connect(const std::string& service, std::string* error);
 
-/* connect to adb, connect to the named service, return 0 if
-** the connection succeeded AND the service returned OKAY
-*/
-int adb_command(const std::string& service, std::string* error);
+// Connect to adb, connect to the named service, returns true if the connection
+// succeeded AND the service returned OKAY. Outputs any returned error otherwise.
+bool adb_command(const std::string& service);
 
 // Connects to the named adb service and fills 'result' with the response.
 // Returns true on success; returns false and fills 'error' on failure.
 bool adb_query(const std::string& service, std::string* result, std::string* error);
 
-/* Set the preferred transport to connect to.
-*/
-void adb_set_transport(transport_type type, const char* serial);
+// Set the preferred transport to connect to.
+void adb_set_transport(TransportType type, const char* serial);
 
-/* Set TCP specifics of the transport to use
-*/
+// Set TCP specifics of the transport to use.
 void adb_set_tcp_specifics(int server_port);
 
-/* Set TCP Hostname of the transport to use
-*/
+// Set TCP Hostname of the transport to use.
 void adb_set_tcp_name(const char* hostname);
 
-/* Return the console port of the currently connected emulator (if any)
- * of -1 if there is no emulator, and -2 if there is more than one.
- * assumes adb_set_transport() was alled previously...
- */
-int  adb_get_emulator_console_port(void);
+// Send commands to the current emulator instance. Will fail if there is not
+// exactly one emulator connected (or if you use -s <serial> with a <serial>
+// that does not designate an emulator).
+int adb_send_emulator_command(int argc, const char** argv, const char* serial);
 
-/* send commands to the current emulator instance. will fail if there
- * is zero, or more than one emulator connected (or if you use -s <serial>
- * with a <serial> that does not designate an emulator)
- */
-int  adb_send_emulator_command(int  argc, const char**  argv);
-
-// Reads a standard adb status response (OKAY|FAIL) and
-// returns true in the event of OKAY, false in the event of FAIL
-// or protocol error.
+// Reads a standard adb status response (OKAY|FAIL) and returns true in the
+// event of OKAY, false in the event of FAIL or protocol error.
 bool adb_status(int fd, std::string* error);
 
 #endif
diff --git a/adb/adb_io.cpp b/adb/adb_io.cpp
index 5ae6ec3..7092609 100644
--- a/adb/adb_io.cpp
+++ b/adb/adb_io.cpp
@@ -32,7 +32,27 @@
         length = 0xffff;
     }
 
-    return WriteFdFmt(fd, "%04x", length) && WriteFdExactly(fd, s);
+    // The cost of sending two strings outweighs the cost of formatting.
+    // "adb sync" performance is affected by this.
+    return WriteFdFmt(fd, "%04x%.*s", length, length, s.c_str());
+}
+
+bool ReadProtocolString(int fd, std::string* s, std::string* error) {
+    char buf[5];
+    if (!ReadFdExactly(fd, buf, 4)) {
+        *error = perror_str("protocol fault (couldn't read status length)");
+        return false;
+    }
+    buf[4] = 0;
+
+    unsigned long len = strtoul(buf, 0, 16);
+    s->resize(len, '\0');
+    if (!ReadFdExactly(fd, &(*s)[0], len)) {
+        *error = perror_str("protocol fault (couldn't read status message)");
+        return false;
+    }
+
+    return true;
 }
 
 bool SendOkay(int fd) {
diff --git a/adb/adb_io.h b/adb/adb_io.h
index 8d50a6d..9c3b2a5 100644
--- a/adb/adb_io.h
+++ b/adb/adb_io.h
@@ -30,23 +30,22 @@
 // Writes a protocol-format string; a four hex digit length followed by the string data.
 bool SendProtocolString(int fd, const std::string& s);
 
-/*
- * Reads exactly len bytes from fd into buf.
- *
- * Returns false if there is an error or if EOF was reached before len bytes
- * were read. If EOF was found, errno will be set to 0.
- *
- * If this function fails, the contents of buf are undefined.
- */
-bool ReadFdExactly(int fd, void *buf, size_t len);
+// Reads a protocol-format string; a four hex digit length followed by the string data.
+bool ReadProtocolString(int fd, std::string* s, std::string* error);
 
-/*
- * Writes exactly len bytes from buf to fd.
- *
- * Returns false if there is an error or if the fd was closed before the write
- * completed. If the other end of the fd (such as in a socket, pipe, or fifo),
- * is closed, errno will be set to 0.
- */
+// Reads exactly len bytes from fd into buf.
+//
+// Returns false if there is an error or if EOF was reached before len bytes
+// were read. If EOF was found, errno will be set to 0.
+//
+// If this function fails, the contents of buf are undefined.
+bool ReadFdExactly(int fd, void* buf, size_t len);
+
+// Writes exactly len bytes from buf to fd.
+//
+// Returns false if there is an error or if the fd was closed before the write
+// completed. If the other end of the fd (such as in a socket, pipe, or fifo),
+// is closed, errno will be set to 0.
 bool WriteFdExactly(int fd, const void* buf, size_t len);
 
 // Same as above, but for strings.
diff --git a/adb/adb_io_test.cpp b/adb/adb_io_test.cpp
index 8fd5cbf..84e3db6 100644
--- a/adb/adb_io_test.cpp
+++ b/adb/adb_io_test.cpp
@@ -28,30 +28,14 @@
 #include <string>
 
 #include "base/file.h"
+#include "base/test_utils.h"
 
-class TemporaryFile {
- public:
-  TemporaryFile() {
-    init("/data/local/tmp");
-    if (fd == -1) {
-      init("/tmp");
-    }
-  }
-
-  ~TemporaryFile() {
-    close(fd);
-    unlink(filename);
-  }
-
-  int fd;
-  char filename[1024];
-
- private:
-  void init(const char* tmp_dir) {
-    snprintf(filename, sizeof(filename), "%s/TemporaryFile-XXXXXX", tmp_dir);
-    fd = mkstemp(filename);
-  }
-};
+// All of these tests fail on Windows because they use the C Runtime open(),
+// but the adb_io APIs expect file descriptors from adb_open(). This could
+// theoretically be fixed by making adb_read()/adb_write() fallback to using
+// read()/write() if an unrecognized fd is used, and by making adb_open() return
+// fds far from the range that open() returns. But all of that might defeat the
+// purpose of the tests.
 
 TEST(io, ReadFdExactly_whole) {
   const char expected[] = "Foobar";
diff --git a/adb/adb_listeners.cpp b/adb/adb_listeners.cpp
index 3fc4719..d5b1fd5 100644
--- a/adb/adb_listeners.cpp
+++ b/adb/adb_listeners.cpp
@@ -20,19 +20,19 @@
 #include <stdlib.h>
 
 #include <base/stringprintf.h>
+#include <cutils/sockets.h>
 
 #include "sysdeps.h"
 #include "transport.h"
 
 int gListenAll = 0; /* Not static because it is used in commandline.c. */
 
-alistener listener_list = {
+static alistener listener_list = {
     .next = &listener_list,
     .prev = &listener_list,
 };
 
-void ss_listener_event_func(int _fd, unsigned ev, void *_l)
-{
+static void ss_listener_event_func(int _fd, unsigned ev, void *_l) {
     asocket *s;
 
     if(ev & FDE_READ) {
@@ -56,7 +56,7 @@
     }
 }
 
-void listener_event_func(int _fd, unsigned ev, void* _l)
+static void listener_event_func(int _fd, unsigned ev, void* _l)
 {
     alistener* listener = reinterpret_cast<alistener*>(_l);
     asocket *s;
@@ -101,47 +101,41 @@
         free((char*)l->connect_to);
 
     if (l->transport) {
-        remove_transport_disconnect(l->transport, &l->disconnect);
+        l->transport->RemoveDisconnect(&l->disconnect);
     }
     free(l);
 }
 
-void listener_disconnect(void* listener, atransport*  t)
-{
-    free_listener(reinterpret_cast<alistener*>(listener));
+static void listener_disconnect(void* arg, atransport*) {
+    alistener* listener = reinterpret_cast<alistener*>(arg);
+    listener->transport = nullptr;
+    free_listener(listener);
 }
 
-int local_name_to_fd(const char *name)
-{
-    int port;
-
-    if(!strncmp("tcp:", name, 4)){
-        int  ret;
-        port = atoi(name + 4);
-
+static int local_name_to_fd(const char* name, std::string* error) {
+    if (!strncmp("tcp:", name, 4)) {
+        int port = atoi(name + 4);
         if (gListenAll > 0) {
-            ret = socket_inaddr_any_server(port, SOCK_STREAM);
+            return network_inaddr_any_server(port, SOCK_STREAM, error);
         } else {
-            ret = socket_loopback_server(port, SOCK_STREAM);
+            return network_loopback_server(port, SOCK_STREAM, error);
         }
-
-        return ret;
     }
-#ifndef HAVE_WIN32_IPC  /* no Unix-domain sockets on Win32 */
-    // It's non-sensical to support the "reserved" space on the adb host side
-    if(!strncmp(name, "local:", 6)) {
-        return socket_local_server(name + 6,
-                ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM);
-    } else if(!strncmp(name, "localabstract:", 14)) {
-        return socket_local_server(name + 14,
-                ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM);
-    } else if(!strncmp(name, "localfilesystem:", 16)) {
-        return socket_local_server(name + 16,
-                ANDROID_SOCKET_NAMESPACE_FILESYSTEM, SOCK_STREAM);
+#if !defined(_WIN32)  // No Unix-domain sockets on Windows.
+    // It's nonsensical to support the "reserved" space on the adb host side
+    if (!strncmp(name, "local:", 6)) {
+        return network_local_server(name + 6,
+                ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM, error);
+    } else if (!strncmp(name, "localabstract:", 14)) {
+        return network_local_server(name + 14,
+                ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM, error);
+    } else if (!strncmp(name, "localfilesystem:", 16)) {
+        return network_local_server(name + 16,
+                ANDROID_SOCKET_NAMESPACE_FILESYSTEM, SOCK_STREAM, error);
     }
 
 #endif
-    printf("unknown local portname '%s'\n", name);
+    *error = android::base::StringPrintf("unknown local portname '%s'", name);
     return -1;
 }
 
@@ -154,19 +148,20 @@
             continue;
         }
         //  <device-serial> " " <local-name> " " <remote-name> "\n"
+        // Entries from "adb reverse" have no serial.
         android::base::StringAppendF(&result, "%s %s %s\n",
-                                     l->transport->serial, l->local_name, l->connect_to);
+                                     l->transport->serial ? l->transport->serial : "(reverse)",
+                                     l->local_name, l->connect_to);
     }
     return result;
 }
 
-install_status_t remove_listener(const char *local_name, atransport* transport)
-{
+InstallStatus remove_listener(const char *local_name, atransport* transport) {
     alistener *l;
 
     for (l = listener_list.next; l != &listener_list; l = l->next) {
         if (!strcmp(local_name, l->local_name)) {
-            listener_disconnect(l, l->transport);
+            free_listener(l);
             return INSTALL_STATUS_OK;
         }
     }
@@ -181,14 +176,15 @@
         // Never remove smart sockets.
         if (l->connect_to[0] == '*')
             continue;
-        listener_disconnect(l, l->transport);
+        free_listener(l);
     }
 }
 
-install_status_t install_listener(const std::string& local_name,
+InstallStatus install_listener(const std::string& local_name,
                                   const char *connect_to,
                                   atransport* transport,
-                                  int no_rebind)
+                                  int no_rebind,
+                                  std::string* error)
 {
     for (alistener* l = listener_list.next; l != &listener_list; l = l->next) {
         if (local_name == l->local_name) {
@@ -196,25 +192,28 @@
 
             /* can't repurpose a smartsocket */
             if(l->connect_to[0] == '*') {
+                *error = "cannot repurpose smartsocket";
                 return INSTALL_STATUS_INTERNAL_ERROR;
             }
 
             /* can't repurpose a listener if 'no_rebind' is true */
             if (no_rebind) {
+                *error = "cannot rebind";
                 return INSTALL_STATUS_CANNOT_REBIND;
             }
 
             cto = strdup(connect_to);
             if(cto == 0) {
+                *error = "cannot duplicate string";
                 return INSTALL_STATUS_INTERNAL_ERROR;
             }
 
             free((void*) l->connect_to);
             l->connect_to = cto;
             if (l->transport != transport) {
-                remove_transport_disconnect(l->transport, &l->disconnect);
+                l->transport->RemoveDisconnect(&l->disconnect);
                 l->transport = transport;
-                add_transport_disconnect(l->transport, &l->disconnect);
+                l->transport->AddDisconnect(&l->disconnect);
             }
             return INSTALL_STATUS_OK;
         }
@@ -236,9 +235,8 @@
         goto nomem;
     }
 
-    listener->fd = local_name_to_fd(listener->local_name);
+    listener->fd = local_name_to_fd(listener->local_name, error);
     if (listener->fd < 0) {
-        printf("cannot bind '%s': %s\n", listener->local_name, strerror(errno));
         free(listener->local_name);
         free(listener->connect_to);
         free(listener);
@@ -264,7 +262,7 @@
     if (transport) {
         listener->disconnect.opaque = listener;
         listener->disconnect.func   = listener_disconnect;
-        add_transport_disconnect(transport, &listener->disconnect);
+        transport->AddDisconnect(&listener->disconnect);
     }
     return INSTALL_STATUS_OK;
 
diff --git a/adb/adb_listeners.h b/adb/adb_listeners.h
index 67168ae..fa98eed 100644
--- a/adb/adb_listeners.h
+++ b/adb/adb_listeners.h
@@ -22,7 +22,7 @@
 #include <string>
 
 // error/status codes for install_listener.
-enum install_status_t {
+enum InstallStatus {
   INSTALL_STATUS_OK = 0,
   INSTALL_STATUS_INTERNAL_ERROR = -1,
   INSTALL_STATUS_CANNOT_BIND = -2,
@@ -30,20 +30,15 @@
   INSTALL_STATUS_LISTENER_NOT_FOUND = -4,
 };
 
-extern alistener listener_list;
-
-void listener_disconnect(void*  _l, atransport*  t);
-void listener_event_func(int _fd, unsigned ev, void *_l);
-void ss_listener_event_func(int _fd, unsigned ev, void *_l);
-
-install_status_t install_listener(const std::string& local_name,
-                                  const char* connect_to,
-                                  atransport* transport,
-                                  int no_rebind);
+InstallStatus install_listener(const std::string& local_name,
+                               const char* connect_to,
+                               atransport* transport,
+                               int no_rebind,
+                               std::string* error);
 
 std::string format_listeners();
 
-install_status_t remove_listener(const char* local_name, atransport* transport);
+InstallStatus remove_listener(const char* local_name, atransport* transport);
 void remove_all_listeners(void);
 
 #endif /* __ADB_LISTENERS_H */
diff --git a/adb/adb_main.cpp b/adb/adb_main.cpp
deleted file mode 100644
index 45a2158..0000000
--- a/adb/adb_main.cpp
+++ /dev/null
@@ -1,411 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define TRACE_TAG TRACE_ADB
-
-#include "sysdeps.h"
-
-#include <errno.h>
-#include <signal.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-#include "adb.h"
-#include "adb_auth.h"
-#include "adb_listeners.h"
-#include "transport.h"
-
-#include <base/stringprintf.h>
-
-#if !ADB_HOST
-#include <getopt.h>
-#include <sys/prctl.h>
-
-#include "cutils/properties.h"
-#include "private/android_filesystem_config.h"
-#include "selinux/selinux.h"
-
-#include "qemu_tracing.h"
-#endif
-
-static void adb_cleanup(void)
-{
-    usb_cleanup();
-}
-
-#if defined(_WIN32)
-static BOOL WINAPI ctrlc_handler(DWORD type)
-{
-    exit(STATUS_CONTROL_C_EXIT);
-    return TRUE;
-}
-#endif
-
-#if ADB_HOST
-#ifdef WORKAROUND_BUG6558362
-#include <sched.h>
-#define AFFINITY_ENVVAR "ADB_CPU_AFFINITY_BUG6558362"
-void adb_set_affinity(void)
-{
-   cpu_set_t cpu_set;
-   const char* cpunum_str = getenv(AFFINITY_ENVVAR);
-   char* strtol_res;
-   int cpu_num;
-
-   if (!cpunum_str || !*cpunum_str)
-       return;
-   cpu_num = strtol(cpunum_str, &strtol_res, 0);
-   if (*strtol_res != '\0')
-     fatal("bad number (%s) in env var %s. Expecting 0..n.\n", cpunum_str, AFFINITY_ENVVAR);
-
-   sched_getaffinity(0, sizeof(cpu_set), &cpu_set);
-   D("orig cpu_set[0]=0x%08lx\n", cpu_set.__bits[0]);
-   CPU_ZERO(&cpu_set);
-   CPU_SET(cpu_num, &cpu_set);
-   sched_setaffinity(0, sizeof(cpu_set), &cpu_set);
-   sched_getaffinity(0, sizeof(cpu_set), &cpu_set);
-   D("new cpu_set[0]=0x%08lx\n", cpu_set.__bits[0]);
-}
-#endif
-#else /* ADB_HOST */
-static const char *root_seclabel = NULL;
-
-static void drop_capabilities_bounding_set_if_needed() {
-#ifdef ALLOW_ADBD_ROOT
-    char value[PROPERTY_VALUE_MAX];
-    property_get("ro.debuggable", value, "");
-    if (strcmp(value, "1") == 0) {
-        return;
-    }
-#endif
-    int i;
-    for (i = 0; prctl(PR_CAPBSET_READ, i, 0, 0, 0) >= 0; i++) {
-        if (i == CAP_SETUID || i == CAP_SETGID) {
-            // CAP_SETUID CAP_SETGID needed by /system/bin/run-as
-            continue;
-        }
-        int err = prctl(PR_CAPBSET_DROP, i, 0, 0, 0);
-
-        // Some kernels don't have file capabilities compiled in, and
-        // prctl(PR_CAPBSET_DROP) returns EINVAL. Don't automatically
-        // die when we see such misconfigured kernels.
-        if ((err < 0) && (errno != EINVAL)) {
-            exit(1);
-        }
-    }
-}
-
-static bool should_drop_privileges() {
-#if defined(ALLOW_ADBD_ROOT)
-    char value[PROPERTY_VALUE_MAX];
-
-    // The emulator is never secure, so don't drop privileges there.
-    // TODO: this seems like a bug --- shouldn't the emulator behave like a device?
-    property_get("ro.kernel.qemu", value, "");
-    if (strcmp(value, "1") == 0) {
-        return false;
-    }
-
-    // The properties that affect `adb root` and `adb unroot` are ro.secure and
-    // ro.debuggable. In this context the names don't make the expected behavior
-    // particularly obvious.
-    //
-    // ro.debuggable:
-    //   Allowed to become root, but not necessarily the default. Set to 1 on
-    //   eng and userdebug builds.
-    //
-    // ro.secure:
-    //   Drop privileges by default. Set to 1 on userdebug and user builds.
-    property_get("ro.secure", value, "1");
-    bool ro_secure = (strcmp(value, "1") == 0);
-
-    property_get("ro.debuggable", value, "");
-    bool ro_debuggable = (strcmp(value, "1") == 0);
-
-    // Drop privileges if ro.secure is set...
-    bool drop = ro_secure;
-
-    property_get("service.adb.root", value, "");
-    bool adb_root = (strcmp(value, "1") == 0);
-    bool adb_unroot = (strcmp(value, "0") == 0);
-
-    // ...except "adb root" lets you keep privileges in a debuggable build.
-    if (ro_debuggable && adb_root) {
-        drop = false;
-    }
-
-    // ...and "adb unroot" lets you explicitly drop privileges.
-    if (adb_unroot) {
-        drop = true;
-    }
-
-    return drop;
-#else
-    return true; // "adb root" not allowed, always drop privileges.
-#endif /* ALLOW_ADBD_ROOT */
-}
-#endif /* ADB_HOST */
-
-void start_logging(void)
-{
-#if defined(_WIN32)
-    char    temp[ MAX_PATH ];
-    FILE*   fnul;
-    FILE*   flog;
-
-    GetTempPath( sizeof(temp) - 8, temp );
-    strcat( temp, "adb.log" );
-
-    /* Win32 specific redirections */
-    fnul = fopen( "NUL", "rt" );
-    if (fnul != NULL)
-        stdin[0] = fnul[0];
-
-    flog = fopen( temp, "at" );
-    if (flog == NULL)
-        flog = fnul;
-
-    setvbuf( flog, NULL, _IONBF, 0 );
-
-    stdout[0] = flog[0];
-    stderr[0] = flog[0];
-    fprintf(stderr,"--- adb starting (pid %d) ---\n", getpid());
-#else
-    int fd;
-
-    fd = unix_open("/dev/null", O_RDONLY);
-    dup2(fd, 0);
-    adb_close(fd);
-
-    fd = unix_open("/tmp/adb.log", O_WRONLY | O_CREAT | O_APPEND, 0640);
-    if(fd < 0) {
-        fd = unix_open("/dev/null", O_WRONLY);
-    }
-    dup2(fd, 1);
-    dup2(fd, 2);
-    adb_close(fd);
-    fprintf(stderr,"--- adb starting (pid %d) ---\n", getpid());
-#endif
-}
-
-int adb_main(int is_daemon, int server_port)
-{
-#if !ADB_HOST
-    int port;
-    char value[PROPERTY_VALUE_MAX];
-
-    umask(000);
-#endif
-
-    atexit(adb_cleanup);
-#if defined(_WIN32)
-    SetConsoleCtrlHandler( ctrlc_handler, TRUE );
-#else
-    // No SIGCHLD. Let the service subproc handle its children.
-    signal(SIGPIPE, SIG_IGN);
-#endif
-
-    init_transport_registration();
-
-#if ADB_HOST
-    HOST = 1;
-
-#ifdef WORKAROUND_BUG6558362
-    if(is_daemon) adb_set_affinity();
-#endif
-    usb_init();
-    local_init(DEFAULT_ADB_LOCAL_TRANSPORT_PORT);
-    adb_auth_init();
-
-    std::string local_name = android::base::StringPrintf("tcp:%d", server_port);
-    if (install_listener(local_name, "*smartsocket*", NULL, 0)) {
-        exit(1);
-    }
-#else
-    // We need to call this even if auth isn't enabled because the file
-    // descriptor will always be open.
-    adbd_cloexec_auth_socket();
-
-    if (ALLOW_ADBD_NO_AUTH && property_get_bool("ro.adb.secure", 0) == 0) {
-        auth_required = false;
-    }
-
-    adbd_auth_init();
-
-    // Our external storage path may be different than apps, since
-    // we aren't able to bind mount after dropping root.
-    const char* adb_external_storage = getenv("ADB_EXTERNAL_STORAGE");
-    if (NULL != adb_external_storage) {
-        setenv("EXTERNAL_STORAGE", adb_external_storage, 1);
-    } else {
-        D("Warning: ADB_EXTERNAL_STORAGE is not set.  Leaving EXTERNAL_STORAGE"
-          " unchanged.\n");
-    }
-
-    /* add extra groups:
-    ** AID_ADB to access the USB driver
-    ** AID_LOG to read system logs (adb logcat)
-    ** AID_INPUT to diagnose input issues (getevent)
-    ** AID_INET to diagnose network issues (ping)
-    ** AID_NET_BT and AID_NET_BT_ADMIN to diagnose bluetooth (hcidump)
-    ** AID_SDCARD_R to allow reading from the SD card
-    ** AID_SDCARD_RW to allow writing to the SD card
-    ** AID_NET_BW_STATS to read out qtaguid statistics
-    */
-    gid_t groups[] = { AID_ADB, AID_LOG, AID_INPUT, AID_INET, AID_NET_BT,
-                       AID_NET_BT_ADMIN, AID_SDCARD_R, AID_SDCARD_RW,
-                       AID_NET_BW_STATS };
-    if (setgroups(sizeof(groups)/sizeof(groups[0]), groups) != 0) {
-        exit(1);
-    }
-
-    /* don't listen on a port (default 5037) if running in secure mode */
-    /* don't run as root if we are running in secure mode */
-    if (should_drop_privileges()) {
-        drop_capabilities_bounding_set_if_needed();
-
-        /* then switch user and group to "shell" */
-        if (setgid(AID_SHELL) != 0) {
-            exit(1);
-        }
-        if (setuid(AID_SHELL) != 0) {
-            exit(1);
-        }
-
-        D("Local port disabled\n");
-    } else {
-        if ((root_seclabel != NULL) && (is_selinux_enabled() > 0)) {
-            // b/12587913: fix setcon to allow const pointers
-            if (setcon((char *)root_seclabel) < 0) {
-                exit(1);
-            }
-        }
-        std::string local_name = android::base::StringPrintf("tcp:%d", server_port);
-        if (install_listener(local_name, "*smartsocket*", NULL, 0)) {
-            exit(1);
-        }
-    }
-
-    int usb = 0;
-    if (access(USB_ADB_PATH, F_OK) == 0 || access(USB_FFS_ADB_EP0, F_OK) == 0) {
-        // listen on USB
-        usb_init();
-        usb = 1;
-    }
-
-    // If one of these properties is set, also listen on that port
-    // If one of the properties isn't set and we couldn't listen on usb,
-    // listen on the default port.
-    property_get("service.adb.tcp.port", value, "");
-    if (!value[0]) {
-        property_get("persist.adb.tcp.port", value, "");
-    }
-    if (sscanf(value, "%d", &port) == 1 && port > 0) {
-        printf("using port=%d\n", port);
-        // listen on TCP port specified by service.adb.tcp.port property
-        local_init(port);
-    } else if (!usb) {
-        // listen on default port
-        local_init(DEFAULT_ADB_LOCAL_TRANSPORT_PORT);
-    }
-
-    D("adb_main(): pre init_jdwp()\n");
-    init_jdwp();
-    D("adb_main(): post init_jdwp()\n");
-#endif
-
-    if (is_daemon)
-    {
-        // inform our parent that we are up and running.
-#if defined(_WIN32)
-        DWORD  count;
-        WriteFile( GetStdHandle( STD_OUTPUT_HANDLE ), "OK\n", 3, &count, NULL );
-#else
-        fprintf(stderr, "OK\n");
-#endif
-        start_logging();
-    }
-    D("Event loop starting\n");
-
-    fdevent_loop();
-
-    usb_cleanup();
-
-    return 0;
-}
-
-#if !ADB_HOST
-void close_stdin() {
-    int fd = unix_open("/dev/null", O_RDONLY);
-    if (fd == -1) {
-        perror("failed to open /dev/null, stdin will remain open");
-        return;
-    }
-    dup2(fd, 0);
-    adb_close(fd);
-}
-#endif
-
-// TODO(danalbert): Split this file up into adb_main.cpp and adbd_main.cpp.
-int main(int argc, char **argv) {
-#if ADB_HOST
-    // adb client/server
-    adb_sysdeps_init();
-    adb_trace_init();
-    D("Handling commandline()\n");
-    return adb_commandline(argc - 1, const_cast<const char**>(argv + 1));
-#else
-    // adbd
-    while (true) {
-        static struct option opts[] = {
-            {"root_seclabel", required_argument, nullptr, 's'},
-            {"device_banner", required_argument, nullptr, 'b'},
-            {"version", no_argument, nullptr, 'v'},
-        };
-
-        int option_index = 0;
-        int c = getopt_long(argc, argv, "", opts, &option_index);
-        if (c == -1)
-            break;
-        switch (c) {
-        case 's':
-            root_seclabel = optarg;
-            break;
-        case 'b':
-            adb_device_banner = optarg;
-            break;
-        case 'v':
-            printf("Android Debug Bridge Daemon version %d.%d.%d %s\n",
-                   ADB_VERSION_MAJOR, ADB_VERSION_MINOR, ADB_SERVER_VERSION,
-                   ADB_REVISION);
-            return 0;
-        default:
-            break;
-        }
-    }
-
-    close_stdin();
-
-    adb_trace_init();
-
-    /* If adbd runs inside the emulator this will enable adb tracing via
-     * adb-debug qemud service in the emulator. */
-    adb_qemu_trace_init();
-
-    D("Handling main()\n");
-    return adb_main(0, DEFAULT_ADB_PORT);
-#endif
-}
diff --git a/adb/adb_trace.h b/adb/adb_trace.h
index 63d4151..4e03165 100644
--- a/adb/adb_trace.h
+++ b/adb/adb_trace.h
@@ -40,76 +40,53 @@
     TRACE_SERVICES,
     TRACE_AUTH,
     TRACE_FDEVENT,
-} ;
+};
 
-#if !ADB_HOST
-/*
- * When running inside the emulator, guest's adbd can connect to 'adb-debug'
- * qemud service that can display adb trace messages (on condition that emulator
- * has been started with '-debug adb' option).
- */
-
-/* Delivers a trace message to the emulator via QEMU pipe. */
-void adb_qemu_trace(const char* fmt, ...);
-/* Macro to use to send ADB trace messages to the emulator. */
-#define DQ(...)    adb_qemu_trace(__VA_ARGS__)
-#else
-#define DQ(...) ((void)0)
-#endif  /* !ADB_HOST */
-
-extern int     adb_trace_mask;
-extern unsigned char    adb_trace_output_count;
-void    adb_trace_init(void);
+extern int adb_trace_mask;
+extern unsigned char adb_trace_output_count;
+void adb_trace_init(char**);
 
 #  define ADB_TRACING  ((adb_trace_mask & (1 << TRACE_TAG)) != 0)
 
 /* you must define TRACE_TAG before using this macro */
 #if ADB_HOST
-#  define  D(...)                                      \
-        do {                                           \
-            if (ADB_TRACING) {                         \
-                int save_errno = errno;                \
-                adb_mutex_lock(&D_lock);               \
-                fprintf(stderr, "%16s: %5d:%5lu | ",   \
-                        __FUNCTION__,                  \
-                        getpid(), adb_thread_id());    \
-                errno = save_errno;                    \
-                fprintf(stderr, __VA_ARGS__ );         \
-                fflush(stderr);                        \
-                adb_mutex_unlock(&D_lock);             \
-                errno = save_errno;                    \
-           }                                           \
+#  define D(fmt, ...) \
+        do { \
+            if (ADB_TRACING) { \
+                int saved_errno = errno; \
+                adb_mutex_lock(&D_lock); \
+                errno = saved_errno; \
+                fprintf(stderr, "%5d:%5lu %s | " fmt, \
+                        getpid(), adb_thread_id(), __FUNCTION__, ## __VA_ARGS__); \
+                fflush(stderr); \
+                adb_mutex_unlock(&D_lock); \
+                errno = saved_errno; \
+           } \
         } while (0)
-#  define  DR(...)                                     \
-        do {                                           \
-            if (ADB_TRACING) {                         \
-                int save_errno = errno;                \
-                adb_mutex_lock(&D_lock);               \
-                errno = save_errno;                    \
-                fprintf(stderr, __VA_ARGS__ );         \
-                fflush(stderr);                        \
-                adb_mutex_unlock(&D_lock);             \
-                errno = save_errno;                    \
-           }                                           \
+#  define DR(...) \
+        do { \
+            if (ADB_TRACING) { \
+                int saved_errno = errno; \
+                adb_mutex_lock(&D_lock); \
+                errno = saved_errno; \
+                fprintf(stderr, __VA_ARGS__); \
+                fflush(stderr); \
+                adb_mutex_unlock(&D_lock); \
+                errno = saved_errno; \
+           } \
         } while (0)
 #else
-#  define  D(...)                                      \
-        do {                                           \
-            if (ADB_TRACING) {                         \
-                __android_log_print(                   \
-                    ANDROID_LOG_INFO,                  \
-                    __FUNCTION__,                      \
-                    __VA_ARGS__ );                     \
-            }                                          \
+#  define D(...) \
+        do { \
+            if (ADB_TRACING) { \
+                __android_log_print(ANDROID_LOG_INFO, __FUNCTION__, __VA_ARGS__); \
+            } \
         } while (0)
-#  define  DR(...)                                     \
-        do {                                           \
-            if (ADB_TRACING) {                         \
-                __android_log_print(                   \
-                    ANDROID_LOG_INFO,                  \
-                    __FUNCTION__,                      \
-                    __VA_ARGS__ );                     \
-            }                                          \
+#  define DR(...) \
+        do { \
+            if (ADB_TRACING) { \
+                __android_log_print(ANDROID_LOG_INFO, __FUNCTION__, __VA_ARGS__); \
+            } \
         } while (0)
 #endif /* ADB_HOST */
 
diff --git a/adb/adb_utils.cpp b/adb/adb_utils.cpp
index 604bd57..e5dc692 100644
--- a/adb/adb_utils.cpp
+++ b/adb/adb_utils.cpp
@@ -18,6 +18,7 @@
 
 #include "adb_utils.h"
 
+#include <libgen.h>
 #include <stdlib.h>
 #include <sys/stat.h>
 #include <sys/types.h>
@@ -25,11 +26,15 @@
 
 #include <algorithm>
 
+#include <base/logging.h>
 #include <base/stringprintf.h>
+#include <base/strings.h>
 
 #include "adb_trace.h"
 #include "sysdeps.h"
 
+ADB_MUTEX_DEFINE(dirname_lock);
+
 bool getcwd(std::string* s) {
   char* cwd = getcwd(nullptr, 0);
   if (cwd != nullptr) *s = cwd;
@@ -63,6 +68,89 @@
   return result;
 }
 
+std::string adb_basename(const std::string& path) {
+  size_t base = path.find_last_of(OS_PATH_SEPARATORS);
+  return (base != std::string::npos) ? path.substr(base + 1) : path;
+}
+
+std::string adb_dirname(const std::string& path) {
+  // Copy path because dirname may modify the string passed in.
+  std::string parent_storage(path);
+
+  // Use lock because dirname() may write to a process global and return a
+  // pointer to that. Note that this locking strategy only works if all other
+  // callers to dirname in the process also grab this same lock.
+  adb_mutex_lock(&dirname_lock);
+
+  // Note that if std::string uses copy-on-write strings, &str[0] will cause
+  // the copy to be made, so there is no chance of us accidentally writing to
+  // the storage for 'path'.
+  char* parent = dirname(&parent_storage[0]);
+
+  // In case dirname returned a pointer to a process global, copy that string
+  // before leaving the lock.
+  const std::string result(parent);
+
+  adb_mutex_unlock(&dirname_lock);
+
+  return result;
+}
+
+// Given a relative or absolute filepath, create the parent directory hierarchy
+// as needed. Returns true if the hierarchy is/was setup.
+bool mkdirs(const std::string& path) {
+  // TODO: all the callers do unlink && mkdirs && adb_creat ---
+  // that's probably the operation we should expose.
+
+  // Implementation Notes:
+  //
+  // Pros:
+  // - Uses dirname, so does not need to deal with OS_PATH_SEPARATOR.
+  // - On Windows, uses mingw dirname which accepts '/' and '\\', drive letters
+  //   (C:\foo), UNC paths (\\server\share\dir\dir\file), and Unicode (when
+  //   combined with our adb_mkdir() which takes UTF-8).
+  // - Is optimistic wrt thinking that a deep directory hierarchy will exist.
+  //   So it does as few stat()s as possible before doing mkdir()s.
+  // Cons:
+  // - Recursive, so it uses stack space relative to number of directory
+  //   components.
+
+  const std::string parent(adb_dirname(path));
+
+  if (directory_exists(parent)) {
+    return true;
+  }
+
+  // If dirname returned the same path as what we passed in, don't go recursive.
+  // This can happen on Windows when walking up the directory hierarchy and not
+  // finding anything that already exists (unlike POSIX that will eventually
+  // find . or /).
+  if (parent == path) {
+    errno = ENOENT;
+    return false;
+  }
+
+  // Recursively make parent directories of 'parent'.
+  if (!mkdirs(parent)) {
+    return false;
+  }
+
+  // Now that the parent directory hierarchy of 'parent' has been ensured,
+  // create parent itself.
+  if (adb_mkdir(parent, 0775) == -1) {
+    // Can't just check for errno == EEXIST because it might be a file that
+    // exists.
+    const int saved_errno = errno;
+    if (directory_exists(parent)) {
+      return true;
+    }
+    errno = saved_errno;
+    return false;
+  }
+
+  return true;
+}
+
 void dump_hex(const void* data, size_t byte_count) {
     byte_count = std::min(byte_count, size_t(16));
 
@@ -84,3 +172,60 @@
 
     DR("%s\n", line.c_str());
 }
+
+bool parse_host_and_port(const std::string& address,
+                         std::string* canonical_address,
+                         std::string* host, int* port,
+                         std::string* error) {
+    host->clear();
+
+    bool ipv6 = true;
+    bool saw_port = false;
+    size_t colons = std::count(address.begin(), address.end(), ':');
+    size_t dots = std::count(address.begin(), address.end(), '.');
+    std::string port_str;
+    if (address[0] == '[') {
+      // [::1]:123
+      if (address.rfind("]:") == std::string::npos) {
+        *error = android::base::StringPrintf("bad IPv6 address '%s'", address.c_str());
+        return false;
+      }
+      *host = address.substr(1, (address.find("]:") - 1));
+      port_str = address.substr(address.rfind("]:") + 2);
+      saw_port = true;
+    } else if (dots == 0 && colons >= 2 && colons <= 7) {
+      // ::1
+      *host = address;
+    } else if (colons <= 1) {
+      // 1.2.3.4 or some.accidental.domain.com
+      ipv6 = false;
+      std::vector<std::string> pieces = android::base::Split(address, ":");
+      *host = pieces[0];
+      if (pieces.size() > 1) {
+        port_str = pieces[1];
+        saw_port = true;
+      }
+    }
+
+    if (host->empty()) {
+      *error = android::base::StringPrintf("no host in '%s'", address.c_str());
+      return false;
+    }
+
+    if (saw_port) {
+      if (sscanf(port_str.c_str(), "%d", port) != 1 || *port <= 0 || *port > 65535) {
+        *error = android::base::StringPrintf("bad port number '%s' in '%s'",
+                                             port_str.c_str(), address.c_str());
+        return false;
+      }
+    }
+
+    *canonical_address = android::base::StringPrintf(ipv6 ? "[%s]:%d" : "%s:%d", host->c_str(), *port);
+    LOG(DEBUG) << "parsed " << address << " as " << *host << " and " << *port
+               << " (" << *canonical_address << ")";
+    return true;
+}
+
+std::string perror_str(const char* msg) {
+    return android::base::StringPrintf("%s: %s", msg, strerror(errno));
+}
diff --git a/adb/adb_utils.h b/adb/adb_utils.h
index 84f7d0c..b38ec59 100644
--- a/adb/adb_utils.h
+++ b/adb/adb_utils.h
@@ -22,8 +22,28 @@
 bool getcwd(std::string* cwd);
 bool directory_exists(const std::string& path);
 
+// Like the regular basename and dirname, but thread-safe on all
+// platforms and capable of correctly handling exotic Windows paths.
+std::string adb_basename(const std::string& path);
+std::string adb_dirname(const std::string& path);
+
+bool mkdirs(const std::string& path);
+
 std::string escape_arg(const std::string& s);
 
 void dump_hex(const void* ptr, size_t byte_count);
 
+// Parses 'address' into 'host' and 'port'.
+// If no port is given, takes the default from *port.
+// 'canonical_address' then becomes "host:port" or "[host]:port" as appropriate.
+// Note that no real checking is done that 'host' or 'port' is valid; that's
+// left to getaddrinfo(3).
+// Returns false on failure and sets *error to an appropriate message.
+bool parse_host_and_port(const std::string& address,
+                         std::string* canonical_address,
+                         std::string* host, int* port,
+                         std::string* error);
+
+std::string perror_str(const char* msg);
+
 #endif
diff --git a/adb/adb_utils_test.cpp b/adb/adb_utils_test.cpp
index 052aea5..17c8d0a 100644
--- a/adb/adb_utils_test.cpp
+++ b/adb/adb_utils_test.cpp
@@ -16,12 +16,60 @@
 
 #include "adb_utils.h"
 
+#ifdef _WIN32
+#include <windows.h>
+#include <userenv.h>
+#endif
+
+#include <string>
+
 #include <gtest/gtest.h>
 
+#include <stdlib.h>
+#include <string.h>
+
+#include "sysdeps.h"
+
+#include <base/macros.h>
+#include <base/test_utils.h>
+
+#ifdef _WIN32
+static std::string subdir(const char* parent, const char* child) {
+  std::string str(parent);
+  str += OS_PATH_SEPARATOR;
+  str += child;
+  return str;
+}
+#endif
+
 TEST(adb_utils, directory_exists) {
+#ifdef _WIN32
+  char profiles_dir[MAX_PATH];
+  DWORD cch = arraysize(profiles_dir);
+
+  // On typical Windows 7, returns C:\Users
+  ASSERT_TRUE(GetProfilesDirectoryA(profiles_dir, &cch));
+
+  ASSERT_TRUE(directory_exists(profiles_dir));
+
+  // On modern (English?) Windows, this is a directory symbolic link to
+  // C:\ProgramData. Symbolic links are rare on Windows and the user requires
+  // a special permission (by default granted to Administrative users) to
+  // create symbolic links.
+  ASSERT_FALSE(directory_exists(subdir(profiles_dir, "All Users")));
+
+  // On modern (English?) Windows, this is a directory junction to
+  // C:\Users\Default. Junctions are used throughout user profile directories
+  // for backwards compatibility and they don't require any special permissions
+  // to create.
+  ASSERT_FALSE(directory_exists(subdir(profiles_dir, "Default User")));
+
+  ASSERT_FALSE(directory_exists(subdir(profiles_dir, "does-not-exist")));
+#else
   ASSERT_TRUE(directory_exists("/proc"));
   ASSERT_FALSE(directory_exists("/proc/self")); // Symbolic link.
   ASSERT_FALSE(directory_exists("/proc/does-not-exist"));
+#endif
 }
 
 TEST(adb_utils, escape_arg) {
@@ -50,3 +98,107 @@
   ASSERT_EQ(R"('abc(')", escape_arg("abc("));
   ASSERT_EQ(R"('abc)')", escape_arg("abc)"));
 }
+
+TEST(adb_utils, adb_basename) {
+  EXPECT_EQ("sh", adb_basename("/system/bin/sh"));
+  EXPECT_EQ("sh", adb_basename("sh"));
+}
+
+TEST(adb_utils, parse_host_and_port) {
+  std::string canonical_address;
+  std::string host;
+  int port;
+  std::string error;
+
+  // Name, default port.
+  port = 123;
+  ASSERT_TRUE(parse_host_and_port("www.google.com", &canonical_address, &host, &port, &error));
+  ASSERT_EQ("www.google.com:123", canonical_address);
+  ASSERT_EQ("www.google.com", host);
+  ASSERT_EQ(123, port);
+
+  // Name, explicit port.
+  ASSERT_TRUE(parse_host_and_port("www.google.com:666", &canonical_address, &host, &port, &error));
+  ASSERT_EQ("www.google.com:666", canonical_address);
+  ASSERT_EQ("www.google.com", host);
+  ASSERT_EQ(666, port);
+
+  // IPv4, default port.
+  port = 123;
+  ASSERT_TRUE(parse_host_and_port("1.2.3.4", &canonical_address, &host, &port, &error));
+  ASSERT_EQ("1.2.3.4:123", canonical_address);
+  ASSERT_EQ("1.2.3.4", host);
+  ASSERT_EQ(123, port);
+
+  // IPv4, explicit port.
+  ASSERT_TRUE(parse_host_and_port("1.2.3.4:666", &canonical_address, &host, &port, &error));
+  ASSERT_EQ("1.2.3.4:666", canonical_address);
+  ASSERT_EQ("1.2.3.4", host);
+  ASSERT_EQ(666, port);
+
+  // Simple IPv6, default port.
+  port = 123;
+  ASSERT_TRUE(parse_host_and_port("::1", &canonical_address, &host, &port, &error));
+  ASSERT_EQ("[::1]:123", canonical_address);
+  ASSERT_EQ("::1", host);
+  ASSERT_EQ(123, port);
+
+  // Simple IPv6, explicit port.
+  ASSERT_TRUE(parse_host_and_port("[::1]:666", &canonical_address, &host, &port, &error));
+  ASSERT_EQ("[::1]:666", canonical_address);
+  ASSERT_EQ("::1", host);
+  ASSERT_EQ(666, port);
+
+  // Hairy IPv6, default port.
+  port = 123;
+  ASSERT_TRUE(parse_host_and_port("fe80::200:5aee:feaa:20a2", &canonical_address, &host, &port, &error));
+  ASSERT_EQ("[fe80::200:5aee:feaa:20a2]:123", canonical_address);
+  ASSERT_EQ("fe80::200:5aee:feaa:20a2", host);
+  ASSERT_EQ(123, port);
+
+  // Simple IPv6, explicit port.
+  ASSERT_TRUE(parse_host_and_port("[fe80::200:5aee:feaa:20a2]:666", &canonical_address, &host, &port, &error));
+  ASSERT_EQ("[fe80::200:5aee:feaa:20a2]:666", canonical_address);
+  ASSERT_EQ("fe80::200:5aee:feaa:20a2", host);
+  ASSERT_EQ(666, port);
+
+  // Invalid IPv4.
+  EXPECT_FALSE(parse_host_and_port("1.2.3.4:", &canonical_address, &host, &port, &error));
+  EXPECT_FALSE(parse_host_and_port("1.2.3.4::", &canonical_address, &host, &port, &error));
+  EXPECT_FALSE(parse_host_and_port("1.2.3.4:hello", &canonical_address, &host, &port, &error));
+  EXPECT_FALSE(parse_host_and_port(":123", &canonical_address, &host, &port, &error));
+
+  // Invalid IPv6.
+  EXPECT_FALSE(parse_host_and_port(":1", &canonical_address, &host, &port, &error));
+  EXPECT_FALSE(parse_host_and_port("::::::::1", &canonical_address, &host, &port, &error));
+  EXPECT_FALSE(parse_host_and_port("[::1", &canonical_address, &host, &port, &error));
+  EXPECT_FALSE(parse_host_and_port("[::1]", &canonical_address, &host, &port, &error));
+  EXPECT_FALSE(parse_host_and_port("[::1]:", &canonical_address, &host, &port, &error));
+  EXPECT_FALSE(parse_host_and_port("[::1]::", &canonical_address, &host, &port, &error));
+  EXPECT_FALSE(parse_host_and_port("[::1]:hello", &canonical_address, &host, &port, &error));
+
+  // Invalid ports.
+  EXPECT_FALSE(parse_host_and_port("[::1]:-1", &canonical_address, &host, &port, &error));
+  EXPECT_FALSE(parse_host_and_port("[::1]:0", &canonical_address, &host, &port, &error));
+  EXPECT_FALSE(parse_host_and_port("[::1]:65536", &canonical_address, &host, &port, &error));
+  EXPECT_FALSE(parse_host_and_port("1.2.3.4:-1", &canonical_address, &host, &port, &error));
+  EXPECT_FALSE(parse_host_and_port("1.2.3.4:0", &canonical_address, &host, &port, &error));
+  EXPECT_FALSE(parse_host_and_port("1.2.3.4:65536", &canonical_address, &host, &port, &error));
+}
+
+void test_mkdirs(const std::string basepath) {
+  EXPECT_TRUE(mkdirs(basepath));
+  EXPECT_NE(-1, adb_creat(basepath.c_str(), 0600));
+  EXPECT_FALSE(mkdirs(basepath + "/subdir/"));
+}
+
+TEST(adb_utils, mkdirs) {
+  TemporaryDir td;
+
+  // Absolute paths.
+  test_mkdirs(std::string(td.path) + "/dir/subdir/file");
+
+  // Relative paths.
+  ASSERT_EQ(0, chdir(td.path)) << strerror(errno);
+  test_mkdirs(std::string("relative/subrel/file"));
+}
diff --git a/adb/adbd.rc b/adb/adbd.rc
new file mode 100644
index 0000000..b91d8b5
--- /dev/null
+++ b/adb/adbd.rc
@@ -0,0 +1,14 @@
+on post-fs-data
+    mkdir /data/misc/adb 02750 system shell
+    mkdir /data/adb 0700 root root
+
+# adbd is controlled via property triggers in init.<platform>.usb.rc
+service adbd /sbin/adbd --root_seclabel=u:r:su:s0
+    class core
+    socket adbd stream 660 system system
+    disabled
+    seclabel u:r:adbd:s0
+
+# adbd on at boot in emulator
+on property:ro.kernel.qemu=1
+    start adbd
diff --git a/adb/client/main.cpp b/adb/client/main.cpp
new file mode 100644
index 0000000..8d644d9
--- /dev/null
+++ b/adb/client/main.cpp
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define TRACE_TAG TRACE_ADB
+
+#include "sysdeps.h"
+
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+// We only build the affinity WAR code for Linux.
+#if defined(__linux__)
+#include <sched.h>
+#endif
+
+#include "base/file.h"
+#include "base/logging.h"
+#include "base/stringprintf.h"
+
+#include "adb.h"
+#include "adb_auth.h"
+#include "adb_listeners.h"
+#include "transport.h"
+
+#if defined(_WIN32)
+static const char kNullFileName[] = "NUL";
+
+static BOOL WINAPI ctrlc_handler(DWORD type) {
+    // TODO: Consider trying to kill a starting up adb server (if we're in
+    // launch_server) by calling GenerateConsoleCtrlEvent().
+    exit(STATUS_CONTROL_C_EXIT);
+    return TRUE;
+}
+
+static std::string GetLogFilePath() {
+    const char log_name[] = "adb.log";
+    WCHAR temp_path[MAX_PATH];
+
+    // https://msdn.microsoft.com/en-us/library/windows/desktop/aa364992%28v=vs.85%29.aspx
+    DWORD nchars = GetTempPathW(arraysize(temp_path), temp_path);
+    if ((nchars >= arraysize(temp_path)) || (nchars == 0)) {
+        // If string truncation or some other error.
+        fatal("cannot retrieve temporary file path: %s\n",
+              SystemErrorCodeToString(GetLastError()).c_str());
+    }
+
+    return narrow(temp_path) + log_name;
+}
+#else
+static const char kNullFileName[] = "/dev/null";
+
+static std::string GetLogFilePath() {
+    return std::string("/tmp/adb.log");
+}
+#endif
+
+static void close_stdin() {
+    int fd = unix_open(kNullFileName, O_RDONLY);
+    if (fd == -1) {
+        fatal("cannot open '%s': %s", kNullFileName, strerror(errno));
+    }
+    if (dup2(fd, STDIN_FILENO) == -1) {
+        fatal("cannot redirect stdin: %s", strerror(errno));
+    }
+    unix_close(fd);
+}
+
+static void setup_daemon_logging(void) {
+    const std::string log_file_path(GetLogFilePath());
+    int fd = unix_open(log_file_path.c_str(), O_WRONLY | O_CREAT | O_APPEND,
+                       0640);
+    if (fd == -1) {
+        fatal("cannot open '%s': %s", log_file_path.c_str(), strerror(errno));
+    }
+    if (dup2(fd, STDOUT_FILENO) == -1) {
+        fatal("cannot redirect stdout: %s", strerror(errno));
+    }
+    if (dup2(fd, STDERR_FILENO) == -1) {
+        fatal("cannot redirect stderr: %s", strerror(errno));
+    }
+    unix_close(fd);
+
+    fprintf(stderr, "--- adb starting (pid %d) ---\n", getpid());
+    LOG(INFO) << adb_version();
+}
+
+int adb_main(int is_daemon, int server_port, int ack_reply_fd) {
+#if defined(_WIN32)
+    // adb start-server starts us up with stdout and stderr hooked up to
+    // anonymous pipes to. When the C Runtime sees this, it makes stderr and
+    // stdout buffered, but to improve the chance that error output is seen,
+    // unbuffer stdout and stderr just like if we were run at the console.
+    // This also keeps stderr unbuffered when it is redirected to adb.log.
+    if (is_daemon) {
+        if (setvbuf(stdout, NULL, _IONBF, 0) == -1) {
+            fatal("cannot make stdout unbuffered: %s", strerror(errno));
+        }
+        if (setvbuf(stderr, NULL, _IONBF, 0) == -1) {
+            fatal("cannot make stderr unbuffered: %s", strerror(errno));
+        }
+    }
+
+    SetConsoleCtrlHandler(ctrlc_handler, TRUE);
+#else
+    signal(SIGPIPE, SIG_IGN);
+#endif
+
+    init_transport_registration();
+
+    usb_init();
+    local_init(DEFAULT_ADB_LOCAL_TRANSPORT_PORT);
+    adb_auth_init();
+
+    std::string error;
+    std::string local_name = android::base::StringPrintf("tcp:%d", server_port);
+    if (install_listener(local_name, "*smartsocket*", nullptr, 0, &error)) {
+        fatal("could not install *smartsocket* listener: %s", error.c_str());
+    }
+
+    // Inform our parent that we are up and running.
+    if (is_daemon) {
+        close_stdin();
+        setup_daemon_logging();
+
+        // Any error output written to stderr now goes to adb.log. We could
+        // keep around a copy of the stderr fd and use that to write any errors
+        // encountered by the following code, but that is probably overkill.
+#if defined(_WIN32)
+        const HANDLE ack_reply_handle = cast_int_to_handle(ack_reply_fd);
+        const CHAR ack[] = "OK\n";
+        const DWORD bytes_to_write = arraysize(ack) - 1;
+        DWORD written = 0;
+        if (!WriteFile(ack_reply_handle, ack, bytes_to_write, &written, NULL)) {
+            fatal("adb: cannot write ACK to handle 0x%p: %s", ack_reply_handle,
+                  SystemErrorCodeToString(GetLastError()).c_str());
+        }
+        if (written != bytes_to_write) {
+            fatal("adb: cannot write %lu bytes of ACK: only wrote %lu bytes",
+                  bytes_to_write, written);
+        }
+        CloseHandle(ack_reply_handle);
+#else
+        // TODO(danalbert): Can't use SendOkay because we're sending "OK\n", not
+        // "OKAY".
+        if (!android::base::WriteStringToFd("OK\n", ack_reply_fd)) {
+            fatal_errno("error writing ACK to fd %d", ack_reply_fd);
+        }
+        unix_close(ack_reply_fd);
+#endif
+    }
+
+    D("Event loop starting\n");
+    fdevent_loop();
+
+    return 0;
+}
+
+#ifdef _WIN32
+static bool _argv_is_utf8 = false;
+#endif
+
+int main(int argc, char** argv) {
+#ifdef _WIN32
+    if (!_argv_is_utf8) {
+        fatal("_argv_is_utf8 is not set, suggesting that wmain was not "
+              "called. Did you forget to link with -municode?");
+    }
+#endif
+
+    adb_sysdeps_init();
+    adb_trace_init(argv);
+    return adb_commandline(argc - 1, const_cast<const char**>(argv + 1));
+}
+
+#ifdef _WIN32
+
+extern "C"
+int wmain(int argc, wchar_t **argv) {
+    // Set diagnostic flag to try to detect if the build system was not
+    // configured to call wmain.
+    _argv_is_utf8 = true;
+
+    // Convert args from UTF-16 to UTF-8 and pass that to main().
+    NarrowArgs narrow_args(argc, argv);
+    return main(argc, narrow_args.data());
+}
+
+#endif
diff --git a/adb/commandline.cpp b/adb/commandline.cpp
index 374a2e5..80c085a 100644
--- a/adb/commandline.cpp
+++ b/adb/commandline.cpp
@@ -47,9 +47,9 @@
 #include "adb_utils.h"
 #include "file_sync_service.h"
 
-static int install_app(transport_type t, const char* serial, int argc, const char** argv);
-static int install_multiple_app(transport_type t, const char* serial, int argc, const char** argv);
-static int uninstall_app(transport_type t, const char* serial, int argc, const char** argv);
+static int install_app(TransportType t, const char* serial, int argc, const char** argv);
+static int install_multiple_app(TransportType t, const char* serial, int argc, const char** argv);
+static int uninstall_app(TransportType t, const char* serial, int argc, const char** argv);
 
 static std::string gProductOutPath;
 extern int gListenAll;
@@ -65,16 +65,9 @@
                                        gProductOutPath.c_str(), OS_PATH_SEPARATOR_STR, extra);
 }
 
-static void version(FILE* out) {
-    fprintf(out, "Android Debug Bridge version %d.%d.%d\nRevision %s\n",
-            ADB_VERSION_MAJOR, ADB_VERSION_MINOR, ADB_SERVER_VERSION, ADB_REVISION);
-}
-
 static void help() {
-    version(stderr);
-
+    fprintf(stderr, "%s\n", adb_version().c_str());
     fprintf(stderr,
-        "\n"
         " -a                            - directs adb to listen on all interfaces for a connection\n"
         " -d                            - directs command to the only connected USB device\n"
         "                                 returns an error if more than one USB device is present.\n"
@@ -110,7 +103,6 @@
         "                                 ('-a' means copy timestamp and mode)\n"
         "  adb sync [ <directory> ]     - copy host->device only if changed\n"
         "                                 (-l means list but don't copy)\n"
-        "                                 (see 'adb help all')\n"
         "  adb shell                    - run remote shell interactively\n"
         "  adb shell <command>          - run remote shell command\n"
         "  adb emu <command>            - run emulator console command\n"
@@ -210,11 +202,12 @@
         "  adb reboot sideload          - reboots the device into the sideload mode in recovery program (adb root required).\n"
         "  adb reboot sideload-auto-reboot\n"
         "                               - reboots into the sideload mode, then reboots automatically after the sideload regardless of the result.\n"
-        "  adb reboot-bootloader        - reboots the device into the bootloader\n"
+        "  adb sideload <file>          - sideloads the given package\n"
         "  adb root                     - restarts the adbd daemon with root permissions\n"
         "  adb unroot                   - restarts the adbd daemon without root permissions\n"
         "  adb usb                      - restarts the adbd daemon listening on USB\n"
         "  adb tcpip <port>             - restarts the adbd daemon listening on TCP on the specified port\n"
+        "\n"
         "networking:\n"
         "  adb ppp <tty> [parameters]   - Run PPP over USB.\n"
         " Note: you should not automatically start a PPP connection.\n"
@@ -229,7 +222,7 @@
         "  - If it is \"system\", \"vendor\", \"oem\" or \"data\", only the corresponding partition\n"
         "    is updated.\n"
         "\n"
-        "environmental variables:\n"
+        "environment variables:\n"
         "  ADB_TRACE                    - Print debug information. A comma separated list of the following values\n"
         "                                 1 or all, adb, sockets, packets, rwx, usb, sync, sysdeps, transport, jdwp\n"
         "  ANDROID_SERIAL               - The serial number to connect to. -s takes priority over this if given.\n"
@@ -291,10 +284,7 @@
     count--;
     while (count > 0) {
         int len = adb_read(fd, buf, count);
-        if (len == 0) {
-            break;
-        } else if (len < 0) {
-            if (errno == EINTR) continue;
+        if (len <= 0) {
             break;
         }
 
@@ -310,13 +300,32 @@
     if (buf == nullptr) fatal("couldn't allocate buffer for copy_to_file");
     int len;
     long total = 0;
+#ifdef _WIN32
+    int old_stdin_mode = -1;
+    int old_stdout_mode = -1;
+#endif
 
     D("copy_to_file(%d -> %d)\n", inFd, outFd);
 
     if (inFd == STDIN_FILENO) {
         stdin_raw_init(STDIN_FILENO);
+#ifdef _WIN32
+        old_stdin_mode = _setmode(STDIN_FILENO, _O_BINARY);
+        if (old_stdin_mode == -1) {
+            fatal_errno("could not set stdin to binary");
+        }
+#endif
     }
 
+#ifdef _WIN32
+    if (outFd == STDOUT_FILENO) {
+        old_stdout_mode = _setmode(STDOUT_FILENO, _O_BINARY);
+        if (old_stdout_mode == -1) {
+            fatal_errno("could not set stdout to binary");
+        }
+    }
+#endif
+
     while (true) {
         if (inFd == STDIN_FILENO) {
             len = unix_read(inFd, buf, BUFSIZE);
@@ -328,11 +337,7 @@
             break;
         }
         if (len < 0) {
-            if (errno == EINTR) {
-                D("copy_to_file() : EINTR, retrying\n");
-                continue;
-            }
-            D("copy_to_file() : error %d\n", errno);
+            D("copy_to_file(): read failed: %s\n", strerror(errno));
             break;
         }
         if (outFd == STDOUT_FILENO) {
@@ -346,8 +351,21 @@
 
     if (inFd == STDIN_FILENO) {
         stdin_raw_restore(STDIN_FILENO);
+#ifdef _WIN32
+        if (_setmode(STDIN_FILENO, old_stdin_mode) == -1) {
+            fatal_errno("could not restore stdin mode");
+        }
+#endif
     }
 
+#ifdef _WIN32
+    if (outFd == STDOUT_FILENO) {
+        if (_setmode(STDOUT_FILENO, old_stdout_mode) == -1) {
+            fatal_errno("could not restore stdout mode");
+        }
+    }
+#endif
+
     D("copy_to_file() finished after %lu bytes\n", total);
     free(buf);
 }
@@ -364,17 +382,15 @@
     fdi = fds[1];
     free(fds);
 
-    for(;;) {
+    adb_thread_setname("stdin reader");
+
+    while (true) {
         /* fdi is really the client's stdin, so use read, not adb_read here */
         D("stdin_read_thread(): pre unix_read(fdi=%d,...)\n", fdi);
         r = unix_read(fdi, buf, 1024);
         D("stdin_read_thread(): post unix_read(fdi=%d,...)\n", fdi);
-        if(r == 0) break;
-        if(r < 0) {
-            if(errno == EINTR) continue;
-            break;
-        }
-        for(n = 0; n < r; n++){
+        if (r <= 0) break;
+        for (n = 0; n < r; n++){
             switch(buf[n]) {
             case '\n':
                 state = 1;
@@ -404,7 +420,6 @@
 }
 
 static int interactive_shell() {
-    adb_thread_t thr;
     int fdi;
 
     std::string error;
@@ -425,14 +440,15 @@
     fds[1] = fdi;
 
     stdin_raw_init(fdi);
-    adb_thread_create(&thr, stdin_read_thread, fds);
+
+    adb_thread_create(stdin_read_thread, fds);
     read_and_dump(fd);
     stdin_raw_restore(fdi);
     return 0;
 }
 
 
-static std::string format_host_command(const char* command, transport_type type, const char* serial) {
+static std::string format_host_command(const char* command, TransportType type, const char* serial) {
     if (serial) {
         return android::base::StringPrintf("host-serial:%s:%s", serial, command);
     }
@@ -463,8 +479,8 @@
     const uint8_t* ptr = reinterpret_cast<const uint8_t*>(data);
 
     if (show_progress) {
-        char *x = strrchr(service, ':');
-        if(x) service = x + 1;
+        const char* x = strrchr(service, ':');
+        if (x) service = x + 1;
     }
 
     while (sz > 0) {
@@ -674,7 +690,7 @@
 #endif /* !defined(_WIN32) */
 }
 
-static bool wait_for_device(const char* service, transport_type t, const char* serial) {
+static bool wait_for_device(const char* service, TransportType t, const char* serial) {
     // Was the caller vague about what they'd like us to wait for?
     // If so, check they weren't more specific in their choice of transport type.
     if (strcmp(service, "wait-for-device") == 0) {
@@ -688,17 +704,10 @@
     }
 
     std::string cmd = format_host_command(service, t, serial);
-    std::string error;
-    if (adb_command(cmd, &error)) {
-        D("failure: %s *\n", error.c_str());
-        fprintf(stderr,"error: %s\n", error.c_str());
-        return false;
-    }
-
-    return true;
+    return adb_command(cmd);
 }
 
-static int send_shell_command(transport_type transport_type, const char* serial,
+static int send_shell_command(TransportType transport_type, const char* serial,
                               const std::string& command) {
     int fd;
     while (true) {
@@ -720,7 +729,7 @@
     return rc;
 }
 
-static int logcat(transport_type transport, const char* serial, int argc, const char** argv) {
+static int logcat(TransportType transport, const char* serial, int argc, const char** argv) {
     char* log_tags = getenv("ANDROID_LOG_TAGS");
     std::string quoted = escape_arg(log_tags == nullptr ? "" : log_tags);
 
@@ -739,27 +748,6 @@
     return send_shell_command(transport, serial, cmd);
 }
 
-static int mkdirs(const char *path)
-{
-    std::string holder(path);
-
-    int ret;
-    char *x = &holder[1];
-
-    for(;;) {
-        x = adb_dirstart(x);
-        if(x == 0) return 0;
-        *x = 0;
-        ret = adb_mkdir(path, 0775);
-        *x = OS_PATH_SEPARATOR;
-        if((ret < 0) && (errno != EEXIST)) {
-            return ret;
-        }
-        x++;
-    }
-    return 0;
-}
-
 static int backup(int argc, const char** argv) {
     const char* filename = "backup.ab";
 
@@ -851,25 +839,25 @@
  * Given <hint>, try to construct an absolute path to the
  * ANDROID_PRODUCT_OUT dir.
  */
-static std::string find_product_out_path(const char* hint) {
-    if (hint == NULL || hint[0] == '\0') {
+static std::string find_product_out_path(const std::string& hint) {
+    if (hint.empty()) {
         return "";
     }
 
     // If it's already absolute, don't bother doing any work.
-    if (adb_is_absolute_host_path(hint)) {
+    if (adb_is_absolute_host_path(hint.c_str())) {
         return hint;
     }
 
     // If there are any slashes in it, assume it's a relative path;
     // make it absolute.
-    if (adb_dirstart(hint) != nullptr) {
+    if (hint.find_first_of(OS_PATH_SEPARATORS) != std::string::npos) {
         std::string cwd;
         if (!getcwd(&cwd)) {
             fprintf(stderr, "adb: getcwd failed: %s\n", strerror(errno));
             return "";
         }
-        return android::base::StringPrintf("%s%s%s", cwd.c_str(), OS_PATH_SEPARATOR_STR, hint);
+        return android::base::StringPrintf("%s%c%s", cwd.c_str(), OS_PATH_SEPARATOR, hint.c_str());
     }
 
     // It's a string without any slashes.  Try to do something with it.
@@ -893,21 +881,21 @@
     path += hint;
     if (!directory_exists(path)) {
         fprintf(stderr, "adb: Couldn't find a product dir based on -p %s; "
-                        "\"%s\" doesn't exist\n", hint, path.c_str());
+                        "\"%s\" doesn't exist\n", hint.c_str(), path.c_str());
         return "";
     }
     return path;
 }
 
 static void parse_push_pull_args(const char **arg, int narg, char const **path1,
-                                 char const **path2, int *show_progress,
+                                 char const **path2, bool* show_progress,
                                  int *copy_attrs) {
-    *show_progress = 0;
+    *show_progress = false;
     *copy_attrs = 0;
 
     while (narg > 0) {
         if (!strcmp(*arg, "-p")) {
-            *show_progress = 1;
+            *show_progress = true;
         } else if (!strcmp(*arg, "-a")) {
             *copy_attrs = 1;
         } else {
@@ -955,11 +943,9 @@
     int no_daemon = 0;
     int is_daemon = 0;
     int is_server = 0;
-    int persist = 0;
     int r;
-    transport_type ttype = kTransportAny;
-    const char* serial = NULL;
-    const char* server_port_str = NULL;
+    TransportType transport_type = kTransportAny;
+    int ack_reply_fd = -1;
 
     // If defined, this should be an absolute path to
     // the directory containing all of the various system images
@@ -972,16 +958,16 @@
     }
     // TODO: also try TARGET_PRODUCT/TARGET_DEVICE as a hint
 
-    serial = getenv("ANDROID_SERIAL");
+    const char* serial = getenv("ANDROID_SERIAL");
 
     /* Validate and assign the server port */
-    server_port_str = getenv("ANDROID_ADB_SERVER_PORT");
+    const char* server_port_str = getenv("ANDROID_ADB_SERVER_PORT");
     int server_port = DEFAULT_ADB_PORT;
     if (server_port_str && strlen(server_port_str) > 0) {
-        server_port = (int) strtol(server_port_str, NULL, 0);
+        server_port = strtol(server_port_str, nullptr, 0);
         if (server_port <= 0 || server_port > 65535) {
             fprintf(stderr,
-                    "adb: Env var ANDROID_ADB_SERVER_PORT must be a positive number less than 65535. Got \"%s\"\n",
+                    "adb: Env var ANDROID_ADB_SERVER_PORT must be a positive number less than 65536. Got \"%s\"\n",
                     server_port_str);
             return usage();
         }
@@ -996,10 +982,25 @@
         } else if (!strcmp(argv[0], "fork-server")) {
             /* this is a special flag used only when the ADB client launches the ADB Server */
             is_daemon = 1;
-        } else if (!strcmp(argv[0],"persist")) {
-            persist = 1;
+        } else if (!strcmp(argv[0], "--reply-fd")) {
+            if (argc < 2) return usage();
+            const char* reply_fd_str = argv[1];
+            argc--;
+            argv++;
+            ack_reply_fd = strtol(reply_fd_str, nullptr, 10);
+#ifdef _WIN32
+            const HANDLE ack_reply_handle = cast_int_to_handle(ack_reply_fd);
+            if ((GetStdHandle(STD_INPUT_HANDLE) == ack_reply_handle) ||
+                (GetStdHandle(STD_OUTPUT_HANDLE) == ack_reply_handle) ||
+                (GetStdHandle(STD_ERROR_HANDLE) == ack_reply_handle)) {
+#else
+            if (ack_reply_fd <= 2) { // Disallow stdin, stdout, and stderr.
+#endif
+                fprintf(stderr, "adb: invalid reply fd \"%s\"\n", reply_fd_str);
+                return usage();
+            }
         } else if (!strncmp(argv[0], "-p", 2)) {
-            const char *product = NULL;
+            const char* product = nullptr;
             if (argv[0][2] == '\0') {
                 if (argc < 2) return usage();
                 product = argv[1];
@@ -1023,9 +1024,9 @@
                 argv++;
             }
         } else if (!strcmp(argv[0],"-d")) {
-            ttype = kTransportUsb;
+            transport_type = kTransportUsb;
         } else if (!strcmp(argv[0],"-e")) {
-            ttype = kTransportLocal;
+            transport_type = kTransportLocal;
         } else if (!strcmp(argv[0],"-a")) {
             gListenAll = 1;
         } else if (!strncmp(argv[0], "-H", 2)) {
@@ -1070,12 +1071,16 @@
         argv++;
     }
 
-    adb_set_transport(ttype, serial);
+    adb_set_transport(transport_type, serial);
     adb_set_tcp_specifics(server_port);
 
     if (is_server) {
         if (no_daemon || is_daemon) {
-            r = adb_main(is_daemon, server_port);
+            if (is_daemon && (ack_reply_fd == -1)) {
+                fprintf(stderr, "reply fd for adb server to client communication not specified.\n");
+                return usage();
+            }
+            r = adb_main(is_daemon, server_port, ack_reply_fd);
         } else {
             r = launch_server(server_port);
         }
@@ -1093,7 +1098,7 @@
     if (!strncmp(argv[0], "wait-for-", strlen("wait-for-"))) {
         const char* service = argv[0];
 
-        if (!wait_for_device(service, ttype, serial)) {
+        if (!wait_for_device(service, transport_type, serial)) {
             return 1;
         }
 
@@ -1144,7 +1149,7 @@
         return adb_query_command(query);
     }
     else if (!strcmp(argv[0], "emu")) {
-        return adb_send_emulator_command(argc, argv);
+        return adb_send_emulator_command(argc, argv, serial);
     }
     else if (!strcmp(argv[0], "shell") || !strcmp(argv[0], "hell")) {
         char h = (argv[0][0] == 'h');
@@ -1189,18 +1194,12 @@
                 r = -1;
             }
 
-            if (persist) {
-                fprintf(stderr,"\n- waiting for device -\n");
-                adb_sleep_ms(1000);
-                wait_for_device("wait-for-device", ttype, serial);
-            } else {
-                if (h) {
-                    printf("\x1b[0m");
-                    fflush(stdout);
-                }
-                D("interactive shell loop. return r=%d\n", r);
-                return r;
+            if (h) {
+                printf("\x1b[0m");
+                fflush(stdout);
             }
+            D("interactive shell loop. return r=%d\n", r);
+            return r;
         }
     }
     else if (!strcmp(argv[0], "exec-in") || !strcmp(argv[0], "exec-out")) {
@@ -1233,11 +1232,22 @@
     else if (!strcmp(argv[0], "kill-server")) {
         std::string error;
         int fd = _adb_connect("host:kill", &error);
-        if (fd == -1) {
+        if (fd == -2) {
+            // Failed to make network connection to server. Don't output the
+            // network error since that is expected.
             fprintf(stderr,"* server not running *\n");
+            // Successful exit code because the server is already "killed".
+            return 0;
+        } else if (fd == -1) {
+            // Some other error.
+            fprintf(stderr, "error: %s\n", error.c_str());
             return 1;
+        } else {
+            // Successfully connected, kill command sent, okay status came back.
+            // Server should exit() in a moment, if not already.
+            adb_close(fd);
+            return 0;
         }
-        return 0;
     }
     else if (!strcmp(argv[0], "sideload")) {
         if (argc != 2) return usage();
@@ -1247,10 +1257,12 @@
             return 0;
         }
     }
+    else if (!strcmp(argv[0], "tcpip") && argc > 1) {
+        return adb_connect_command(android::base::StringPrintf("tcpip:%s", argv[1]));
+    }
     else if (!strcmp(argv[0], "remount") ||
              !strcmp(argv[0], "reboot") ||
              !strcmp(argv[0], "reboot-bootloader") ||
-             !strcmp(argv[0], "tcpip") ||
              !strcmp(argv[0], "usb") ||
              !strcmp(argv[0], "root") ||
              !strcmp(argv[0], "unroot") ||
@@ -1268,135 +1280,87 @@
     }
     else if (!strcmp(argv[0], "bugreport")) {
         if (argc != 1) return usage();
-        return send_shell_command(ttype, serial, "shell:bugreport");
+        return send_shell_command(transport_type, serial, "shell:bugreport");
     }
-    /* adb_command() wrapper commands */
     else if (!strcmp(argv[0], "forward") || !strcmp(argv[0], "reverse")) {
-        std::string cmd;
-        char host_prefix[64];
-        char reverse = (char) !strcmp(argv[0], "reverse");
-        char remove = 0;
-        char remove_all = 0;
-        char list = 0;
-        char no_rebind = 0;
-
-        // Parse options here.
-        while (argc > 1 && argv[1][0] == '-') {
-            if (!strcmp(argv[1], "--list"))
-                list = 1;
-            else if (!strcmp(argv[1], "--remove"))
-                remove = 1;
-            else if (!strcmp(argv[1], "--remove-all"))
-                remove_all = 1;
-            else if (!strcmp(argv[1], "--no-rebind"))
-                no_rebind = 1;
-            else {
-                return usage();
-            }
-            argc--;
-            argv++;
-        }
-
-        // Ensure we can only use one option at a time.
-        if (list + remove + remove_all + no_rebind > 1) {
-            return usage();
-        }
+        bool reverse = !strcmp(argv[0], "reverse");
+        ++argv;
+        --argc;
+        if (argc < 1) return usage();
 
         // Determine the <host-prefix> for this command.
+        std::string host_prefix;
         if (reverse) {
-            snprintf(host_prefix, sizeof host_prefix, "reverse");
+            host_prefix = "reverse";
         } else {
             if (serial) {
-                snprintf(host_prefix, sizeof host_prefix, "host-serial:%s",
-                        serial);
-            } else if (ttype == kTransportUsb) {
-                snprintf(host_prefix, sizeof host_prefix, "host-usb");
-            } else if (ttype == kTransportLocal) {
-                snprintf(host_prefix, sizeof host_prefix, "host-local");
+                host_prefix = android::base::StringPrintf("host-serial:%s", serial);
+            } else if (transport_type == kTransportUsb) {
+                host_prefix = "host-usb";
+            } else if (transport_type == kTransportLocal) {
+                host_prefix = "host-local";
             } else {
-                snprintf(host_prefix, sizeof host_prefix, "host");
+                host_prefix = "host";
             }
         }
 
-        // Implement forward --list
-        if (list) {
-            if (argc != 1) {
-                return usage();
-            }
-
-            std::string query = android::base::StringPrintf("%s:list-forward", host_prefix);
-            return adb_query_command(query);
-        }
-
-        // Implement forward --remove-all
-        else if (remove_all) {
+        std::string cmd;
+        if (strcmp(argv[0], "--list") == 0) {
             if (argc != 1) return usage();
-            cmd = android::base::StringPrintf("%s:killforward-all", host_prefix);
-        }
-
-        // Implement forward --remove <local>
-        else if (remove) {
+            return adb_query_command(host_prefix + ":list-forward");
+        } else if (strcmp(argv[0], "--remove-all") == 0) {
+            if (argc != 1) return usage();
+            cmd = host_prefix + ":killforward-all";
+        } else if (strcmp(argv[0], "--remove") == 0) {
+            // forward --remove <local>
             if (argc != 2) return usage();
-            cmd = android::base::StringPrintf("%s:killforward:%s", host_prefix, argv[1]);
-        }
-        // Or implement one of:
-        //    forward <local> <remote>
-        //    forward --no-rebind <local> <remote>
-        else {
+            cmd = host_prefix + ":killforward:" + argv[1];
+        } else if (strcmp(argv[0], "--no-rebind") == 0) {
+            // forward --no-rebind <local> <remote>
             if (argc != 3) return usage();
-            const char* command = no_rebind ? "forward:norebind" : "forward";
-            cmd = android::base::StringPrintf("%s:%s:%s;%s", host_prefix, command, argv[1], argv[2]);
+            cmd = host_prefix + ":forward:norebind:" + argv[1] + ";" + argv[2];
+        } else {
+            // forward <local> <remote>
+            if (argc != 2) return usage();
+            cmd = host_prefix + ":forward:" + argv[0] + ";" + argv[1];
         }
 
-        std::string error;
-        if (adb_command(cmd, &error)) {
-            fprintf(stderr, "error: %s\n", error.c_str());
-            return 1;
-        }
-        return 0;
+        return adb_command(cmd) ? 0 : 1;
     }
     /* do_sync_*() commands */
     else if (!strcmp(argv[0], "ls")) {
         if (argc != 2) return usage();
-        return do_sync_ls(argv[1]);
+        return do_sync_ls(argv[1]) ? 0 : 1;
     }
     else if (!strcmp(argv[0], "push")) {
-        int show_progress = 0;
-        int copy_attrs = 0; // unused
+        bool show_progress = false;
+        int copy_attrs = 0;
         const char* lpath = NULL, *rpath = NULL;
 
         parse_push_pull_args(&argv[1], argc - 1, &lpath, &rpath, &show_progress, &copy_attrs);
-
-        if ((lpath != NULL) && (rpath != NULL)) {
-            return do_sync_push(lpath, rpath, show_progress);
-        }
-
-        return usage();
+        if (!lpath || !rpath || copy_attrs != 0) return usage();
+        return do_sync_push(lpath, rpath, show_progress) ? 0 : 1;
     }
     else if (!strcmp(argv[0], "pull")) {
-        int show_progress = 0;
+        bool show_progress = false;
         int copy_attrs = 0;
         const char* rpath = NULL, *lpath = ".";
 
         parse_push_pull_args(&argv[1], argc - 1, &rpath, &lpath, &show_progress, &copy_attrs);
-
-        if (rpath != NULL) {
-            return do_sync_pull(rpath, lpath, show_progress, copy_attrs);
-        }
-
-        return usage();
+        if (!rpath) return usage();
+        return do_sync_pull(rpath, lpath, show_progress, copy_attrs) ? 0 : 1;
     }
     else if (!strcmp(argv[0], "install")) {
         if (argc < 2) return usage();
-        return install_app(ttype, serial, argc, argv);
+        return install_app(transport_type, serial, argc, argv);
     }
     else if (!strcmp(argv[0], "install-multiple")) {
         if (argc < 2) return usage();
-        return install_multiple_app(ttype, serial, argc, argv);
+        return install_multiple_app(transport_type, serial, argc, argv);
     }
     else if (!strcmp(argv[0], "uninstall")) {
         if (argc < 2) return usage();
-        return uninstall_app(ttype, serial, argc, argv);
+        return uninstall_app(transport_type, serial, argc, argv);
     }
     else if (!strcmp(argv[0], "sync")) {
         std::string src;
@@ -1428,38 +1392,42 @@
         std::string vendor_src_path = product_file("vendor");
         std::string oem_src_path = product_file("oem");
 
-        int rc = 0;
-        if (rc == 0 && (src.empty() || src == "system")) {
-            rc = do_sync_sync(system_src_path, "/system", list_only);
+        bool okay = true;
+        if (okay && (src.empty() || src == "system")) {
+            okay = do_sync_sync(system_src_path, "/system", list_only);
         }
-        if (rc == 0 && (src.empty() || src == "vendor") && directory_exists(vendor_src_path)) {
-            rc = do_sync_sync(vendor_src_path, "/vendor", list_only);
+        if (okay && (src.empty() || src == "vendor") && directory_exists(vendor_src_path)) {
+            okay = do_sync_sync(vendor_src_path, "/vendor", list_only);
         }
-        if (rc == 0 && (src.empty() || src == "oem") && directory_exists(oem_src_path)) {
-            rc = do_sync_sync(oem_src_path, "/oem", list_only);
+        if (okay && (src.empty() || src == "oem") && directory_exists(oem_src_path)) {
+            okay = do_sync_sync(oem_src_path, "/oem", list_only);
         }
-        if (rc == 0 && (src.empty() || src == "data")) {
-            rc = do_sync_sync(data_src_path, "/data", list_only);
+        if (okay && (src.empty() || src == "data")) {
+            okay = do_sync_sync(data_src_path, "/data", list_only);
         }
-        return rc;
+        return okay ? 0 : 1;
     }
     /* passthrough commands */
     else if (!strcmp(argv[0],"get-state") ||
         !strcmp(argv[0],"get-serialno") ||
         !strcmp(argv[0],"get-devpath"))
     {
-        return adb_query_command(format_host_command(argv[0], ttype, serial));
+        return adb_query_command(format_host_command(argv[0], transport_type, serial));
     }
     /* other commands */
     else if (!strcmp(argv[0],"logcat") || !strcmp(argv[0],"lolcat") || !strcmp(argv[0],"longcat")) {
-        return logcat(ttype, serial, argc, argv);
+        return logcat(transport_type, serial, argc, argv);
     }
     else if (!strcmp(argv[0],"ppp")) {
         return ppp(argc, argv);
     }
     else if (!strcmp(argv[0], "start-server")) {
         std::string error;
-        return adb_connect("host:start-server", &error);
+        const int result = adb_connect("host:start-server", &error);
+        if (result < 0) {
+            fprintf(stderr, "error: %s\n", error.c_str());
+        }
+        return result;
     }
     else if (!strcmp(argv[0], "backup")) {
         return backup(argc, argv);
@@ -1480,17 +1448,18 @@
         return 0;
     }
     else if (!strcmp(argv[0], "version")) {
-        version(stdout);
+        fprintf(stdout, "%s", adb_version().c_str());
         return 0;
     }
+    else if (!strcmp(argv[0], "features")) {
+        return adb_query_command("host:features");
+    }
 
     usage();
     return 1;
 }
 
-static int pm_command(transport_type transport, const char* serial,
-                      int argc, const char** argv)
-{
+static int pm_command(TransportType transport, const char* serial, int argc, const char** argv) {
     std::string cmd = "shell:pm";
 
     while (argc-- > 0) {
@@ -1500,9 +1469,7 @@
     return send_shell_command(transport, serial, cmd);
 }
 
-static int uninstall_app(transport_type transport, const char* serial, int argc,
-                         const char** argv)
-{
+static int uninstall_app(TransportType transport, const char* serial, int argc, const char** argv) {
     /* if the user choose the -k option, we refuse to do it until devices are
        out with the option to uninstall the remaining data somehow (adb/ui) */
     if (argc == 3 && strcmp(argv[1], "-k") == 0)
@@ -1519,26 +1486,12 @@
     return pm_command(transport, serial, argc, argv);
 }
 
-static int delete_file(transport_type transport, const char* serial, char* filename)
-{
+static int delete_file(TransportType transport, const char* serial, const std::string& filename) {
     std::string cmd = "shell:rm -f " + escape_arg(filename);
     return send_shell_command(transport, serial, cmd);
 }
 
-static const char* get_basename(const char* filename)
-{
-    const char* basename = adb_dirstop(filename);
-    if (basename) {
-        basename++;
-        return basename;
-    } else {
-        return filename;
-    }
-}
-
-static int install_app(transport_type transport, const char* serial, int argc,
-                       const char** argv)
-{
+static int install_app(TransportType transport, const char* serial, int argc, const char** argv) {
     static const char *const DATA_DEST = "/data/local/tmp/%s";
     static const char *const SD_DEST = "/sdcard/tmp/%s";
     const char* where = DATA_DEST;
@@ -1556,7 +1509,7 @@
     int last_apk = -1;
     for (i = argc - 1; i >= 0; i--) {
         const char* file = argv[i];
-        char* dot = strrchr(file, '.');
+        const char* dot = strrchr(file, '.');
         if (dot && !strcasecmp(dot, ".apk")) {
             if (stat(file, &sb) == -1 || !S_ISREG(sb.st_mode)) {
                 fprintf(stderr, "Invalid APK file: %s\n", file);
@@ -1573,24 +1526,19 @@
         return -1;
     }
 
+    int result = -1;
     const char* apk_file = argv[last_apk];
-    char apk_dest[PATH_MAX];
-    snprintf(apk_dest, sizeof apk_dest, where, get_basename(apk_file));
-    int err = do_sync_push(apk_file, apk_dest, 0 /* no show progress */);
-    if (err) {
-        goto cleanup_apk;
-    } else {
-        argv[last_apk] = apk_dest; /* destination name, not source location */
-    }
-
-    err = pm_command(transport, serial, argc, argv);
+    std::string apk_dest = android::base::StringPrintf(where, adb_basename(apk_file).c_str());
+    if (!do_sync_push(apk_file, apk_dest.c_str(), false)) goto cleanup_apk;
+    argv[last_apk] = apk_dest.c_str(); /* destination name, not source location */
+    result = pm_command(transport, serial, argc, argv);
 
 cleanup_apk:
     delete_file(transport, serial, apk_dest);
-    return err;
+    return result;
 }
 
-static int install_multiple_app(transport_type transport, const char* serial, int argc,
+static int install_multiple_app(TransportType transport, const char* serial, int argc,
                                 const char** argv)
 {
     int i;
@@ -1602,7 +1550,7 @@
     int first_apk = -1;
     for (i = argc - 1; i >= 0; i--) {
         const char* file = argv[i];
-        char* dot = strrchr(file, '.');
+        const char* dot = strrchr(file, '.');
         if (dot && !strcasecmp(dot, ".apk")) {
             if (stat(file, &sb) == -1 || !S_ISREG(sb.st_mode)) {
                 fprintf(stderr, "Invalid APK file: %s\n", file);
@@ -1621,11 +1569,7 @@
         return 1;
     }
 
-#if defined(_WIN32) // Remove when we're using clang for Win32.
-    std::string cmd = android::base::StringPrintf("exec:pm install-create -S %u", (unsigned) total_size);
-#else
     std::string cmd = android::base::StringPrintf("exec:pm install-create -S %" PRIu64, total_size);
-#endif
     for (i = 1; i < first_apk; i++) {
         cmd += " " + escape_arg(argv[i]);
     }
@@ -1666,15 +1610,9 @@
             goto finalize_session;
         }
 
-#if defined(_WIN32) // Remove when we're using clang for Win32.
-        std::string cmd = android::base::StringPrintf(
-                "exec:pm install-write -S %u %d %d_%s -",
-                (unsigned) sb.st_size, session_id, i, get_basename(file));
-#else
         std::string cmd = android::base::StringPrintf(
                 "exec:pm install-write -S %" PRIu64 " %d %d_%s -",
-                static_cast<uint64_t>(sb.st_size), session_id, i, get_basename(file));
-#endif
+                static_cast<uint64_t>(sb.st_size), session_id, i, adb_basename(file).c_str());
 
         int localFd = adb_open(file, O_RDONLY);
         if (localFd < 0) {
diff --git a/adb/console.cpp b/adb/console.cpp
index 452ee41..ba5a72b 100644
--- a/adb/console.cpp
+++ b/adb/console.cpp
@@ -1,44 +1,118 @@
+/*
+ * Copyright (C) 2015 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 "sysdeps.h"
-#include "adb.h"
-#include "adb_client.h"
+
 #include <stdio.h>
 
-static int  connect_to_console(void)
-{
-    int  fd, port;
+#include <base/file.h>
+#include <base/logging.h>
+#include <base/strings.h>
+#include <cutils/sockets.h>
 
-    port = adb_get_emulator_console_port();
-    if (port < 0) {
-        if (port == -2)
-            fprintf(stderr, "error: more than one emulator detected. use -s option\n");
-        else
-            fprintf(stderr, "error: no emulator detected\n");
+#include "adb.h"
+#include "adb_client.h"
+
+// Return the console port of the currently connected emulator (if any) or -1 if
+// there is no emulator, and -2 if there is more than one.
+static int adb_get_emulator_console_port(const char* serial) {
+    if (serial) {
+        // The user specified a serial number; is it an emulator?
+        int port;
+        return (sscanf(serial, "emulator-%d", &port) == 1) ? port : -1;
+    }
+
+    // No specific device was given, so get the list of connected devices and
+    // search for emulators. If there's one, we'll take it. If there are more
+    // than one, that's an error.
+    std::string devices;
+    std::string error;
+    if (!adb_query("host:devices", &devices, &error)) {
+        fprintf(stderr, "error: no emulator connected: %s\n", error.c_str());
         return -1;
     }
-    fd = socket_loopback_client( port, SOCK_STREAM );
-    if (fd < 0) {
-        fprintf(stderr, "error: could not connect to TCP port %d\n", port);
+
+    int port;
+    size_t emulator_count = 0;
+    for (const auto& device : android::base::Split(devices, "\n")) {
+        if (sscanf(device.c_str(), "emulator-%d", &port) == 1) {
+            if (++emulator_count > 1) {
+                fprintf(
+                    stderr, "error: more than one emulator detected; use -s\n");
+                return -1;
+            }
+        }
+    }
+
+    if (emulator_count == 0) {
+        fprintf(stderr, "error: no emulator detected\n");
         return -1;
     }
-    return  fd;
+
+    return port;
 }
 
-
-int  adb_send_emulator_command(int  argc, const char**  argv)
-{
-    int   fd, nn;
-
-    fd = connect_to_console();
-    if (fd < 0)
-        return 1;
-
-#define  QUIT  "quit\n"
-
-    for (nn = 1; nn < argc; nn++) {
-        adb_write( fd, argv[nn], strlen(argv[nn]) );
-        adb_write( fd, (nn == argc-1) ? "\n" : " ", 1 );
+static int connect_to_console(const char* serial) {
+    int port = adb_get_emulator_console_port(serial);
+    if (port == -1) {
+        return -1;
     }
-    adb_write( fd, QUIT, sizeof(QUIT)-1 );
+
+    std::string error;
+    int fd = network_loopback_client(port, SOCK_STREAM, &error);
+    if (fd == -1) {
+        fprintf(stderr, "error: could not connect to TCP port %d: %s\n", port,
+                error.c_str());
+        return -1;
+    }
+    return fd;
+}
+
+int adb_send_emulator_command(int argc, const char** argv, const char* serial) {
+    int fd = connect_to_console(serial);
+    if (fd == -1) {
+        return 1;
+    }
+
+    for (int i = 1; i < argc; i++) {
+        adb_write(fd, argv[i], strlen(argv[i]));
+        adb_write(fd, i == argc - 1 ? "\n" : " ", 1);
+    }
+
+    const char disconnect_command[] = "quit\n";
+    if (adb_write(fd, disconnect_command, sizeof(disconnect_command) - 1) == -1) {
+        LOG(FATAL) << "Could not finalize emulator command";
+    }
+
+    // Drain output that the emulator console has sent us to prevent a problem
+    // on Windows where if adb closes the socket without reading all the data,
+    // the emulator's next call to recv() will have an ECONNABORTED error,
+    // preventing the emulator from reading the command that adb has sent.
+    // https://code.google.com/p/android/issues/detail?id=21021
+    int result;
+    do {
+        char buf[BUFSIZ];
+        result = adb_read(fd, buf, sizeof(buf));
+        // Keep reading until zero bytes (EOF) or an error. If 'adb emu kill'
+        // is executed, the emulator calls exit() which causes adb to get
+        // ECONNRESET. Any other emu command is followed by the quit command
+        // that we sent above, and that causes the emulator to close the socket
+        // which should cause zero bytes (EOF) to be returned.
+    } while (result > 0);
+
     adb_close(fd);
 
     return 0;
diff --git a/adb/daemon/main.cpp b/adb/daemon/main.cpp
new file mode 100644
index 0000000..2eba625
--- /dev/null
+++ b/adb/daemon/main.cpp
@@ -0,0 +1,266 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define TRACE_TAG TRACE_ADB
+
+#include "sysdeps.h"
+
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <sys/prctl.h>
+
+#include "base/logging.h"
+#include "base/stringprintf.h"
+#include "cutils/properties.h"
+#include "private/android_filesystem_config.h"
+#include "selinux/selinux.h"
+
+#include "adb.h"
+#include "adb_auth.h"
+#include "adb_listeners.h"
+#include "transport.h"
+
+static const char* root_seclabel = nullptr;
+
+static void drop_capabilities_bounding_set_if_needed() {
+#ifdef ALLOW_ADBD_ROOT
+    char value[PROPERTY_VALUE_MAX];
+    property_get("ro.debuggable", value, "");
+    if (strcmp(value, "1") == 0) {
+        return;
+    }
+#endif
+    for (int i = 0; prctl(PR_CAPBSET_READ, i, 0, 0, 0) >= 0; i++) {
+        if (i == CAP_SETUID || i == CAP_SETGID) {
+            // CAP_SETUID CAP_SETGID needed by /system/bin/run-as
+            continue;
+        }
+
+        int err = prctl(PR_CAPBSET_DROP, i, 0, 0, 0);
+
+        // Some kernels don't have file capabilities compiled in, and
+        // prctl(PR_CAPBSET_DROP) returns EINVAL. Don't automatically
+        // die when we see such misconfigured kernels.
+        if ((err < 0) && (errno != EINVAL)) {
+            PLOG(FATAL) << "Could not drop capabilities";
+        }
+    }
+}
+
+static bool should_drop_privileges() {
+#if defined(ALLOW_ADBD_ROOT)
+    char value[PROPERTY_VALUE_MAX];
+
+    // The properties that affect `adb root` and `adb unroot` are ro.secure and
+    // ro.debuggable. In this context the names don't make the expected behavior
+    // particularly obvious.
+    //
+    // ro.debuggable:
+    //   Allowed to become root, but not necessarily the default. Set to 1 on
+    //   eng and userdebug builds.
+    //
+    // ro.secure:
+    //   Drop privileges by default. Set to 1 on userdebug and user builds.
+    property_get("ro.secure", value, "1");
+    bool ro_secure = (strcmp(value, "1") == 0);
+
+    property_get("ro.debuggable", value, "");
+    bool ro_debuggable = (strcmp(value, "1") == 0);
+
+    // Drop privileges if ro.secure is set...
+    bool drop = ro_secure;
+
+    property_get("service.adb.root", value, "");
+    bool adb_root = (strcmp(value, "1") == 0);
+    bool adb_unroot = (strcmp(value, "0") == 0);
+
+    // ...except "adb root" lets you keep privileges in a debuggable build.
+    if (ro_debuggable && adb_root) {
+        drop = false;
+    }
+
+    // ...and "adb unroot" lets you explicitly drop privileges.
+    if (adb_unroot) {
+        drop = true;
+    }
+
+    return drop;
+#else
+    return true; // "adb root" not allowed, always drop privileges.
+#endif // ALLOW_ADBD_ROOT
+}
+
+int adbd_main(int server_port) {
+    umask(0);
+
+    signal(SIGPIPE, SIG_IGN);
+
+    init_transport_registration();
+
+    // We need to call this even if auth isn't enabled because the file
+    // descriptor will always be open.
+    adbd_cloexec_auth_socket();
+
+    if (ALLOW_ADBD_NO_AUTH && property_get_bool("ro.adb.secure", 0) == 0) {
+        auth_required = false;
+    }
+
+    adbd_auth_init();
+
+    // Our external storage path may be different than apps, since
+    // we aren't able to bind mount after dropping root.
+    const char* adb_external_storage = getenv("ADB_EXTERNAL_STORAGE");
+    if (adb_external_storage != nullptr) {
+        setenv("EXTERNAL_STORAGE", adb_external_storage, 1);
+    } else {
+        D("Warning: ADB_EXTERNAL_STORAGE is not set.  Leaving EXTERNAL_STORAGE"
+          " unchanged.\n");
+    }
+
+    // Add extra groups:
+    // AID_ADB to access the USB driver
+    // AID_LOG to read system logs (adb logcat)
+    // AID_INPUT to diagnose input issues (getevent)
+    // AID_INET to diagnose network issues (ping)
+    // AID_NET_BT and AID_NET_BT_ADMIN to diagnose bluetooth (hcidump)
+    // AID_SDCARD_R to allow reading from the SD card
+    // AID_SDCARD_RW to allow writing to the SD card
+    // AID_NET_BW_STATS to read out qtaguid statistics
+    gid_t groups[] = {AID_ADB,      AID_LOG,       AID_INPUT,
+                      AID_INET,     AID_NET_BT,    AID_NET_BT_ADMIN,
+                      AID_SDCARD_R, AID_SDCARD_RW, AID_NET_BW_STATS};
+    if (setgroups(sizeof(groups) / sizeof(groups[0]), groups) != 0) {
+        PLOG(FATAL) << "Could not set supplental groups";
+    }
+
+    /* don't listen on a port (default 5037) if running in secure mode */
+    /* don't run as root if we are running in secure mode */
+    if (should_drop_privileges()) {
+        drop_capabilities_bounding_set_if_needed();
+
+        /* then switch user and group to "shell" */
+        if (setgid(AID_SHELL) != 0) {
+            PLOG(FATAL) << "Could not setgid";
+        }
+        if (setuid(AID_SHELL) != 0) {
+            PLOG(FATAL) << "Could not setuid";
+        }
+
+        D("Local port disabled\n");
+    } else {
+        if (root_seclabel != nullptr) {
+            if (setcon(root_seclabel) < 0) {
+                LOG(FATAL) << "Could not set selinux context";
+            }
+        }
+        std::string error;
+        std::string local_name =
+            android::base::StringPrintf("tcp:%d", server_port);
+        if (install_listener(local_name, "*smartsocket*", nullptr, 0,
+                             &error)) {
+            LOG(FATAL) << "Could not install *smartsocket* listener: "
+                << error;
+        }
+    }
+
+    bool is_usb = false;
+    if (access(USB_ADB_PATH, F_OK) == 0 || access(USB_FFS_ADB_EP0, F_OK) == 0) {
+        // Listen on USB.
+        usb_init();
+        is_usb = true;
+    }
+
+    // If one of these properties is set, also listen on that port.
+    // If one of the properties isn't set and we couldn't listen on usb, listen
+    // on the default port.
+    char prop_port[PROPERTY_VALUE_MAX];
+    property_get("service.adb.tcp.port", prop_port, "");
+    if (prop_port[0] == '\0') {
+        property_get("persist.adb.tcp.port", prop_port, "");
+    }
+
+    int port;
+    if (sscanf(prop_port, "%d", &port) == 1 && port > 0) {
+        printf("using port=%d\n", port);
+        // Listen on TCP port specified by service.adb.tcp.port property.
+        local_init(port);
+    } else if (!is_usb) {
+        // Listen on default port.
+        local_init(DEFAULT_ADB_LOCAL_TRANSPORT_PORT);
+    }
+
+    D("adbd_main(): pre init_jdwp()\n");
+    init_jdwp();
+    D("adbd_main(): post init_jdwp()\n");
+
+    D("Event loop starting\n");
+    fdevent_loop();
+
+    return 0;
+}
+
+static void close_stdin() {
+    int fd = unix_open("/dev/null", O_RDONLY);
+    if (fd == -1) {
+        perror("failed to open /dev/null, stdin will remain open");
+        return;
+    }
+    dup2(fd, STDIN_FILENO);
+    unix_close(fd);
+}
+
+int main(int argc, char** argv) {
+    while (true) {
+        static struct option opts[] = {
+            {"root_seclabel", required_argument, nullptr, 's'},
+            {"device_banner", required_argument, nullptr, 'b'},
+            {"version", no_argument, nullptr, 'v'},
+        };
+
+        int option_index = 0;
+        int c = getopt_long(argc, argv, "", opts, &option_index);
+        if (c == -1) {
+            break;
+        }
+
+        switch (c) {
+        case 's':
+            root_seclabel = optarg;
+            break;
+        case 'b':
+            adb_device_banner = optarg;
+            break;
+        case 'v':
+            printf("Android Debug Bridge Daemon version %d.%d.%d %s\n",
+                   ADB_VERSION_MAJOR, ADB_VERSION_MINOR, ADB_SERVER_VERSION,
+                   ADB_REVISION);
+            return 0;
+        default:
+            // getopt already prints "adbd: invalid option -- %c" for us.
+            return 1;
+        }
+    }
+
+    close_stdin();
+
+    adb_trace_init(argv);
+
+    D("Handling main()\n");
+    return adbd_main(DEFAULT_ADB_PORT);
+}
diff --git a/adb/device.py b/adb/device.py
new file mode 100644
index 0000000..c5b5eea
--- /dev/null
+++ b/adb/device.py
@@ -0,0 +1,294 @@
+#
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+import logging
+import os
+import re
+import subprocess
+import tempfile
+
+
+class FindDeviceError(RuntimeError):
+    pass
+
+
+class DeviceNotFoundError(FindDeviceError):
+    def __init__(self, serial):
+        self.serial = serial
+        super(DeviceNotFoundError, self).__init__(
+            'No device with serial {}'.format(serial))
+
+
+class NoUniqueDeviceError(FindDeviceError):
+    def __init__(self):
+        super(NoUniqueDeviceError, self).__init__('No unique device')
+
+
+def get_devices():
+    with open(os.devnull, 'wb') as devnull:
+        subprocess.check_call(['adb', 'start-server'], stdout=devnull,
+                              stderr=devnull)
+    out = subprocess.check_output(['adb', 'devices']).splitlines()
+
+    # The first line of `adb devices` just says "List of attached devices", so
+    # skip that.
+    devices = []
+    for line in out[1:]:
+        if not line.strip():
+            continue
+        if 'offline' in line:
+            continue
+
+        serial, _ = re.split(r'\s+', line, maxsplit=1)
+        devices.append(serial)
+    return devices
+
+
+def _get_unique_device(product=None):
+    devices = get_devices()
+    if len(devices) != 1:
+        raise NoUniqueDeviceError()
+    return AndroidDevice(devices[0], product)
+
+
+def _get_device_by_serial(serial, product=None):
+    for device in get_devices():
+        if device == serial:
+            return AndroidDevice(serial, product)
+    raise DeviceNotFoundError(serial)
+
+
+def get_device(serial=None, product=None):
+    """Get a uniquely identified AndroidDevice if one is available.
+
+    Raises:
+        DeviceNotFoundError:
+            The serial specified by `serial` or $ANDROID_SERIAL is not
+            connected.
+
+        NoUniqueDeviceError:
+            Neither `serial` nor $ANDROID_SERIAL was set, and the number of
+            devices connected to the system is not 1. Having 0 connected
+            devices will also result in this error.
+
+    Returns:
+        An AndroidDevice associated with the first non-None identifier in the
+        following order of preference:
+
+        1) The `serial` argument.
+        2) The environment variable $ANDROID_SERIAL.
+        3) The single device connnected to the system.
+    """
+    if serial is not None:
+        return _get_device_by_serial(serial, product)
+
+    android_serial = os.getenv('ANDROID_SERIAL')
+    if android_serial is not None:
+        return _get_device_by_serial(android_serial, product)
+
+    return _get_unique_device(product)
+
+# Call this instead of subprocess.check_output() to work-around issue in Python
+# 2's subprocess class on Windows where it doesn't support Unicode. This
+# writes the command line to a UTF-8 batch file that is properly interpreted
+# by cmd.exe.
+def _subprocess_check_output(*popenargs, **kwargs):
+    # Only do this slow work-around if Unicode is in the cmd line.
+    if (os.name == 'nt' and
+            any(isinstance(arg, unicode) for arg in popenargs[0])):
+        # cmd.exe requires a suffix to know that it is running a batch file
+        tf = tempfile.NamedTemporaryFile('wb', suffix='.cmd', delete=False)
+        # @ in batch suppresses echo of the current line.
+        # Change the codepage to 65001, the UTF-8 codepage.
+        tf.write('@chcp 65001 > nul\r\n')
+        tf.write('@')
+        # Properly quote all the arguments and encode in UTF-8.
+        tf.write(subprocess.list2cmdline(popenargs[0]).encode('utf-8'))
+        tf.close()
+
+        try:
+            result = subprocess.check_output(['cmd.exe', '/c', tf.name],
+                                             **kwargs)
+        except subprocess.CalledProcessError as e:
+            # Show real command line instead of the cmd.exe command line.
+            raise subprocess.CalledProcessError(e.returncode, popenargs[0],
+                                                output=e.output)
+        finally:
+            os.remove(tf.name)
+        return result
+    else:
+        return subprocess.check_output(*popenargs, **kwargs)
+
+class AndroidDevice(object):
+    # Delimiter string to indicate the start of the exit code.
+    _RETURN_CODE_DELIMITER = 'x'
+
+    # Follow any shell command with this string to get the exit
+    # status of a program since this isn't propagated by adb.
+    #
+    # The delimiter is needed because `printf 1; echo $?` would print
+    # "10", and we wouldn't be able to distinguish the exit code.
+    _RETURN_CODE_PROBE_STRING = 'echo "{0}$?"'.format(_RETURN_CODE_DELIMITER)
+
+    # Maximum search distance from the output end to find the delimiter.
+    # adb on Windows returns \r\n even if adbd returns \n.
+    _RETURN_CODE_SEARCH_LENGTH = len('{0}255\r\n'.format(_RETURN_CODE_DELIMITER))
+
+    def __init__(self, serial, product=None):
+        self.serial = serial
+        self.product = product
+        self.adb_cmd = ['adb']
+        if self.serial is not None:
+            self.adb_cmd.extend(['-s', serial])
+        if self.product is not None:
+            self.adb_cmd.extend(['-p', product])
+        self._linesep = None
+
+    @property
+    def linesep(self):
+        if self._linesep is None:
+            self._linesep = subprocess.check_output(self.adb_cmd +
+                                                    ['shell', 'echo'])
+        return self._linesep
+
+    def _make_shell_cmd(self, user_cmd):
+        return (self.adb_cmd + ['shell'] + user_cmd +
+                ['; ' + self._RETURN_CODE_PROBE_STRING])
+
+    def _parse_shell_output(self, out):
+        """Finds the exit code string from shell output.
+
+        Args:
+            out: Shell output string.
+
+        Returns:
+            An (exit_code, output_string) tuple. The output string is
+            cleaned of any additional stuff we appended to find the
+            exit code.
+
+        Raises:
+            RuntimeError: Could not find the exit code in |out|.
+        """
+        search_text = out
+        if len(search_text) > self._RETURN_CODE_SEARCH_LENGTH:
+            # We don't want to search over massive amounts of data when we know
+            # the part we want is right at the end.
+            search_text = search_text[-self._RETURN_CODE_SEARCH_LENGTH:]
+        partition = search_text.rpartition(self._RETURN_CODE_DELIMITER)
+        if partition[1] == '':
+            raise RuntimeError('Could not find exit status in shell output.')
+        result = int(partition[2])
+        # partition[0] won't contain the full text if search_text was truncated,
+        # pull from the original string instead.
+        out = out[:-len(partition[1]) - len(partition[2])]
+        return result, out
+
+    def _simple_call(self, cmd):
+        logging.info(' '.join(self.adb_cmd + cmd))
+        return _subprocess_check_output(
+            self.adb_cmd + cmd, stderr=subprocess.STDOUT)
+
+    def shell(self, cmd):
+        logging.info(' '.join(self.adb_cmd + ['shell'] + cmd))
+        cmd = self._make_shell_cmd(cmd)
+        out = _subprocess_check_output(cmd)
+        rc, out = self._parse_shell_output(out)
+        if rc != 0:
+            error = subprocess.CalledProcessError(rc, cmd)
+            error.out = out
+            raise error
+        return out
+
+    def shell_nocheck(self, cmd):
+        cmd = self._make_shell_cmd(cmd)
+        logging.info(' '.join(cmd))
+        p = subprocess.Popen(
+            cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+        out, _ = p.communicate()
+        return self._parse_shell_output(out)
+
+    def install(self, filename, replace=False):
+        cmd = ['install']
+        if replace:
+            cmd.append('-r')
+        cmd.append(filename)
+        return self._simple_call(cmd)
+
+    def push(self, local, remote):
+        return self._simple_call(['push', local, remote])
+
+    def pull(self, remote, local):
+        return self._simple_call(['pull', remote, local])
+
+    def sync(self, directory=None):
+        cmd = ['sync']
+        if directory is not None:
+            cmd.append(directory)
+        return self._simple_call(cmd)
+
+    def forward(self, local, remote):
+        return self._simple_call(['forward', local, remote])
+
+    def tcpip(self, port):
+        return self._simple_call(['tcpip', port])
+
+    def usb(self):
+        return self._simple_call(['usb'])
+
+    def reboot(self):
+        return self._simple_call(['reboot'])
+
+    def root(self):
+        return self._simple_call(['root'])
+
+    def unroot(self):
+        return self._simple_call(['unroot'])
+
+    def forward_remove(self, local):
+        return self._simple_call(['forward', '--remove', local])
+
+    def forward_remove_all(self):
+        return self._simple_call(['forward', '--remove-all'])
+
+    def connect(self, host):
+        return self._simple_call(['connect', host])
+
+    def disconnect(self, host):
+        return self._simple_call(['disconnect', host])
+
+    def reverse(self, remote, local):
+        return self._simple_call(['reverse', remote, local])
+
+    def reverse_remove_all(self):
+        return self._simple_call(['reverse', '--remove-all'])
+
+    def reverse_remove(self, remote):
+        return self._simple_call(['reverse', '--remove', remote])
+
+    def wait(self):
+        return self._simple_call(['wait-for-device'])
+
+    def get_prop(self, prop_name):
+        output = self.shell(['getprop', prop_name]).splitlines()
+        if len(output) != 1:
+            raise RuntimeError('Too many lines in getprop output:\n' +
+                               '\n'.join(output))
+        value = output[0]
+        if not value.strip():
+            return None
+        return value
+
+    def set_prop(self, prop_name, value):
+        self.shell(['setprop', prop_name, value])
diff --git a/adb/fdevent.cpp b/adb/fdevent.cpp
index 0c43c5e..d25bbfb 100644
--- a/adb/fdevent.cpp
+++ b/adb/fdevent.cpp
@@ -2,16 +2,16 @@
 **
 ** 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 
+** 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 
+**     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 
+** 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.
 */
 
@@ -42,7 +42,9 @@
 // This socket is used when a subproc shell service exists.
 // It wakes up the fdevent_loop() and cause the correct handling
 // of the shell's pseudo-tty master. I.e. force close it.
+#if !ADB_HOST
 int SHELL_EXIT_NOTIFY_FD = -1;
+#endif // !ADB_HOST
 
 static void fatal(const char *fn, const char *fmt, ...)
 {
@@ -81,7 +83,6 @@
 static void fdevent_plist_enqueue(fdevent *node);
 static void fdevent_plist_remove(fdevent *node);
 static fdevent *fdevent_plist_dequeue(void);
-static void fdevent_subproc_event_func(int fd, unsigned events, void *userdata);
 
 static fdevent list_pending = {
     .next = &list_pending,
@@ -204,8 +205,8 @@
 
     n = epoll_wait(epoll_fd, events, 256, -1);
 
-    if(n < 0) {
-        if(errno == EINTR) return;
+    if (n < 0) {
+        if (errno == EINTR) return;
         perror("epoll_wait");
         exit(1);
     }
@@ -233,7 +234,7 @@
 
 #else /* USE_SELECT */
 
-#ifdef HAVE_WINSOCK
+#if defined(_WIN32)
 #include <winsock2.h>
 #else
 #include <sys/select.h>
@@ -510,6 +511,7 @@
     fde->func(fde->fd, events, fde->arg);
 }
 
+#if !ADB_HOST
 static void fdevent_subproc_event_func(int fd, unsigned ev,
                                        void* /* userdata */)
 {
@@ -569,6 +571,24 @@
     }
 }
 
+void fdevent_subproc_setup()
+{
+    int s[2];
+
+    if(adb_socketpair(s)) {
+        FATAL("cannot create shell-exit socket-pair\n");
+    }
+    D("socketpair: (%d,%d)\n", s[0], s[1]);
+
+    SHELL_EXIT_NOTIFY_FD = s[0];
+    fdevent *fde;
+    fde = fdevent_create(s[1], fdevent_subproc_event_func, NULL);
+    if(!fde)
+      FATAL("cannot create fdevent for shell-exit handler\n");
+    fdevent_add(fde, FDE_READ);
+}
+#endif // !ADB_HOST
+
 fdevent *fdevent_create(int fd, fd_func func, void *arg)
 {
     fdevent *fde = (fdevent*) malloc(sizeof(fdevent));
@@ -597,7 +617,7 @@
     fde->func = func;
     fde->arg = arg;
 
-#ifndef HAVE_WINSOCK
+#if !defined(_WIN32)
     fcntl(fd, F_SETFL, O_NONBLOCK);
 #endif
     fdevent_register(fde);
@@ -661,29 +681,14 @@
         fde, (fde->state & FDE_EVENTMASK) & (~(events & FDE_EVENTMASK)));
 }
 
-void fdevent_subproc_setup()
-{
-    int s[2];
-
-    if(adb_socketpair(s)) {
-        FATAL("cannot create shell-exit socket-pair\n");
-    }
-    D("socketpair: (%d,%d)", s[0], s[1]);
-
-    SHELL_EXIT_NOTIFY_FD = s[0];
-    fdevent *fde;
-    fde = fdevent_create(s[1], fdevent_subproc_event_func, NULL);
-    if(!fde)
-      FATAL("cannot create fdevent for shell-exit handler\n");
-    fdevent_add(fde, FDE_READ);
-}
-
 void fdevent_loop()
 {
     fdevent *fde;
+#if !ADB_HOST
     fdevent_subproc_setup();
+#endif // !ADB_HOST
 
-    for(;;) {
+    while (true) {
         D("--- ---- waiting for events\n");
 
         fdevent_process();
diff --git a/adb/fdevent.h b/adb/fdevent.h
index 8d84b29..ca1494c 100644
--- a/adb/fdevent.h
+++ b/adb/fdevent.h
@@ -23,7 +23,6 @@
 #define FDE_READ              0x0001
 #define FDE_WRITE             0x0002
 #define FDE_ERROR             0x0004
-#define FDE_TIMEOUT           0x0008
 
 /* features that may be set (via the events set/add/del interface) */
 #define FDE_DONT_CLOSE        0x0080
diff --git a/adb/fdevent_test.cpp b/adb/fdevent_test.cpp
new file mode 100644
index 0000000..f7622b5
--- /dev/null
+++ b/adb/fdevent_test.cpp
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2015 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 "fdevent.h"
+
+#include <gtest/gtest.h>
+
+#include <queue>
+#include <string>
+#include <vector>
+
+#include <pthread.h>
+#include <signal.h>
+
+#include "adb_io.h"
+
+class SignalHandlerRegister {
+  public:
+    SignalHandlerRegister(const std::vector<int>& signums, void (*handler)(int)) {
+        for (auto& sig : signums) {
+            sig_t old_handler = signal(sig, handler);
+            saved_signal_handlers_.push_back(std::make_pair(sig, old_handler));
+        }
+    }
+
+    ~SignalHandlerRegister() {
+        for (auto& pair : saved_signal_handlers_) {
+            signal(pair.first, pair.second);
+        }
+    }
+
+  private:
+    std::vector<std::pair<int, sig_t>> saved_signal_handlers_;
+};
+
+class FdHandler {
+  public:
+    FdHandler(int read_fd, int write_fd) : read_fd_(read_fd), write_fd_(write_fd) {
+        fdevent_install(&read_fde_, read_fd_, FdEventCallback, this);
+        fdevent_add(&read_fde_, FDE_READ | FDE_ERROR);
+        fdevent_install(&write_fde_, write_fd_, FdEventCallback, this);
+        fdevent_add(&write_fde_, FDE_ERROR);
+    }
+
+  private:
+    static void FdEventCallback(int fd, unsigned events, void* userdata) {
+        FdHandler* handler = reinterpret_cast<FdHandler*>(userdata);
+        ASSERT_EQ(0u, (events & ~(FDE_READ | FDE_WRITE))) << "unexpected events: " << events;
+        if (events & FDE_READ) {
+            ASSERT_EQ(fd, handler->read_fd_);
+            char c;
+            ASSERT_EQ(1, read(fd, &c, 1));
+            handler->queue_.push(c);
+            fdevent_add(&handler->write_fde_, FDE_WRITE);
+        }
+        if (events & FDE_WRITE) {
+            ASSERT_EQ(fd, handler->write_fd_);
+            ASSERT_FALSE(handler->queue_.empty());
+            char c = handler->queue_.front();
+            handler->queue_.pop();
+            ASSERT_EQ(1, write(fd, &c, 1));
+            if (handler->queue_.empty()) {
+              fdevent_del(&handler->write_fde_, FDE_WRITE);
+            }
+        }
+    }
+
+  private:
+    const int read_fd_;
+    const int write_fd_;
+    fdevent read_fde_;
+    fdevent write_fde_;
+    std::queue<char> queue_;
+};
+
+static void signal_handler(int) {
+    pthread_exit(nullptr);
+}
+
+struct ThreadArg {
+    int first_read_fd;
+    int last_write_fd;
+    size_t middle_pipe_count;
+};
+
+static void FdEventThreadFunc(ThreadArg* arg) {
+    SignalHandlerRegister signal_handler_register({SIGUSR1}, signal_handler);
+
+    std::vector<int> read_fds;
+    std::vector<int> write_fds;
+
+    read_fds.push_back(arg->first_read_fd);
+    for (size_t i = 0; i < arg->middle_pipe_count; ++i) {
+        int fds[2];
+        ASSERT_EQ(0, pipe(fds));
+        read_fds.push_back(fds[0]);
+        write_fds.push_back(fds[1]);
+    }
+    write_fds.push_back(arg->last_write_fd);
+
+    std::vector<std::unique_ptr<FdHandler>> fd_handlers;
+    for (size_t i = 0; i < read_fds.size(); ++i) {
+        fd_handlers.push_back(std::unique_ptr<FdHandler>(new FdHandler(read_fds[i], write_fds[i])));
+    }
+
+    fdevent_loop();
+}
+
+TEST(fdevent, smoke) {
+    const size_t PIPE_COUNT = 10;
+    const size_t MESSAGE_LOOP_COUNT = 100;
+    const std::string MESSAGE = "fdevent_test";
+    int fd_pair1[2];
+    int fd_pair2[2];
+    ASSERT_EQ(0, pipe(fd_pair1));
+    ASSERT_EQ(0, pipe(fd_pair2));
+    pthread_t thread;
+    ThreadArg thread_arg;
+    thread_arg.first_read_fd = fd_pair1[0];
+    thread_arg.last_write_fd = fd_pair2[1];
+    thread_arg.middle_pipe_count = PIPE_COUNT;
+    int writer = fd_pair1[1];
+    int reader = fd_pair2[0];
+
+    ASSERT_EQ(0, pthread_create(&thread, nullptr,
+                                reinterpret_cast<void* (*)(void*)>(FdEventThreadFunc),
+                                &thread_arg));
+
+    for (size_t i = 0; i < MESSAGE_LOOP_COUNT; ++i) {
+        std::string read_buffer = MESSAGE;
+        std::string write_buffer(MESSAGE.size(), 'a');
+        ASSERT_TRUE(WriteFdExactly(writer, read_buffer.c_str(), read_buffer.size()));
+        ASSERT_TRUE(ReadFdExactly(reader, &write_buffer[0], write_buffer.size()));
+        ASSERT_EQ(read_buffer, write_buffer);
+    }
+
+    ASSERT_EQ(0, pthread_kill(thread, SIGUSR1));
+    ASSERT_EQ(0, pthread_join(thread, nullptr));
+}
diff --git a/adb/file_sync_client.cpp b/adb/file_sync_client.cpp
index aded301..94876ee 100644
--- a/adb/file_sync_client.cpp
+++ b/adb/file_sync_client.cpp
@@ -16,6 +16,7 @@
 
 #include <dirent.h>
 #include <errno.h>
+#include <inttypes.h>
 #include <limits.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -26,109 +27,18 @@
 #include <time.h>
 #include <utime.h>
 
+#include <memory>
+
 #include "sysdeps.h"
 
 #include "adb.h"
 #include "adb_client.h"
 #include "adb_io.h"
+#include "adb_utils.h"
 #include "file_sync_service.h"
 
-static unsigned long long total_bytes;
-static long long start_time;
-
-static long long NOW()
-{
-    struct timeval tv;
-    gettimeofday(&tv, 0);
-    return ((long long) tv.tv_usec) +
-        1000000LL * ((long long) tv.tv_sec);
-}
-
-static void BEGIN()
-{
-    total_bytes = 0;
-    start_time = NOW();
-}
-
-static void END()
-{
-    long long t = NOW() - start_time;
-    if(total_bytes == 0) return;
-
-    if (t == 0)  /* prevent division by 0 :-) */
-        t = 1000000;
-
-    fprintf(stderr,"%lld KB/s (%lld bytes in %lld.%03llds)\n",
-            ((total_bytes * 1000000LL) / t) / 1024LL,
-            total_bytes, (t / 1000000LL), (t % 1000000LL) / 1000LL);
-}
-
-static const char* transfer_progress_format = "\rTransferring: %llu/%llu (%d%%)";
-
-static void print_transfer_progress(unsigned long long bytes_current,
-                                    unsigned long long bytes_total) {
-    if (bytes_total == 0) return;
-
-    fprintf(stderr, transfer_progress_format, bytes_current, bytes_total,
-            (int) (bytes_current * 100 / bytes_total));
-
-    if (bytes_current == bytes_total) {
-        fputc('\n', stderr);
-    }
-
-    fflush(stderr);
-}
-
-void sync_quit(int fd)
-{
-    syncmsg msg;
-
-    msg.req.id = ID_QUIT;
-    msg.req.namelen = 0;
-
-    WriteFdExactly(fd, &msg.req, sizeof(msg.req));
-}
-
-typedef void (*sync_ls_cb)(unsigned mode, unsigned size, unsigned time, const char *name, void *cookie);
-
-int sync_ls(int fd, const char *path, sync_ls_cb func, void *cookie)
-{
-    syncmsg msg;
-    char buf[257];
-    int len;
-
-    len = strlen(path);
-    if(len > 1024) goto fail;
-
-    msg.req.id = ID_LIST;
-    msg.req.namelen = htoll(len);
-
-    if(!WriteFdExactly(fd, &msg.req, sizeof(msg.req)) ||
-       !WriteFdExactly(fd, path, len)) {
-        goto fail;
-    }
-
-    for(;;) {
-        if(!ReadFdExactly(fd, &msg.dent, sizeof(msg.dent))) break;
-        if(msg.dent.id == ID_DONE) return 0;
-        if(msg.dent.id != ID_DENT) break;
-
-        len = ltohl(msg.dent.namelen);
-        if(len > 256) break;
-
-        if(!ReadFdExactly(fd, buf, len)) break;
-        buf[len] = 0;
-
-        func(ltohl(msg.dent.mode),
-             ltohl(msg.dent.size),
-             ltohl(msg.dent.time),
-             buf, cookie);
-    }
-
-fail:
-    adb_close(fd);
-    return -1;
-}
+#include <base/strings.h>
+#include <base/stringprintf.h>
 
 struct syncsendbuf {
     unsigned id;
@@ -138,337 +48,276 @@
 
 static syncsendbuf send_buffer;
 
-int sync_readtime(int fd, const char *path, unsigned int *timestamp,
-                  unsigned int *mode)
-{
-    syncmsg msg;
-    int len = strlen(path);
-
-    msg.req.id = ID_STAT;
-    msg.req.namelen = htoll(len);
-
-    if(!WriteFdExactly(fd, &msg.req, sizeof(msg.req)) ||
-       !WriteFdExactly(fd, path, len)) {
-        return -1;
-    }
-
-    if(!ReadFdExactly(fd, &msg.stat, sizeof(msg.stat))) {
-        return -1;
-    }
-
-    if(msg.stat.id != ID_STAT) {
-        return -1;
-    }
-
-    *timestamp = ltohl(msg.stat.time);
-    *mode = ltohl(msg.stat.mode);
-    return 0;
+static long long NOW() {
+    struct timeval tv;
+    gettimeofday(&tv, 0);
+    return ((long long) tv.tv_usec) + 1000000LL * ((long long) tv.tv_sec);
 }
 
-static int sync_start_readtime(int fd, const char *path)
-{
-    syncmsg msg;
-    int len = strlen(path);
+static void print_transfer_progress(uint64_t bytes_current,
+                                    uint64_t bytes_total) {
+    if (bytes_total == 0) return;
 
-    msg.req.id = ID_STAT;
-    msg.req.namelen = htoll(len);
+    fprintf(stderr, "\rTransferring: %" PRIu64 "/%" PRIu64 " (%d%%)",
+            bytes_current, bytes_total,
+            (int) (bytes_current * 100 / bytes_total));
 
-    if(!WriteFdExactly(fd, &msg.req, sizeof(msg.req)) ||
-       !WriteFdExactly(fd, path, len)) {
-        return -1;
+    if (bytes_current == bytes_total) {
+        fputc('\n', stderr);
     }
 
-    return 0;
+    fflush(stderr);
 }
 
-static int sync_finish_readtime(int fd, unsigned int *timestamp,
-                                unsigned int *mode, unsigned int *size)
-{
-    syncmsg msg;
+static bool SendRequest(int fd, int id, const char* path) {
+    size_t path_length = strlen(path);
+    if (path_length > 1024) {
+        fprintf(stderr, "SendRequest failed: path too long: %zu", path_length);
+        return false;
+    }
 
-    if(!ReadFdExactly(fd, &msg.stat, sizeof(msg.stat)))
-        return -1;
+    // Sending header and payload in a single write makes a noticeable
+    // difference to "adb sync" performance.
+    char buf[sizeof(SyncRequest) + path_length] __attribute__((aligned(8)));
+    SyncRequest* req = reinterpret_cast<SyncRequest*>(buf);
+    req->id = id;
+    req->path_length = path_length;
+    char* data = reinterpret_cast<char*>(req + 1);
+    memcpy(data, path, path_length);
 
-    if(msg.stat.id != ID_STAT)
-        return -1;
-
-    *timestamp = ltohl(msg.stat.time);
-    *mode = ltohl(msg.stat.mode);
-    *size = ltohl(msg.stat.size);
-
-    return 0;
+    return WriteFdExactly(fd, buf, sizeof(buf));
 }
 
-int sync_readmode(int fd, const char *path, unsigned *mode)
-{
-    syncmsg msg;
-    int len = strlen(path);
+class SyncConnection {
+  public:
+    SyncConnection() : total_bytes(0), start_time_(NOW()) {
+        max = SYNC_DATA_MAX; // TODO: decide at runtime.
 
-    msg.req.id = ID_STAT;
-    msg.req.namelen = htoll(len);
-
-    if(!WriteFdExactly(fd, &msg.req, sizeof(msg.req)) ||
-       !WriteFdExactly(fd, path, len)) {
-        return -1;
+        std::string error;
+        fd = adb_connect("sync:", &error);
+        if (fd < 0) {
+            fprintf(stderr, "error: %s\n", error.c_str());
+        }
     }
 
-    if(!ReadFdExactly(fd, &msg.stat, sizeof(msg.stat))) {
-        return -1;
+    ~SyncConnection() {
+        if (!IsValid()) return;
+
+        SendQuit();
+        ShowTransferRate();
+        adb_close(fd);
     }
 
-    if(msg.stat.id != ID_STAT) {
-        return -1;
+    bool IsValid() { return fd >= 0; }
+
+    uint64_t total_bytes;
+
+    // TODO: add a char[max] buffer here, to replace syncsendbuf...
+    int fd;
+    size_t max;
+
+  private:
+    uint64_t start_time_;
+
+    void SendQuit() {
+        SendRequest(fd, ID_QUIT, ""); // TODO: add a SendResponse?
     }
 
-    *mode = ltohl(msg.stat.mode);
-    return 0;
+    void ShowTransferRate() {
+        uint64_t t = NOW() - start_time_;
+        if (total_bytes == 0 || t == 0) return;
+
+        fprintf(stderr, "%lld KB/s (%" PRId64 " bytes in %lld.%03llds)\n",
+                ((total_bytes * 1000000LL) / t) / 1024LL,
+                total_bytes, (t / 1000000LL), (t % 1000000LL) / 1000LL);
+    }
+};
+
+typedef void (*sync_ls_cb)(unsigned mode, unsigned size, unsigned time, const char* name, void* cookie);
+
+static bool sync_ls(int fd, const char* path, sync_ls_cb func, void* cookie) {
+    if (!SendRequest(fd, ID_LIST, path)) return false;
+
+    while (true) {
+        syncmsg msg;
+        if (!ReadFdExactly(fd, &msg.dent, sizeof(msg.dent))) return false;
+
+        if (msg.dent.id == ID_DONE) return true;
+        if (msg.dent.id != ID_DENT) return false;
+
+        size_t len = msg.dent.namelen;
+        if (len > 256) return false; // TODO: resize buffer? continue?
+
+        char buf[257];
+        if (!ReadFdExactly(fd, buf, len)) return false;
+        buf[len] = 0;
+
+        func(msg.dent.mode, msg.dent.size, msg.dent.time, buf, cookie);
+    }
 }
 
-static int write_data_file(int fd, const char *path, syncsendbuf *sbuf, int show_progress)
-{
-    int lfd, err = 0;
+static bool sync_start_stat(SyncConnection& sc, const char* path) {
+    return SendRequest(sc.fd, ID_STAT, path);
+}
+
+static bool sync_finish_stat(SyncConnection& sc, unsigned int* timestamp,
+                             unsigned int* mode, unsigned int* size) {
+    syncmsg msg;
+    if (!ReadFdExactly(sc.fd, &msg.stat, sizeof(msg.stat)) || msg.stat.id != ID_STAT) {
+        return false;
+    }
+
+    if (timestamp) *timestamp = msg.stat.time;
+    if (mode) *mode = msg.stat.mode;
+    if (size) *size = msg.stat.size;
+
+    return true;
+}
+
+static bool sync_stat(SyncConnection& sc, const char* path,
+                      unsigned int* timestamp, unsigned int* mode, unsigned int* size) {
+    return sync_start_stat(sc, path) && sync_finish_stat(sc, timestamp, mode, size);
+}
+
+static bool write_data_file(SyncConnection& sc, const char* path, syncsendbuf* sbuf, bool show_progress) {
     unsigned long long size = 0;
-
-    lfd = adb_open(path, O_RDONLY);
-    if(lfd < 0) {
-        fprintf(stderr,"cannot open '%s': %s\n", path, strerror(errno));
-        return -1;
-    }
-
     if (show_progress) {
         // Determine local file size.
         struct stat st;
-        if (stat(path, &st)) {
-            fprintf(stderr,"cannot stat '%s': %s\n", path, strerror(errno));
-            return -1;
+        if (stat(path, &st) == -1) {
+            fprintf(stderr, "cannot stat '%s': %s\n", path, strerror(errno));
+            return false;
         }
 
         size = st.st_size;
     }
 
+    int lfd = adb_open(path, O_RDONLY);
+    if (lfd < 0) {
+        fprintf(stderr, "cannot open '%s': %s\n", path, strerror(errno));
+        return false;
+    }
+
     sbuf->id = ID_DATA;
-    for(;;) {
-        int ret;
-
-        ret = adb_read(lfd, sbuf->data, SYNC_DATA_MAX);
-        if(!ret)
-            break;
-
-        if(ret < 0) {
-            if(errno == EINTR)
-                continue;
-            fprintf(stderr,"cannot read '%s': %s\n", path, strerror(errno));
+    while (true) {
+        int ret = adb_read(lfd, sbuf->data, sc.max);
+        if (ret <= 0) {
+            if (ret < 0) {
+                fprintf(stderr, "cannot read '%s': %s\n", path, strerror(errno));
+                adb_close(lfd);
+                return false;
+            }
             break;
         }
 
-        sbuf->size = htoll(ret);
-        if(!WriteFdExactly(fd, sbuf, sizeof(unsigned) * 2 + ret)){
-            err = -1;
-            break;
+        sbuf->size = ret;
+        if (!WriteFdExactly(sc.fd, sbuf, sizeof(unsigned) * 2 + ret)) {
+            adb_close(lfd);
+            return false;
         }
-        total_bytes += ret;
+        sc.total_bytes += ret;
 
         if (show_progress) {
-            print_transfer_progress(total_bytes, size);
+            print_transfer_progress(sc.total_bytes, size);
         }
     }
 
     adb_close(lfd);
-    return err;
-}
-
-static int write_data_buffer(int fd, char* file_buffer, int size, syncsendbuf *sbuf,
-                             int show_progress)
-{
-    int err = 0;
-    int total = 0;
-
-    sbuf->id = ID_DATA;
-    while (total < size) {
-        int count = size - total;
-        if (count > SYNC_DATA_MAX) {
-            count = SYNC_DATA_MAX;
-        }
-
-        memcpy(sbuf->data, &file_buffer[total], count);
-        sbuf->size = htoll(count);
-        if(!WriteFdExactly(fd, sbuf, sizeof(unsigned) * 2 + count)){
-            err = -1;
-            break;
-        }
-        total += count;
-        total_bytes += count;
-
-        if (show_progress) {
-            print_transfer_progress(total, size);
-        }
-    }
-
-    return err;
+    return true;
 }
 
 #if defined(_WIN32)
-extern int write_data_link(int fd, const char *path, syncsendbuf *sbuf) __attribute__((error("no symlinks on Windows")));
+extern bool write_data_link(SyncConnection& sc, const char* path, syncsendbuf* sbuf) __attribute__((error("no symlinks on Windows")));
 #else
-static int write_data_link(int fd, const char *path, syncsendbuf *sbuf)
-{
-    int len, ret;
-
-    len = readlink(path, sbuf->data, SYNC_DATA_MAX-1);
-    if(len < 0) {
+static bool write_data_link(SyncConnection& sc, const char* path, syncsendbuf* sbuf) {
+    ssize_t len = readlink(path, sbuf->data, sc.max - 1);
+    if (len < 0) {
         fprintf(stderr, "error reading link '%s': %s\n", path, strerror(errno));
-        return -1;
+        return false;
     }
     sbuf->data[len] = '\0';
 
-    sbuf->size = htoll(len + 1);
+    sbuf->size = len + 1;
     sbuf->id = ID_DATA;
 
-    ret = !WriteFdExactly(fd, sbuf, sizeof(unsigned) * 2 + len + 1);
-    if(ret)
-        return -1;
+    if (!WriteFdExactly(sc.fd, sbuf, sizeof(unsigned) * 2 + len + 1)) {
+        return false;
+    }
 
-    total_bytes += len + 1;
+    sc.total_bytes += len + 1;
 
-    return 0;
+    return true;
 }
 #endif
 
-static int sync_send(int fd, const char *lpath, const char *rpath,
-                     unsigned mtime, mode_t mode, int show_progress)
+static bool sync_send(SyncConnection& sc, const char *lpath, const char *rpath,
+                      unsigned mtime, mode_t mode, bool show_progress)
 {
+    syncsendbuf* sbuf = &send_buffer;
+
+    std::string path_and_mode = android::base::StringPrintf("%s,%d", rpath, mode);
+    if (!SendRequest(sc.fd, ID_SEND, path_and_mode.c_str())) goto fail;
+
+    if (S_ISREG(mode)) {
+        if (!write_data_file(sc, lpath, sbuf, show_progress)) return false;
+    } else if (S_ISLNK(mode)) {
+        if (!write_data_link(sc, lpath, sbuf)) return false;
+    } else {
+        goto fail;
+    }
+
     syncmsg msg;
-    int len, r;
-    syncsendbuf *sbuf = &send_buffer;
-    char* file_buffer = NULL;
-    int size = 0;
-    char tmp[64];
-
-    len = strlen(rpath);
-    if(len > 1024) goto fail;
-
-    snprintf(tmp, sizeof(tmp), ",%d", mode);
-    r = strlen(tmp);
-
-    msg.req.id = ID_SEND;
-    msg.req.namelen = htoll(len + r);
-
-    if(!WriteFdExactly(fd, &msg.req, sizeof(msg.req)) ||
-       !WriteFdExactly(fd, rpath, len) || !WriteFdExactly(fd, tmp, r)) {
-        free(file_buffer);
-        goto fail;
-    }
-
-    if (file_buffer) {
-        write_data_buffer(fd, file_buffer, size, sbuf, show_progress);
-        free(file_buffer);
-    } else if (S_ISREG(mode))
-        write_data_file(fd, lpath, sbuf, show_progress);
-    else if (S_ISLNK(mode))
-        write_data_link(fd, lpath, sbuf);
-    else
-        goto fail;
-
     msg.data.id = ID_DONE;
-    msg.data.size = htoll(mtime);
-    if(!WriteFdExactly(fd, &msg.data, sizeof(msg.data)))
-        goto fail;
+    msg.data.size = mtime;
+    if (!WriteFdExactly(sc.fd, &msg.data, sizeof(msg.data))) goto fail;
 
-    if(!ReadFdExactly(fd, &msg.status, sizeof(msg.status)))
-        return -1;
+    if (!ReadFdExactly(sc.fd, &msg.status, sizeof(msg.status))) goto fail;
 
-    if(msg.status.id != ID_OKAY) {
-        if(msg.status.id == ID_FAIL) {
-            len = ltohl(msg.status.msglen);
-            if(len > 256) len = 256;
-            if(!ReadFdExactly(fd, sbuf->data, len)) {
-                return -1;
-            }
+    if (msg.status.id != ID_OKAY) {
+        if (msg.status.id == ID_FAIL) {
+            size_t len = msg.status.msglen;
+            if (len > 256) len = 256;
+            if (!ReadFdExactly(sc.fd, sbuf->data, len)) goto fail;
             sbuf->data[len] = 0;
-        } else
+        } else {
             strcpy(sbuf->data, "unknown reason");
-
-        fprintf(stderr,"failed to copy '%s' to '%s': %s\n", lpath, rpath, sbuf->data);
-        return -1;
+        }
+        fprintf(stderr, "failed to copy '%s' to '%s': %s\n", lpath, rpath, sbuf->data);
+        return false;
     }
 
-    return 0;
+    return true;
 
 fail:
-    fprintf(stderr,"protocol failure\n");
-    adb_close(fd);
-    return -1;
+    fprintf(stderr, "protocol failure\n");
+    return false;
 }
 
-static int mkdirs(const char *name)
-{
-    int ret;
-    char *x = (char *)name + 1;
-
-    for(;;) {
-        x = adb_dirstart(x);
-        if(x == 0) return 0;
-        *x = 0;
-        ret = adb_mkdir(name, 0775);
-        *x = OS_PATH_SEPARATOR;
-        if((ret < 0) && (errno != EEXIST)) {
-            return ret;
-        }
-        x++;
-    }
-    return 0;
-}
-
-int sync_recv(int fd, const char *rpath, const char *lpath, int show_progress)
-{
+static int sync_recv(SyncConnection& sc, const char* rpath, const char* lpath, bool show_progress) {
     syncmsg msg;
-    int len;
     int lfd = -1;
     char *buffer = send_buffer.data;
     unsigned id;
-    unsigned long long size = 0;
 
-    len = strlen(rpath);
-    if(len > 1024) return -1;
+    size_t len = strlen(rpath);
+    if (len > 1024) return -1;
 
+    unsigned size = 0;
     if (show_progress) {
-        // Determine remote file size.
-        syncmsg stat_msg;
-        stat_msg.req.id = ID_STAT;
-        stat_msg.req.namelen = htoll(len);
-
-        if (!WriteFdExactly(fd, &stat_msg.req, sizeof(stat_msg.req)) ||
-            !WriteFdExactly(fd, rpath, len)) {
-            return -1;
-        }
-
-        if (!ReadFdExactly(fd, &stat_msg.stat, sizeof(stat_msg.stat))) {
-            return -1;
-        }
-
-        if (stat_msg.stat.id != ID_STAT) return -1;
-
-        size = ltohl(stat_msg.stat.size);
+        if (!sync_stat(sc, rpath, nullptr, nullptr, &size)) return -1;
     }
 
-    msg.req.id = ID_RECV;
-    msg.req.namelen = htoll(len);
-    if(!WriteFdExactly(fd, &msg.req, sizeof(msg.req)) ||
-       !WriteFdExactly(fd, rpath, len)) {
-        return -1;
-    }
+    if (!SendRequest(sc.fd, ID_RECV, rpath)) return -1;
+    if (!ReadFdExactly(sc.fd, &msg.data, sizeof(msg.data))) return -1;
 
-    if(!ReadFdExactly(fd, &msg.data, sizeof(msg.data))) {
-        return -1;
-    }
     id = msg.data.id;
 
-    if((id == ID_DATA) || (id == ID_DONE)) {
+    if (id == ID_DATA || id == ID_DONE) {
         adb_unlink(lpath);
         mkdirs(lpath);
         lfd = adb_creat(lpath, 0644);
         if(lfd < 0) {
-            fprintf(stderr,"cannot create '%s': %s\n", lpath, strerror(errno));
+            fprintf(stderr, "cannot create '%s': %s\n", lpath, strerror(errno));
             return -1;
         }
         goto handle_data;
@@ -476,37 +325,38 @@
         goto remote_error;
     }
 
-    for(;;) {
-        if(!ReadFdExactly(fd, &msg.data, sizeof(msg.data))) {
+    while (true) {
+        if(!ReadFdExactly(sc.fd, &msg.data, sizeof(msg.data))) {
+            adb_close(lfd);
             return -1;
         }
         id = msg.data.id;
 
     handle_data:
-        len = ltohl(msg.data.size);
-        if(id == ID_DONE) break;
-        if(id != ID_DATA) goto remote_error;
-        if(len > SYNC_DATA_MAX) {
-            fprintf(stderr,"data overrun\n");
+        len = msg.data.size;
+        if (id == ID_DONE) break;
+        if (id != ID_DATA) goto remote_error;
+        if (len > sc.max) {
+            fprintf(stderr, "msg.data.size too large: %zu (max %zu)\n", len, sc.max);
             adb_close(lfd);
             return -1;
         }
 
-        if(!ReadFdExactly(fd, buffer, len)) {
+        if(!ReadFdExactly(sc.fd, buffer, len)) {
             adb_close(lfd);
             return -1;
         }
 
         if(!WriteFdExactly(lfd, buffer, len)) {
-            fprintf(stderr,"cannot write '%s': %s\n", rpath, strerror(errno));
+            fprintf(stderr, "cannot write '%s': %s\n", rpath, strerror(errno));
             adb_close(lfd);
             return -1;
         }
 
-        total_bytes += len;
+        sc.total_bytes += len;
 
         if (show_progress) {
-            print_transfer_progress(total_bytes, size);
+            print_transfer_progress(sc.total_bytes, size);
         }
     }
 
@@ -518,9 +368,9 @@
     adb_unlink(lpath);
 
     if(id == ID_FAIL) {
-        len = ltohl(msg.data.size);
+        len = msg.data.size;
         if(len > 256) len = 256;
-        if(!ReadFdExactly(fd, buffer, len)) {
+        if(!ReadFdExactly(sc.fd, buffer, len)) {
             return -1;
         }
         buffer[len] = 0;
@@ -528,31 +378,20 @@
         memcpy(buffer, &id, 4);
         buffer[4] = 0;
     }
-    fprintf(stderr,"failed to copy '%s' to '%s': %s\n", rpath, lpath, buffer);
+    fprintf(stderr, "failed to copy '%s' to '%s': %s\n", rpath, lpath, buffer);
     return 0;
 }
 
-/* --- */
 static void do_sync_ls_cb(unsigned mode, unsigned size, unsigned time,
-                          const char *name, void *cookie)
-{
+                          const char* name, void* /*cookie*/) {
     printf("%08x %08x %08x %s\n", mode, size, time, name);
 }
 
-int do_sync_ls(const char* path) {
-    std::string error;
-    int fd = adb_connect("sync:", &error);
-    if (fd < 0) {
-        fprintf(stderr,"error: %s\n", error.c_str());
-        return 1;
-    }
+bool do_sync_ls(const char* path) {
+    SyncConnection sc;
+    if (!sc.IsValid()) return false;
 
-    if(sync_ls(fd, path, do_sync_ls_cb, 0)) {
-        return 1;
-    } else {
-        sync_quit(fd);
-        return 0;
-    }
+    return sync_ls(sc.fd, path, do_sync_ls_cb, 0);
 }
 
 struct copyinfo
@@ -566,19 +405,16 @@
     int flag;
 };
 
-copyinfo *mkcopyinfo(const char *spath, const char *dpath,
-                     const char *name, int isdir)
-{
+static copyinfo* mkcopyinfo(const char* spath, const char* dpath, const char* name, int isdir) {
     int slen = strlen(spath);
     int dlen = strlen(dpath);
     int nlen = strlen(name);
     int ssize = slen + nlen + 2;
     int dsize = dlen + nlen + 2;
 
-    copyinfo *ci = reinterpret_cast<copyinfo*>(
-        malloc(sizeof(copyinfo) + ssize + dsize));
+    copyinfo *ci = reinterpret_cast<copyinfo*>(malloc(sizeof(copyinfo) + ssize + dsize));
     if(ci == 0) {
-        fprintf(stderr,"out of memory\n");
+        fprintf(stderr, "out of memory\n");
         abort();
     }
 
@@ -595,54 +431,41 @@
     return ci;
 }
 
+static bool IsDotOrDotDot(const char* name) {
+    return name[0] == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0'));
+}
 
-static int local_build_list(copyinfo **filelist,
-                            const char *lpath, const char *rpath)
-{
-    DIR *d;
-    struct dirent *de;
-    struct stat st;
+static int local_build_list(copyinfo** filelist, const char* lpath, const char* rpath) {
     copyinfo *dirlist = 0;
     copyinfo *ci, *next;
 
-    d = opendir(lpath);
-    if(d == 0) {
-        fprintf(stderr,"cannot open '%s': %s\n", lpath, strerror(errno));
+    std::unique_ptr<DIR, int(*)(DIR*)> dir(opendir(lpath), closedir);
+    if (!dir) {
+        fprintf(stderr, "cannot open '%s': %s\n", lpath, strerror(errno));
         return -1;
     }
 
-    while((de = readdir(d))) {
+    dirent *de;
+    while ((de = readdir(dir.get()))) {
+        if (IsDotOrDotDot(de->d_name)) continue;
+
         char stat_path[PATH_MAX];
-        char *name = de->d_name;
-
-        if(name[0] == '.') {
-            if(name[1] == 0) continue;
-            if((name[1] == '.') && (name[2] == 0)) continue;
-        }
-
-        /*
-         * We could use d_type if HAVE_DIRENT_D_TYPE is defined, but reiserfs
-         * always returns DT_UNKNOWN, so we just use stat() for all cases.
-         */
-        if (strlen(lpath) + strlen(de->d_name) + 1 > sizeof(stat_path))
+        if (strlen(lpath) + strlen(de->d_name) + 1 > sizeof(stat_path)) {
+            fprintf(stderr, "skipping long path '%s%s'\n", lpath, de->d_name);
             continue;
+        }
         strcpy(stat_path, lpath);
         strcat(stat_path, de->d_name);
 
-        if(!lstat(stat_path, &st)) {
+        struct stat st;
+        if (!lstat(stat_path, &st)) {
             if (S_ISDIR(st.st_mode)) {
-                ci = mkcopyinfo(lpath, rpath, name, 1);
+                ci = mkcopyinfo(lpath, rpath, de->d_name, 1);
                 ci->next = dirlist;
                 dirlist = ci;
             } else {
-                ci = mkcopyinfo(lpath, rpath, name, 0);
-                if(lstat(ci->src, &st)) {
-                    fprintf(stderr,"cannot stat '%s': %s\n", ci->src, strerror(errno));
-                    free(ci);
-                    closedir(d);
-                    return -1;
-                }
-                if(!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) {
+                ci = mkcopyinfo(lpath, rpath, de->d_name, 0);
+                if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) {
                     fprintf(stderr, "skipping special file '%s'\n", ci->src);
                     free(ci);
                 } else {
@@ -658,9 +481,9 @@
         }
     }
 
-    closedir(d);
-
-    for(ci = dirlist; ci != 0; ci = next) {
+    // Close this directory and recurse.
+    dir.reset();
+    for (ci = dirlist; ci != 0; ci = next) {
         next = ci->next;
         local_build_list(filelist, ci->src, ci->dst);
         free(ci);
@@ -669,60 +492,55 @@
     return 0;
 }
 
-
-static int copy_local_dir_remote(int fd, const char *lpath, const char *rpath, int checktimestamps, int listonly)
-{
+static bool copy_local_dir_remote(SyncConnection& sc, const char* lpath, const char* rpath,
+                                  bool check_timestamps, bool list_only) {
     copyinfo *filelist = 0;
     copyinfo *ci, *next;
     int pushed = 0;
     int skipped = 0;
 
-    if((lpath[0] == 0) || (rpath[0] == 0)) return -1;
-    if(lpath[strlen(lpath) - 1] != '/') {
+    if ((lpath[0] == 0) || (rpath[0] == 0)) return false;
+    if (lpath[strlen(lpath) - 1] != '/') {
         int  tmplen = strlen(lpath)+2;
         char *tmp = reinterpret_cast<char*>(malloc(tmplen));
-        if(tmp == 0) return -1;
+        if(tmp == 0) return false;
         snprintf(tmp, tmplen, "%s/",lpath);
         lpath = tmp;
     }
-    if(rpath[strlen(rpath) - 1] != '/') {
+    if (rpath[strlen(rpath) - 1] != '/') {
         int tmplen = strlen(rpath)+2;
         char *tmp = reinterpret_cast<char*>(malloc(tmplen));
-        if(tmp == 0) return -1;
+        if(tmp == 0) return false;
         snprintf(tmp, tmplen, "%s/",rpath);
         rpath = tmp;
     }
 
-    if(local_build_list(&filelist, lpath, rpath)) {
-        return -1;
+    if (local_build_list(&filelist, lpath, rpath)) {
+        return false;
     }
 
-    if(checktimestamps){
-        for(ci = filelist; ci != 0; ci = ci->next) {
-            if(sync_start_readtime(fd, ci->dst)) {
-                return 1;
-            }
+    if (check_timestamps) {
+        for (ci = filelist; ci != 0; ci = ci->next) {
+            if (!sync_start_stat(sc, ci->dst)) return false;
         }
         for(ci = filelist; ci != 0; ci = ci->next) {
             unsigned int timestamp, mode, size;
-            if(sync_finish_readtime(fd, &timestamp, &mode, &size))
-                return 1;
-            if(size == ci->size) {
+            if (!sync_finish_stat(sc, &timestamp, &mode, &size)) return false;
+            if (size == ci->size) {
                 /* for links, we cannot update the atime/mtime */
-                if((S_ISREG(ci->mode & mode) && timestamp == ci->time) ||
-                    (S_ISLNK(ci->mode & mode) && timestamp >= ci->time))
+                if ((S_ISREG(ci->mode & mode) && timestamp == ci->time) ||
+                        (S_ISLNK(ci->mode & mode) && timestamp >= ci->time)) {
                     ci->flag = 1;
+                }
             }
         }
     }
-    for(ci = filelist; ci != 0; ci = next) {
+    for (ci = filelist; ci != 0; ci = next) {
         next = ci->next;
-        if(ci->flag == 0) {
-            fprintf(stderr,"%spush: %s -> %s\n", listonly ? "would " : "", ci->src, ci->dst);
-            if(!listonly &&
-               sync_send(fd, ci->src, ci->dst, ci->time, ci->mode,
-                         0 /* no show progress */)) {
-                return 1;
+        if (ci->flag == 0) {
+            fprintf(stderr, "%spush: %s -> %s\n", list_only ? "would " : "", ci->src, ci->dst);
+            if (!list_only && !sync_send(sc, ci->src, ci->dst, ci->time, ci->mode, false)) {
+                return false;
             }
             pushed++;
         } else {
@@ -731,72 +549,37 @@
         free(ci);
     }
 
-    fprintf(stderr,"%d file%s pushed. %d file%s skipped.\n",
+    fprintf(stderr, "%d file%s pushed. %d file%s skipped.\n",
             pushed, (pushed == 1) ? "" : "s",
             skipped, (skipped == 1) ? "" : "s");
 
-    return 0;
+    return true;
 }
 
+bool do_sync_push(const char* lpath, const char* rpath, bool show_progress) {
+    SyncConnection sc;
+    if (!sc.IsValid()) return false;
 
-int do_sync_push(const char *lpath, const char *rpath, int show_progress)
-{
     struct stat st;
+    if (stat(lpath, &st)) {
+        fprintf(stderr, "cannot stat '%s': %s\n", lpath, strerror(errno));
+        return false;
+    }
+
+    if (S_ISDIR(st.st_mode)) {
+        return copy_local_dir_remote(sc, lpath, rpath, false, false);
+    }
+
     unsigned mode;
-
-    std::string error;
-    int fd = adb_connect("sync:", &error);
-    if (fd < 0) {
-        fprintf(stderr,"error: %s\n", error.c_str());
-        return 1;
+    if (!sync_stat(sc, rpath, nullptr, &mode, nullptr)) return false;
+    std::string path_holder;
+    if (mode != 0 && S_ISDIR(mode)) {
+        // If we're copying a local file to a remote directory,
+        // we really want to copy to remote_dir + "/" + local_filename.
+        path_holder = android::base::StringPrintf("%s/%s", rpath, adb_basename(lpath).c_str());
+        rpath = path_holder.c_str();
     }
-
-    if(stat(lpath, &st)) {
-        fprintf(stderr,"cannot stat '%s': %s\n", lpath, strerror(errno));
-        sync_quit(fd);
-        return 1;
-    }
-
-    if(S_ISDIR(st.st_mode)) {
-        BEGIN();
-        if(copy_local_dir_remote(fd, lpath, rpath, 0, 0)) {
-            return 1;
-        } else {
-            END();
-            sync_quit(fd);
-        }
-    } else {
-        if(sync_readmode(fd, rpath, &mode)) {
-            return 1;
-        }
-        if((mode != 0) && S_ISDIR(mode)) {
-                /* if we're copying a local file to a remote directory,
-                ** we *really* want to copy to remotedir + "/" + localfilename
-                */
-            const char *name = adb_dirstop(lpath);
-            if(name == 0) {
-                name = lpath;
-            } else {
-                name++;
-            }
-            int  tmplen = strlen(name) + strlen(rpath) + 2;
-            char *tmp = reinterpret_cast<char*>(
-                malloc(strlen(name) + strlen(rpath) + 2));
-            if(tmp == 0) return 1;
-            snprintf(tmp, tmplen, "%s/%s", rpath, name);
-            rpath = tmp;
-        }
-        BEGIN();
-        if(sync_send(fd, lpath, rpath, st.st_mtime, st.st_mode, show_progress)) {
-            return 1;
-        } else {
-            END();
-            sync_quit(fd);
-            return 0;
-        }
-    }
-
-    return 0;
+    return sync_send(sc, lpath, rpath, st.st_mtime, st.st_mode, show_progress);
 }
 
 
@@ -807,9 +590,8 @@
     const char *lpath;
 };
 
-void
-sync_ls_build_list_cb(unsigned mode, unsigned size, unsigned time,
-                      const char *name, void *cookie)
+static void sync_ls_build_list_cb(unsigned mode, unsigned size, unsigned time,
+                                  const char* name, void* cookie)
 {
     sync_ls_build_list_cb_args *args = (sync_ls_build_list_cb_args *)cookie;
     copyinfo *ci;
@@ -817,11 +599,8 @@
     if (S_ISDIR(mode)) {
         copyinfo **dirlist = args->dirlist;
 
-        /* Don't try recursing down "." or ".." */
-        if (name[0] == '.') {
-            if (name[1] == '\0') return;
-            if ((name[1] == '.') && (name[2] == '\0')) return;
-        }
+        // Don't try recursing down "." or "..".
+        if (IsDotOrDotDot(name)) return;
 
         ci = mkcopyinfo(args->rpath, args->lpath, name, 1);
         ci->next = *dirlist;
@@ -840,9 +619,8 @@
     }
 }
 
-static int remote_build_list(int syncfd, copyinfo **filelist,
-                             const char *rpath, const char *lpath)
-{
+static bool remote_build_list(int syncfd, copyinfo **filelist,
+                              const char *rpath, const char *lpath) {
     copyinfo *dirlist = NULL;
     sync_ls_build_list_cb_args args;
 
@@ -851,22 +629,22 @@
     args.rpath = rpath;
     args.lpath = lpath;
 
-    /* Put the files/dirs in rpath on the lists. */
-    if (sync_ls(syncfd, rpath, sync_ls_build_list_cb, (void *)&args)) {
-        return 1;
+    // Put the files/dirs in rpath on the lists.
+    if (!sync_ls(syncfd, rpath, sync_ls_build_list_cb, (void *)&args)) {
+        return false;
     }
 
-    /* Recurse into each directory we found. */
+    // Recurse into each directory we found.
     while (dirlist != NULL) {
         copyinfo *next = dirlist->next;
-        if (remote_build_list(syncfd, filelist, dirlist->src, dirlist->dst)) {
-            return 1;
+        if (!remote_build_list(syncfd, filelist, dirlist->src, dirlist->dst)) {
+            return false;
         }
         free(dirlist);
         dirlist = next;
     }
 
-    return 0;
+    return true;
 }
 
 static int set_time_and_mode(const char *lpath, time_t time, unsigned int mode)
@@ -882,167 +660,91 @@
     return r1 ? : r2;
 }
 
-/* Return a copy of the path string with / appended if needed */
-static char *add_slash_to_path(const char *path)
-{
-    if (path[strlen(path) - 1] != '/') {
-        size_t len = strlen(path) + 2;
-        char *path_with_slash = reinterpret_cast<char*>(malloc(len));
-        if (path_with_slash == NULL)
-            return NULL;
-        snprintf(path_with_slash, len, "%s/", path);
-        return path_with_slash;
-    } else {
-        return strdup(path);
-    }
-}
+static bool copy_remote_dir_local(SyncConnection& sc, const char* rpath, const char* lpath,
+                                  int copy_attrs) {
+    // Make sure that both directory paths end in a slash.
+    std::string rpath_clean(rpath);
+    std::string lpath_clean(lpath);
+    if (rpath_clean.empty() || lpath_clean.empty()) return false;
+    if (rpath_clean.back() != '/') rpath_clean.push_back('/');
+    if (lpath_clean.back() != '/') lpath_clean.push_back('/');
 
-static int copy_remote_dir_local(int fd, const char *rpath, const char *lpath,
-                                 int copy_attrs)
-{
-    copyinfo *filelist = 0;
-    copyinfo *ci, *next;
+    // Recursively build the list of files to copy.
+    fprintf(stderr, "pull: building file list...\n");
+    copyinfo* filelist = nullptr;
+    if (!remote_build_list(sc.fd, &filelist, rpath_clean.c_str(), lpath_clean.c_str())) return false;
+
     int pulled = 0;
     int skipped = 0;
-    char *rpath_clean = NULL;
-    char *lpath_clean = NULL;
-    int ret = 0;
-
-    if (rpath[0] == '\0' || lpath[0] == '\0') {
-        ret = -1;
-        goto finish;
-    }
-
-    /* Make sure that both directory paths end in a slash. */
-    rpath_clean = add_slash_to_path(rpath);
-    if (!rpath_clean) {
-        ret = -1;
-        goto finish;
-    }
-    lpath_clean = add_slash_to_path(lpath);
-    if (!lpath_clean) {
-        ret = -1;
-        goto finish;
-    }
-
-    /* Recursively build the list of files to copy. */
-    fprintf(stderr, "pull: building file list...\n");
-    if (remote_build_list(fd, &filelist, rpath_clean, lpath_clean)) {
-        ret = -1;
-        goto finish;
-    }
-
-    for (ci = filelist; ci != 0; ci = next) {
-        next = ci->next;
+    copyinfo* ci = filelist;
+    while (ci) {
+        copyinfo* next = ci->next;
         if (ci->flag == 0) {
             fprintf(stderr, "pull: %s -> %s\n", ci->src, ci->dst);
-            if (sync_recv(fd, ci->src, ci->dst, 0 /* no show progress */)) {
-                ret = -1;
-                goto finish;
+            if (sync_recv(sc, ci->src, ci->dst, false)) {
+                return false;
             }
 
             if (copy_attrs && set_time_and_mode(ci->dst, ci->time, ci->mode)) {
-                ret = -1;
-                goto finish;
+                return false;
             }
             pulled++;
         } else {
             skipped++;
         }
         free(ci);
+        ci = next;
     }
 
     fprintf(stderr, "%d file%s pulled. %d file%s skipped.\n",
             pulled, (pulled == 1) ? "" : "s",
             skipped, (skipped == 1) ? "" : "s");
-
-finish:
-    free(lpath_clean);
-    free(rpath_clean);
-    return ret;
+    return true;
 }
 
-int do_sync_pull(const char *rpath, const char *lpath, int show_progress, int copy_attrs)
-{
+bool do_sync_pull(const char* rpath, const char* lpath, bool show_progress, int copy_attrs) {
+    SyncConnection sc;
+    if (!sc.IsValid()) return false;
+
     unsigned mode, time;
-    struct stat st;
-
-    std::string error;
-    int fd = adb_connect("sync:", &error);
-    if (fd < 0) {
-        fprintf(stderr,"error: %s\n", error.c_str());
-        return 1;
+    if (!sync_stat(sc, rpath, &time, &mode, nullptr)) return false;
+    if (mode == 0) {
+        fprintf(stderr, "remote object '%s' does not exist\n", rpath);
+        return false;
     }
 
-    if(sync_readtime(fd, rpath, &time, &mode)) {
-        return 1;
-    }
-    if(mode == 0) {
-        fprintf(stderr,"remote object '%s' does not exist\n", rpath);
-        return 1;
-    }
-
-    if(S_ISREG(mode) || S_ISLNK(mode) || S_ISCHR(mode) || S_ISBLK(mode)) {
-        if(stat(lpath, &st) == 0) {
-            if(S_ISDIR(st.st_mode)) {
-                    /* if we're copying a remote file to a local directory,
-                    ** we *really* want to copy to localdir + "/" + remotefilename
-                    */
-                const char *name = adb_dirstop(rpath);
-                if(name == 0) {
-                    name = rpath;
-                } else {
-                    name++;
-                }
-                int  tmplen = strlen(name) + strlen(lpath) + 2;
-                char *tmp = reinterpret_cast<char*>(malloc(tmplen));
-                if(tmp == 0) return 1;
-                snprintf(tmp, tmplen, "%s/%s", lpath, name);
-                lpath = tmp;
+    if (S_ISREG(mode) || S_ISLNK(mode) || S_ISCHR(mode) || S_ISBLK(mode)) {
+        std::string path_holder;
+        struct stat st;
+        if (stat(lpath, &st) == 0) {
+            if (S_ISDIR(st.st_mode)) {
+                // If we're copying a remote file to a local directory,
+                // we really want to copy to local_dir + "/" + basename(remote).
+                path_holder = android::base::StringPrintf("%s/%s", lpath, adb_basename(rpath).c_str());
+                lpath = path_holder.c_str();
             }
         }
-        BEGIN();
-        if (sync_recv(fd, rpath, lpath, show_progress)) {
-            return 1;
+        if (sync_recv(sc, rpath, lpath, show_progress)) {
+            return false;
         } else {
-            if (copy_attrs && set_time_and_mode(lpath, time, mode))
-                return 1;
-            END();
-            sync_quit(fd);
-            return 0;
+            if (copy_attrs && set_time_and_mode(lpath, time, mode)) {
+                return false;
+            }
         }
-    } else if(S_ISDIR(mode)) {
-        BEGIN();
-        if (copy_remote_dir_local(fd, rpath, lpath, copy_attrs)) {
-            return 1;
-        } else {
-            END();
-            sync_quit(fd);
-            return 0;
-        }
-    } else {
-        fprintf(stderr,"remote object '%s' not a file or directory\n", rpath);
-        return 1;
+        return true;
+    } else if (S_ISDIR(mode)) {
+        return copy_remote_dir_local(sc, rpath, lpath, copy_attrs);
     }
+
+    fprintf(stderr, "remote object '%s' not a file or directory\n", rpath);
+    return false;
 }
 
-int do_sync_sync(const std::string& lpath, const std::string& rpath, bool list_only)
-{
+bool do_sync_sync(const std::string& lpath, const std::string& rpath, bool list_only) {
     fprintf(stderr, "syncing %s...\n", rpath.c_str());
 
-    std::string error;
-    int fd = adb_connect("sync:", &error);
-    if (fd < 0) {
-        fprintf(stderr, "error: %s\n", error.c_str());
-        return 1;
-    }
+    SyncConnection sc;
+    if (!sc.IsValid()) return false;
 
-    BEGIN();
-    if (copy_local_dir_remote(fd, lpath.c_str(), rpath.c_str(), 1, list_only)) {
-        return 1;
-    } else {
-        END();
-        sync_quit(fd);
-        return 0;
-    }
+    return copy_local_dir_remote(sc, lpath.c_str(), rpath.c_str(), true, list_only);
 }
diff --git a/adb/file_sync_service.cpp b/adb/file_sync_service.cpp
index 5d17335..536dbbc 100644
--- a/adb/file_sync_service.cpp
+++ b/adb/file_sync_service.cpp
@@ -34,369 +34,307 @@
 #include "adb_io.h"
 #include "private/android_filesystem_config.h"
 
-static bool should_use_fs_config(const char* path) {
+#include <base/stringprintf.h>
+#include <base/strings.h>
+
+static bool should_use_fs_config(const std::string& path) {
     // TODO: use fs_config to configure permissions on /data.
-    return strncmp("/system/", path, strlen("/system/")) == 0 ||
-           strncmp("/vendor/", path, strlen("/vendor/")) == 0 ||
-           strncmp("/oem/", path, strlen("/oem/")) == 0;
+    return android::base::StartsWith(path, "/system/") ||
+           android::base::StartsWith(path, "/vendor/") ||
+           android::base::StartsWith(path, "/oem/");
 }
 
-static int mkdirs(char *name)
-{
-    int ret;
-    char *x = name + 1;
+static bool secure_mkdirs(const std::string& path) {
     uid_t uid = -1;
     gid_t gid = -1;
     unsigned int mode = 0775;
     uint64_t cap = 0;
 
-    if(name[0] != '/') return -1;
+    if (path[0] != '/') return false;
 
-    for(;;) {
-        x = adb_dirstart(x);
-        if(x == 0) return 0;
-        *x = 0;
-        if (should_use_fs_config(name)) {
-            fs_config(name, 1, NULL, &uid, &gid, &mode, &cap);
+    std::vector<std::string> path_components = android::base::Split(path, "/");
+    path_components.pop_back(); // For "/system/bin/sh", only create "/system/bin".
+
+    std::string partial_path;
+    for (auto& path_component : path_components) {
+        if (partial_path.back() != OS_PATH_SEPARATOR) partial_path += OS_PATH_SEPARATOR;
+        partial_path += path_component;
+
+        if (should_use_fs_config(partial_path)) {
+            fs_config(partial_path.c_str(), 1, nullptr, &uid, &gid, &mode, &cap);
         }
-        ret = adb_mkdir(name, mode);
-        if((ret < 0) && (errno != EEXIST)) {
-            D("mkdir(\"%s\") -> %s\n", name, strerror(errno));
-            *x = '/';
-            return ret;
-        } else if(ret == 0) {
-            ret = chown(name, uid, gid);
-            if (ret < 0) {
-                *x = '/';
-                return ret;
+        if (adb_mkdir(partial_path.c_str(), mode) == -1) {
+            if (errno != EEXIST) {
+                return false;
             }
-            selinux_android_restorecon(name, 0);
+        } else {
+            if (chown(partial_path.c_str(), uid, gid) == -1) {
+                return false;
+            }
+            // Not all filesystems support setting SELinux labels. http://b/23530370.
+            selinux_android_restorecon(partial_path.c_str(), 0);
         }
-        *x++ = '/';
     }
-    return 0;
+    return true;
 }
 
-static int do_stat(int s, const char *path)
-{
+static bool do_stat(int s, const char* path) {
     syncmsg msg;
-    struct stat st;
-
     msg.stat.id = ID_STAT;
 
-    if(lstat(path, &st)) {
-        msg.stat.mode = 0;
-        msg.stat.size = 0;
-        msg.stat.time = 0;
-    } else {
-        msg.stat.mode = htoll(st.st_mode);
-        msg.stat.size = htoll(st.st_size);
-        msg.stat.time = htoll(st.st_mtime);
-    }
+    struct stat st;
+    memset(&st, 0, sizeof(st));
+    // TODO: add a way to report that the stat failed!
+    lstat(path, &st);
+    msg.stat.mode = st.st_mode;
+    msg.stat.size = st.st_size;
+    msg.stat.time = st.st_mtime;
 
-    return WriteFdExactly(s, &msg.stat, sizeof(msg.stat)) ? 0 : -1;
+    return WriteFdExactly(s, &msg.stat, sizeof(msg.stat));
 }
 
-static int do_list(int s, const char *path)
-{
-    DIR *d;
-    struct dirent *de;
-    struct stat st;
+static bool do_list(int s, const char* path) {
+    dirent* de;
+
     syncmsg msg;
-    int len;
-
-    char tmp[1024 + 256 + 1];
-    char *fname;
-
-    len = strlen(path);
-    memcpy(tmp, path, len);
-    tmp[len] = '/';
-    fname = tmp + len + 1;
-
     msg.dent.id = ID_DENT;
 
-    d = opendir(path);
-    if(d == 0) goto done;
+    std::unique_ptr<DIR, int(*)(DIR*)> d(opendir(path), closedir);
+    if (!d) goto done;
 
-    while((de = readdir(d))) {
-        int len = strlen(de->d_name);
+    while ((de = readdir(d.get()))) {
+        std::string filename(android::base::StringPrintf("%s/%s", path, de->d_name));
 
-            /* not supposed to be possible, but
-               if it does happen, let's not buffer overrun */
-        if(len > 256) continue;
+        struct stat st;
+        if (lstat(filename.c_str(), &st) == 0) {
+            size_t d_name_length = strlen(de->d_name);
+            msg.dent.mode = st.st_mode;
+            msg.dent.size = st.st_size;
+            msg.dent.time = st.st_mtime;
+            msg.dent.namelen = d_name_length;
 
-        strcpy(fname, de->d_name);
-        if(lstat(tmp, &st) == 0) {
-            msg.dent.mode = htoll(st.st_mode);
-            msg.dent.size = htoll(st.st_size);
-            msg.dent.time = htoll(st.st_mtime);
-            msg.dent.namelen = htoll(len);
-
-            if(!WriteFdExactly(s, &msg.dent, sizeof(msg.dent)) ||
-               !WriteFdExactly(s, de->d_name, len)) {
-                closedir(d);
-                return -1;
+            if (!WriteFdExactly(s, &msg.dent, sizeof(msg.dent)) ||
+                    !WriteFdExactly(s, de->d_name, d_name_length)) {
+                return false;
             }
         }
     }
 
-    closedir(d);
-
 done:
     msg.dent.id = ID_DONE;
     msg.dent.mode = 0;
     msg.dent.size = 0;
     msg.dent.time = 0;
     msg.dent.namelen = 0;
-    return WriteFdExactly(s, &msg.dent, sizeof(msg.dent)) ? 0 : -1;
+    return WriteFdExactly(s, &msg.dent, sizeof(msg.dent));
 }
 
-static int fail_message(int s, const char *reason)
-{
+static bool SendSyncFail(int fd, const std::string& reason) {
+    D("sync: failure: %s\n", reason.c_str());
+
     syncmsg msg;
-    int len = strlen(reason);
-
-    D("sync: failure: %s\n", reason);
-
     msg.data.id = ID_FAIL;
-    msg.data.size = htoll(len);
-    if(!WriteFdExactly(s, &msg.data, sizeof(msg.data)) ||
-       !WriteFdExactly(s, reason, len)) {
-        return -1;
-    } else {
-        return 0;
-    }
+    msg.data.size = reason.size();
+    return WriteFdExactly(fd, &msg.data, sizeof(msg.data)) && WriteFdExactly(fd, reason);
 }
 
-static int fail_errno(int s)
-{
-    return fail_message(s, strerror(errno));
+static bool SendSyncFailErrno(int fd, const std::string& reason) {
+    return SendSyncFail(fd, android::base::StringPrintf("%s: %s", reason.c_str(), strerror(errno)));
 }
 
-static int handle_send_file(int s, char *path, uid_t uid,
-        gid_t gid, mode_t mode, char *buffer, bool do_unlink)
-{
+static bool handle_send_file(int s, const char* path, uid_t uid,
+                             gid_t gid, mode_t mode, std::vector<char>& buffer, bool do_unlink) {
     syncmsg msg;
     unsigned int timestamp = 0;
-    int fd;
 
-    fd = adb_open_mode(path, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, mode);
-    if(fd < 0 && errno == ENOENT) {
-        if(mkdirs(path) != 0) {
-            if(fail_errno(s))
-                return -1;
-            fd = -1;
-        } else {
-            fd = adb_open_mode(path, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, mode);
+    int fd = adb_open_mode(path, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, mode);
+    if (fd < 0 && errno == ENOENT) {
+        if (!secure_mkdirs(path)) {
+            SendSyncFailErrno(s, "secure_mkdirs failed");
+            goto fail;
         }
+        fd = adb_open_mode(path, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, mode);
     }
-    if(fd < 0 && errno == EEXIST) {
+    if (fd < 0 && errno == EEXIST) {
         fd = adb_open_mode(path, O_WRONLY | O_CLOEXEC, mode);
     }
-    if(fd < 0) {
-        if(fail_errno(s))
-            return -1;
-        fd = -1;
+    if (fd < 0) {
+        SendSyncFailErrno(s, "couldn't create file");
+        goto fail;
     } else {
-        if(fchown(fd, uid, gid) != 0) {
-            fail_errno(s);
-            errno = 0;
+        if (fchown(fd, uid, gid) == -1) {
+            SendSyncFailErrno(s, "fchown failed");
+            goto fail;
         }
 
-        /*
-         * fchown clears the setuid bit - restore it if present.
-         * Ignore the result of calling fchmod. It's not supported
-         * by all filesystems. b/12441485
-         */
+        // Not all filesystems support setting SELinux labels. http://b/23530370.
+        selinux_android_restorecon(path, 0);
+
+        // fchown clears the setuid bit - restore it if present.
+        // Ignore the result of calling fchmod. It's not supported
+        // by all filesystems. b/12441485
         fchmod(fd, mode);
     }
 
-    for(;;) {
+    while (true) {
         unsigned int len;
 
-        if(!ReadFdExactly(s, &msg.data, sizeof(msg.data)))
-            goto fail;
+        if (!ReadFdExactly(s, &msg.data, sizeof(msg.data))) goto fail;
 
-        if(msg.data.id != ID_DATA) {
-            if(msg.data.id == ID_DONE) {
-                timestamp = ltohl(msg.data.size);
+        if (msg.data.id != ID_DATA) {
+            if (msg.data.id == ID_DONE) {
+                timestamp = msg.data.size;
                 break;
             }
-            fail_message(s, "invalid data message");
+            SendSyncFail(s, "invalid data message");
             goto fail;
         }
-        len = ltohl(msg.data.size);
-        if(len > SYNC_DATA_MAX) {
-            fail_message(s, "oversize data message");
+        len = msg.data.size;
+        if (len > buffer.size()) { // TODO: resize buffer?
+            SendSyncFail(s, "oversize data message");
             goto fail;
         }
-        if(!ReadFdExactly(s, buffer, len))
-            goto fail;
 
-        if(fd < 0)
-            continue;
-        if(!WriteFdExactly(fd, buffer, len)) {
-            int saved_errno = errno;
-            adb_close(fd);
-            if (do_unlink) adb_unlink(path);
-            fd = -1;
-            errno = saved_errno;
-            if(fail_errno(s)) return -1;
+        if (!ReadFdExactly(s, &buffer[0], len)) goto fail;
+
+        if (!WriteFdExactly(fd, &buffer[0], len)) {
+            SendSyncFailErrno(s, "write failed");
+            goto fail;
         }
     }
 
-    if(fd >= 0) {
-        struct utimbuf u;
-        adb_close(fd);
-        selinux_android_restorecon(path, 0);
-        u.actime = timestamp;
-        u.modtime = timestamp;
-        utime(path, &u);
+    adb_close(fd);
 
-        msg.status.id = ID_OKAY;
-        msg.status.msglen = 0;
-        if(!WriteFdExactly(s, &msg.status, sizeof(msg.status)))
-            return -1;
-    }
-    return 0;
+    utimbuf u;
+    u.actime = timestamp;
+    u.modtime = timestamp;
+    utime(path, &u);
+
+    msg.status.id = ID_OKAY;
+    msg.status.msglen = 0;
+    return WriteFdExactly(s, &msg.status, sizeof(msg.status));
 
 fail:
-    if(fd >= 0)
-        adb_close(fd);
+    if (fd >= 0) adb_close(fd);
     if (do_unlink) adb_unlink(path);
-    return -1;
+    return false;
 }
 
 #if defined(_WIN32)
-extern int handle_send_link(int s, char *path, char *buffer) __attribute__((error("no symlinks on Windows")));
+extern bool handle_send_link(int s, const std::string& path, std::vector<char>& buffer) __attribute__((error("no symlinks on Windows")));
 #else
-static int handle_send_link(int s, char *path, char *buffer)
-{
+static bool handle_send_link(int s, const std::string& path, std::vector<char>& buffer) {
     syncmsg msg;
     unsigned int len;
     int ret;
 
-    if(!ReadFdExactly(s, &msg.data, sizeof(msg.data)))
-        return -1;
+    if (!ReadFdExactly(s, &msg.data, sizeof(msg.data))) return false;
 
-    if(msg.data.id != ID_DATA) {
-        fail_message(s, "invalid data message: expected ID_DATA");
-        return -1;
+    if (msg.data.id != ID_DATA) {
+        SendSyncFail(s, "invalid data message: expected ID_DATA");
+        return false;
     }
 
-    len = ltohl(msg.data.size);
-    if(len > SYNC_DATA_MAX) {
-        fail_message(s, "oversize data message");
-        return -1;
+    len = msg.data.size;
+    if (len > buffer.size()) { // TODO: resize buffer?
+        SendSyncFail(s, "oversize data message");
+        return false;
     }
-    if(!ReadFdExactly(s, buffer, len))
-        return -1;
+    if (!ReadFdExactly(s, &buffer[0], len)) return false;
 
-    ret = symlink(buffer, path);
-    if(ret && errno == ENOENT) {
-        if(mkdirs(path) != 0) {
-            fail_errno(s);
-            return -1;
+    ret = symlink(&buffer[0], path.c_str());
+    if (ret && errno == ENOENT) {
+        if (!secure_mkdirs(path)) {
+            SendSyncFailErrno(s, "secure_mkdirs failed");
+            return false;
         }
-        ret = symlink(buffer, path);
+        ret = symlink(&buffer[0], path.c_str());
     }
-    if(ret) {
-        fail_errno(s);
-        return -1;
+    if (ret) {
+        SendSyncFailErrno(s, "symlink failed");
+        return false;
     }
 
-    if(!ReadFdExactly(s, &msg.data, sizeof(msg.data)))
-        return -1;
+    if (!ReadFdExactly(s, &msg.data, sizeof(msg.data))) return false;
 
-    if(msg.data.id == ID_DONE) {
+    if (msg.data.id == ID_DONE) {
         msg.status.id = ID_OKAY;
         msg.status.msglen = 0;
-        if(!WriteFdExactly(s, &msg.status, sizeof(msg.status)))
-            return -1;
+        if (!WriteFdExactly(s, &msg.status, sizeof(msg.status))) return false;
     } else {
-        fail_message(s, "invalid data message: expected ID_DONE");
-        return -1;
+        SendFail(s, "invalid data message: expected ID_DONE");
+        return false;
     }
 
-    return 0;
+    return true;
 }
 #endif
 
-static int do_send(int s, char *path, char *buffer)
-{
-    unsigned int mode;
-    bool is_link = false;
-    bool do_unlink;
-
-    char* tmp = strrchr(path,',');
-    if(tmp) {
-        *tmp = 0;
-        errno = 0;
-        mode = strtoul(tmp + 1, NULL, 0);
-        is_link = S_ISLNK((mode_t) mode);
-        mode &= 0777;
-    }
-    if(!tmp || errno) {
-        mode = 0644;
-        is_link = 0;
-        do_unlink = true;
-    } else {
-        struct stat st;
-        /* Don't delete files before copying if they are not "regular" */
-        do_unlink = lstat(path, &st) || S_ISREG(st.st_mode) || S_ISLNK(st.st_mode);
-        if (do_unlink) {
-            adb_unlink(path);
-        }
+static bool do_send(int s, const std::string& spec, std::vector<char>& buffer) {
+    // 'spec' is of the form "/some/path,0755". Break it up.
+    size_t comma = spec.find_last_of(',');
+    if (comma == std::string::npos) {
+        SendFail(s, "missing , in ID_SEND");
+        return false;
     }
 
-    if (is_link) {
-        return handle_send_link(s, path, buffer);
+    std::string path = spec.substr(0, comma);
+
+    errno = 0;
+    mode_t mode = strtoul(spec.substr(comma + 1).c_str(), nullptr, 0);
+    if (errno != 0) {
+        SendFail(s, "bad mode");
+        return false;
     }
 
+    // Don't delete files before copying if they are not "regular" or symlinks.
+    struct stat st;
+    bool do_unlink = (lstat(path.c_str(), &st) == -1) || S_ISREG(st.st_mode) || S_ISLNK(st.st_mode);
+    if (do_unlink) {
+        adb_unlink(path.c_str());
+    }
+
+    if (S_ISLNK(mode)) {
+        return handle_send_link(s, path.c_str(), buffer);
+    }
+
+    // Copy user permission bits to "group" and "other" permissions.
+    mode &= 0777;
+    mode |= ((mode >> 3) & 0070);
+    mode |= ((mode >> 3) & 0007);
+
     uid_t uid = -1;
     gid_t gid = -1;
     uint64_t cap = 0;
-
-    /* copy user permission bits to "group" and "other" permissions */
-    mode |= ((mode >> 3) & 0070);
-    mode |= ((mode >> 3) & 0007);
-
-    tmp = path;
-    if(*tmp == '/') {
-        tmp++;
-    }
     if (should_use_fs_config(path)) {
-        fs_config(tmp, 0, NULL, &uid, &gid, &mode, &cap);
+        unsigned int broken_api_hack = mode;
+        fs_config(path.c_str(), 0, nullptr, &uid, &gid, &broken_api_hack, &cap);
+        mode = broken_api_hack;
     }
-    return handle_send_file(s, path, uid, gid, mode, buffer, do_unlink);
+    return handle_send_file(s, path.c_str(), uid, gid, mode, buffer, do_unlink);
 }
 
-static int do_recv(int s, const char *path, char *buffer)
-{
-    syncmsg msg;
-    int fd, r;
-
-    fd = adb_open(path, O_RDONLY | O_CLOEXEC);
-    if(fd < 0) {
-        if(fail_errno(s)) return -1;
-        return 0;
+static bool do_recv(int s, const char* path, std::vector<char>& buffer) {
+    int fd = adb_open(path, O_RDONLY | O_CLOEXEC);
+    if (fd < 0) {
+        SendSyncFailErrno(s, "open failed");
+        return false;
     }
 
+    syncmsg msg;
     msg.data.id = ID_DATA;
-    for(;;) {
-        r = adb_read(fd, buffer, SYNC_DATA_MAX);
-        if(r <= 0) {
-            if(r == 0) break;
-            if(errno == EINTR) continue;
-            r = fail_errno(s);
+    while (true) {
+        int r = adb_read(fd, &buffer[0], buffer.size());
+        if (r <= 0) {
+            if (r == 0) break;
+            SendSyncFailErrno(s, "read failed");
             adb_close(fd);
-            return r;
+            return false;
         }
-        msg.data.size = htoll(r);
-        if(!WriteFdExactly(s, &msg.data, sizeof(msg.data)) ||
-           !WriteFdExactly(s, buffer, r)) {
+        msg.data.size = r;
+        if (!WriteFdExactly(s, &msg.data, sizeof(msg.data)) || !WriteFdExactly(s, &buffer[0], r)) {
             adb_close(fd);
-            return -1;
+            return false;
         }
     }
 
@@ -404,66 +342,62 @@
 
     msg.data.id = ID_DONE;
     msg.data.size = 0;
-    if(!WriteFdExactly(s, &msg.data, sizeof(msg.data))) {
-        return -1;
-    }
-
-    return 0;
+    return WriteFdExactly(s, &msg.data, sizeof(msg.data));
 }
 
-void file_sync_service(int fd, void *cookie)
-{
-    syncmsg msg;
+static bool handle_sync_command(int fd, std::vector<char>& buffer) {
+    D("sync: waiting for request\n");
+
+    SyncRequest request;
+    if (!ReadFdExactly(fd, &request, sizeof(request))) {
+        SendSyncFail(fd, "command read failure");
+        return false;
+    }
+    size_t path_length = request.path_length;
+    if (path_length > 1024) {
+        SendSyncFail(fd, "path too long");
+        return false;
+    }
     char name[1025];
-    unsigned namelen;
+    if (!ReadFdExactly(fd, name, path_length)) {
+        SendSyncFail(fd, "filename read failure");
+        return false;
+    }
+    name[path_length] = 0;
 
-    char *buffer = reinterpret_cast<char*>(malloc(SYNC_DATA_MAX));
-    if(buffer == 0) goto fail;
+    const char* id = reinterpret_cast<const char*>(&request.id);
+    D("sync: '%.4s' '%s'\n", id, name);
 
-    for(;;) {
-        D("sync: waiting for command\n");
-
-        if(!ReadFdExactly(fd, &msg.req, sizeof(msg.req))) {
-            fail_message(fd, "command read failure");
-            break;
-        }
-        namelen = ltohl(msg.req.namelen);
-        if(namelen > 1024) {
-            fail_message(fd, "invalid namelen");
-            break;
-        }
-        if(!ReadFdExactly(fd, name, namelen)) {
-            fail_message(fd, "filename read failure");
-            break;
-        }
-        name[namelen] = 0;
-
-        msg.req.namelen = 0;
-        D("sync: '%s' '%s'\n", (char*) &msg.req, name);
-
-        switch(msg.req.id) {
-        case ID_STAT:
-            if(do_stat(fd, name)) goto fail;
-            break;
-        case ID_LIST:
-            if(do_list(fd, name)) goto fail;
-            break;
-        case ID_SEND:
-            if(do_send(fd, name, buffer)) goto fail;
-            break;
-        case ID_RECV:
-            if(do_recv(fd, name, buffer)) goto fail;
-            break;
-        case ID_QUIT:
-            goto fail;
-        default:
-            fail_message(fd, "unknown command");
-            goto fail;
-        }
+    switch (request.id) {
+      case ID_STAT:
+        if (!do_stat(fd, name)) return false;
+        break;
+      case ID_LIST:
+        if (!do_list(fd, name)) return false;
+        break;
+      case ID_SEND:
+        if (!do_send(fd, name, buffer)) return false;
+        break;
+      case ID_RECV:
+        if (!do_recv(fd, name, buffer)) return false;
+        break;
+      case ID_QUIT:
+        return false;
+      default:
+        SendSyncFail(fd, android::base::StringPrintf("unknown command '%.4s' (%08x)",
+                                                     id, request.id));
+        return false;
     }
 
-fail:
-    if(buffer != 0) free(buffer);
+    return true;
+}
+
+void file_sync_service(int fd, void* cookie) {
+    std::vector<char> buffer(SYNC_DATA_MAX);
+
+    while (handle_sync_command(fd, buffer)) {
+    }
+
     D("sync: done\n");
     adb_close(fd);
 }
diff --git a/adb/file_sync_service.h b/adb/file_sync_service.h
index 344eb98..67ed3fc 100644
--- a/adb/file_sync_service.h
+++ b/adb/file_sync_service.h
@@ -19,14 +19,10 @@
 
 #include <string>
 
-#define htoll(x) (x)
-#define ltohl(x) (x)
-
 #define MKID(a,b,c,d) ((a) | ((b) << 8) | ((c) << 16) | ((d) << 24))
 
 #define ID_STAT MKID('S','T','A','T')
 #define ID_LIST MKID('L','I','S','T')
-#define ID_ULNK MKID('U','L','N','K')
 #define ID_SEND MKID('S','E','N','D')
 #define ID_RECV MKID('R','E','C','V')
 #define ID_DENT MKID('D','E','N','T')
@@ -36,41 +32,41 @@
 #define ID_FAIL MKID('F','A','I','L')
 #define ID_QUIT MKID('Q','U','I','T')
 
+struct SyncRequest {
+    uint32_t id;  // ID_STAT, et cetera.
+    uint32_t path_length;  // <= 1024
+    // Followed by 'path_length' bytes of path (not NUL-terminated).
+} __attribute__((packed)) ;
+
 union syncmsg {
-    unsigned id;
-    struct {
-        unsigned id;
-        unsigned namelen;
-    } req;
-    struct {
+    struct __attribute__((packed)) {
         unsigned id;
         unsigned mode;
         unsigned size;
         unsigned time;
     } stat;
-    struct {
+    struct __attribute__((packed)) {
         unsigned id;
         unsigned mode;
         unsigned size;
         unsigned time;
         unsigned namelen;
     } dent;
-    struct {
+    struct __attribute__((packed)) {
         unsigned id;
         unsigned size;
     } data;
-    struct {
+    struct __attribute__((packed)) {
         unsigned id;
         unsigned msglen;
     } status;
-} ;
+};
 
-
-void file_sync_service(int fd, void *cookie);
-int do_sync_ls(const char *path);
-int do_sync_push(const char *lpath, const char *rpath, int show_progress);
-int do_sync_sync(const std::string& lpath, const std::string& rpath, bool list_only);
-int do_sync_pull(const char *rpath, const char *lpath, int show_progress, int pullTime);
+void file_sync_service(int fd, void* cookie);
+bool do_sync_ls(const char* path);
+bool do_sync_push(const char* lpath, const char* rpath, bool show_progress);
+bool do_sync_sync(const std::string& lpath, const std::string& rpath, bool list_only);
+bool do_sync_pull(const char* rpath, const char* lpath, bool show_progress, int copy_attrs);
 
 #define SYNC_DATA_MAX (64*1024)
 
diff --git a/adb/get_my_path_windows.cpp b/adb/get_my_path_windows.cpp
deleted file mode 100644
index 9d23e1c..0000000
--- a/adb/get_my_path_windows.cpp
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * 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 <assert.h>
-#include <limits.h>
-#include <windows.h>
-
-#include "adb.h"
-
-void get_my_path(char *exe, size_t maxLen)
-{
-    char  *r;
-
-    /* XXX: should be GetModuleFileNameA */
-    if (GetModuleFileName(NULL, exe, maxLen) > 0) {
-        r = strrchr(exe, '\\');
-        if (r != NULL)
-            *r = '\0';
-    } else {
-        exe[0] = '\0';
-    }
-}
-
diff --git a/adb/jdwp_service.cpp b/adb/jdwp_service.cpp
index c0f7ec2..06e7780 100644
--- a/adb/jdwp_service.cpp
+++ b/adb/jdwp_service.cpp
@@ -22,6 +22,7 @@
 
 #include <errno.h>
 #include <stdio.h>
+#include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
 
@@ -435,7 +436,7 @@
               __FUNCTION__, strerror(errno));
             return -1;
         }
-        D("socketpair: (%d,%d)", fds[0], fds[1]);
+        D("socketpair: (%d,%d)\n", fds[0], fds[1]);
 
         proc->out_fds[ proc->out_count ] = fds[1];
         if (++proc->out_count == 1)
@@ -608,7 +609,7 @@
     */
     if (jdwp->pass == 0) {
         apacket*  p = get_apacket();
-        p->len = jdwp_process_list((char*)p->data, MAX_PAYLOAD);
+        p->len = jdwp_process_list((char*)p->data, s->get_max_payload());
         peer->enqueue(peer, p);
         jdwp->pass = 1;
     }
@@ -695,7 +696,7 @@
     if (t->need_update) {
         apacket*  p = get_apacket();
         t->need_update = 0;
-        p->len = jdwp_process_list_msg((char*)p->data, sizeof(p->data));
+        p->len = jdwp_process_list_msg((char*)p->data, s->get_max_payload());
         s->peer->enqueue(s->peer, p);
     }
 }
diff --git a/adb/mutex_list.h b/adb/mutex_list.h
index ff72751..9003361 100644
--- a/adb/mutex_list.h
+++ b/adb/mutex_list.h
@@ -6,6 +6,7 @@
 #ifndef ADB_MUTEX
 #error ADB_MUTEX not defined when including this file
 #endif
+ADB_MUTEX(dirname_lock)
 ADB_MUTEX(socket_list_lock)
 ADB_MUTEX(transport_lock)
 #if ADB_HOST
diff --git a/adb/protocol.txt b/adb/protocol.txt
index c9d3c24..5c7c6ba 100644
--- a/adb/protocol.txt
+++ b/adb/protocol.txt
@@ -60,11 +60,14 @@
 declares the maximum message body size that the remote system
 is willing to accept.
 
-Currently, version=0x01000000 and maxdata=4096
+Currently, version=0x01000000 and maxdata=256*1024. Older versions of adb
+hard-coded maxdata=4096, so CONNECT and AUTH packets sent to a device must not
+be larger than that because they're sent before the CONNECT from the device
+that tells the adb server what maxdata the device can support.
 
 Both sides send a CONNECT message when the connection between them is
 established.  Until a CONNECT message is received no other messages may
-be sent.  Any messages received before a CONNECT message MUST be ignored.
+be sent. Any messages received before a CONNECT message MUST be ignored.
 
 If a CONNECT message is received with an unknown version or insufficiently
 large maxdata value, the connection with the other side must be closed.
@@ -133,7 +136,7 @@
 
 
 
---- WRITE(0, remote-id, "data") ----------------------------------------
+--- WRITE(local-id, remote-id, "data") ---------------------------------
 
 The WRITE message sends data to the recipient's stream identified by
 remote-id.  The payload MUST be <= maxdata in length.
diff --git a/adb/qemu_tracing.cpp b/adb/qemu_tracing.cpp
deleted file mode 100644
index f31eae8..0000000
--- a/adb/qemu_tracing.cpp
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (C) 2015 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.
- */
-
-/*
- * Implements ADB tracing inside the emulator.
- */
-
-#include <stdarg.h>
-
-#include "sysdeps.h"
-#include "qemu_tracing.h"
-
-/*
- * Redefine open and write for qemu_pipe.h that contains inlined references
- * to those routines. We will redifine them back after qemu_pipe.h inclusion.
- */
-
-#undef open
-#undef write
-#define open    adb_open
-#define write   adb_write
-#include <hardware/qemu_pipe.h>
-#undef open
-#undef write
-#define open    ___xxx_open
-#define write   ___xxx_write
-
-/* A handle to adb-debug qemud service in the emulator. */
-int   adb_debug_qemu = -1;
-
-/* Initializes connection with the adb-debug qemud service in the emulator. */
-int adb_qemu_trace_init(void)
-{
-    char con_name[32];
-
-    if (adb_debug_qemu >= 0) {
-        return 0;
-    }
-
-    /* adb debugging QEMUD service connection request. */
-    snprintf(con_name, sizeof(con_name), "qemud:adb-debug");
-    adb_debug_qemu = qemu_pipe_open(con_name);
-    return (adb_debug_qemu >= 0) ? 0 : -1;
-}
-
-void adb_qemu_trace(const char* fmt, ...)
-{
-    va_list args;
-    va_start(args, fmt);
-    char msg[1024];
-
-    if (adb_debug_qemu >= 0) {
-        vsnprintf(msg, sizeof(msg), fmt, args);
-        adb_write(adb_debug_qemu, msg, strlen(msg));
-    }
-}
diff --git a/adb/qemu_tracing.h b/adb/qemu_tracing.h
deleted file mode 100644
index ff42d4f..0000000
--- a/adb/qemu_tracing.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2015 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.
- */
-
-/*
- * Implements ADB tracing inside the emulator.
- */
-
-#ifndef __QEMU_TRACING_H
-#define __QEMU_TRACING_H
-
-/* Initializes connection with the adb-debug qemud service in the emulator. */
-int adb_qemu_trace_init(void);
-void adb_qemu_trace(const char* fmt, ...);
-
-#endif /* __QEMU_TRACING_H */
diff --git a/adb/remount_service.cpp b/adb/remount_service.cpp
index 7a3b89a..2893263 100644
--- a/adb/remount_service.cpp
+++ b/adb/remount_service.cpp
@@ -33,9 +33,12 @@
 #include "adb_io.h"
 #include "adb_utils.h"
 #include "cutils/properties.h"
+#include "fs_mgr.h"
+
+const std::string kFstab_Prefix = "/fstab.";
 
 // Returns the device used to mount a directory in /proc/mounts.
-static std::string find_mount(const char* dir) {
+static std::string find_proc_mount(const char* dir) {
     std::unique_ptr<FILE, int(*)(FILE*)> fp(setmntent("/proc/mounts", "r"), endmntent);
     if (!fp) {
         return "";
@@ -50,6 +53,29 @@
     return "";
 }
 
+// Returns the device used to mount a directory in the fstab.
+static std::string find_fstab_mount(const char* dir) {
+    char propbuf[PROPERTY_VALUE_MAX];
+
+    property_get("ro.hardware", propbuf, "");
+    std::string fstab_filename = kFstab_Prefix + propbuf;
+    struct fstab* fstab = fs_mgr_read_fstab(fstab_filename.c_str());
+    struct fstab_rec* rec = fs_mgr_get_entry_for_mount_point(fstab, dir);
+    std::string dev = rec ? std::string(rec->blk_device) : "";
+    fs_mgr_free_fstab(fstab);
+    return dev;
+}
+
+// The proc entry for / is full of lies, so check fstab instead.
+// /proc/mounts lists rootfs and /dev/root, neither of which is what we want.
+static std::string find_mount(const char* dir) {
+    if (strcmp(dir, "/") == 0) {
+       return find_fstab_mount(dir);
+    } else {
+       return find_proc_mount(dir);
+    }
+}
+
 bool make_block_device_writable(const std::string& dev) {
     int fd = unix_open(dev.c_str(), O_RDONLY | O_CLOEXEC);
     if (fd == -1) {
@@ -58,7 +84,7 @@
 
     int OFF = 0;
     bool result = (ioctl(fd, BLKROSET, &OFF) != -1);
-    adb_close(fd);
+    unix_close(fd);
     return result;
 }
 
@@ -112,7 +138,13 @@
     }
 
     bool success = true;
-    success &= remount_partition(fd, "/system");
+    property_get("ro.build.system_root_image", prop_buf, "");
+    bool system_root = !strcmp(prop_buf, "true");
+    if (system_root) {
+        success &= remount_partition(fd, "/");
+    } else {
+        success &= remount_partition(fd, "/system");
+    }
     success &= remount_partition(fd, "/vendor");
     success &= remount_partition(fd, "/oem");
 
diff --git a/adb/services.cpp b/adb/services.cpp
index 1847447..4606804 100644
--- a/adb/services.cpp
+++ b/adb/services.cpp
@@ -24,6 +24,11 @@
 #include <stdlib.h>
 #include <string.h>
 
+#if !ADB_HOST
+#include <pty.h>
+#include <termios.h>
+#endif
+
 #ifndef _WIN32
 #include <netdb.h>
 #include <netinet/in.h>
@@ -34,6 +39,7 @@
 #include <base/file.h>
 #include <base/stringprintf.h>
 #include <base/strings.h>
+#include <cutils/sockets.h>
 
 #if !ADB_HOST
 #include "cutils/android_reboot.h"
@@ -42,6 +48,7 @@
 
 #include "adb.h"
 #include "adb_io.h"
+#include "adb_utils.h"
 #include "file_sync_service.h"
 #include "remount_service.h"
 #include "transport.h"
@@ -52,10 +59,15 @@
     void *cookie;
 };
 
+enum class SubprocessType {
+    kPty,
+    kRaw,
+};
 
 void *service_bootstrap_func(void *x)
 {
     stinfo* sti = reinterpret_cast<stinfo*>(x);
+    adb_thread_setname(android::base::StringPrintf("service %d", sti->fd));
     sti->func(sti->fd, sti->cookie);
     free(sti);
     return 0;
@@ -200,7 +212,7 @@
         printf("cannot create service socket pair\n");
         return -1;
     }
-    D("socketpair: (%d,%d)", s[0], s[1]);
+    D("socketpair: (%d,%d)\n", s[0], s[1]);
 
     stinfo* sti = reinterpret_cast<stinfo*>(malloc(sizeof(stinfo)));
     if (sti == nullptr) {
@@ -210,8 +222,7 @@
     sti->cookie = cookie;
     sti->fd = s[1];
 
-    adb_thread_t t;
-    if (adb_thread_create(&t, service_bootstrap_func, sti)) {
+    if (!adb_thread_create(service_bootstrap_func, sti)) {
         free(sti);
         adb_close(s[0]);
         adb_close(s[1]);
@@ -239,60 +250,65 @@
     }
 }
 
-static int create_subproc_pty(const char *cmd, const char *arg0, const char *arg1, pid_t *pid)
-{
+#if !ADB_HOST
+static int create_subproc_pty(const char* cmd, const char* arg0,
+                              const char* arg1, pid_t* pid) {
     D("create_subproc_pty(cmd=%s, arg0=%s, arg1=%s)\n", cmd, arg0, arg1);
-#if defined(_WIN32)
-    fprintf(stderr, "error: create_subproc_pty not implemented on Win32 (%s %s %s)\n", cmd, arg0, arg1);
-    return -1;
-#else
+    char pts_name[PATH_MAX];
     int ptm;
-
-    ptm = unix_open("/dev/ptmx", O_RDWR | O_CLOEXEC); // | O_NOCTTY);
-    if(ptm < 0){
-        printf("[ cannot open /dev/ptmx - %s ]\n",strerror(errno));
-        return -1;
-    }
-
-    char devname[64];
-    if(grantpt(ptm) || unlockpt(ptm) || ptsname_r(ptm, devname, sizeof(devname)) != 0) {
-        printf("[ trouble with /dev/ptmx - %s ]\n", strerror(errno));
-        adb_close(ptm);
-        return -1;
-    }
-
-    *pid = fork();
-    if(*pid < 0) {
+    *pid = forkpty(&ptm, pts_name, nullptr, nullptr);
+    if (*pid == -1) {
         printf("- fork failed: %s -\n", strerror(errno));
-        adb_close(ptm);
+        unix_close(ptm);
         return -1;
     }
 
     if (*pid == 0) {
         init_subproc_child();
 
-        int pts = unix_open(devname, O_RDWR | O_CLOEXEC);
-        if (pts < 0) {
-            fprintf(stderr, "child failed to open pseudo-term slave: %s\n", devname);
+        int pts = unix_open(pts_name, O_RDWR | O_CLOEXEC);
+        if (pts == -1) {
+            fprintf(stderr, "child failed to open pseudo-term slave %s: %s\n",
+                    pts_name, strerror(errno));
+            unix_close(ptm);
             exit(-1);
         }
 
+        // arg0 is "-c" in batch mode and "-" in interactive mode.
+        if (strcmp(arg0, "-c") == 0) {
+            termios tattr;
+            if (tcgetattr(pts, &tattr) == -1) {
+                fprintf(stderr, "tcgetattr failed: %s\n", strerror(errno));
+                unix_close(pts);
+                unix_close(ptm);
+                exit(-1);
+            }
+
+            cfmakeraw(&tattr);
+            if (tcsetattr(pts, TCSADRAIN, &tattr) == -1) {
+                fprintf(stderr, "tcsetattr failed: %s\n", strerror(errno));
+                unix_close(pts);
+                unix_close(ptm);
+                exit(-1);
+            }
+        }
+
         dup2(pts, STDIN_FILENO);
         dup2(pts, STDOUT_FILENO);
         dup2(pts, STDERR_FILENO);
 
-        adb_close(pts);
-        adb_close(ptm);
+        unix_close(pts);
+        unix_close(ptm);
 
-        execl(cmd, cmd, arg0, arg1, NULL);
+        execl(cmd, cmd, arg0, arg1, nullptr);
         fprintf(stderr, "- exec '%s' failed: %s (%d) -\n",
                 cmd, strerror(errno), errno);
         exit(-1);
     } else {
         return ptm;
     }
-#endif /* !defined(_WIN32) */
 }
+#endif // !ADB_HOST
 
 static int create_subproc_raw(const char *cmd, const char *arg0, const char *arg1, pid_t *pid)
 {
@@ -308,7 +324,7 @@
         printf("[ cannot create socket pair - %s ]\n", strerror(errno));
         return -1;
     }
-    D("socketpair: (%d,%d)", sv[0], sv[1]);
+    D("socketpair: (%d,%d)\n", sv[0], sv[1]);
 
     *pid = fork();
     if (*pid < 0) {
@@ -378,29 +394,30 @@
     }
 }
 
-static int create_subproc_thread(const char *name, const subproc_mode mode)
-{
-    adb_thread_t t;
-    int ret_fd;
-    pid_t pid = -1;
-
+// Starts a subprocess and spawns a thread to wait for the subprocess to finish
+// and trigger the necessary cleanup.
+//
+// |name| is the command to execute in the subprocess; empty string will start
+//     an interactive session.
+// |type| selects between a PTY or raw subprocess.
+//
+// Returns an open file descriptor tied to the subprocess stdin/stdout/stderr.
+static int create_subproc_thread(const char *name, SubprocessType type) {
     const char *arg0, *arg1;
-    if (name == 0 || *name == 0) {
-        arg0 = "-"; arg1 = 0;
+    if (*name == '\0') {
+        arg0 = "-";
+        arg1 = nullptr;
     } else {
-        arg0 = "-c"; arg1 = name;
+        arg0 = "-c";
+        arg1 = name;
     }
 
-    switch (mode) {
-    case SUBPROC_PTY:
+    pid_t pid = -1;
+    int ret_fd;
+    if (type == SubprocessType::kPty) {
         ret_fd = create_subproc_pty(SHELL_COMMAND, arg0, arg1, &pid);
-        break;
-    case SUBPROC_RAW:
+    } else {
         ret_fd = create_subproc_raw(SHELL_COMMAND, arg0, arg1, &pid);
-        break;
-    default:
-        fprintf(stderr, "invalid subproc_mode %d\n", mode);
-        return -1;
     }
     D("create_subproc ret_fd=%d pid=%d\n", ret_fd, pid);
 
@@ -410,7 +427,7 @@
     sti->cookie = (void*) (uintptr_t) pid;
     sti->fd = ret_fd;
 
-    if (adb_thread_create(&t, service_bootstrap_func, sti)) {
+    if (!adb_thread_create(service_bootstrap_func, sti)) {
         free(sti);
         adb_close(ret_fd);
         fprintf(stderr, "cannot create service thread\n");
@@ -422,25 +439,26 @@
 }
 #endif
 
-int service_to_fd(const char *name)
-{
+int service_to_fd(const char* name) {
     int ret = -1;
 
     if(!strncmp(name, "tcp:", 4)) {
         int port = atoi(name + 4);
         name = strchr(name + 4, ':');
         if(name == 0) {
-            ret = socket_loopback_client(port, SOCK_STREAM);
+            std::string error;
+            ret = network_loopback_client(port, SOCK_STREAM, &error);
             if (ret >= 0)
                 disable_tcp_nagle(ret);
         } else {
 #if ADB_HOST
-            ret = socket_network_client(name + 1, port, SOCK_STREAM);
+            std::string error;
+            ret = network_connect(name + 1, port, SOCK_STREAM, 0, &error);
 #else
             return -1;
 #endif
         }
-#ifndef HAVE_WINSOCK   /* winsock doesn't implement unix domain sockets */
+#if !defined(_WIN32)   /* winsock doesn't implement unix domain sockets */
     } else if(!strncmp(name, "local:", 6)) {
         ret = socket_local_client(name + 6,
                 ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM);
@@ -461,10 +479,17 @@
         ret = create_service_thread(framebuffer_service, 0);
     } else if (!strncmp(name, "jdwp:", 5)) {
         ret = create_jdwp_connection_fd(atoi(name+5));
-    } else if(!HOST && !strncmp(name, "shell:", 6)) {
-        ret = create_subproc_thread(name + 6, SUBPROC_PTY);
-    } else if(!HOST && !strncmp(name, "exec:", 5)) {
-        ret = create_subproc_thread(name + 5, SUBPROC_RAW);
+    } else if(!strncmp(name, "shell:", 6)) {
+        const char* args = name + 6;
+        if (*args) {
+            // Non-interactive session uses a raw subprocess.
+            ret = create_subproc_thread(args, SubprocessType::kRaw);
+        } else {
+            // Interactive session uses a PTY subprocess.
+            ret = create_subproc_thread(args, SubprocessType::kPty);
+        }
+    } else if(!strncmp(name, "exec:", 5)) {
+        ret = create_subproc_thread(name + 5, SubprocessType::kRaw);
     } else if(!strncmp(name, "sync:", 5)) {
         ret = create_service_thread(file_sync_service, NULL);
     } else if(!strncmp(name, "remount:", 8)) {
@@ -479,13 +504,14 @@
         ret = create_service_thread(restart_unroot_service, NULL);
     } else if(!strncmp(name, "backup:", 7)) {
         ret = create_subproc_thread(android::base::StringPrintf("/system/bin/bu backup %s",
-                                                                (name + 7)).c_str(), SUBPROC_RAW);
+                                                                (name + 7)).c_str(),
+                                    SubprocessType::kRaw);
     } else if(!strncmp(name, "restore:", 8)) {
-        ret = create_subproc_thread("/system/bin/bu restore", SUBPROC_RAW);
+        ret = create_subproc_thread("/system/bin/bu restore", SubprocessType::kRaw);
     } else if(!strncmp(name, "tcpip:", 6)) {
         int port;
         if (sscanf(name + 6, "%d", &port) != 1) {
-            port = 0;
+            return -1;
         }
         ret = create_service_thread(restart_tcp_service, (void *) (uintptr_t) port);
     } else if(!strncmp(name, "usb:", 4)) {
@@ -514,9 +540,9 @@
 
 #if ADB_HOST
 struct state_info {
-    transport_type transport;
+    TransportType transport_type;
     char* serial;
-    int state;
+    ConnectionState state;
 };
 
 static void wait_for_state(int fd, void* cookie)
@@ -526,8 +552,9 @@
     D("wait_for_state %d\n", sinfo->state);
 
     std::string error_msg = "unknown error";
-    atransport* t = acquire_one_transport(sinfo->state, sinfo->transport, sinfo->serial, &error_msg);
-    if (t != 0) {
+    atransport* t = acquire_one_transport(sinfo->state, sinfo->transport_type, sinfo->serial,
+                                          &error_msg);
+    if (t != nullptr) {
         SendOkay(fd);
     } else {
         SendFail(fd, error_msg);
@@ -540,35 +567,28 @@
     D("wait_for_state is done\n");
 }
 
-static void connect_device(const std::string& host, std::string* response) {
-    if (host.empty()) {
-        *response = "empty host name";
+static void connect_device(const std::string& address, std::string* response) {
+    if (address.empty()) {
+        *response = "empty address";
         return;
     }
 
-    std::vector<std::string> pieces = android::base::Split(host, ":");
-    const std::string& hostname = pieces[0];
-
+    std::string serial;
+    std::string host;
     int port = DEFAULT_ADB_LOCAL_TRANSPORT_PORT;
-    if (pieces.size() > 1) {
-        if (sscanf(pieces[1].c_str(), "%d", &port) != 1) {
-            *response = android::base::StringPrintf("bad port number %s", pieces[1].c_str());
-            return;
-        }
-    }
-
-    // This may look like we're putting 'host' back together,
-    // but we're actually inserting the default port if necessary.
-    std::string serial = android::base::StringPrintf("%s:%d", hostname.c_str(), port);
-
-    int fd = socket_network_client_timeout(hostname.c_str(), port, SOCK_STREAM, 10);
-    if (fd < 0) {
-        *response = android::base::StringPrintf("unable to connect to %s:%d",
-                                                hostname.c_str(), port);
+    if (!parse_host_and_port(address, &serial, &host, &port, response)) {
         return;
     }
 
-    D("client: connected on remote on fd %d\n", fd);
+    std::string error;
+    int fd = network_connect(host.c_str(), port, SOCK_STREAM, 10, &error);
+    if (fd == -1) {
+        *response = android::base::StringPrintf("unable to connect to %s: %s",
+                                                serial.c_str(), error.c_str());
+        return;
+    }
+
+    D("client: connected %s remote on fd %d\n", serial.c_str(), fd);
     close_on_exec(fd);
     disable_tcp_nagle(fd);
 
@@ -618,25 +638,25 @@
     }
 
     // Preconditions met, try to connect to the emulator.
-    if (!local_connect_arbitrary_ports(console_port, adb_port)) {
+    std::string error;
+    if (!local_connect_arbitrary_ports(console_port, adb_port, &error)) {
         *response = android::base::StringPrintf("Connected to emulator on ports %d,%d",
                                                 console_port, adb_port);
     } else {
-        *response = android::base::StringPrintf("Could not connect to emulator on ports %d,%d",
-                                                console_port, adb_port);
+        *response = android::base::StringPrintf("Could not connect to emulator on ports %d,%d: %s",
+                                                console_port, adb_port, error.c_str());
     }
 }
 
-static void connect_service(int fd, void* cookie)
-{
-    char *host = reinterpret_cast<char*>(cookie);
-
+static void connect_service(int fd, void* data) {
+    char* host = reinterpret_cast<char*>(data);
     std::string response;
     if (!strncmp(host, "emu:", 4)) {
         connect_emulator(host + 4, &response);
     } else {
         connect_device(host, &response);
     }
+    free(host);
 
     // Send response for emulator and device
     SendProtocolString(fd, response);
@@ -645,8 +665,7 @@
 #endif
 
 #if ADB_HOST
-asocket*  host_service_to_socket(const char*  name, const char *serial)
-{
+asocket* host_service_to_socket(const char* name, const char* serial) {
     if (!strcmp(name,"track-devices")) {
         return create_device_tracker();
     } else if (!strncmp(name, "wait-for-", strlen("wait-for-"))) {
@@ -664,15 +683,18 @@
         name += strlen("wait-for-");
 
         if (!strncmp(name, "local", strlen("local"))) {
-            sinfo->transport = kTransportLocal;
-            sinfo->state = CS_DEVICE;
+            sinfo->transport_type = kTransportLocal;
+            sinfo->state = kCsDevice;
         } else if (!strncmp(name, "usb", strlen("usb"))) {
-            sinfo->transport = kTransportUsb;
-            sinfo->state = CS_DEVICE;
+            sinfo->transport_type = kTransportUsb;
+            sinfo->state = kCsDevice;
         } else if (!strncmp(name, "any", strlen("any"))) {
-            sinfo->transport = kTransportAny;
-            sinfo->state = CS_DEVICE;
+            sinfo->transport_type = kTransportAny;
+            sinfo->state = kCsDevice;
         } else {
+            if (sinfo->serial) {
+                free(sinfo->serial);
+            }
             free(sinfo);
             return NULL;
         }
@@ -680,8 +702,8 @@
         int fd = create_service_thread(wait_for_state, sinfo);
         return create_local_socket(fd);
     } else if (!strncmp(name, "connect:", 8)) {
-        const char *host = name + 8;
-        int fd = create_service_thread(connect_service, (void *)host);
+        char* host = strdup(name + 8);
+        int fd = create_service_thread(connect_service, host);
         return create_local_socket(fd);
     }
     return NULL;
diff --git a/adb/sockets.cpp b/adb/sockets.cpp
index 32ca17d..9c13936 100644
--- a/adb/sockets.cpp
+++ b/adb/sockets.cpp
@@ -330,8 +330,9 @@
     if (ev & FDE_READ) {
         apacket *p = get_apacket();
         unsigned char *x = p->data;
-        size_t avail = MAX_PAYLOAD;
-        int r;
+        const size_t max_payload = s->get_max_payload();
+        size_t avail = max_payload;
+        int r = 0;
         int is_eof = 0;
 
         while (avail > 0) {
@@ -354,14 +355,17 @@
         }
         D("LS(%d): fd=%d post avail loop. r=%d is_eof=%d forced_eof=%d\n",
           s->id, s->fd, r, is_eof, s->fde.force_eof);
-        if ((avail == MAX_PAYLOAD) || (s->peer == 0)) {
+        if ((avail == max_payload) || (s->peer == 0)) {
             put_apacket(p);
         } else {
-            p->len = MAX_PAYLOAD - avail;
+            p->len = max_payload - avail;
 
+            // s->peer->enqueue() may call s->close() and free s,
+            // so save variables for debug printing below.
+            unsigned saved_id = s->id;
+            int saved_fd = s->fd;
             r = s->peer->enqueue(s->peer, p);
-            D("LS(%d): fd=%d post peer->enqueue(). r=%d\n", s->id, s->fd,
-              r);
+            D("LS(%u): fd=%d post peer->enqueue(). r=%d\n", saved_id, saved_fd, r);
 
             if (r < 0) {
                     /* error return means they closed us as a side-effect
@@ -467,14 +471,6 @@
 }
 #endif /* ADB_HOST */
 
-/* a Remote socket is used to send/receive data to/from a given transport object
-** it needs to be closed when the transport is forcibly destroyed by the user
-*/
-struct aremotesocket {
-    asocket      socket;
-    adisconnect  disconnect;
-};
-
 static int remote_socket_enqueue(asocket *s, apacket *p)
 {
     D("entered remote_socket_enqueue RS(%d) WRITE fd=%d peer.fd=%d\n",
@@ -522,33 +518,17 @@
     D("entered remote_socket_close RS(%d) CLOSE fd=%d peer->fd=%d\n",
       s->id, s->fd, s->peer?s->peer->fd:-1);
     D("RS(%d): closed\n", s->id);
-    remove_transport_disconnect( s->transport, &((aremotesocket*)s)->disconnect );
     free(s);
 }
 
-static void remote_socket_disconnect(void*  _s, atransport*  t)
-{
-    asocket* s = reinterpret_cast<asocket*>(_s);
-    asocket* peer = s->peer;
-
-    D("remote_socket_disconnect RS(%d)\n", s->id);
-    if (peer) {
-        peer->peer = NULL;
-        peer->close(peer);
-    }
-    remove_transport_disconnect( s->transport, &((aremotesocket*)s)->disconnect );
-    free(s);
-}
-
-/* Create an asocket to exchange packets with a remote service through transport
-  |t|. Where |id| is the socket id of the corresponding service on the other
-   side of the transport (it is allocated by the remote side and _cannot_ be 0).
-   Returns a new non-NULL asocket handle. */
+// Create a remote socket to exchange packets with a remote service through transport
+// |t|. Where |id| is the socket id of the corresponding service on the other
+//  side of the transport (it is allocated by the remote side and _cannot_ be 0).
+// Returns a new non-NULL asocket handle.
 asocket *create_remote_socket(unsigned id, atransport *t)
 {
     if (id == 0) fatal("invalid remote socket id (0)");
-    asocket* s = reinterpret_cast<asocket*>(calloc(1, sizeof(aremotesocket)));
-    adisconnect* dis = &reinterpret_cast<aremotesocket*>(s)->disconnect;
+    asocket* s = reinterpret_cast<asocket*>(calloc(1, sizeof(asocket)));
 
     if (s == NULL) fatal("cannot allocate socket");
     s->id = id;
@@ -558,9 +538,6 @@
     s->close = remote_socket_close;
     s->transport = t;
 
-    dis->func   = remote_socket_disconnect;
-    dis->opaque = s;
-    add_transport_disconnect( t, dis );
     D("RS(%d): created\n", s->id);
     return s;
 }
@@ -569,9 +546,9 @@
 {
     D("Connect_to_remote call RS(%d) fd=%d\n", s->id, s->fd);
     apacket *p = get_apacket();
-    int len = strlen(destination) + 1;
+    size_t len = strlen(destination) + 1;
 
-    if(len > (MAX_PAYLOAD-1)) {
+    if(len > (s->get_max_payload()-1)) {
         fatal("destination oversized");
     }
 
@@ -691,7 +668,7 @@
 #if ADB_HOST
     char *service = NULL;
     char* serial = NULL;
-    transport_type ttype = kTransportAny;
+    TransportType type = kTransportAny;
 #endif
 
     D("SS(%d): enqueue %d\n", s->id, p->len);
@@ -700,7 +677,7 @@
         s->pkt_first = p;
         s->pkt_last = p;
     } else {
-        if((s->pkt_first->len + p->len) > MAX_PAYLOAD) {
+        if((s->pkt_first->len + p->len) > s->get_max_payload()) {
             D("SS(%d): overflow\n", s->id);
             put_apacket(p);
             goto fail;
@@ -748,13 +725,13 @@
             service = serial_end + 1;
         }
     } else if (!strncmp(service, "host-usb:", strlen("host-usb:"))) {
-        ttype = kTransportUsb;
+        type = kTransportUsb;
         service += strlen("host-usb:");
     } else if (!strncmp(service, "host-local:", strlen("host-local:"))) {
-        ttype = kTransportLocal;
+        type = kTransportLocal;
         service += strlen("host-local:");
     } else if (!strncmp(service, "host:", strlen("host:"))) {
-        ttype = kTransportAny;
+        type = kTransportAny;
         service += strlen("host:");
     } else {
         service = NULL;
@@ -768,7 +745,7 @@
             ** the OKAY or FAIL message and all we have to do
             ** is clean up.
             */
-        if(handle_host_request(service, ttype, serial, s->peer->fd, s) == 0) {
+        if(handle_host_request(service, type, serial, s->peer->fd, s) == 0) {
                 /* XXX fail message? */
             D( "SS(%d): handled host service '%s'\n", s->id, service );
             goto fail;
@@ -815,7 +792,8 @@
 #else /* !ADB_HOST */
     if (s->transport == NULL) {
         std::string error_msg = "unknown failure";
-        s->transport = acquire_one_transport(CS_ANY, kTransportAny, NULL, &error_msg);
+        s->transport =
+            acquire_one_transport(kCsAny, kTransportAny, NULL, &error_msg);
 
         if (s->transport == NULL) {
             SendFail(s->peer->fd, error_msg);
@@ -824,7 +802,7 @@
     }
 #endif
 
-    if(!(s->transport) || (s->transport->connection_state == CS_OFFLINE)) {
+    if(!(s->transport) || (s->transport->connection_state == kCsOffline)) {
            /* if there's no remote we fail the connection
             ** right here and terminate it
             */
@@ -900,3 +878,14 @@
     ss->peer = s;
     s->ready(s);
 }
+
+size_t asocket::get_max_payload() const {
+    size_t max_payload = MAX_PAYLOAD;
+    if (transport) {
+        max_payload = std::min(max_payload, transport->get_max_payload());
+    }
+    if (peer && peer->transport) {
+        max_payload = std::min(max_payload, peer->transport->get_max_payload());
+    }
+    return max_payload;
+}
diff --git a/adb/sysdeps.h b/adb/sysdeps.h
index 59e5b0b..5918a94 100644
--- a/adb/sysdeps.h
+++ b/adb/sysdeps.h
@@ -24,6 +24,10 @@
 #  undef _WIN32
 #endif
 
+#include <errno.h>
+
+#include <string>
+
 /*
  * TEMP_FAILURE_RETRY is defined by some, but not all, versions of
  * <unistd.h>. (Alas, it is not as standard as we'd hoped!) So, if it's
@@ -39,21 +43,40 @@
     _rc; })
 #endif
 
+// Some printf-like functions are implemented in terms of
+// android::base::StringAppendV, so they should use the same attribute for
+// compile-time format string checking. On Windows, if the mingw version of
+// vsnprintf is used in StringAppendV, use `gnu_printf' which allows z in %zd
+// and PRIu64 (and related) to be recognized by the compile-time checking.
+#define ADB_FORMAT_ARCHETYPE __printf__
+#ifdef __USE_MINGW_ANSI_STDIO
+#if __USE_MINGW_ANSI_STDIO
+#undef ADB_FORMAT_ARCHETYPE
+#define ADB_FORMAT_ARCHETYPE gnu_printf
+#endif
+#endif
+
 #ifdef _WIN32
 
 #include <ctype.h>
 #include <direct.h>
+#include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <io.h>
 #include <process.h>
 #include <sys/stat.h>
+#include <utime.h>
 #include <winsock2.h>
 #include <windows.h>
 #include <ws2tcpip.h>
 
+#include <memory>   // unique_ptr
+#include <string>   // Prototypes for narrow() and widen() use std::(w)string.
+
 #include "fdevent.h"
 
+#define OS_PATH_SEPARATORS "\\/"
 #define OS_PATH_SEPARATOR '\\'
 #define OS_PATH_SEPARATOR_STR "\\"
 #define ENV_PATH_SEPARATOR_STR ";"
@@ -79,18 +102,19 @@
     LeaveCriticalSection( lock );
 }
 
-typedef struct { unsigned  tid; }  adb_thread_t;
-
 typedef  void*  (*adb_thread_func_t)(void*  arg);
 
 typedef  void (*win_thread_func_t)(void*  arg);
 
-static __inline__ int  adb_thread_create( adb_thread_t  *thread, adb_thread_func_t  func, void*  arg)
-{
-    thread->tid = _beginthread( (win_thread_func_t)func, 0, arg );
-    if (thread->tid == (unsigned)-1L) {
-        return -1;
-    }
+static __inline__ bool adb_thread_create(adb_thread_func_t func, void* arg) {
+    uintptr_t tid = _beginthread((win_thread_func_t)func, 0, arg);
+    return (tid != static_cast<uintptr_t>(-1L));
+}
+
+static __inline__ int adb_thread_setname(const std::string& name) {
+    // TODO: See https://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx for how to set
+    // the thread name in Windows. Unfortunately, it only works during debugging, but
+    // our build process doesn't generate PDB files needed for debugging.
     return 0;
 }
 
@@ -108,29 +132,15 @@
 
 #define  S_ISLNK(m)   0   /* no symlinks on Win32 */
 
-static __inline__  int    adb_unlink(const char*  path)
-{
-    int  rc = unlink(path);
-
-    if (rc == -1 && errno == EACCES) {
-        /* unlink returns EACCES when the file is read-only, so we first */
-        /* try to make it writable, then unlink again...                  */
-        rc = chmod(path, _S_IREAD|_S_IWRITE );
-        if (rc == 0)
-            rc = unlink(path);
-    }
-    return rc;
-}
+extern int  adb_unlink(const char*  path);
 #undef  unlink
 #define unlink  ___xxx_unlink
 
-static __inline__ int  adb_mkdir(const char*  path, int mode)
-{
-	return _mkdir(path);
-}
+extern int adb_mkdir(const std::string& path, int mode);
 #undef   mkdir
 #define  mkdir  ___xxx_mkdir
 
+// See the comments for the !defined(_WIN32) versions of adb_*().
 extern int  adb_open(const char*  path, int  options);
 extern int  adb_creat(const char*  path, int  mode);
 extern int  adb_read(int  fd, void* buf, int len);
@@ -139,6 +149,7 @@
 extern int  adb_shutdown(int  fd);
 extern int  adb_close(int  fd);
 
+// See the comments for the !defined(_WIN32) version of unix_close().
 static __inline__ int  unix_close(int fd)
 {
     return close(fd);
@@ -146,11 +157,13 @@
 #undef   close
 #define  close   ____xxx_close
 
+// See the comments for the !defined(_WIN32) version of unix_read().
 extern int  unix_read(int  fd, void*  buf, size_t  len);
 
 #undef   read
 #define  read  ___xxx_read
 
+// See the comments for the !defined(_WIN32) version of unix_write().
 static __inline__  int  unix_write(int  fd, const void*  buf, size_t  len)
 {
     return write(fd, buf, len);
@@ -158,41 +171,20 @@
 #undef   write
 #define  write  ___xxx_write
 
+// See the comments for the !defined(_WIN32) version of adb_open_mode().
 static __inline__ int  adb_open_mode(const char* path, int options, int mode)
 {
     return adb_open(path, options);
 }
 
-static __inline__ int  unix_open(const char*  path, int options,...)
-{
-    if ((options & O_CREAT) == 0)
-    {
-        return  open(path, options);
-    }
-    else
-    {
-        int      mode;
-        va_list  args;
-        va_start( args, options );
-        mode = va_arg( args, int );
-        va_end( args );
-        return open(path, options, mode);
-    }
-}
+// See the comments for the !defined(_WIN32) version of unix_open().
+extern int unix_open(const char* path, int options, ...);
 #define  open    ___xxx_unix_open
 
 
 /* normally provided by <cutils/misc.h> */
 extern void*  load_file(const char*  pathname, unsigned*  psize);
 
-/* normally provided by <cutils/sockets.h> */
-extern int socket_loopback_client(int port, int type);
-extern int socket_network_client(const char *host, int port, int type);
-extern int socket_network_client_timeout(const char *host, int port, int type,
-                                         int timeout);
-extern int socket_loopback_server(int port, int type);
-extern int socket_inaddr_any_server(int port, int type);
-
 /* normally provided by "fdevent.h" */
 
 #define FDE_READ              0x0001
@@ -216,6 +208,12 @@
     Sleep( mseconds );
 }
 
+int network_loopback_client(int port, int type, std::string* error);
+int network_loopback_server(int port, int type, std::string* error);
+int network_inaddr_any_server(int port, int type, std::string* error);
+int network_connect(const std::string& host, int port, int type, int timeout,
+                    std::string* error);
+
 extern int  adb_socket_accept(int  serverfd, struct sockaddr*  addr, socklen_t  *addrlen);
 
 #undef   accept
@@ -240,42 +238,152 @@
 
 extern int  adb_socketpair( int  sv[2] );
 
-static __inline__  char*  adb_dirstart( const char*  path )
-{
-    char*  p  = strchr(path, '/');
-    char*  p2 = strchr(path, '\\');
-
-    if ( !p )
-        p = p2;
-    else if ( p2 && p2 > p )
-        p = p2;
-
-    return p;
-}
-
-static __inline__  char*  adb_dirstop( const char*  path )
-{
-    char*  p  = strrchr(path, '/');
-    char*  p2 = strrchr(path, '\\');
-
-    if ( !p )
-        p = p2;
-    else if ( p2 && p2 > p )
-        p = p2;
-
-    return p;
-}
-
-static __inline__  int  adb_is_absolute_host_path( const char*  path )
-{
+static __inline__ int adb_is_absolute_host_path(const char* path) {
     return isalpha(path[0]) && path[1] == ':' && path[2] == '\\';
 }
 
+// Like strerror(), but for Win32 error codes.
+std::string SystemErrorCodeToString(DWORD error_code);
+
+// We later define a macro mapping 'stat' to 'adb_stat'. This causes:
+//   struct stat s;
+//   stat(filename, &s);
+// To turn into the following:
+//   struct adb_stat s;
+//   adb_stat(filename, &s);
+// To get this to work, we need to make 'struct adb_stat' the same as
+// 'struct stat'. Note that this definition of 'struct adb_stat' uses the
+// *current* macro definition of stat, so it may actually be inheriting from
+// struct _stat32i64 (or some other remapping).
+struct adb_stat : public stat {};
+
+static_assert(sizeof(struct adb_stat) == sizeof(struct stat),
+    "structures should be the same");
+
+extern int adb_stat(const char* f, struct adb_stat* s);
+
+// stat is already a macro, undefine it so we can redefine it.
+#undef stat
+#define stat adb_stat
+
+// UTF-8 versions of POSIX APIs.
+extern DIR* adb_opendir(const char* dirname);
+extern struct dirent* adb_readdir(DIR* dir);
+extern int adb_closedir(DIR* dir);
+
+extern int adb_utime(const char *, struct utimbuf *);
+extern int adb_chmod(const char *, int);
+
+extern int adb_vfprintf(FILE *stream, const char *format, va_list ap)
+    __attribute__((__format__(ADB_FORMAT_ARCHETYPE, 2, 0)));
+extern int adb_fprintf(FILE *stream, const char *format, ...)
+    __attribute__((__format__(ADB_FORMAT_ARCHETYPE, 2, 3)));
+extern int adb_printf(const char *format, ...)
+    __attribute__((__format__(ADB_FORMAT_ARCHETYPE, 1, 2)));
+
+extern int adb_fputs(const char* buf, FILE* stream);
+extern int adb_fputc(int ch, FILE* stream);
+extern size_t adb_fwrite(const void* ptr, size_t size, size_t nmemb,
+                         FILE* stream);
+
+extern FILE* adb_fopen(const char* f, const char* m);
+
+extern char* adb_getenv(const char* name);
+
+extern char* adb_getcwd(char* buf, int size);
+
+// Remap calls to POSIX APIs to our UTF-8 versions.
+#define opendir adb_opendir
+#define readdir adb_readdir
+#define closedir adb_closedir
+#define rewinddir rewinddir_utf8_not_yet_implemented
+#define telldir telldir_utf8_not_yet_implemented
+#define seekdir seekdir_utf8_not_yet_implemented
+
+#define utime adb_utime
+#define chmod adb_chmod
+
+#define vfprintf adb_vfprintf
+#define fprintf adb_fprintf
+#define printf adb_printf
+#define fputs adb_fputs
+#define fputc adb_fputc
+#define fwrite adb_fwrite
+
+#define fopen adb_fopen
+
+#define getenv adb_getenv
+#define putenv putenv_utf8_not_yet_implemented
+#define setenv setenv_utf8_not_yet_implemented
+#define unsetenv unsetenv_utf8_not_yet_implemented
+
+#define getcwd adb_getcwd
+
+// Convert from UTF-8 to UTF-16, typically used to convert char strings into
+// wchar_t strings that can be passed to wchar_t-based OS and C Runtime APIs
+// on Windows.
+extern std::wstring widen(const std::string& utf8);
+extern std::wstring widen(const char* utf8);
+
+// Convert from UTF-16 to UTF-8, typically used to convert strings from OS and
+// C Runtime APIs that return wchar_t, to a format for our char-based data
+// structures.
+extern std::string narrow(const std::wstring& utf16);
+extern std::string narrow(const wchar_t* utf16);
+
+// Helper class to convert UTF-16 argv from wmain() to UTF-8 args that can be
+// passed to main().
+class NarrowArgs {
+public:
+    NarrowArgs(int argc, wchar_t** argv);
+    ~NarrowArgs();
+
+    inline char** data() {
+        return narrow_args;
+    }
+
+private:
+    char** narrow_args;
+};
+
+// Windows HANDLE values only use 32-bits of the type, even on 64-bit machines,
+// so they can fit in an int. To convert back, we just need to sign-extend.
+// https://msdn.microsoft.com/en-us/library/windows/desktop/aa384203%28v=vs.85%29.aspx
+// Note that this does not make a HANDLE value work with APIs like open(), nor
+// does this make a value from open() passable to APIs taking a HANDLE. This
+// just lets you take a HANDLE, pass it around as an int, and then use it again
+// as a HANDLE.
+inline int cast_handle_to_int(const HANDLE h) {
+    // truncate
+    return static_cast<int>(reinterpret_cast<INT_PTR>(h));
+}
+
+inline HANDLE cast_int_to_handle(const int fd) {
+    // sign-extend
+    return reinterpret_cast<HANDLE>(static_cast<INT_PTR>(fd));
+}
+
+// Deleter for unique_handle. Adapted from many sources, including:
+// http://stackoverflow.com/questions/14841396/stdunique-ptr-deleters-and-the-win32-api
+// https://visualstudiomagazine.com/articles/2013/09/01/get-a-handle-on-the-windows-api.aspx
+class handle_deleter {
+public:
+    typedef HANDLE pointer;
+
+    void operator()(HANDLE h);
+};
+
+// Like std::unique_ptr, but for Windows HANDLE objects that should be
+// CloseHandle()'d. Operator bool() only checks if the handle != nullptr,
+// but does not check if the handle != INVALID_HANDLE_VALUE.
+typedef std::unique_ptr<HANDLE, handle_deleter> unique_handle;
+
 #else /* !_WIN32 a.k.a. Unix */
 
 #include "fdevent.h"
-#include <cutils/sockets.h>
 #include <cutils/misc.h>
+#include <cutils/sockets.h>
+#include <cutils/threads.h>
 #include <signal.h>
 #include <sys/wait.h>
 #include <sys/stat.h>
@@ -285,11 +393,15 @@
 #include <unistd.h>
 #include <fcntl.h>
 #include <stdarg.h>
+#include <netdb.h>
 #include <netinet/in.h>
 #include <netinet/tcp.h>
 #include <string.h>
 #include <unistd.h>
 
+#include <string>
+
+#define OS_PATH_SEPARATORS "/"
 #define OS_PATH_SEPARATOR '/'
 #define OS_PATH_SEPARATOR_STR "/"
 #define ENV_PATH_SEPARATOR_STR ":"
@@ -320,6 +432,15 @@
     fcntl( fd, F_SETFD, FD_CLOEXEC );
 }
 
+// Open a file and return a file descriptor that may be used with unix_read(),
+// unix_write(), unix_close(), but not adb_read(), adb_write(), adb_close().
+//
+// On Unix, this is based on open(), so the file descriptor is a real OS file
+// descriptor, but the Windows implementation (in sysdeps_win32.cpp) returns a
+// file descriptor that can only be used with C Runtime APIs (which are wrapped
+// by unix_read(), unix_write(), unix_close()). Also, the C Runtime has
+// configurable CR/LF translation which defaults to text mode, but is settable
+// with _setmode().
 static __inline__ int  unix_open(const char*  path, int options,...)
 {
     if ((options & O_CREAT) == 0)
@@ -337,12 +458,21 @@
     }
 }
 
+// Similar to the two-argument adb_open(), but takes a mode parameter for file
+// creation. See adb_open() for more info.
 static __inline__ int  adb_open_mode( const char*  pathname, int  options, int  mode )
 {
     return TEMP_FAILURE_RETRY( open( pathname, options, mode ) );
 }
 
 
+// Open a file and return a file descriptor that may be used with adb_read(),
+// adb_write(), adb_close(), but not unix_read(), unix_write(), unix_close().
+//
+// On Unix, this is based on open(), but the Windows implementation (in
+// sysdeps_win32.cpp) uses Windows native file I/O and bypasses the C Runtime
+// and its CR/LF translation. The returned file descriptor should be used with
+// adb_read(), adb_write(), adb_close(), etc.
 static __inline__ int  adb_open( const char*  pathname, int  options )
 {
     int  fd = TEMP_FAILURE_RETRY( open( pathname, options ) );
@@ -361,6 +491,9 @@
 #undef   shutdown
 #define  shutdown   ____xxx_shutdown
 
+// Closes a file descriptor that came from adb_open() or adb_open_mode(), but
+// not designed to take a file descriptor from unix_open(). See the comments
+// for adb_open() for more info.
 static __inline__ int  adb_close(int fd)
 {
     return close(fd);
@@ -411,6 +544,48 @@
 #undef   creat
 #define  creat  ___xxx_creat
 
+// Helper for network_* functions.
+inline int _fd_set_error_str(int fd, std::string* error) {
+  if (fd == -1) {
+    *error = strerror(errno);
+  }
+  return fd;
+}
+
+inline int network_loopback_client(int port, int type, std::string* error) {
+  return _fd_set_error_str(socket_loopback_client(port, type), error);
+}
+
+inline int network_loopback_server(int port, int type, std::string* error) {
+  return _fd_set_error_str(socket_loopback_server(port, type), error);
+}
+
+inline int network_inaddr_any_server(int port, int type, std::string* error) {
+  return _fd_set_error_str(socket_inaddr_any_server(port, type), error);
+}
+
+inline int network_local_server(const char *name, int namespace_id, int type,
+                                std::string* error) {
+  return _fd_set_error_str(socket_local_server(name, namespace_id, type),
+                           error);
+}
+
+inline int network_connect(const std::string& host, int port, int type,
+                           int timeout, std::string* error) {
+  int getaddrinfo_error = 0;
+  int fd = socket_network_client_timeout(host.c_str(), port, type, timeout,
+                                         &getaddrinfo_error);
+  if (fd != -1) {
+    return fd;
+  }
+  if (getaddrinfo_error != 0) {
+    *error = gai_strerror(getaddrinfo_error);
+  } else {
+    *error = strerror(errno);
+  }
+  return -1;
+}
+
 static __inline__ int  adb_socket_accept(int  serverfd, struct sockaddr*  addr, socklen_t  *addrlen)
 {
     int fd;
@@ -425,25 +600,50 @@
 #undef   accept
 #define  accept  ___xxx_accept
 
+// Operate on a file descriptor returned from unix_open() or a well-known file
+// descriptor such as STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO.
+//
+// On Unix, unix_read(), unix_write(), unix_close() map to adb_read(),
+// adb_write(), adb_close() (which all map to Unix system calls), but the
+// Windows implementations (in the ifdef above and in sysdeps_win32.cpp) call
+// into the C Runtime and its configurable CR/LF translation (which is settable
+// via _setmode()).
 #define  unix_read   adb_read
 #define  unix_write  adb_write
 #define  unix_close  adb_close
 
-typedef  pthread_t                 adb_thread_t;
-
 typedef void*  (*adb_thread_func_t)( void*  arg );
 
-static __inline__ int  adb_thread_create( adb_thread_t  *pthread, adb_thread_func_t  start, void*  arg )
-{
-    pthread_attr_t   attr;
+static __inline__ bool adb_thread_create(adb_thread_func_t start, void* arg) {
+    pthread_attr_t attr;
+    pthread_attr_init(&attr);
+    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
 
-    pthread_attr_init (&attr);
-    pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
-
-    return pthread_create( pthread, &attr, start, arg );
+    pthread_t thread;
+    errno = pthread_create(&thread, &attr, start, arg);
+    return (errno == 0);
 }
 
-static __inline__  int  adb_socket_setbufsize( int   fd, int  bufsize )
+static __inline__ int adb_thread_setname(const std::string& name) {
+#ifdef __APPLE__
+    return pthread_setname_np(name.c_str());
+#else
+    const char *s = name.c_str();
+
+    // pthread_setname_np fails rather than truncating long strings.
+    const int max_task_comm_len = 16; // including the null terminator
+    if (name.length() > (max_task_comm_len - 1)) {
+        char buf[max_task_comm_len];
+        strncpy(buf, name.c_str(), sizeof(buf) - 1);
+        buf[sizeof(buf) - 1] = '\0';
+        s = buf;
+    }
+
+    return pthread_setname_np(pthread_self(), s) ;
+#endif
+}
+
+static __inline__  int  adb_socket_setbufsize(int fd, int  bufsize )
 {
     int opt = bufsize;
     return setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt));
@@ -489,10 +689,11 @@
     usleep( mseconds*1000 );
 }
 
-static __inline__ int  adb_mkdir(const char*  path, int mode)
+static __inline__ int  adb_mkdir(const std::string& path, int mode)
 {
-    return mkdir(path, mode);
+    return mkdir(path.c_str(), mode);
 }
+
 #undef   mkdir
 #define  mkdir  ___xxx_mkdir
 
@@ -500,24 +701,13 @@
 {
 }
 
-static __inline__ char*  adb_dirstart(const char*  path)
-{
-    return strchr(path, '/');
-}
-
-static __inline__ char*  adb_dirstop(const char*  path)
-{
-    return strrchr(path, '/');
-}
-
-static __inline__  int  adb_is_absolute_host_path( const char*  path )
-{
+static __inline__ int adb_is_absolute_host_path(const char* path) {
     return path[0] == '/';
 }
 
 static __inline__ unsigned long adb_thread_id()
 {
-    return (unsigned long)pthread_self();
+    return (unsigned long)gettid();
 }
 
 #endif /* !_WIN32 */
diff --git a/adb/sysdeps_win32.cpp b/adb/sysdeps_win32.cpp
index a21272f..015f89e 100644
--- a/adb/sysdeps_win32.cpp
+++ b/adb/sysdeps_win32.cpp
@@ -25,6 +25,16 @@
 #include <stdio.h>
 #include <stdlib.h>
 
+#include <memory>
+#include <string>
+#include <unordered_map>
+
+#include <cutils/sockets.h>
+
+#include <base/logging.h>
+#include <base/stringprintf.h>
+#include <base/strings.h>
+
 #include "adb.h"
 
 extern void fatal(const char *fmt, ...);
@@ -78,6 +88,46 @@
 
 #define assert(cond)  do { if (!(cond)) fatal( "assertion failed '%s' on %s:%ld\n", #cond, __FILE__, __LINE__ ); } while (0)
 
+std::string SystemErrorCodeToString(const DWORD error_code) {
+  const int kErrorMessageBufferSize = 256;
+  WCHAR msgbuf[kErrorMessageBufferSize];
+  DWORD flags = FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS;
+  DWORD len = FormatMessageW(flags, nullptr, error_code, 0, msgbuf,
+                             arraysize(msgbuf), nullptr);
+  if (len == 0) {
+    return android::base::StringPrintf(
+        "Error (%lu) while retrieving error. (%lu)", GetLastError(),
+        error_code);
+  }
+
+  // Convert UTF-16 to UTF-8.
+  std::string msg(narrow(msgbuf));
+  // Messages returned by the system end with line breaks.
+  msg = android::base::Trim(msg);
+  // There are many Windows error messages compared to POSIX, so include the
+  // numeric error code for easier, quicker, accurate identification. Use
+  // decimal instead of hex because there are decimal ranges like 10000-11999
+  // for Winsock.
+  android::base::StringAppendF(&msg, " (%lu)", error_code);
+  return msg;
+}
+
+void handle_deleter::operator()(HANDLE h) {
+    // CreateFile() is documented to return INVALID_HANDLE_FILE on error,
+    // implying that NULL is a valid handle, but this is probably impossible.
+    // Other APIs like CreateEvent() are documented to return NULL on error,
+    // implying that INVALID_HANDLE_VALUE is a valid handle, but this is also
+    // probably impossible. Thus, consider both NULL and INVALID_HANDLE_VALUE
+    // as invalid handles. std::unique_ptr won't call a deleter with NULL, so we
+    // only need to check for INVALID_HANDLE_VALUE.
+    if (h != INVALID_HANDLE_VALUE) {
+        if (!CloseHandle(h)) {
+            D("CloseHandle(%p) failed: %s\n", h,
+              SystemErrorCodeToString(GetLastError()).c_str());
+        }
+    }
+}
+
 /**************************************************************************/
 /**************************************************************************/
 /*****                                                                *****/
@@ -92,13 +142,13 @@
     char     *data;
     DWORD     file_size;
 
-    file = CreateFile( fn,
-                       GENERIC_READ,
-                       FILE_SHARE_READ,
-                       NULL,
-                       OPEN_EXISTING,
-                       0,
-                       NULL );
+    file = CreateFileW( widen(fn).c_str(),
+                        GENERIC_READ,
+                        FILE_SHARE_READ,
+                        NULL,
+                        OPEN_EXISTING,
+                        0,
+                        NULL );
 
     if (file == INVALID_HANDLE_VALUE)
         return NULL;
@@ -169,17 +219,18 @@
 
 static adb_mutex_t   _win32_lock;
 static  FHRec        _win32_fhs[ WIN32_MAX_FHS ];
-static  int          _win32_fh_count;
+static  int          _win32_fh_next;  // where to start search for free FHRec
 
 static FH
-_fh_from_int( int   fd )
+_fh_from_int( int   fd, const char*   func )
 {
     FH  f;
 
     fd -= WIN32_FH_BASE;
 
-    if (fd < 0 || fd >= _win32_fh_count) {
-        D( "_fh_from_int: invalid fd %d\n", fd + WIN32_FH_BASE );
+    if (fd < 0 || fd >= WIN32_MAX_FHS) {
+        D( "_fh_from_int: invalid fd %d passed to %s\n", fd + WIN32_FH_BASE,
+           func );
         errno = EBADF;
         return NULL;
     }
@@ -187,7 +238,8 @@
     f = &_win32_fhs[fd];
 
     if (f->used == 0) {
-        D( "_fh_from_int: invalid fd %d\n", fd + WIN32_FH_BASE );
+        D( "_fh_from_int: invalid fd %d passed to %s\n", fd + WIN32_FH_BASE,
+           func );
         errno = EBADF;
         return NULL;
     }
@@ -208,28 +260,32 @@
 static FH
 _fh_alloc( FHClass  clazz )
 {
-    int  nn;
     FH   f = NULL;
 
     adb_mutex_lock( &_win32_lock );
 
-    if (_win32_fh_count < WIN32_MAX_FHS) {
-        f = &_win32_fhs[ _win32_fh_count++ ];
-        goto Exit;
-    }
-
-    for (nn = 0; nn < WIN32_MAX_FHS; nn++) {
-        if ( _win32_fhs[nn].clazz == NULL) {
-            f = &_win32_fhs[nn];
+    // Search entire array, starting from _win32_fh_next.
+    for (int nn = 0; nn < WIN32_MAX_FHS; nn++) {
+        // Keep incrementing _win32_fh_next to avoid giving out an index that
+        // was recently closed, to try to avoid use-after-free.
+        const int index = _win32_fh_next++;
+        // Handle wrap-around of _win32_fh_next.
+        if (_win32_fh_next == WIN32_MAX_FHS) {
+            _win32_fh_next = 0;
+        }
+        if (_win32_fhs[index].clazz == NULL) {
+            f = &_win32_fhs[index];
             goto Exit;
         }
     }
     D( "_fh_alloc: no more free file descriptors\n" );
+    errno = EMFILE;   // Too many open files
 Exit:
     if (f) {
-        f->clazz = clazz;
-        f->used  = 1;
-        f->eof   = 0;
+        f->clazz   = clazz;
+        f->used    = 1;
+        f->eof     = 0;
+        f->name[0] = '\0';
         clazz->_fh_init(f);
     }
     adb_mutex_unlock( &_win32_lock );
@@ -240,15 +296,37 @@
 static int
 _fh_close( FH   f )
 {
-    if ( f->used ) {
+    // Use lock so that closing only happens once and so that _fh_alloc can't
+    // allocate a FH that we're in the middle of closing.
+    adb_mutex_lock(&_win32_lock);
+    if (f->used) {
         f->clazz->_fh_close( f );
-        f->used = 0;
-        f->eof  = 0;
-        f->clazz = NULL;
+        f->name[0] = '\0';
+        f->eof     = 0;
+        f->used    = 0;
+        f->clazz   = NULL;
     }
+    adb_mutex_unlock(&_win32_lock);
     return 0;
 }
 
+// Deleter for unique_fh.
+class fh_deleter {
+ public:
+  void operator()(struct FHRec_* fh) {
+    // We're called from a destructor and destructors should not overwrite
+    // errno because callers may do:
+    //   errno = EBLAH;
+    //   return -1; // calls destructor, which should not overwrite errno
+    const int saved_errno = errno;
+    _fh_close(fh);
+    errno = saved_errno;
+  }
+};
+
+// Like std::unique_ptr, but calls _fh_close() instead of operator delete().
+typedef std::unique_ptr<struct FHRec_, fh_deleter> unique_fh;
+
 /**************************************************************************/
 /**************************************************************************/
 /*****                                                                *****/
@@ -351,17 +429,17 @@
 
     f = _fh_alloc( &_fh_file_class );
     if ( !f ) {
-        errno = ENOMEM;
         return -1;
     }
 
-    f->fh_handle = CreateFile( path, desiredAccess, shareMode, NULL, OPEN_EXISTING,
-                               0, NULL );
+    f->fh_handle = CreateFileW( widen(path).c_str(), desiredAccess, shareMode,
+                                NULL, OPEN_EXISTING, 0, NULL );
 
     if ( f->fh_handle == INVALID_HANDLE_VALUE ) {
+        const DWORD err = GetLastError();
         _fh_close(f);
-        D( "adb_open: could not open '%s':", path );
-        switch (GetLastError()) {
+        D( "adb_open: could not open '%s': ", path );
+        switch (err) {
             case ERROR_FILE_NOT_FOUND:
                 D( "file not found\n" );
                 errno = ENOENT;
@@ -373,7 +451,8 @@
                 return -1;
 
             default:
-                D( "unknown error\n" );
+                D( "unknown error: %s\n",
+                   SystemErrorCodeToString( err ).c_str() );
                 errno = ENOENT;
                 return -1;
         }
@@ -391,18 +470,19 @@
 
     f = _fh_alloc( &_fh_file_class );
     if ( !f ) {
-        errno = ENOMEM;
         return -1;
     }
 
-    f->fh_handle = CreateFile( path, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
-                               NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,
-                               NULL );
+    f->fh_handle = CreateFileW( widen(path).c_str(), GENERIC_WRITE,
+                                FILE_SHARE_READ | FILE_SHARE_WRITE,
+                                NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,
+                                NULL );
 
     if ( f->fh_handle == INVALID_HANDLE_VALUE ) {
+        const DWORD err = GetLastError();
         _fh_close(f);
-        D( "adb_creat: could not open '%s':", path );
-        switch (GetLastError()) {
+        D( "adb_creat: could not open '%s': ", path );
+        switch (err) {
             case ERROR_FILE_NOT_FOUND:
                 D( "file not found\n" );
                 errno = ENOENT;
@@ -414,7 +494,8 @@
                 return -1;
 
             default:
-                D( "unknown error\n" );
+                D( "unknown error: %s\n",
+                   SystemErrorCodeToString( err ).c_str() );
                 errno = ENOENT;
                 return -1;
         }
@@ -427,7 +508,7 @@
 
 int  adb_read(int  fd, void* buf, int len)
 {
-    FH     f = _fh_from_int(fd);
+    FH     f = _fh_from_int(fd, __func__);
 
     if (f == NULL) {
         return -1;
@@ -439,7 +520,7 @@
 
 int  adb_write(int  fd, const void*  buf, int  len)
 {
-    FH     f = _fh_from_int(fd);
+    FH     f = _fh_from_int(fd, __func__);
 
     if (f == NULL) {
         return -1;
@@ -451,7 +532,7 @@
 
 int  adb_lseek(int  fd, int  pos, int  where)
 {
-    FH     f = _fh_from_int(fd);
+    FH     f = _fh_from_int(fd, __func__);
 
     if (!f) {
         return -1;
@@ -461,24 +542,9 @@
 }
 
 
-int  adb_shutdown(int  fd)
-{
-    FH   f = _fh_from_int(fd);
-
-    if (!f || f->clazz != &_fh_socket_class) {
-        D("adb_shutdown: invalid fd %d\n", fd);
-        return -1;
-    }
-
-    D( "adb_shutdown: %s\n", f->name);
-    shutdown( f->fh_socket, SD_BOTH );
-    return 0;
-}
-
-
 int  adb_close(int  fd)
 {
-    FH   f = _fh_from_int(fd);
+    FH   f = _fh_from_int(fd, __func__);
 
     if (!f) {
         return -1;
@@ -499,29 +565,66 @@
 
 #undef setsockopt
 
-static void _socket_set_errno( void ) {
-    switch (WSAGetLastError()) {
+static void _socket_set_errno( const DWORD err ) {
+    // The Windows C Runtime (MSVCRT.DLL) strerror() does not support a lot of
+    // POSIX and socket error codes, so this can only meaningfully map so much.
+    switch ( err ) {
     case 0:              errno = 0; break;
+    // Mapping WSAEWOULDBLOCK to EAGAIN is absolutely critical because
+    // non-blocking sockets can cause an error code of WSAEWOULDBLOCK and
+    // callers check specifically for EAGAIN.
     case WSAEWOULDBLOCK: errno = EAGAIN; break;
     case WSAEINTR:       errno = EINTR; break;
+    case WSAEFAULT:      errno = EFAULT; break;
+    case WSAEINVAL:      errno = EINVAL; break;
+    case WSAEMFILE:      errno = EMFILE; break;
     default:
-        D( "_socket_set_errno: unhandled value %d\n", WSAGetLastError() );
         errno = EINVAL;
+        D( "_socket_set_errno: mapping Windows error code %lu to errno %d\n",
+           err, errno );
     }
 }
 
 static void _fh_socket_init( FH  f ) {
     f->fh_socket = INVALID_SOCKET;
     f->event     = WSACreateEvent();
+    if (f->event == WSA_INVALID_EVENT) {
+        D("WSACreateEvent failed: %s\n",
+          SystemErrorCodeToString(WSAGetLastError()).c_str());
+
+        // _event_socket_start assumes that this field is INVALID_HANDLE_VALUE
+        // on failure, instead of NULL which is what Windows really returns on
+        // error. It might be better to change all the other code to look for
+        // NULL, but that is a much riskier change.
+        f->event = INVALID_HANDLE_VALUE;
+    }
     f->mask      = 0;
 }
 
 static int _fh_socket_close( FH  f ) {
-    /* gently tell any peer that we're closing the socket */
-    shutdown( f->fh_socket, SD_BOTH );
-    closesocket( f->fh_socket );
-    f->fh_socket = INVALID_SOCKET;
-    CloseHandle( f->event );
+    if (f->fh_socket != INVALID_SOCKET) {
+        /* gently tell any peer that we're closing the socket */
+        if (shutdown(f->fh_socket, SD_BOTH) == SOCKET_ERROR) {
+            // If the socket is not connected, this returns an error. We want to
+            // minimize logging spam, so don't log these errors for now.
+#if 0
+            D("socket shutdown failed: %s\n",
+              SystemErrorCodeToString(WSAGetLastError()).c_str());
+#endif
+        }
+        if (closesocket(f->fh_socket) == SOCKET_ERROR) {
+            D("closesocket failed: %s\n",
+              SystemErrorCodeToString(WSAGetLastError()).c_str());
+        }
+        f->fh_socket = INVALID_SOCKET;
+    }
+    if (f->event != NULL) {
+        if (!CloseHandle(f->event)) {
+            D("CloseHandle failed: %s\n",
+              SystemErrorCodeToString(GetLastError()).c_str());
+        }
+        f->event = NULL;
+    }
     f->mask = 0;
     return 0;
 }
@@ -534,7 +637,14 @@
 static int _fh_socket_read(FH f, void* buf, int len) {
     int  result = recv(f->fh_socket, reinterpret_cast<char*>(buf), len, 0);
     if (result == SOCKET_ERROR) {
-        _socket_set_errno();
+        const DWORD err = WSAGetLastError();
+        // WSAEWOULDBLOCK is normal with a non-blocking socket, so don't trace
+        // that to reduce spam and confusion.
+        if (err != WSAEWOULDBLOCK) {
+            D("recv fd %d failed: %s\n", _fh_to_int(f),
+              SystemErrorCodeToString(err).c_str());
+        }
+        _socket_set_errno(err);
         result = -1;
     }
     return  result;
@@ -543,7 +653,10 @@
 static int _fh_socket_write(FH f, const void* buf, int len) {
     int  result = send(f->fh_socket, reinterpret_cast<const char*>(buf), len, 0);
     if (result == SOCKET_ERROR) {
-        _socket_set_errno();
+        const DWORD err = WSAGetLastError();
+        D("send fd %d failed: %s\n", _fh_to_int(f),
+          SystemErrorCodeToString(err).c_str());
+        _socket_set_errno(err);
         result = -1;
     }
     return result;
@@ -562,33 +675,44 @@
 static int  _winsock_init;
 
 static void
-_cleanup_winsock( void )
-{
-    WSACleanup();
-}
-
-static void
 _init_winsock( void )
 {
+    // TODO: Multiple threads calling this may potentially cause multiple calls
+    // to WSAStartup() which offers no real benefit.
     if (!_winsock_init) {
         WSADATA  wsaData;
         int      rc = WSAStartup( MAKEWORD(2,2), &wsaData);
         if (rc != 0) {
-            fatal( "adb: could not initialize Winsock\n" );
+            fatal( "adb: could not initialize Winsock: %s",
+                   SystemErrorCodeToString( rc ).c_str());
         }
-        atexit( _cleanup_winsock );
         _winsock_init = 1;
+
+        // Note that we do not call atexit() to register WSACleanup to be called
+        // at normal process termination because:
+        // 1) When exit() is called, there are still threads actively using
+        //    Winsock because we don't cleanly shutdown all threads, so it
+        //    doesn't make sense to call WSACleanup() and may cause problems
+        //    with those threads.
+        // 2) A deadlock can occur when exit() holds a C Runtime lock, then it
+        //    calls WSACleanup() which tries to unload a DLL, which tries to
+        //    grab the LoaderLock. This conflicts with the device_poll_thread
+        //    which holds the LoaderLock because AdbWinApi.dll calls
+        //    setupapi.dll which tries to load wintrust.dll which tries to load
+        //    crypt32.dll which calls atexit() which tries to acquire the C
+        //    Runtime lock that the other thread holds.
     }
 }
 
-int socket_loopback_client(int port, int type)
-{
-    FH  f = _fh_alloc( &_fh_socket_class );
+int network_loopback_client(int port, int type, std::string* error) {
     struct sockaddr_in addr;
     SOCKET  s;
 
-    if (!f)
+    unique_fh  f(_fh_alloc(&_fh_socket_class));
+    if (!f) {
+        *error = strerror(errno);
         return -1;
+    }
 
     if (!_winsock_init)
         _init_winsock();
@@ -600,32 +724,45 @@
 
     s = socket(AF_INET, type, 0);
     if(s == INVALID_SOCKET) {
-        D("socket_loopback_client: could not create socket\n" );
-        _fh_close(f);
+        *error = android::base::StringPrintf("cannot create socket: %s",
+                SystemErrorCodeToString(WSAGetLastError()).c_str());
+        D("%s\n", error->c_str());
+        return -1;
+    }
+    f->fh_socket = s;
+
+    if(connect(s, (struct sockaddr *) &addr, sizeof(addr)) == SOCKET_ERROR) {
+        // Save err just in case inet_ntoa() or ntohs() changes the last error.
+        const DWORD err = WSAGetLastError();
+        *error = android::base::StringPrintf("cannot connect to %s:%u: %s",
+                inet_ntoa(addr.sin_addr), ntohs(addr.sin_port),
+                SystemErrorCodeToString(err).c_str());
+        D("could not connect to %s:%d: %s\n",
+          type != SOCK_STREAM ? "udp" : "tcp", port, error->c_str());
         return -1;
     }
 
-    f->fh_socket = s;
-    if(connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
-        D("socket_loopback_client: could not connect to %s:%d\n", type != SOCK_STREAM ? "udp" : "tcp", port );
-        _fh_close(f);
-        return -1;
-    }
-    snprintf( f->name, sizeof(f->name), "%d(lo-client:%s%d)", _fh_to_int(f), type != SOCK_STREAM ? "udp:" : "", port );
-    D( "socket_loopback_client: port %d type %s => fd %d\n", port, type != SOCK_STREAM ? "udp" : "tcp", _fh_to_int(f) );
-    return _fh_to_int(f);
+    const int fd = _fh_to_int(f.get());
+    snprintf( f->name, sizeof(f->name), "%d(lo-client:%s%d)", fd,
+              type != SOCK_STREAM ? "udp:" : "", port );
+    D( "port %d type %s => fd %d\n", port, type != SOCK_STREAM ? "udp" : "tcp",
+       fd );
+    f.release();
+    return fd;
 }
 
 #define LISTEN_BACKLOG 4
 
-int socket_loopback_server(int port, int type)
-{
-    FH   f = _fh_alloc( &_fh_socket_class );
+// interface_address is INADDR_LOOPBACK or INADDR_ANY.
+static int _network_server(int port, int type, u_long interface_address,
+                           std::string* error) {
     struct sockaddr_in addr;
     SOCKET  s;
     int  n;
 
+    unique_fh   f(_fh_alloc(&_fh_socket_class));
     if (!f) {
+        *error = strerror(errno);
         return -1;
     }
 
@@ -635,171 +772,218 @@
     memset(&addr, 0, sizeof(addr));
     addr.sin_family = AF_INET;
     addr.sin_port = htons(port);
-    addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+    addr.sin_addr.s_addr = htonl(interface_address);
 
+    // TODO: Consider using dual-stack socket that can simultaneously listen on
+    // IPv4 and IPv6.
     s = socket(AF_INET, type, 0);
-    if(s == INVALID_SOCKET) return -1;
+    if (s == INVALID_SOCKET) {
+        *error = android::base::StringPrintf("cannot create socket: %s",
+                SystemErrorCodeToString(WSAGetLastError()).c_str());
+        D("%s\n", error->c_str());
+        return -1;
+    }
 
     f->fh_socket = s;
 
+    // Note: SO_REUSEADDR on Windows allows multiple processes to bind to the
+    // same port, so instead use SO_EXCLUSIVEADDRUSE.
     n = 1;
-    setsockopt(s, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (const char*)&n, sizeof(n));
+    if (setsockopt(s, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (const char*)&n,
+                   sizeof(n)) == SOCKET_ERROR) {
+        *error = android::base::StringPrintf(
+                "cannot set socket option SO_EXCLUSIVEADDRUSE: %s",
+                SystemErrorCodeToString(WSAGetLastError()).c_str());
+        D("%s\n", error->c_str());
+        return -1;
+    }
 
-    if(bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
-        _fh_close(f);
+    if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) == SOCKET_ERROR) {
+        // Save err just in case inet_ntoa() or ntohs() changes the last error.
+        const DWORD err = WSAGetLastError();
+        *error = android::base::StringPrintf("cannot bind to %s:%u: %s",
+                inet_ntoa(addr.sin_addr), ntohs(addr.sin_port),
+                SystemErrorCodeToString(err).c_str());
+        D("could not bind to %s:%d: %s\n",
+          type != SOCK_STREAM ? "udp" : "tcp", port, error->c_str());
         return -1;
     }
     if (type == SOCK_STREAM) {
-        int ret;
-
-        ret = listen(s, LISTEN_BACKLOG);
-        if (ret < 0) {
-            _fh_close(f);
+        if (listen(s, LISTEN_BACKLOG) == SOCKET_ERROR) {
+            *error = android::base::StringPrintf("cannot listen on socket: %s",
+                    SystemErrorCodeToString(WSAGetLastError()).c_str());
+            D("could not listen on %s:%d: %s\n",
+              type != SOCK_STREAM ? "udp" : "tcp", port, error->c_str());
             return -1;
         }
     }
-    snprintf( f->name, sizeof(f->name), "%d(lo-server:%s%d)", _fh_to_int(f), type != SOCK_STREAM ? "udp:" : "", port );
-    D( "socket_loopback_server: port %d type %s => fd %d\n", port, type != SOCK_STREAM ? "udp" : "tcp", _fh_to_int(f) );
-    return _fh_to_int(f);
+    const int fd = _fh_to_int(f.get());
+    snprintf( f->name, sizeof(f->name), "%d(%s-server:%s%d)", fd,
+              interface_address == INADDR_LOOPBACK ? "lo" : "any",
+              type != SOCK_STREAM ? "udp:" : "", port );
+    D( "port %d type %s => fd %d\n", port, type != SOCK_STREAM ? "udp" : "tcp",
+       fd );
+    f.release();
+    return fd;
 }
 
+int network_loopback_server(int port, int type, std::string* error) {
+    return _network_server(port, type, INADDR_LOOPBACK, error);
+}
 
-int socket_network_client(const char *host, int port, int type)
-{
-    FH  f = _fh_alloc( &_fh_socket_class );
-    struct hostent *hp;
-    struct sockaddr_in addr;
-    SOCKET s;
+int network_inaddr_any_server(int port, int type, std::string* error) {
+    return _network_server(port, type, INADDR_ANY, error);
+}
 
-    if (!f)
-        return -1;
-
-    if (!_winsock_init)
-        _init_winsock();
-
-    hp = gethostbyname(host);
-    if(hp == 0) {
-        _fh_close(f);
+int network_connect(const std::string& host, int port, int type, int timeout, std::string* error) {
+    unique_fh f(_fh_alloc(&_fh_socket_class));
+    if (!f) {
+        *error = strerror(errno);
         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);
+    if (!_winsock_init) _init_winsock();
 
-    s = socket(hp->h_addrtype, type, 0);
+    struct addrinfo hints;
+    memset(&hints, 0, sizeof(hints));
+    hints.ai_family = AF_UNSPEC;
+    hints.ai_socktype = type;
+
+    char port_str[16];
+    snprintf(port_str, sizeof(port_str), "%d", port);
+
+    struct addrinfo* addrinfo_ptr = nullptr;
+
+#if (NTDDI_VERSION >= NTDDI_WINXPSP2) || (_WIN32_WINNT >= _WIN32_WINNT_WS03)
+    // TODO: When the Android SDK tools increases the Windows system
+    // requirements >= WinXP SP2, switch to GetAddrInfoW(widen(host).c_str()).
+#else
+    // Otherwise, keep using getaddrinfo(), or do runtime API detection
+    // with GetProcAddress("GetAddrInfoW").
+#endif
+    if (getaddrinfo(host.c_str(), port_str, &hints, &addrinfo_ptr) != 0) {
+        *error = android::base::StringPrintf(
+                "cannot resolve host '%s' and port %s: %s", host.c_str(),
+                port_str, SystemErrorCodeToString(WSAGetLastError()).c_str());
+        D("%s\n", error->c_str());
+        return -1;
+    }
+    std::unique_ptr<struct addrinfo, decltype(freeaddrinfo)*>
+        addrinfo(addrinfo_ptr, freeaddrinfo);
+    addrinfo_ptr = nullptr;
+
+    // TODO: Try all the addresses if there's more than one? This just uses
+    // the first. Or, could call WSAConnectByName() (Windows Vista and newer)
+    // which tries all addresses, takes a timeout and more.
+    SOCKET s = socket(addrinfo->ai_family, addrinfo->ai_socktype,
+                      addrinfo->ai_protocol);
     if(s == INVALID_SOCKET) {
-        _fh_close(f);
+        *error = android::base::StringPrintf("cannot create socket: %s",
+                SystemErrorCodeToString(WSAGetLastError()).c_str());
+        D("%s\n", error->c_str());
         return -1;
     }
     f->fh_socket = s;
 
-    if(connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
-        _fh_close(f);
+    // TODO: Implement timeouts for Windows. Seems like the default in theory
+    // (according to http://serverfault.com/a/671453) and in practice is 21 sec.
+    if(connect(s, addrinfo->ai_addr, addrinfo->ai_addrlen) == SOCKET_ERROR) {
+        // TODO: Use WSAAddressToString or inet_ntop on address.
+        *error = android::base::StringPrintf("cannot connect to %s:%s: %s",
+                host.c_str(), port_str,
+                SystemErrorCodeToString(WSAGetLastError()).c_str());
+        D("could not connect to %s:%s:%s: %s\n",
+          type != SOCK_STREAM ? "udp" : "tcp", host.c_str(), port_str,
+          error->c_str());
         return -1;
     }
 
-    snprintf( f->name, sizeof(f->name), "%d(net-client:%s%d)", _fh_to_int(f), type != SOCK_STREAM ? "udp:" : "", port );
-    D( "socket_network_client: host '%s' port %d type %s => fd %d\n", host, port, type != SOCK_STREAM ? "udp" : "tcp", _fh_to_int(f) );
-    return _fh_to_int(f);
-}
-
-
-int socket_network_client_timeout(const char *host, int port, int type, int timeout)
-{
-    // TODO: implement timeouts for Windows.
-    return socket_network_client(host, port, type);
-}
-
-
-int socket_inaddr_any_server(int port, int type)
-{
-    FH  f = _fh_alloc( &_fh_socket_class );
-    struct sockaddr_in addr;
-    SOCKET  s;
-    int n;
-
-    if (!f)
-        return -1;
-
-    if (!_winsock_init)
-        _init_winsock();
-
-    memset(&addr, 0, sizeof(addr));
-    addr.sin_family = AF_INET;
-    addr.sin_port = htons(port);
-    addr.sin_addr.s_addr = htonl(INADDR_ANY);
-
-    s = socket(AF_INET, type, 0);
-    if(s == INVALID_SOCKET) {
-        _fh_close(f);
-        return -1;
-    }
-
-    f->fh_socket = s;
-    n = 1;
-    setsockopt(s, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (const char*)&n, sizeof(n));
-
-    if(bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
-        _fh_close(f);
-        return -1;
-    }
-
-    if (type == SOCK_STREAM) {
-        int ret;
-
-        ret = listen(s, LISTEN_BACKLOG);
-        if (ret < 0) {
-            _fh_close(f);
-            return -1;
-        }
-    }
-    snprintf( f->name, sizeof(f->name), "%d(any-server:%s%d)", _fh_to_int(f), type != SOCK_STREAM ? "udp:" : "", port );
-    D( "socket_inaddr_server: port %d type %s => fd %d\n", port, type != SOCK_STREAM ? "udp" : "tcp", _fh_to_int(f) );
-    return _fh_to_int(f);
+    const int fd = _fh_to_int(f.get());
+    snprintf( f->name, sizeof(f->name), "%d(net-client:%s%d)", fd,
+              type != SOCK_STREAM ? "udp:" : "", port );
+    D( "host '%s' port %d type %s => fd %d\n", host.c_str(), port,
+       type != SOCK_STREAM ? "udp" : "tcp", fd );
+    f.release();
+    return fd;
 }
 
 #undef accept
 int  adb_socket_accept(int  serverfd, struct sockaddr*  addr, socklen_t  *addrlen)
 {
-    FH   serverfh = _fh_from_int(serverfd);
-    FH   fh;
+    FH   serverfh = _fh_from_int(serverfd, __func__);
 
     if ( !serverfh || serverfh->clazz != &_fh_socket_class ) {
-        D( "adb_socket_accept: invalid fd %d\n", serverfd );
+        D("adb_socket_accept: invalid fd %d\n", serverfd);
+        errno = EBADF;
         return -1;
     }
 
-    fh = _fh_alloc( &_fh_socket_class );
+    unique_fh fh(_fh_alloc( &_fh_socket_class ));
     if (!fh) {
-        D( "adb_socket_accept: not enough memory to allocate accepted socket descriptor\n" );
+        PLOG(ERROR) << "adb_socket_accept: failed to allocate accepted socket "
+                       "descriptor";
         return -1;
     }
 
     fh->fh_socket = accept( serverfh->fh_socket, addr, addrlen );
     if (fh->fh_socket == INVALID_SOCKET) {
-        _fh_close( fh );
-        D( "adb_socket_accept: accept on fd %d return error %ld\n", serverfd, GetLastError() );
+        const DWORD err = WSAGetLastError();
+        LOG(ERROR) << "adb_socket_accept: accept on fd " << serverfd <<
+                      " failed: " + SystemErrorCodeToString(err);
+        _socket_set_errno( err );
         return -1;
     }
 
-    snprintf( fh->name, sizeof(fh->name), "%d(accept:%s)", _fh_to_int(fh), serverfh->name );
-    D( "adb_socket_accept on fd %d returns fd %d\n", serverfd, _fh_to_int(fh) );
-    return  _fh_to_int(fh);
+    const int fd = _fh_to_int(fh.get());
+    snprintf( fh->name, sizeof(fh->name), "%d(accept:%s)", fd, serverfh->name );
+    D( "adb_socket_accept on fd %d returns fd %d\n", serverfd, fd );
+    fh.release();
+    return  fd;
 }
 
 
 int  adb_setsockopt( int  fd, int  level, int  optname, const void*  optval, socklen_t  optlen )
 {
-    FH   fh = _fh_from_int(fd);
+    FH   fh = _fh_from_int(fd, __func__);
 
     if ( !fh || fh->clazz != &_fh_socket_class ) {
         D("adb_setsockopt: invalid fd %d\n", fd);
+        errno = EBADF;
+        return -1;
+    }
+    int result = setsockopt( fh->fh_socket, level, optname,
+                             reinterpret_cast<const char*>(optval), optlen );
+    if ( result == SOCKET_ERROR ) {
+        const DWORD err = WSAGetLastError();
+        D( "adb_setsockopt: setsockopt on fd %d level %d optname %d "
+           "failed: %s\n", fd, level, optname,
+           SystemErrorCodeToString(err).c_str() );
+        _socket_set_errno( err );
+        result = -1;
+    }
+    return result;
+}
+
+
+int  adb_shutdown(int  fd)
+{
+    FH   f = _fh_from_int(fd, __func__);
+
+    if (!f || f->clazz != &_fh_socket_class) {
+        D("adb_shutdown: invalid fd %d\n", fd);
+        errno = EBADF;
         return -1;
     }
 
-    return setsockopt( fh->fh_socket, level, optname, reinterpret_cast<const char*>(optval), optlen );
+    D( "adb_shutdown: %s\n", f->name);
+    if (shutdown(f->fh_socket, SD_BOTH) == SOCKET_ERROR) {
+        const DWORD err = WSAGetLastError();
+        D("socket shutdown fd %d failed: %s\n", fd,
+          SystemErrorCodeToString(err).c_str());
+        _socket_set_errno(err);
+        return -1;
+    }
+    return 0;
 }
 
 /**************************************************************************/
@@ -1202,16 +1386,19 @@
 int  adb_socketpair(int sv[2]) {
     SocketPair pair;
 
-    FH fa = _fh_alloc(&_fh_socketpair_class);
-    FH fb = _fh_alloc(&_fh_socketpair_class);
-
-    if (!fa || !fb)
-        goto Fail;
+    unique_fh fa(_fh_alloc(&_fh_socketpair_class));
+    if (!fa) {
+        return -1;
+    }
+    unique_fh fb(_fh_alloc(&_fh_socketpair_class));
+    if (!fb) {
+        return -1;
+    }
 
     pair = reinterpret_cast<SocketPair>(malloc(sizeof(*pair)));
     if (pair == NULL) {
         D("adb_socketpair: not enough memory to allocate pipes\n" );
-        goto Fail;
+        return -1;
     }
 
     bip_buffer_init( &pair->a2b_bip );
@@ -1220,10 +1407,10 @@
     fa->fh_pair = pair;
     fb->fh_pair = pair;
     pair->used  = 2;
-    pair->a_fd  = fa;
+    pair->a_fd  = fa.get();
 
-    sv[0] = _fh_to_int(fa);
-    sv[1] = _fh_to_int(fb);
+    sv[0] = _fh_to_int(fa.get());
+    sv[1] = _fh_to_int(fb.get());
 
     pair->a2b_bip.fdin  = sv[0];
     pair->a2b_bip.fdout = sv[1];
@@ -1233,12 +1420,9 @@
     snprintf( fa->name, sizeof(fa->name), "%d(pair:%d)", sv[0], sv[1] );
     snprintf( fb->name, sizeof(fb->name), "%d(pair:%d)", sv[1], sv[0] );
     D( "adb_socketpair: returns (%d, %d)\n", sv[0], sv[1] );
+    fa.release();
+    fb.release();
     return 0;
-
-Fail:
-    _fh_close(fb);
-    _fh_close(fa);
-    return -1;
 }
 
 /**************************************************************************/
@@ -1386,7 +1570,7 @@
 static void
 event_looper_hook( EventLooper  looper, int  fd, int  events )
 {
-    FH          f = _fh_from_int(fd);
+    FH          f = _fh_from_int(fd, __func__);
     EventHook  *pnode;
     EventHook   node;
 
@@ -1418,7 +1602,7 @@
 static void
 event_looper_unhook( EventLooper  looper, int  fd, int  events )
 {
-    FH          fh    = _fh_from_int(fd);
+    FH          fh    = _fh_from_int(fd, __func__);
     EventHook  *pnode = event_looper_find_p( looper, fh );
     EventHook   node  = *pnode;
 
@@ -1544,7 +1728,7 @@
     threads = (WaitForAllParam*)malloc((chunks + (remains ? 1 : 0)) *
                                         sizeof(WaitForAllParam));
     if (threads == NULL) {
-        D("Unable to allocate thread array for %d handles.", handles_count);
+        D("Unable to allocate thread array for %d handles.\n", handles_count);
         return (int)WAIT_FAILED;
     }
 
@@ -1552,7 +1736,7 @@
      * reset" event that will remain set once it was set. */
     main_event = CreateEvent(NULL, TRUE, FALSE, NULL);
     if (main_event == NULL) {
-        D("Unable to create main event. Error: %d", (int)GetLastError());
+        D("Unable to create main event. Error: %ld\n", GetLastError());
         free(threads);
         return (int)WAIT_FAILED;
     }
@@ -1585,7 +1769,7 @@
                                                        &threads[chunk], 0, NULL);
         if (threads[chunk].thread == NULL) {
             /* Unable to create a waiter thread. Collapse. */
-            D("Unable to create a waiting thread %d of %d. errno=%d",
+            D("Unable to create a waiting thread %d of %d. errno=%d\n",
               chunk, chunks, errno);
             chunks = chunk;
             SetEvent(main_event);
@@ -2086,6 +2270,7 @@
     hook->check   = _event_socket_check;
     hook->peek    = _event_socket_peek;
 
+    // TODO: check return value?
     _event_socket_start( hook );
 }
 
@@ -2193,7 +2378,7 @@
         memset(input_record, 0, sizeof(*input_record));
         if (!ReadConsoleInputA(console, input_record, 1, &read_count)) {
             D("_get_interesting_input_record_uncached: ReadConsoleInputA() "
-              "failure, error %ld\n", GetLastError());
+              "failed: %s\n", SystemErrorCodeToString(GetLastError()).c_str());
             errno = EIO;
             return false;
         }
@@ -3007,8 +3192,8 @@
         if (!SetConsoleMode(in, _old_console_mode & ~(ENABLE_PROCESSED_INPUT |
             ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT))) {
             // This really should not fail.
-            D("stdin_raw_init: SetConsoleMode() failure, error %ld\n",
-                GetLastError());
+            D("stdin_raw_init: SetConsoleMode() failed: %s\n",
+              SystemErrorCodeToString(GetLastError()).c_str());
         }
 
         // Once this is set, it means that stdin has been configured for
@@ -3029,14 +3214,14 @@
 
             if (!SetConsoleMode(in, _old_console_mode)) {
                 // This really should not fail.
-                D("stdin_raw_restore: SetConsoleMode() failure, error %ld\n",
-                    GetLastError());
+                D("stdin_raw_restore: SetConsoleMode() failed: %s\n",
+                  SystemErrorCodeToString(GetLastError()).c_str());
             }
         }
     }
 }
 
-// Called by 'adb shell' command to read from stdin.
+// Called by 'adb shell' and 'adb exec-in' to read from stdin.
 int unix_read(int fd, void* buf, size_t len) {
     if ((fd == STDIN_FILENO) && (_console_handle != NULL)) {
         // If it is a request to read from stdin, and stdin_raw_init() has been
@@ -3046,8 +3231,633 @@
         return _console_read(_console_handle, buf, len);
     } else {
         // Just call into C Runtime which can read from pipes/files and which
-        // can do LF/CR translation.
+        // can do LF/CR translation (which is overridable with _setmode()).
+        // Undefine the macro that is set in sysdeps.h which bans calls to
+        // plain read() in favor of unix_read() or adb_read().
+#pragma push_macro("read")
 #undef read
         return read(fd, buf, len);
+#pragma pop_macro("read")
     }
 }
+
+/**************************************************************************/
+/**************************************************************************/
+/*****                                                                *****/
+/*****      Unicode support                                           *****/
+/*****                                                                *****/
+/**************************************************************************/
+/**************************************************************************/
+
+// This implements support for using files with Unicode filenames and for
+// outputting Unicode text to a Win32 console window. This is inspired from
+// http://utf8everywhere.org/.
+//
+// Background
+// ----------
+//
+// On POSIX systems, to deal with files with Unicode filenames, just pass UTF-8
+// filenames to APIs such as open(). This works because filenames are largely
+// opaque 'cookies' (perhaps excluding path separators).
+//
+// On Windows, the native file APIs such as CreateFileW() take 2-byte wchar_t
+// UTF-16 strings. There is an API, CreateFileA() that takes 1-byte char
+// strings, but the strings are in the ANSI codepage and not UTF-8. (The
+// CreateFile() API is really just a macro that adds the W/A based on whether
+// the UNICODE preprocessor symbol is defined).
+//
+// Options
+// -------
+//
+// Thus, to write a portable program, there are a few options:
+//
+// 1. Write the program with wchar_t filenames (wchar_t path[256];).
+//    For Windows, just call CreateFileW(). For POSIX, write a wrapper openW()
+//    that takes a wchar_t string, converts it to UTF-8 and then calls the real
+//    open() API.
+//
+// 2. Write the program with a TCHAR typedef that is 2 bytes on Windows and
+//    1 byte on POSIX. Make T-* wrappers for various OS APIs and call those,
+//    potentially touching a lot of code.
+//
+// 3. Write the program with a 1-byte char filenames (char path[256];) that are
+//    UTF-8. For POSIX, just call open(). For Windows, write a wrapper that
+//    takes a UTF-8 string, converts it to UTF-16 and then calls the real OS
+//    or C Runtime API.
+//
+// The Choice
+// ----------
+//
+// The code below chooses option 3, the UTF-8 everywhere strategy. It
+// introduces narrow() which converts UTF-16 to UTF-8. This is used by the
+// NarrowArgs helper class that is used to convert wmain() args into UTF-8
+// args that are passed to main() at the beginning of program startup. We also
+// introduce widen() which converts from UTF-8 to UTF-16. This is used to
+// implement wrappers below that call UTF-16 OS and C Runtime APIs.
+//
+// Unicode console output
+// ----------------------
+//
+// The way to output Unicode to a Win32 console window is to call
+// WriteConsoleW() with UTF-16 text. (The user must also choose a proper font
+// such as Lucida Console or Consolas, and in the case of East Asian languages
+// (such as Chinese, Japanese, Korean), the user must go to the Control Panel
+// and change the "system locale" to Chinese, etc., which allows a Chinese, etc.
+// font to be used in console windows.)
+//
+// The problem is getting the C Runtime to make fprintf and related APIs call
+// WriteConsoleW() under the covers. The C Runtime API, _setmode() sounds
+// promising, but the various modes have issues:
+//
+// 1. _setmode(_O_TEXT) (the default) does not use WriteConsoleW() so UTF-8 and
+//    UTF-16 do not display properly.
+// 2. _setmode(_O_BINARY) does not use WriteConsoleW() and the text comes out
+//    totally wrong.
+// 3. _setmode(_O_U8TEXT) seems to cause the C Runtime _invalid_parameter
+//    handler to be called (upon a later I/O call), aborting the process.
+// 4. _setmode(_O_U16TEXT) and _setmode(_O_WTEXT) cause non-wide printf/fprintf
+//    to output nothing.
+//
+// So the only solution is to write our own adb_fprintf() that converts UTF-8
+// to UTF-16 and then calls WriteConsoleW().
+
+
+// Function prototype because attributes cannot be placed on func definitions.
+static void _widen_fatal(const char *fmt, ...)
+    __attribute__((__format__(ADB_FORMAT_ARCHETYPE, 1, 2)));
+
+// A version of fatal() that does not call adb_(v)fprintf(), so it can be
+// called from those functions.
+static void _widen_fatal(const char *fmt, ...) {
+    va_list ap;
+    va_start(ap, fmt);
+    // If (v)fprintf are macros that point to adb_(v)fprintf, when random adb
+    // code calls (v)fprintf, it may end up calling adb_(v)fprintf, which then
+    // calls _widen_fatal(). So then how does _widen_fatal() output a error?
+    // By directly calling real C Runtime APIs that don't properly output
+    // Unicode, but will be able to get a comprehendible message out. To do
+    // this, make sure we don't call (v)fprintf macros by undefining them.
+#pragma push_macro("fprintf")
+#pragma push_macro("vfprintf")
+#undef fprintf
+#undef vfprintf
+    fprintf(stderr, "error: ");
+    vfprintf(stderr, fmt, ap);
+    fprintf(stderr, "\n");
+#pragma pop_macro("vfprintf")
+#pragma pop_macro("fprintf")
+    va_end(ap);
+    exit(-1);
+}
+
+// TODO: Consider implementing widen() and narrow() out of std::wstring_convert
+// once libcxx is supported on Windows. Or, consider libutils/Unicode.cpp.
+
+// Convert from UTF-8 to UTF-16. A size of -1 specifies a NULL terminated
+// string. Any other size specifies the number of chars to convert, excluding
+// any NULL terminator (if you're passing an explicit size, you probably don't
+// have a NULL terminated string in the first place).
+std::wstring widen(const char* utf8, const int size) {
+    // Note: Do not call SystemErrorCodeToString() from widen() because
+    // SystemErrorCodeToString() calls narrow() which may call fatal() which
+    // calls adb_vfprintf() which calls widen(), potentially causing infinite
+    // recursion.
+    const int chars_to_convert = MultiByteToWideChar(CP_UTF8, 0, utf8, size,
+                                                     NULL, 0);
+    if (chars_to_convert <= 0) {
+        // UTF-8 to UTF-16 should be lossless, so we don't expect this to fail.
+        _widen_fatal("MultiByteToWideChar failed counting: %d, "
+                     "GetLastError: %lu", chars_to_convert, GetLastError());
+    }
+
+    std::wstring utf16;
+    size_t chars_to_allocate = chars_to_convert;
+    if (size == -1) {
+        // chars_to_convert includes a NULL terminator, so subtract space
+        // for that because resize() includes that itself.
+        --chars_to_allocate;
+    }
+    utf16.resize(chars_to_allocate);
+
+    // This uses &string[0] to get write-access to the entire string buffer
+    // which may be assuming that the chars are all contiguous, but it seems
+    // to work and saves us the hassle of using a temporary
+    // std::vector<wchar_t>.
+    const int result = MultiByteToWideChar(CP_UTF8, 0, utf8, size, &utf16[0],
+                                           chars_to_convert);
+    if (result != chars_to_convert) {
+        // UTF-8 to UTF-16 should be lossless, so we don't expect this to fail.
+        _widen_fatal("MultiByteToWideChar failed conversion: %d, "
+                     "GetLastError: %lu", result, GetLastError());
+    }
+
+    // If a size was passed in (size != -1), then the string is NULL terminated
+    // by a NULL char that was written by std::string::resize(). If size == -1,
+    // then MultiByteToWideChar() read a NULL terminator from the original
+    // string and converted it to a NULL UTF-16 char in the output.
+
+    return utf16;
+}
+
+// Convert a NULL terminated string from UTF-8 to UTF-16.
+std::wstring widen(const char* utf8) {
+    // Pass -1 to let widen() determine the string length.
+    return widen(utf8, -1);
+}
+
+// Convert from UTF-8 to UTF-16.
+std::wstring widen(const std::string& utf8) {
+    return widen(utf8.c_str(), utf8.length());
+}
+
+// Convert from UTF-16 to UTF-8.
+std::string narrow(const std::wstring& utf16) {
+    return narrow(utf16.c_str());
+}
+
+// Convert from UTF-16 to UTF-8.
+std::string narrow(const wchar_t* utf16) {
+    // Note: Do not call SystemErrorCodeToString() from narrow() because
+    // SystemErrorCodeToString() calls narrow() and we don't want potential
+    // infinite recursion.
+    const int chars_required = WideCharToMultiByte(CP_UTF8, 0, utf16, -1, NULL,
+                                                   0, NULL, NULL);
+    if (chars_required <= 0) {
+        // UTF-16 to UTF-8 should be lossless, so we don't expect this to fail.
+        fatal("WideCharToMultiByte failed counting: %d, GetLastError: %lu",
+              chars_required, GetLastError());
+    }
+
+    std::string utf8;
+    // Subtract space for the NULL terminator because resize() includes
+    // that itself. Note that this could potentially throw a std::bad_alloc
+    // exception.
+    utf8.resize(chars_required - 1);
+
+    // This uses &string[0] to get write-access to the entire string buffer
+    // which may be assuming that the chars are all contiguous, but it seems
+    // to work and saves us the hassle of using a temporary
+    // std::vector<char>.
+    const int result = WideCharToMultiByte(CP_UTF8, 0, utf16, -1, &utf8[0],
+                                           chars_required, NULL, NULL);
+    if (result != chars_required) {
+        // UTF-16 to UTF-8 should be lossless, so we don't expect this to fail.
+        fatal("WideCharToMultiByte failed conversion: %d, GetLastError: %lu",
+              result, GetLastError());
+    }
+
+    return utf8;
+}
+
+// Constructor for helper class to convert wmain() UTF-16 args to UTF-8 to
+// be passed to main().
+NarrowArgs::NarrowArgs(const int argc, wchar_t** const argv) {
+    narrow_args = new char*[argc + 1];
+
+    for (int i = 0; i < argc; ++i) {
+        narrow_args[i] = strdup(narrow(argv[i]).c_str());
+    }
+    narrow_args[argc] = nullptr;   // terminate
+}
+
+NarrowArgs::~NarrowArgs() {
+    if (narrow_args != nullptr) {
+        for (char** argp = narrow_args; *argp != nullptr; ++argp) {
+            free(*argp);
+        }
+        delete[] narrow_args;
+        narrow_args = nullptr;
+    }
+}
+
+int unix_open(const char* path, int options, ...) {
+    if ((options & O_CREAT) == 0) {
+        return _wopen(widen(path).c_str(), options);
+    } else {
+        int      mode;
+        va_list  args;
+        va_start(args, options);
+        mode = va_arg(args, int);
+        va_end(args);
+        return _wopen(widen(path).c_str(), options, mode);
+    }
+}
+
+// Version of stat() that takes a UTF-8 path.
+int adb_stat(const char* f, struct adb_stat* s) {
+#pragma push_macro("wstat")
+// This definition of wstat seems to be missing from <sys/stat.h>.
+#if defined(_FILE_OFFSET_BITS) && (_FILE_OFFSET_BITS == 64)
+#ifdef _USE_32BIT_TIME_T
+#define wstat _wstat32i64
+#else
+#define wstat _wstat64
+#endif
+#else
+// <sys/stat.h> has a function prototype for wstat() that should be available.
+#endif
+
+    return wstat(widen(f).c_str(), s);
+
+#pragma pop_macro("wstat")
+}
+
+// Version of opendir() that takes a UTF-8 path.
+DIR* adb_opendir(const char* name) {
+    // Just cast _WDIR* to DIR*. This doesn't work if the caller reads any of
+    // the fields, but right now all the callers treat the structure as
+    // opaque.
+    return reinterpret_cast<DIR*>(_wopendir(widen(name).c_str()));
+}
+
+// Version of readdir() that returns UTF-8 paths.
+struct dirent* adb_readdir(DIR* dir) {
+    _WDIR* const wdir = reinterpret_cast<_WDIR*>(dir);
+    struct _wdirent* const went = _wreaddir(wdir);
+    if (went == nullptr) {
+        return nullptr;
+    }
+    // Convert from UTF-16 to UTF-8.
+    const std::string name_utf8(narrow(went->d_name));
+
+    // Cast the _wdirent* to dirent* and overwrite the d_name field (which has
+    // space for UTF-16 wchar_t's) with UTF-8 char's.
+    struct dirent* ent = reinterpret_cast<struct dirent*>(went);
+
+    if (name_utf8.length() + 1 > sizeof(went->d_name)) {
+        // Name too big to fit in existing buffer.
+        errno = ENOMEM;
+        return nullptr;
+    }
+
+    // Note that sizeof(_wdirent::d_name) is bigger than sizeof(dirent::d_name)
+    // because _wdirent contains wchar_t instead of char. So even if name_utf8
+    // can fit in _wdirent::d_name, the resulting dirent::d_name field may be
+    // bigger than the caller expects because they expect a dirent structure
+    // which has a smaller d_name field. Ignore this since the caller should be
+    // resilient.
+
+    // Rewrite the UTF-16 d_name field to UTF-8.
+    strcpy(ent->d_name, name_utf8.c_str());
+
+    return ent;
+}
+
+// Version of closedir() to go with our version of adb_opendir().
+int adb_closedir(DIR* dir) {
+    return _wclosedir(reinterpret_cast<_WDIR*>(dir));
+}
+
+// Version of unlink() that takes a UTF-8 path.
+int adb_unlink(const char* path) {
+    const std::wstring wpath(widen(path));
+
+    int  rc = _wunlink(wpath.c_str());
+
+    if (rc == -1 && errno == EACCES) {
+        /* unlink returns EACCES when the file is read-only, so we first */
+        /* try to make it writable, then unlink again...                 */
+        rc = _wchmod(wpath.c_str(), _S_IREAD | _S_IWRITE);
+        if (rc == 0)
+            rc = _wunlink(wpath.c_str());
+    }
+    return rc;
+}
+
+// Version of mkdir() that takes a UTF-8 path.
+int adb_mkdir(const std::string& path, int mode) {
+    return _wmkdir(widen(path.c_str()).c_str());
+}
+
+// Version of utime() that takes a UTF-8 path.
+int adb_utime(const char* path, struct utimbuf* u) {
+    static_assert(sizeof(struct utimbuf) == sizeof(struct _utimbuf),
+        "utimbuf and _utimbuf should be the same size because they both "
+        "contain the same types, namely time_t");
+    return _wutime(widen(path).c_str(), reinterpret_cast<struct _utimbuf*>(u));
+}
+
+// Version of chmod() that takes a UTF-8 path.
+int adb_chmod(const char* path, int mode) {
+    return _wchmod(widen(path).c_str(), mode);
+}
+
+// Internal function to get a Win32 console HANDLE from a C Runtime FILE*.
+static HANDLE _get_console_handle(FILE* const stream) {
+    // Get a C Runtime file descriptor number from the FILE* structure.
+    const int fd = fileno(stream);
+    if (fd < 0) {
+        return NULL;
+    }
+
+    // If it is not a "character device", it is probably a file and not a
+    // console. Do this check early because it is probably cheap. Still do more
+    // checks after this since there are devices that pass this test, but are
+    // not a console, such as NUL, the Windows /dev/null equivalent (I think).
+    if (!isatty(fd)) {
+        return NULL;
+    }
+
+    // Given a C Runtime file descriptor number, get the underlying OS
+    // file handle.
+    const intptr_t osfh = _get_osfhandle(fd);
+    if (osfh == -1) {
+        return NULL;
+    }
+
+    const HANDLE h = reinterpret_cast<const HANDLE>(osfh);
+
+    DWORD old_mode = 0;
+    if (!GetConsoleMode(h, &old_mode)) {
+        return NULL;
+    }
+
+    // If GetConsoleMode() was successful, assume this is a console.
+    return h;
+}
+
+// Internal helper function to write UTF-8 bytes to a console. Returns -1
+// on error.
+static int _console_write_utf8(const char* buf, size_t size, FILE* stream,
+                               HANDLE console) {
+    // Convert from UTF-8 to UTF-16.
+    // This could throw std::bad_alloc.
+    const std::wstring output(widen(buf, size));
+
+    // Note that this does not do \n => \r\n translation because that
+    // doesn't seem necessary for the Windows console. For the Windows
+    // console \r moves to the beginning of the line and \n moves to a new
+    // line.
+
+    // Flush any stream buffering so that our output is afterwards which
+    // makes sense because our call is afterwards.
+    (void)fflush(stream);
+
+    // Write UTF-16 to the console.
+    DWORD written = 0;
+    if (!WriteConsoleW(console, output.c_str(), output.length(), &written,
+                       NULL)) {
+        errno = EIO;
+        return -1;
+    }
+
+    // This is the number of UTF-16 chars written, which might be different
+    // than the number of UTF-8 chars passed in. It doesn't seem practical to
+    // get this count correct.
+    return written;
+}
+
+// Function prototype because attributes cannot be placed on func definitions.
+static int _console_vfprintf(const HANDLE console, FILE* stream,
+                             const char *format, va_list ap)
+    __attribute__((__format__(ADB_FORMAT_ARCHETYPE, 3, 0)));
+
+// Internal function to format a UTF-8 string and write it to a Win32 console.
+// Returns -1 on error.
+static int _console_vfprintf(const HANDLE console, FILE* stream,
+                             const char *format, va_list ap) {
+    std::string output_utf8;
+
+    // Format the string.
+    // This could throw std::bad_alloc.
+    android::base::StringAppendV(&output_utf8, format, ap);
+
+    return _console_write_utf8(output_utf8.c_str(), output_utf8.length(),
+                               stream, console);
+}
+
+// Version of vfprintf() that takes UTF-8 and can write Unicode to a
+// Windows console.
+int adb_vfprintf(FILE *stream, const char *format, va_list ap) {
+    const HANDLE console = _get_console_handle(stream);
+
+    // If there is an associated Win32 console, write to it specially,
+    // otherwise defer to the regular C Runtime, passing it UTF-8.
+    if (console != NULL) {
+        return _console_vfprintf(console, stream, format, ap);
+    } else {
+        // If vfprintf is a macro, undefine it, so we can call the real
+        // C Runtime API.
+#pragma push_macro("vfprintf")
+#undef vfprintf
+        return vfprintf(stream, format, ap);
+#pragma pop_macro("vfprintf")
+    }
+}
+
+// Version of fprintf() that takes UTF-8 and can write Unicode to a
+// Windows console.
+int adb_fprintf(FILE *stream, const char *format, ...) {
+    va_list ap;
+    va_start(ap, format);
+    const int result = adb_vfprintf(stream, format, ap);
+    va_end(ap);
+
+    return result;
+}
+
+// Version of printf() that takes UTF-8 and can write Unicode to a
+// Windows console.
+int adb_printf(const char *format, ...) {
+    va_list ap;
+    va_start(ap, format);
+    const int result = adb_vfprintf(stdout, format, ap);
+    va_end(ap);
+
+    return result;
+}
+
+// Version of fputs() that takes UTF-8 and can write Unicode to a
+// Windows console.
+int adb_fputs(const char* buf, FILE* stream) {
+    // adb_fprintf returns -1 on error, which is conveniently the same as EOF
+    // which fputs (and hence adb_fputs) should return on error.
+    return adb_fprintf(stream, "%s", buf);
+}
+
+// Version of fputc() that takes UTF-8 and can write Unicode to a
+// Windows console.
+int adb_fputc(int ch, FILE* stream) {
+    const int result = adb_fprintf(stream, "%c", ch);
+    if (result <= 0) {
+        // If there was an error, or if nothing was printed (which should be an
+        // error), return an error, which fprintf signifies with EOF.
+        return EOF;
+    }
+    // For success, fputc returns the char, cast to unsigned char, then to int.
+    return static_cast<unsigned char>(ch);
+}
+
+// Internal function to write UTF-8 to a Win32 console. Returns the number of
+// items (of length size) written. On error, returns a short item count or 0.
+static size_t _console_fwrite(const void* ptr, size_t size, size_t nmemb,
+                              FILE* stream, HANDLE console) {
+    // TODO: Note that a Unicode character could be several UTF-8 bytes. But
+    // if we're passed only some of the bytes of a character (for example, from
+    // the network socket for adb shell), we won't be able to convert the char
+    // to a complete UTF-16 char (or surrogate pair), so the output won't look
+    // right.
+    //
+    // To fix this, see libutils/Unicode.cpp for hints on decoding UTF-8.
+    //
+    // For now we ignore this problem because the alternative is that we'd have
+    // to parse UTF-8 and buffer things up (doable). At least this is better
+    // than what we had before -- always incorrect multi-byte UTF-8 output.
+    int result = _console_write_utf8(reinterpret_cast<const char*>(ptr),
+                                     size * nmemb, stream, console);
+    if (result == -1) {
+        return 0;
+    }
+    return result / size;
+}
+
+// Version of fwrite() that takes UTF-8 and can write Unicode to a
+// Windows console.
+size_t adb_fwrite(const void* ptr, size_t size, size_t nmemb, FILE* stream) {
+    const HANDLE console = _get_console_handle(stream);
+
+    // If there is an associated Win32 console, write to it specially,
+    // otherwise defer to the regular C Runtime, passing it UTF-8.
+    if (console != NULL) {
+        return _console_fwrite(ptr, size, nmemb, stream, console);
+    } else {
+        // If fwrite is a macro, undefine it, so we can call the real
+        // C Runtime API.
+#pragma push_macro("fwrite")
+#undef fwrite
+        return fwrite(ptr, size, nmemb, stream);
+#pragma pop_macro("fwrite")
+    }
+}
+
+// Version of fopen() that takes a UTF-8 filename and can access a file with
+// a Unicode filename.
+FILE* adb_fopen(const char* f, const char* m) {
+    return _wfopen(widen(f).c_str(), widen(m).c_str());
+}
+
+// Shadow UTF-8 environment variable name/value pairs that are created from
+// _wenviron the first time that adb_getenv() is called. Note that this is not
+// currently updated if putenv, setenv, unsetenv are called. Note that no
+// thread synchronization is done, but we're called early enough in
+// single-threaded startup that things work ok.
+static std::unordered_map<std::string, char*> g_environ_utf8;
+
+// Make sure that shadow UTF-8 environment variables are setup.
+static void _ensure_env_setup() {
+    // If some name/value pairs exist, then we've already done the setup below.
+    if (g_environ_utf8.size() != 0) {
+        return;
+    }
+
+    // Read name/value pairs from UTF-16 _wenviron and write new name/value
+    // pairs to UTF-8 g_environ_utf8. Note that it probably does not make sense
+    // to use the D() macro here because that tracing only works if the
+    // ADB_TRACE environment variable is setup, but that env var can't be read
+    // until this code completes.
+    for (wchar_t** env = _wenviron; *env != nullptr; ++env) {
+        wchar_t* const equal = wcschr(*env, L'=');
+        if (equal == nullptr) {
+            // Malformed environment variable with no equal sign. Shouldn't
+            // really happen, but we should be resilient to this.
+            continue;
+        }
+
+        const std::string name_utf8(narrow(std::wstring(*env, equal - *env)));
+        char* const value_utf8 = strdup(narrow(equal + 1).c_str());
+
+        // Overwrite any duplicate name, but there shouldn't be a dup in the
+        // first place.
+        g_environ_utf8[name_utf8] = value_utf8;
+    }
+}
+
+// Version of getenv() that takes a UTF-8 environment variable name and
+// retrieves a UTF-8 value.
+char* adb_getenv(const char* name) {
+    _ensure_env_setup();
+
+    const auto it = g_environ_utf8.find(std::string(name));
+    if (it == g_environ_utf8.end()) {
+        return nullptr;
+    }
+
+    return it->second;
+}
+
+// Version of getcwd() that returns the current working directory in UTF-8.
+char* adb_getcwd(char* buf, int size) {
+    wchar_t* wbuf = _wgetcwd(nullptr, 0);
+    if (wbuf == nullptr) {
+        return nullptr;
+    }
+
+    const std::string buf_utf8(narrow(wbuf));
+    free(wbuf);
+    wbuf = nullptr;
+
+    // If size was specified, make sure all the chars will fit.
+    if (size != 0) {
+        if (size < static_cast<int>(buf_utf8.length() + 1)) {
+            errno = ERANGE;
+            return nullptr;
+        }
+    }
+
+    // If buf was not specified, allocate storage.
+    if (buf == nullptr) {
+        if (size == 0) {
+            size = buf_utf8.length() + 1;
+        }
+        buf = reinterpret_cast<char*>(malloc(size));
+        if (buf == nullptr) {
+            return nullptr;
+        }
+    }
+
+    // Destination buffer was allocated with enough space, or we've already
+    // checked an existing buffer size for enough space.
+    strcpy(buf, buf_utf8.c_str());
+
+    return buf;
+}
diff --git a/adb/test_adb.py b/adb/test_adb.py
new file mode 100644
index 0000000..59aa14d
--- /dev/null
+++ b/adb/test_adb.py
@@ -0,0 +1,77 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2015 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.
+#
+"""Tests for the adb program itself.
+
+This differs from things in test_device.py in that there is no API for these
+things. Most of these tests involve specific error messages or the help text.
+"""
+from __future__ import print_function
+
+import random
+import subprocess
+import unittest
+
+import adb
+
+
+class NonApiTest(unittest.TestCase):
+    """Tests for ADB that aren't a part of the AndroidDevice API."""
+
+    def test_help(self):
+        """Make sure we get _something_ out of help."""
+        out = subprocess.check_output(
+            ['adb', 'help'], stderr=subprocess.STDOUT)
+        self.assertGreater(len(out), 0)
+
+    def test_version(self):
+        """Get a version number out of the output of adb."""
+        lines = subprocess.check_output(['adb', 'version']).splitlines()
+        version_line = lines[0]
+        self.assertRegexpMatches(
+            version_line, r'^Android Debug Bridge version \d+\.\d+\.\d+$')
+        if len(lines) == 2:
+            # Newer versions of ADB have a second line of output for the
+            # version that includes a specific revision (git SHA).
+            revision_line = lines[1]
+            self.assertRegexpMatches(
+                revision_line, r'^Revision [0-9a-f]{12}-android$')
+
+    def test_tcpip_error_messages(self):
+        p = subprocess.Popen(['adb', 'tcpip'], stdout=subprocess.PIPE,
+                             stderr=subprocess.STDOUT)
+        out, _ = p.communicate()
+        self.assertEqual(1, p.returncode)
+        self.assertIn('help message', out)
+
+        p = subprocess.Popen(['adb', 'tcpip', 'foo'], stdout=subprocess.PIPE,
+                             stderr=subprocess.STDOUT)
+        out, _ = p.communicate()
+        self.assertEqual(1, p.returncode)
+        self.assertIn('error', out)
+
+
+def main():
+    random.seed(0)
+    if len(adb.get_devices()) > 0:
+        suite = unittest.TestLoader().loadTestsFromName(__name__)
+        unittest.TextTestRunner(verbosity=3).run(suite)
+    else:
+        print('Test suite must be run with attached devices')
+
+
+if __name__ == '__main__':
+    main()
diff --git a/adb/test_device.py b/adb/test_device.py
new file mode 100644
index 0000000..2006937
--- /dev/null
+++ b/adb/test_device.py
@@ -0,0 +1,490 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2015 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.
+#
+from __future__ import print_function
+
+import hashlib
+import os
+import posixpath
+import random
+import shlex
+import shutil
+import subprocess
+import tempfile
+import unittest
+
+import mock
+
+import adb
+
+
+def requires_root(func):
+    def wrapper(self, *args):
+        if self.device.get_prop('ro.debuggable') != '1':
+            raise unittest.SkipTest('requires rootable build')
+
+        was_root = self.device.shell(['id', '-un']).strip() == 'root'
+        if not was_root:
+            self.device.root()
+            self.device.wait()
+
+        try:
+            func(self, *args)
+        finally:
+            if not was_root:
+                self.device.unroot()
+                self.device.wait()
+
+    return wrapper
+
+
+class GetDeviceTest(unittest.TestCase):
+    def setUp(self):
+        self.android_serial = os.getenv('ANDROID_SERIAL')
+        if 'ANDROID_SERIAL' in os.environ:
+            del os.environ['ANDROID_SERIAL']
+
+    def tearDown(self):
+        if self.android_serial is not None:
+            os.environ['ANDROID_SERIAL'] = self.android_serial
+        else:
+            if 'ANDROID_SERIAL' in os.environ:
+                del os.environ['ANDROID_SERIAL']
+
+    @mock.patch('adb.device.get_devices')
+    def test_explicit(self, mock_get_devices):
+        mock_get_devices.return_value = ['foo', 'bar']
+        device = adb.get_device('foo')
+        self.assertEqual(device.serial, 'foo')
+
+    @mock.patch('adb.device.get_devices')
+    def test_from_env(self, mock_get_devices):
+        mock_get_devices.return_value = ['foo', 'bar']
+        os.environ['ANDROID_SERIAL'] = 'foo'
+        device = adb.get_device()
+        self.assertEqual(device.serial, 'foo')
+
+    @mock.patch('adb.device.get_devices')
+    def test_arg_beats_env(self, mock_get_devices):
+        mock_get_devices.return_value = ['foo', 'bar']
+        os.environ['ANDROID_SERIAL'] = 'bar'
+        device = adb.get_device('foo')
+        self.assertEqual(device.serial, 'foo')
+
+    @mock.patch('adb.device.get_devices')
+    def test_no_such_device(self, mock_get_devices):
+        mock_get_devices.return_value = ['foo', 'bar']
+        self.assertRaises(adb.DeviceNotFoundError, adb.get_device, ['baz'])
+
+        os.environ['ANDROID_SERIAL'] = 'baz'
+        self.assertRaises(adb.DeviceNotFoundError, adb.get_device)
+
+    @mock.patch('adb.device.get_devices')
+    def test_unique_device(self, mock_get_devices):
+        mock_get_devices.return_value = ['foo']
+        device = adb.get_device()
+        self.assertEqual(device.serial, 'foo')
+
+    @mock.patch('adb.device.get_devices')
+    def test_no_unique_device(self, mock_get_devices):
+        mock_get_devices.return_value = ['foo', 'bar']
+        self.assertRaises(adb.NoUniqueDeviceError, adb.get_device)
+
+
+class DeviceTest(unittest.TestCase):
+    def setUp(self):
+        self.device = adb.get_device()
+
+
+class ShellTest(DeviceTest):
+    def test_cat(self):
+        """Check that we can at least cat a file."""
+        out = self.device.shell(['cat', '/proc/uptime']).strip()
+        elements = out.split()
+        self.assertEqual(len(elements), 2)
+
+        uptime, idle = elements
+        self.assertGreater(float(uptime), 0.0)
+        self.assertGreater(float(idle), 0.0)
+
+    def test_throws_on_failure(self):
+        self.assertRaises(subprocess.CalledProcessError,
+                          self.device.shell, ['false'])
+
+    def test_output_not_stripped(self):
+        out = self.device.shell(['echo', 'foo'])
+        self.assertEqual(out, 'foo' + self.device.linesep)
+
+    def test_shell_nocheck_failure(self):
+        rc, out = self.device.shell_nocheck(['false'])
+        self.assertNotEqual(rc, 0)
+        self.assertEqual(out, '')
+
+    def test_shell_nocheck_output_not_stripped(self):
+        rc, out = self.device.shell_nocheck(['echo', 'foo'])
+        self.assertEqual(rc, 0)
+        self.assertEqual(out, 'foo' + self.device.linesep)
+
+    def test_can_distinguish_tricky_results(self):
+        # If result checking on ADB shell is naively implemented as
+        # `adb shell <cmd>; echo $?`, we would be unable to distinguish the
+        # output from the result for a cmd of `echo -n 1`.
+        rc, out = self.device.shell_nocheck(['echo', '-n', '1'])
+        self.assertEqual(rc, 0)
+        self.assertEqual(out, '1')
+
+    def test_line_endings(self):
+        """Ensure that line ending translation is not happening in the pty.
+
+        Bug: http://b/19735063
+        """
+        output = self.device.shell(['uname'])
+        self.assertEqual(output, 'Linux' + self.device.linesep)
+
+    def test_pty_logic(self):
+        """Verify PTY logic for shells.
+
+        Interactive shells should use a PTY, non-interactive should not.
+
+        Bug: http://b/21215503
+        """
+        proc = subprocess.Popen(
+                self.device.adb_cmd + ['shell'], stdin=subprocess.PIPE,
+                stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+        # [ -t 0 ] is used (rather than `tty`) to provide portability. This
+        # gives an exit code of 0 iff stdin is connected to a terminal.
+        #
+        # Closing host-side stdin doesn't currently trigger the interactive
+        # shell to exit so we need to explicitly add an exit command to
+        # close the session from the device side, and append \n to complete
+        # the interactive command.
+        result = proc.communicate('[ -t 0 ]; echo x$?; exit 0\n')[0]
+        partition = result.rpartition('x')
+        self.assertEqual(partition[1], 'x')
+        self.assertEqual(int(partition[2]), 0)
+
+        exit_code = self.device.shell_nocheck(['[ -t 0 ]'])[0]
+        self.assertEqual(exit_code, 1)
+
+
+class ArgumentEscapingTest(DeviceTest):
+    def test_shell_escaping(self):
+        """Make sure that argument escaping is somewhat sane."""
+
+        # http://b/19734868
+        # Note that this actually matches ssh(1)'s behavior --- it's
+        # converted to `sh -c echo hello; echo world` which sh interprets
+        # as `sh -c echo` (with an argument to that shell of "hello"),
+        # and then `echo world` back in the first shell.
+        result = self.device.shell(
+            shlex.split("sh -c 'echo hello; echo world'"))
+        result = result.splitlines()
+        self.assertEqual(['', 'world'], result)
+        # If you really wanted "hello" and "world", here's what you'd do:
+        result = self.device.shell(
+            shlex.split(r'echo hello\;echo world')).splitlines()
+        self.assertEqual(['hello', 'world'], result)
+
+        # http://b/15479704
+        result = self.device.shell(shlex.split("'true && echo t'")).strip()
+        self.assertEqual('t', result)
+        result = self.device.shell(
+            shlex.split("sh -c 'true && echo t'")).strip()
+        self.assertEqual('t', result)
+
+        # http://b/20564385
+        result = self.device.shell(shlex.split('FOO=a BAR=b echo t')).strip()
+        self.assertEqual('t', result)
+        result = self.device.shell(shlex.split(r'echo -n 123\;uname')).strip()
+        self.assertEqual('123Linux', result)
+
+    def test_install_argument_escaping(self):
+        """Make sure that install argument escaping works."""
+        # http://b/20323053
+        tf = tempfile.NamedTemporaryFile('wb', suffix='-text;ls;1.apk',
+                                         delete=False)
+        tf.close()
+        self.assertIn("-text;ls;1.apk", self.device.install(tf.name))
+        os.remove(tf.name)
+
+        # http://b/3090932
+        tf = tempfile.NamedTemporaryFile('wb', suffix="-Live Hold'em.apk",
+                                         delete=False)
+        tf.close()
+        self.assertIn("-Live Hold'em.apk", self.device.install(tf.name))
+        os.remove(tf.name)
+
+
+class RootUnrootTest(DeviceTest):
+    def _test_root(self):
+        message = self.device.root()
+        if 'adbd cannot run as root in production builds' in message:
+            return
+        self.device.wait()
+        self.assertEqual('root', self.device.shell(['id', '-un']).strip())
+
+    def _test_unroot(self):
+        self.device.unroot()
+        self.device.wait()
+        self.assertEqual('shell', self.device.shell(['id', '-un']).strip())
+
+    def test_root_unroot(self):
+        """Make sure that adb root and adb unroot work, using id(1)."""
+        if self.device.get_prop('ro.debuggable') != '1':
+            raise unittest.SkipTest('requires rootable build')
+
+        original_user = self.device.shell(['id', '-un']).strip()
+        try:
+            if original_user == 'root':
+                self._test_unroot()
+                self._test_root()
+            elif original_user == 'shell':
+                self._test_root()
+                self._test_unroot()
+        finally:
+            if original_user == 'root':
+                self.device.root()
+            else:
+                self.device.unroot()
+            self.device.wait()
+
+
+class TcpIpTest(DeviceTest):
+    def test_tcpip_failure_raises(self):
+        """adb tcpip requires a port.
+
+        Bug: http://b/22636927
+        """
+        self.assertRaises(
+            subprocess.CalledProcessError, self.device.tcpip, '')
+        self.assertRaises(
+            subprocess.CalledProcessError, self.device.tcpip, 'foo')
+
+
+class SystemPropertiesTest(DeviceTest):
+    def test_get_prop(self):
+        self.assertEqual(self.device.get_prop('init.svc.adbd'), 'running')
+
+    @requires_root
+    def test_set_prop(self):
+        prop_name = 'foo.bar'
+        self.device.shell(['setprop', prop_name, '""'])
+
+        self.device.set_prop(prop_name, 'qux')
+        self.assertEqual(
+            self.device.shell(['getprop', prop_name]).strip(), 'qux')
+
+
+def compute_md5(string):
+    hsh = hashlib.md5()
+    hsh.update(string)
+    return hsh.hexdigest()
+
+
+def get_md5_prog(device):
+    """Older platforms (pre-L) had the name md5 rather than md5sum."""
+    try:
+        device.shell(['md5sum', '/proc/uptime'])
+        return 'md5sum'
+    except subprocess.CalledProcessError:
+        return 'md5'
+
+
+class HostFile(object):
+    def __init__(self, handle, checksum):
+        self.handle = handle
+        self.checksum = checksum
+        self.full_path = handle.name
+        self.base_name = os.path.basename(self.full_path)
+
+
+class DeviceFile(object):
+    def __init__(self, checksum, full_path):
+        self.checksum = checksum
+        self.full_path = full_path
+        self.base_name = posixpath.basename(self.full_path)
+
+
+def make_random_host_files(in_dir, num_files):
+    min_size = 1 * (1 << 10)
+    max_size = 16 * (1 << 10)
+
+    files = []
+    for _ in xrange(num_files):
+        file_handle = tempfile.NamedTemporaryFile(dir=in_dir, delete=False)
+
+        size = random.randrange(min_size, max_size, 1024)
+        rand_str = os.urandom(size)
+        file_handle.write(rand_str)
+        file_handle.flush()
+        file_handle.close()
+
+        md5 = compute_md5(rand_str)
+        files.append(HostFile(file_handle, md5))
+    return files
+
+
+def make_random_device_files(device, in_dir, num_files):
+    min_size = 1 * (1 << 10)
+    max_size = 16 * (1 << 10)
+
+    files = []
+    for file_num in xrange(num_files):
+        size = random.randrange(min_size, max_size, 1024)
+
+        base_name = 'device_tmpfile' + str(file_num)
+        full_path = posixpath.join(in_dir, base_name)
+
+        device.shell(['dd', 'if=/dev/urandom', 'of={}'.format(full_path),
+                      'bs={}'.format(size), 'count=1'])
+        dev_md5, _ = device.shell([get_md5_prog(device), full_path]).split()
+
+        files.append(DeviceFile(dev_md5, full_path))
+    return files
+
+
+class FileOperationsTest(DeviceTest):
+    SCRATCH_DIR = '/data/local/tmp'
+    DEVICE_TEMP_FILE = SCRATCH_DIR + '/adb_test_file'
+    DEVICE_TEMP_DIR = SCRATCH_DIR + '/adb_test_dir'
+
+    def _test_push(self, local_file, checksum):
+        self.device.shell(['rm', '-rf', self.DEVICE_TEMP_FILE])
+        self.device.push(local=local_file, remote=self.DEVICE_TEMP_FILE)
+        dev_md5, _ = self.device.shell([get_md5_prog(self.device),
+                                       self.DEVICE_TEMP_FILE]).split()
+        self.assertEqual(checksum, dev_md5)
+        self.device.shell(['rm', '-f', self.DEVICE_TEMP_FILE])
+
+    def test_push(self):
+        """Push a randomly generated file to specified device."""
+        kbytes = 512
+        tmp = tempfile.NamedTemporaryFile(mode='wb', delete=False)
+        rand_str = os.urandom(1024 * kbytes)
+        tmp.write(rand_str)
+        tmp.close()
+        self._test_push(tmp.name, compute_md5(rand_str))
+        os.remove(tmp.name)
+
+    # TODO: write push directory test.
+
+    def _test_pull(self, remote_file, checksum):
+        tmp_write = tempfile.NamedTemporaryFile(mode='wb', delete=False)
+        tmp_write.close()
+        self.device.pull(remote=remote_file, local=tmp_write.name)
+        with open(tmp_write.name, 'rb') as tmp_read:
+            host_contents = tmp_read.read()
+            host_md5 = compute_md5(host_contents)
+        self.assertEqual(checksum, host_md5)
+        os.remove(tmp_write.name)
+
+    def test_pull(self):
+        """Pull a randomly generated file from specified device."""
+        kbytes = 512
+        self.device.shell(['rm', '-rf', self.DEVICE_TEMP_FILE])
+        cmd = ['dd', 'if=/dev/urandom',
+               'of={}'.format(self.DEVICE_TEMP_FILE), 'bs=1024',
+               'count={}'.format(kbytes)]
+        self.device.shell(cmd)
+        dev_md5, _ = self.device.shell(
+            [get_md5_prog(self.device), self.DEVICE_TEMP_FILE]).split()
+        self._test_pull(self.DEVICE_TEMP_FILE, dev_md5)
+        self.device.shell_nocheck(['rm', self.DEVICE_TEMP_FILE])
+
+    def test_pull_dir(self):
+        """Pull a randomly generated directory of files from the device."""
+        host_dir = tempfile.mkdtemp()
+        self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+        self.device.shell(['mkdir', '-p', self.DEVICE_TEMP_DIR])
+
+        # Populate device directory with random files.
+        temp_files = make_random_device_files(
+            self.device, in_dir=self.DEVICE_TEMP_DIR, num_files=32)
+
+        self.device.pull(remote=self.DEVICE_TEMP_DIR, local=host_dir)
+
+        for temp_file in temp_files:
+            host_path = os.path.join(host_dir, temp_file.base_name)
+            with open(host_path, 'rb') as host_file:
+                host_md5 = compute_md5(host_file.read())
+                self.assertEqual(host_md5, temp_file.checksum)
+
+        self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+        if host_dir is not None:
+            shutil.rmtree(host_dir)
+
+    def test_sync(self):
+        """Sync a randomly generated directory of files to specified device."""
+        base_dir = tempfile.mkdtemp()
+
+        # Create mirror device directory hierarchy within base_dir.
+        full_dir_path = base_dir + self.DEVICE_TEMP_DIR
+        os.makedirs(full_dir_path)
+
+        # Create 32 random files within the host mirror.
+        temp_files = make_random_host_files(in_dir=full_dir_path, num_files=32)
+
+        # Clean up any trash on the device.
+        device = adb.get_device(product=base_dir)
+        device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+
+        device.sync('data')
+
+        # Confirm that every file on the device mirrors that on the host.
+        for temp_file in temp_files:
+            device_full_path = posixpath.join(self.DEVICE_TEMP_DIR,
+                                              temp_file.base_name)
+            dev_md5, _ = device.shell(
+                [get_md5_prog(self.device), device_full_path]).split()
+            self.assertEqual(temp_file.checksum, dev_md5)
+
+        self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+        shutil.rmtree(base_dir + self.DEVICE_TEMP_DIR)
+
+    def test_unicode_paths(self):
+        """Ensure that we can support non-ASCII paths, even on Windows."""
+        name = u'로보카 폴리'
+
+        ## push.
+        tf = tempfile.NamedTemporaryFile('wb', suffix=name, delete=False)
+        tf.close()
+        self.device.push(tf.name, u'/data/local/tmp/adb-test-{}'.format(name))
+        os.remove(tf.name)
+        self.device.shell(['rm', '-f', '/data/local/tmp/adb-test-*'])
+
+        # pull.
+        cmd = ['touch', u'"/data/local/tmp/adb-test-{}"'.format(name)]
+        self.device.shell(cmd)
+
+        tf = tempfile.NamedTemporaryFile('wb', suffix=name, delete=False)
+        tf.close()
+        self.device.pull(u'/data/local/tmp/adb-test-{}'.format(name), tf.name)
+        os.remove(tf.name)
+        self.device.shell(['rm', '-f', '/data/local/tmp/adb-test-*'])
+
+
+def main():
+    random.seed(0)
+    if len(adb.get_devices()) > 0:
+        suite = unittest.TestLoader().loadTestsFromName(__name__)
+        unittest.TextTestRunner(verbosity=3).run(suite)
+    else:
+        print('Test suite must be run with attached devices')
+
+
+if __name__ == '__main__':
+    main()
diff --git a/adb/test_track_devices.cpp b/adb/test_track_devices.cpp
index 3e823e9..6f658f6 100644
--- a/adb/test_track_devices.cpp
+++ b/adb/test_track_devices.cpp
@@ -1,12 +1,13 @@
 // TODO: replace this with a shell/python script.
 
 /* a simple test program, connects to ADB server, and opens a track-devices session */
-#include <netdb.h>
-#include <sys/socket.h>
-#include <stdio.h>
-#include <stdlib.h>
 #include <errno.h>
 #include <memory.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <unistd.h>
 
 #include <base/file.h>
 
@@ -62,7 +63,7 @@
         if (!android::base::ReadFully(s, buffer, len))
             panic("could not read data");
 
-        printf( "received header %.*s (%d bytes):\n%.*s", 4, head, len, len, buffer );
+        printf( "received header %.*s (%d bytes):\n%.*s----\n", 4, head, len, len, buffer );
     }
     close(s);
 }
diff --git a/adb/tests/test_adb.py b/adb/tests/test_adb.py
deleted file mode 100755
index 0ff87d2..0000000
--- a/adb/tests/test_adb.py
+++ /dev/null
@@ -1,439 +0,0 @@
-#!/usr/bin/env python2
-"""Simple conformance test for adb.
-
-This script will use the available adb in path and run simple
-tests that attempt to touch all accessible attached devices.
-"""
-import hashlib
-import os
-import pipes
-import random
-import re
-import shlex
-import subprocess
-import sys
-import tempfile
-import unittest
-
-
-def trace(cmd):
-    """Print debug message if tracing enabled."""
-    if False:
-        print >> sys.stderr, cmd
-
-
-def call(cmd_str):
-    """Run process and return output tuple (stdout, stderr, ret code)."""
-    trace(cmd_str)
-    process = subprocess.Popen(shlex.split(cmd_str),
-                               stdout=subprocess.PIPE,
-                               stderr=subprocess.PIPE)
-    stdout, stderr = process.communicate()
-    return stdout, stderr, process.returncode
-
-
-def call_combined(cmd_str):
-    """Run process and return output tuple (stdout+stderr, ret code)."""
-    trace(cmd_str)
-    process = subprocess.Popen(shlex.split(cmd_str),
-                               stdout=subprocess.PIPE,
-                               stderr=subprocess.STDOUT)
-    stdout, _ = process.communicate()
-    return stdout, process.returncode
-
-
-def call_checked(cmd_str):
-    """Run process and get stdout+stderr, raise an exception on trouble."""
-    trace(cmd_str)
-    return subprocess.check_output(shlex.split(cmd_str),
-                                   stderr=subprocess.STDOUT)
-
-
-def call_checked_list(cmd_str):
-    return call_checked(cmd_str).split('\n')
-
-
-def call_checked_list_skip(cmd_str):
-    out_list = call_checked_list(cmd_str)
-
-    def is_init_line(line):
-        if (len(line) >= 3) and (line[0] == "*") and (line[-2] == "*"):
-            return True
-        else:
-            return False
-
-    return [line for line in out_list if not is_init_line(line)]
-
-
-def get_device_list():
-    output = call_checked_list_skip("adb devices")
-    dev_list = []
-    for line in output[1:]:
-        if line.strip() == "":
-            continue
-        device, _ = line.split()
-        dev_list.append(device)
-    return dev_list
-
-
-def get_attached_device_count():
-    return len(get_device_list())
-
-
-def compute_md5(string):
-    hsh = hashlib.md5()
-    hsh.update(string)
-    return hsh.hexdigest()
-
-
-class HostFile(object):
-    def __init__(self, handle, md5):
-        self.handle = handle
-        self.md5 = md5
-        self.full_path = handle.name
-        self.base_name = os.path.basename(self.full_path)
-
-
-class DeviceFile(object):
-    def __init__(self, md5, full_path):
-        self.md5 = md5
-        self.full_path = full_path
-        self.base_name = os.path.basename(self.full_path)
-
-
-def make_random_host_files(in_dir, num_files, rand_size=True):
-    files = {}
-    min_size = 1 * (1 << 10)
-    max_size = 16 * (1 << 10)
-    fixed_size = min_size
-
-    for _ in range(num_files):
-        file_handle = tempfile.NamedTemporaryFile(dir=in_dir)
-
-        if rand_size:
-            size = random.randrange(min_size, max_size, 1024)
-        else:
-            size = fixed_size
-        rand_str = os.urandom(size)
-        file_handle.write(rand_str)
-        file_handle.flush()
-
-        md5 = compute_md5(rand_str)
-        files[file_handle.name] = HostFile(file_handle, md5)
-    return files
-
-
-def make_random_device_files(adb, in_dir, num_files, rand_size=True):
-    files = {}
-    min_size = 1 * (1 << 10)
-    max_size = 16 * (1 << 10)
-    fixed_size = min_size
-
-    for i in range(num_files):
-        if rand_size:
-            size = random.randrange(min_size, max_size, 1024)
-        else:
-            size = fixed_size
-
-        base_name = "device_tmpfile" + str(i)
-        full_path = in_dir + "/" + base_name
-
-        adb.shell("dd if=/dev/urandom of={} bs={} count=1".format(full_path,
-                                                                  size))
-        dev_md5, _ = adb.shell("md5sum {}".format(full_path)).split()
-
-        files[full_path] = DeviceFile(dev_md5, full_path)
-    return files
-
-
-class AdbWrapper(object):
-    """Convenience wrapper object for the adb command."""
-    def __init__(self, device=None, out_dir=None):
-        self.device = device
-        self.out_dir = out_dir
-        self.adb_cmd = "adb "
-        if self.device:
-            self.adb_cmd += "-s {} ".format(device)
-        if self.out_dir:
-            self.adb_cmd += "-p {} ".format(out_dir)
-
-    def shell(self, cmd):
-        return call_checked(self.adb_cmd + "shell " + cmd)
-
-    def shell_nocheck(self, cmd):
-        return call_combined(self.adb_cmd + "shell " + cmd)
-
-    def install(self, filename):
-        return call_checked(self.adb_cmd + "install {}".format(pipes.quote(filename)))
-
-    def push(self, local, remote):
-        return call_checked(self.adb_cmd + "push {} {}".format(local, remote))
-
-    def pull(self, remote, local):
-        return call_checked(self.adb_cmd + "pull {} {}".format(remote, local))
-
-    def sync(self, directory=""):
-        return call_checked(self.adb_cmd + "sync {}".format(directory))
-
-    def forward(self, local, remote):
-        return call_checked(self.adb_cmd + "forward {} {}".format(local,
-                                                                  remote))
-
-    def tcpip(self, port):
-        return call_checked(self.adb_cmd + "tcpip {}".format(port))
-
-    def usb(self):
-        return call_checked(self.adb_cmd + "usb")
-
-    def root(self):
-        return call_checked(self.adb_cmd + "root")
-
-    def unroot(self):
-        return call_checked(self.adb_cmd + "unroot")
-
-    def forward_remove(self, local):
-        return call_checked(self.adb_cmd + "forward --remove {}".format(local))
-
-    def forward_remove_all(self):
-        return call_checked(self.adb_cmd + "forward --remove-all")
-
-    def connect(self, host):
-        return call_checked(self.adb_cmd + "connect {}".format(host))
-
-    def disconnect(self, host):
-        return call_checked(self.adb_cmd + "disconnect {}".format(host))
-
-    def reverse(self, remote, local):
-        return call_checked(self.adb_cmd + "reverse {} {}".format(remote,
-                                                                  local))
-
-    def reverse_remove_all(self):
-        return call_checked(self.adb_cmd + "reverse --remove-all")
-
-    def reverse_remove(self, remote):
-        return call_checked(
-            self.adb_cmd + "reverse --remove {}".format(remote))
-
-    def wait(self):
-        return call_checked(self.adb_cmd + "wait-for-device")
-
-
-class AdbBasic(unittest.TestCase):
-    def test_shell(self):
-        """Check that we can at least cat a file."""
-        adb = AdbWrapper()
-        out = adb.shell("cat /proc/uptime")
-        self.assertEqual(len(out.split()), 2)
-        self.assertGreater(float(out.split()[0]), 0.0)
-        self.assertGreater(float(out.split()[1]), 0.0)
-
-    def test_help(self):
-        """Make sure we get _something_ out of help."""
-        out = call_checked("adb help")
-        self.assertTrue(len(out) > 0)
-
-    def test_version(self):
-        """Get a version number out of the output of adb."""
-        out = call_checked("adb version").split()
-        version_num = False
-        for item in out:
-            if re.match(r"[\d+\.]*\d", item):
-                version_num = True
-        self.assertTrue(version_num)
-
-    def _test_root(self):
-        adb = AdbWrapper()
-        adb.root()
-        adb.wait()
-        self.assertEqual("root", adb.shell("id -un").strip())
-
-    def _test_unroot(self):
-        adb = AdbWrapper()
-        adb.unroot()
-        adb.wait()
-        self.assertEqual("shell", adb.shell("id -un").strip())
-
-    def test_root_unroot(self):
-        """Make sure that adb root and adb unroot work, using id(1)."""
-        adb = AdbWrapper()
-        original_user = adb.shell("id -un").strip()
-        try:
-            if original_user == "root":
-                self._test_unroot()
-                self._test_root()
-            elif original_user == "shell":
-                self._test_root()
-                self._test_unroot()
-        finally:
-            if original_user == "root":
-                adb.root()
-            else:
-                adb.unroot()
-            adb.wait()
-
-    def test_argument_escaping(self):
-        """Make sure that argument escaping is somewhat sane."""
-        adb = AdbWrapper()
-
-        # http://b/19734868
-        # Note that this actually matches ssh(1)'s behavior --- it's
-        # converted to "sh -c echo hello; echo world" which sh interprets
-        # as "sh -c echo" (with an argument to that shell of "hello"),
-        # and then "echo world" back in the first shell.
-        result = adb.shell("sh -c 'echo hello; echo world'").splitlines()
-        self.assertEqual(["", "world"], result)
-        # If you really wanted "hello" and "world", here's what you'd do:
-        result = adb.shell("echo hello\;echo world").splitlines()
-        self.assertEqual(["hello", "world"], result)
-
-        # http://b/15479704
-        self.assertEqual('t', adb.shell("'true && echo t'").strip())
-        self.assertEqual('t', adb.shell("sh -c 'true && echo t'").strip())
-
-        # http://b/20564385
-        self.assertEqual('t', adb.shell("FOO=a BAR=b echo t").strip())
-        self.assertEqual('123Linux', adb.shell("echo -n 123\;uname").strip())
-
-    def test_install_argument_escaping(self):
-        """Make sure that install argument escaping works."""
-        adb = AdbWrapper()
-
-        # http://b/20323053
-        tf = tempfile.NamedTemporaryFile("w", suffix="-text;ls;1.apk")
-        self.assertIn("-text;ls;1.apk", adb.install(tf.name))
-
-        # http://b/3090932
-        tf = tempfile.NamedTemporaryFile("w", suffix="-Live Hold'em.apk")
-        self.assertIn("-Live Hold'em.apk", adb.install(tf.name))
-
-
-class AdbFile(unittest.TestCase):
-    SCRATCH_DIR = "/data/local/tmp"
-    DEVICE_TEMP_FILE = SCRATCH_DIR + "/adb_test_file"
-    DEVICE_TEMP_DIR = SCRATCH_DIR + "/adb_test_dir"
-
-    def test_push(self):
-        """Push a randomly generated file to specified device."""
-        kbytes = 512
-        adb = AdbWrapper()
-        with tempfile.NamedTemporaryFile(mode="w") as tmp:
-            rand_str = os.urandom(1024 * kbytes)
-            tmp.write(rand_str)
-            tmp.flush()
-
-            host_md5 = compute_md5(rand_str)
-            adb.shell_nocheck("rm -r {}".format(AdbFile.DEVICE_TEMP_FILE))
-            try:
-                adb.push(local=tmp.name, remote=AdbFile.DEVICE_TEMP_FILE)
-                dev_md5, _ = adb.shell(
-                    "md5sum {}".format(AdbFile.DEVICE_TEMP_FILE)).split()
-                self.assertEqual(host_md5, dev_md5)
-            finally:
-                adb.shell_nocheck("rm {}".format(AdbFile.DEVICE_TEMP_FILE))
-
-    # TODO: write push directory test.
-
-    def test_pull(self):
-        """Pull a randomly generated file from specified device."""
-        kbytes = 512
-        adb = AdbWrapper()
-        adb.shell_nocheck("rm -r {}".format(AdbFile.DEVICE_TEMP_FILE))
-        try:
-            adb.shell("dd if=/dev/urandom of={} bs=1024 count={}".format(
-                AdbFile.DEVICE_TEMP_FILE, kbytes))
-            dev_md5, _ = adb.shell(
-                "md5sum {}".format(AdbFile.DEVICE_TEMP_FILE)).split()
-
-            with tempfile.NamedTemporaryFile(mode="w") as tmp_write:
-                adb.pull(remote=AdbFile.DEVICE_TEMP_FILE, local=tmp_write.name)
-                with open(tmp_write.name) as tmp_read:
-                    host_contents = tmp_read.read()
-                    host_md5 = compute_md5(host_contents)
-                self.assertEqual(dev_md5, host_md5)
-        finally:
-            adb.shell_nocheck("rm {}".format(AdbFile.DEVICE_TEMP_FILE))
-
-    def test_pull_dir(self):
-        """Pull a randomly generated directory of files from the device."""
-        adb = AdbWrapper()
-        temp_files = {}
-        host_dir = None
-        try:
-            # create temporary host directory
-            host_dir = tempfile.mkdtemp()
-
-            # create temporary dir on device
-            adb.shell_nocheck("rm -r {}".format(AdbFile.DEVICE_TEMP_DIR))
-            adb.shell("mkdir -p {}".format(AdbFile.DEVICE_TEMP_DIR))
-
-            # populate device dir with random files
-            temp_files = make_random_device_files(
-                adb, in_dir=AdbFile.DEVICE_TEMP_DIR, num_files=32)
-
-            adb.pull(remote=AdbFile.DEVICE_TEMP_DIR, local=host_dir)
-
-            for device_full_path in temp_files:
-                host_path = os.path.join(
-                    host_dir, temp_files[device_full_path].base_name)
-                with open(host_path) as host_file:
-                    host_md5 = compute_md5(host_file.read())
-                    self.assertEqual(host_md5,
-                                     temp_files[device_full_path].md5)
-        finally:
-            for dev_file in temp_files.values():
-                host_path = os.path.join(host_dir, dev_file.base_name)
-                os.remove(host_path)
-            adb.shell_nocheck("rm -r {}".format(AdbFile.DEVICE_TEMP_DIR))
-            if host_dir:
-                os.removedirs(host_dir)
-
-    def test_sync(self):
-        """Sync a randomly generated directory of files to specified device."""
-        try:
-            adb = AdbWrapper()
-            temp_files = {}
-
-            # create temporary host directory
-            base_dir = tempfile.mkdtemp()
-
-            # create mirror device directory hierarchy within base_dir
-            full_dir_path = base_dir + AdbFile.DEVICE_TEMP_DIR
-            os.makedirs(full_dir_path)
-
-            # create 32 random files within the host mirror
-            temp_files = make_random_host_files(in_dir=full_dir_path,
-                                                num_files=32)
-
-            # clean up any trash on the device
-            adb = AdbWrapper(out_dir=base_dir)
-            adb.shell_nocheck("rm -r {}".format(AdbFile.DEVICE_TEMP_DIR))
-
-            # issue the sync
-            adb.sync("data")
-
-            # confirm that every file on the device mirrors that on the host
-            for host_full_path in temp_files.keys():
-                device_full_path = os.path.join(
-                    AdbFile.DEVICE_TEMP_DIR,
-                    temp_files[host_full_path].base_name)
-                dev_md5, _ = adb.shell(
-                    "md5sum {}".format(device_full_path)).split()
-                self.assertEqual(temp_files[host_full_path].md5, dev_md5)
-
-        finally:
-            adb.shell_nocheck("rm -r {}".format(AdbFile.DEVICE_TEMP_DIR))
-            if temp_files:
-                for tf in temp_files.values():
-                    tf.handle.close()
-            if base_dir:
-                os.removedirs(base_dir + AdbFile.DEVICE_TEMP_DIR)
-
-
-if __name__ == '__main__':
-    random.seed(0)
-    dev_count = get_attached_device_count()
-    if dev_count:
-        suite = unittest.TestLoader().loadTestsFromName(__name__)
-        unittest.TextTestRunner(verbosity=3).run(suite)
-    else:
-        print "Test suite must be run with attached devices"
diff --git a/adb/transport.cpp b/adb/transport.cpp
index 2cd6ac2..5a962de 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -26,72 +26,22 @@
 #include <string.h>
 #include <unistd.h>
 
+#include <list>
+
+#include <base/logging.h>
 #include <base/stringprintf.h>
+#include <base/strings.h>
 
 #include "adb.h"
 #include "adb_utils.h"
 
 static void transport_unref(atransport *t);
 
-static atransport transport_list = {
-    .next = &transport_list,
-    .prev = &transport_list,
-};
-
-static atransport pending_list = {
-    .next = &pending_list,
-    .prev = &pending_list,
-};
+static std::list<atransport*> transport_list;
+static std::list<atransport*> pending_list;
 
 ADB_MUTEX_DEFINE( transport_lock );
 
-void kick_transport(atransport* t)
-{
-    if (t && !t->kicked)
-    {
-        int  kicked;
-
-        adb_mutex_lock(&transport_lock);
-        kicked = t->kicked;
-        if (!kicked)
-            t->kicked = 1;
-        adb_mutex_unlock(&transport_lock);
-
-        if (!kicked)
-            t->kick(t);
-    }
-}
-
-// Each atransport contains a list of adisconnects (t->disconnects).
-// An adisconnect contains a link to the next/prev adisconnect, a function
-// pointer to a disconnect callback which takes a void* piece of user data and
-// the atransport, and some user data for the callback (helpfully named
-// "opaque").
-//
-// The list is circular. New items are added to the entry member of the list
-// (t->disconnects) by add_transport_disconnect.
-//
-// run_transport_disconnects invokes each function in the list.
-//
-// Gotchas:
-//   * run_transport_disconnects assumes that t->disconnects is non-null, so
-//     this can't be run on a zeroed atransport.
-//   * The callbacks in this list are not removed when called, and this function
-//     is not guarded against running more than once. As such, ensure that this
-//     function is not called multiple times on the same atransport.
-//     TODO(danalbert): Just fix this so that it is guarded once you have tests.
-void run_transport_disconnects(atransport* t)
-{
-    adisconnect*  dis = t->disconnects.next;
-
-    D("%s: run_transport_disconnects\n", t->serial);
-    while (dis != &t->disconnects) {
-        adisconnect*  next = dis->next;
-        dis->func( dis->opaque, t );
-        dis = next;
-    }
-}
-
 static void dump_packet(const char* name, const char* func, apacket* p) {
     unsigned  command = p->msg.command;
     int       len     = p->msg.data_length;
@@ -131,22 +81,20 @@
 static int
 read_packet(int  fd, const char* name, apacket** ppacket)
 {
-    char *p = (char*)ppacket;  /* really read a packet address */
-    int   r;
-    int   len = sizeof(*ppacket);
-    char  buff[8];
+    char buff[8];
     if (!name) {
         snprintf(buff, sizeof buff, "fd=%d", fd);
         name = buff;
     }
+    char* p = reinterpret_cast<char*>(ppacket);  /* really read a packet address */
+    int len = sizeof(apacket*);
     while(len > 0) {
-        r = adb_read(fd, p, len);
+        int r = adb_read(fd, p, len);
         if(r > 0) {
             len -= r;
-            p   += r;
+            p += r;
         } else {
-            D("%s: read_packet (fd=%d), error ret=%d errno=%d: %s\n", name, fd, r, errno, strerror(errno));
-            if((r < 0) && (errno == EINTR)) continue;
+            D("%s: read_packet (fd=%d), error ret=%d: %s\n", name, fd, r, strerror(errno));
             return -1;
         }
     }
@@ -160,26 +108,23 @@
 static int
 write_packet(int  fd, const char* name, apacket** ppacket)
 {
-    char *p = (char*) ppacket;  /* we really write the packet address */
-    int r, len = sizeof(ppacket);
     char buff[8];
     if (!name) {
         snprintf(buff, sizeof buff, "fd=%d", fd);
         name = buff;
     }
-
     if (ADB_TRACING) {
         dump_packet(name, "to remote", *ppacket);
     }
-    len = sizeof(ppacket);
+    char* p = reinterpret_cast<char*>(ppacket);  /* we really write the packet address */
+    int len = sizeof(apacket*);
     while(len > 0) {
-        r = adb_write(fd, p, len);
+        int r = adb_write(fd, p, len);
         if(r > 0) {
             len -= r;
             p += r;
         } else {
-            D("%s: write_packet (fd=%d) error ret=%d errno=%d: %s\n", name, fd, r, errno, strerror(errno));
-            if((r < 0) && (errno == EINTR)) continue;
+            D("%s: write_packet (fd=%d) error ret=%d: %s\n", name, fd, r, strerror(errno));
             return -1;
         }
     }
@@ -230,25 +175,27 @@
     }
 }
 
-/* The transport is opened by transport_register_func before
-** the input and output threads are started.
-**
-** The output thread issues a SYNC(1, token) message to let
-** the input thread know to start things up.  In the event
-** of transport IO failure, the output thread will post a
-** SYNC(0,0) message to ensure shutdown.
-**
-** The transport will not actually be closed until both
-** threads exit, but the input thread will kick the transport
-** on its way out to disconnect the underlying device.
-*/
-
-static void *output_thread(void *_t)
+// The transport is opened by transport_register_func before
+// the read_transport and write_transport threads are started.
+//
+// The read_transport thread issues a SYNC(1, token) message to let
+// the write_transport thread know to start things up.  In the event
+// of transport IO failure, the read_transport thread will post a
+// SYNC(0,0) message to ensure shutdown.
+//
+// The transport will not actually be closed until both threads exit, but the threads
+// will kick the transport on their way out to disconnect the underlying device.
+//
+// read_transport thread reads data from a transport (representing a usb/tcp connection),
+// and makes the main thread call handle_packet().
+static void *read_transport_thread(void *_t)
 {
     atransport *t = reinterpret_cast<atransport*>(_t);
     apacket *p;
 
-    D("%s: starting transport output thread on fd %d, SYNC online (%d)\n",
+    adb_thread_setname(android::base::StringPrintf("<-%s",
+                                                   (t->serial != nullptr ? t->serial : "transport")));
+    D("%s: starting read_transport thread on fd %d, SYNC online (%d)\n",
        t->serial, t->fd, t->sync_token + 1);
     p = get_apacket();
     p->msg.command = A_SYNC;
@@ -288,23 +235,27 @@
     p->msg.magic = A_SYNC ^ 0xffffffff;
     if(write_packet(t->fd, t->serial, &p)) {
         put_apacket(p);
-        D("%s: failed to write SYNC apacket to transport", t->serial);
+        D("%s: failed to write SYNC apacket to transport\n", t->serial);
     }
 
 oops:
-    D("%s: transport output thread is exiting\n", t->serial);
+    D("%s: read_transport thread is exiting\n", t->serial);
     kick_transport(t);
     transport_unref(t);
     return 0;
 }
 
-static void *input_thread(void *_t)
+// write_transport thread gets packets sent by the main thread (through send_packet()),
+// and writes to a transport (representing a usb/tcp connection).
+static void *write_transport_thread(void *_t)
 {
     atransport *t = reinterpret_cast<atransport*>(_t);
     apacket *p;
     int active = 0;
 
-    D("%s: starting transport input thread, reading from fd %d\n",
+    adb_thread_setname(android::base::StringPrintf("->%s",
+                                                   (t->serial != nullptr ? t->serial : "transport")));
+    D("%s: starting write_transport thread, reading from fd %d\n",
        t->serial, t->fd);
 
     for(;;){
@@ -339,16 +290,25 @@
         put_apacket(p);
     }
 
-    // this is necessary to avoid a race condition that occured when a transport closes
-    // while a client socket is still active.
-    close_all_sockets(t);
-
-    D("%s: transport input thread is exiting, fd %d\n", t->serial, t->fd);
+    D("%s: write_transport thread is exiting, fd %d\n", t->serial, t->fd);
     kick_transport(t);
     transport_unref(t);
     return 0;
 }
 
+static void kick_transport_locked(atransport* t) {
+    CHECK(t != nullptr);
+    if (!t->kicked) {
+        t->kicked = true;
+        t->kick(t);
+    }
+}
+
+void kick_transport(atransport* t) {
+    adb_mutex_lock(&transport_lock);
+    kick_transport_locked(t);
+    adb_mutex_unlock(&transport_lock);
+}
 
 static int transport_registration_send = -1;
 static int transport_registration_recv = -1;
@@ -496,9 +456,7 @@
             len -= r;
             p   += r;
         } else {
-            if((r < 0) && (errno == EINTR)) continue;
-            D("transport_read_action: on fd %d, error %d: %s\n",
-              fd, errno, strerror(errno));
+            D("transport_read_action: on fd %d: %s\n", fd, strerror(errno));
             return -1;
         }
     }
@@ -518,9 +476,7 @@
             len -= r;
             p   += r;
         } else {
-            if((r < 0) && (errno == EINTR)) continue;
-            D("transport_write_action: on fd %d, error %d: %s\n",
-              fd, errno, strerror(errno));
+            D("transport_write_action: on fd %d: %s\n", fd, strerror(errno));
             return -1;
         }
     }
@@ -530,8 +486,6 @@
 static void transport_registration_func(int _fd, unsigned ev, void *data)
 {
     tmsg m;
-    adb_thread_t output_thread_ptr;
-    adb_thread_t input_thread_ptr;
     int s[2];
     atransport *t;
 
@@ -545,7 +499,7 @@
 
     t = m.transport;
 
-    if(m.action == 0){
+    if (m.action == 0) {
         D("transport: %s removing and free'ing %d\n", t->serial, t->transport_socket);
 
             /* IMPORTANT: the remove closes one half of the
@@ -555,12 +509,9 @@
         adb_close(t->fd);
 
         adb_mutex_lock(&transport_lock);
-        t->next->prev = t->prev;
-        t->prev->next = t->next;
+        transport_list.remove(t);
         adb_mutex_unlock(&transport_lock);
 
-        run_transport_disconnects(t);
-
         if (t->product)
             free(t->product);
         if (t->serial)
@@ -572,23 +523,22 @@
         if (t->devpath)
             free(t->devpath);
 
-        memset(t,0xee,sizeof(atransport));
-        free(t);
+        delete t;
 
         update_transports();
         return;
     }
 
     /* don't create transport threads for inaccessible devices */
-    if (t->connection_state != CS_NOPERM) {
+    if (t->connection_state != kCsNoPerm) {
         /* initial references are the two threads */
         t->ref_count = 2;
 
-        if(adb_socketpair(s)) {
+        if (adb_socketpair(s)) {
             fatal_errno("cannot open transport socketpair");
         }
 
-        D("transport: %s socketpair: (%d,%d) starting", t->serial, s[0], s[1]);
+        D("transport: %s socketpair: (%d,%d) starting\n", t->serial, s[0], s[1]);
 
         t->transport_socket = s[0];
         t->fd = s[1];
@@ -600,28 +550,20 @@
 
         fdevent_set(&(t->transport_fde), FDE_READ);
 
-        if(adb_thread_create(&input_thread_ptr, input_thread, t)){
-            fatal_errno("cannot create input thread");
+        if (!adb_thread_create(write_transport_thread, t)) {
+            fatal_errno("cannot create write_transport thread");
         }
 
-        if(adb_thread_create(&output_thread_ptr, output_thread, t)){
-            fatal_errno("cannot create output thread");
+        if (!adb_thread_create(read_transport_thread, t)) {
+            fatal_errno("cannot create read_transport thread");
         }
     }
 
     adb_mutex_lock(&transport_lock);
-    /* remove from pending list */
-    t->next->prev = t->prev;
-    t->prev->next = t->next;
-    /* put us on the master device list */
-    t->next = &transport_list;
-    t->prev = transport_list.prev;
-    t->next->prev = t;
-    t->prev->next = t;
+    pending_list.remove(t);
+    transport_list.push_front(t);
     adb_mutex_unlock(&transport_lock);
 
-    t->disconnects.next = t->disconnects.prev = &t->disconnects;
-
     update_transports();
 }
 
@@ -632,7 +574,7 @@
     if(adb_socketpair(s)){
         fatal_errno("cannot open transport registration socketpair");
     }
-    D("socketpair: (%d,%d)", s[0], s[1]);
+    D("socketpair: (%d,%d)\n", s[0], s[1]);
 
     transport_registration_send = s[0];
     transport_registration_recv = s[1];
@@ -669,48 +611,22 @@
 }
 
 
-static void transport_unref_locked(atransport *t)
-{
+static void transport_unref(atransport* t) {
+    CHECK(t != nullptr);
+    adb_mutex_lock(&transport_lock);
+    CHECK_GT(t->ref_count, 0u);
     t->ref_count--;
     if (t->ref_count == 0) {
         D("transport: %s unref (kicking and closing)\n", t->serial);
-        if (!t->kicked) {
-            t->kicked = 1;
-            t->kick(t);
-        }
+        kick_transport_locked(t);
         t->close(t);
         remove_transport(t);
     } else {
-        D("transport: %s unref (count=%d)\n", t->serial, t->ref_count);
+        D("transport: %s unref (count=%zu)\n", t->serial, t->ref_count);
     }
-}
-
-static void transport_unref(atransport *t)
-{
-    if (t) {
-        adb_mutex_lock(&transport_lock);
-        transport_unref_locked(t);
-        adb_mutex_unlock(&transport_lock);
-    }
-}
-
-void add_transport_disconnect(atransport*  t, adisconnect*  dis)
-{
-    adb_mutex_lock(&transport_lock);
-    dis->next       = &t->disconnects;
-    dis->prev       = dis->next->prev;
-    dis->prev->next = dis;
-    dis->next->prev = dis;
     adb_mutex_unlock(&transport_lock);
 }
 
-void remove_transport_disconnect(atransport*  t, adisconnect*  dis)
-{
-    dis->prev->next = dis->next;
-    dis->next->prev = dis->prev;
-    dis->next = dis->prev = dis;
-}
-
 static int qual_match(const char *to_test,
                       const char *prefix, const char *qual, bool sanitize_qual)
 {
@@ -740,20 +656,18 @@
     return !*to_test;
 }
 
-atransport* acquire_one_transport(int state, transport_type ttype,
-                                  const char* serial, std::string* error_out)
-{
-    atransport *t;
+atransport* acquire_one_transport(ConnectionState state, TransportType type,
+                                  const char* serial, std::string* error_out) {
     atransport *result = NULL;
     int ambiguous = 0;
 
 retry:
-    if (error_out) *error_out = android::base::StringPrintf("device '%s' not found", serial);
+    *error_out = serial ? android::base::StringPrintf("device '%s' not found", serial) : "no devices found";
 
     adb_mutex_lock(&transport_lock);
-    for (t = transport_list.next; t != &transport_list; t = t->next) {
-        if (t->connection_state == CS_NOPERM) {
-            if (error_out) *error_out = "insufficient permissions for device";
+    for (auto t : transport_list) {
+        if (t->connection_state == kCsNoPerm) {
+            *error_out = "insufficient permissions for device";
             continue;
         }
 
@@ -765,7 +679,7 @@
                 qual_match(serial, "model:", t->model, true) ||
                 qual_match(serial, "device:", t->device, false)) {
                 if (result) {
-                    if (error_out) *error_out = "more than one device";
+                    *error_out = "more than one device";
                     ambiguous = 1;
                     result = NULL;
                     break;
@@ -773,25 +687,25 @@
                 result = t;
             }
         } else {
-            if (ttype == kTransportUsb && t->type == kTransportUsb) {
+            if (type == kTransportUsb && t->type == kTransportUsb) {
                 if (result) {
-                    if (error_out) *error_out = "more than one device";
+                    *error_out = "more than one device";
                     ambiguous = 1;
                     result = NULL;
                     break;
                 }
                 result = t;
-            } else if (ttype == kTransportLocal && t->type == kTransportLocal) {
+            } else if (type == kTransportLocal && t->type == kTransportLocal) {
                 if (result) {
-                    if (error_out) *error_out = "more than one emulator";
+                    *error_out = "more than one emulator";
                     ambiguous = 1;
                     result = NULL;
                     break;
                 }
                 result = t;
-            } else if (ttype == kTransportAny) {
+            } else if (type == kTransportAny) {
                 if (result) {
-                    if (error_out) *error_out = "more than one device/emulator";
+                    *error_out = "more than one device/emulator";
                     ambiguous = 1;
                     result = NULL;
                     break;
@@ -803,35 +717,34 @@
     adb_mutex_unlock(&transport_lock);
 
     if (result) {
-        if (result->connection_state == CS_UNAUTHORIZED) {
-            if (error_out) {
-                *error_out = "device unauthorized.\n";
-                char* ADB_VENDOR_KEYS = getenv("ADB_VENDOR_KEYS");
-                *error_out += "This adbd's $ADB_VENDOR_KEYS is ";
-                *error_out += ADB_VENDOR_KEYS ? ADB_VENDOR_KEYS : "not set";
-                *error_out += "; try 'adb kill-server' if that seems wrong.\n";
-                *error_out += "Otherwise check for a confirmation dialog on your device.";
-            }
+        if (result->connection_state == kCsUnauthorized) {
+            *error_out = "device unauthorized.\n";
+            char* ADB_VENDOR_KEYS = getenv("ADB_VENDOR_KEYS");
+            *error_out += "This adb server's $ADB_VENDOR_KEYS is ";
+            *error_out += ADB_VENDOR_KEYS ? ADB_VENDOR_KEYS : "not set";
+            *error_out += "\n";
+            *error_out += "Try 'adb kill-server' if that seems wrong.\n";
+            *error_out += "Otherwise check for a confirmation dialog on your device.";
             result = NULL;
         }
 
         /* offline devices are ignored -- they are either being born or dying */
-        if (result && result->connection_state == CS_OFFLINE) {
-            if (error_out) *error_out = "device offline";
+        if (result && result->connection_state == kCsOffline) {
+            *error_out = "device offline";
             result = NULL;
         }
 
         /* check for required connection state */
-        if (result && state != CS_ANY && result->connection_state != state) {
-            if (error_out) *error_out = "invalid device state";
+        if (result && state != kCsAny && result->connection_state != state) {
+            *error_out = "invalid device state";
             result = NULL;
         }
     }
 
     if (result) {
         /* found one that we can take */
-        if (error_out) *error_out = "success";
-    } else if (state != CS_ANY && (serial || !ambiguous)) {
+        *error_out = "success";
+    } else if (state != kCsAny && (serial || !ambiguous)) {
         adb_sleep_ms(1000);
         goto retry;
     }
@@ -841,18 +754,68 @@
 
 const char* atransport::connection_state_name() const {
     switch (connection_state) {
-    case CS_OFFLINE: return "offline";
-    case CS_BOOTLOADER: return "bootloader";
-    case CS_DEVICE: return "device";
-    case CS_HOST: return "host";
-    case CS_RECOVERY: return "recovery";
-    case CS_NOPERM: return "no permissions";
-    case CS_SIDELOAD: return "sideload";
-    case CS_UNAUTHORIZED: return "unauthorized";
+    case kCsOffline: return "offline";
+    case kCsBootloader: return "bootloader";
+    case kCsDevice: return "device";
+    case kCsHost: return "host";
+    case kCsRecovery: return "recovery";
+    case kCsNoPerm: return "no permissions";
+    case kCsSideload: return "sideload";
+    case kCsUnauthorized: return "unauthorized";
     default: return "unknown";
     }
 }
 
+void atransport::update_version(int version, size_t payload) {
+    protocol_version = std::min(version, A_VERSION);
+    max_payload = std::min(payload, MAX_PAYLOAD);
+}
+
+int atransport::get_protocol_version() const {
+    return protocol_version;
+}
+
+size_t atransport::get_max_payload() const {
+    return max_payload;
+}
+
+// The list of features supported by the current system. Will be sent to the
+// other side of the connection in the banner.
+static const FeatureSet gSupportedFeatures = {
+    // None yet.
+};
+
+const FeatureSet& supported_features() {
+    return gSupportedFeatures;
+}
+
+bool atransport::has_feature(const std::string& feature) const {
+    return features_.count(feature) > 0;
+}
+
+void atransport::add_feature(const std::string& feature) {
+    features_.insert(feature);
+}
+
+bool atransport::CanUseFeature(const std::string& feature) const {
+    return has_feature(feature) && supported_features().count(feature) > 0;
+}
+
+void atransport::AddDisconnect(adisconnect* disconnect) {
+    disconnects_.push_back(disconnect);
+}
+
+void atransport::RemoveDisconnect(adisconnect* disconnect) {
+    disconnects_.remove(disconnect);
+}
+
+void atransport::RunDisconnects() {
+    for (auto& disconnect : disconnects_) {
+        disconnect->func(disconnect->opaque, this);
+    }
+    disconnects_.clear();
+}
+
 #if ADB_HOST
 
 static void append_transport_info(std::string* result, const char* key,
@@ -869,7 +832,8 @@
     }
 }
 
-static void append_transport(atransport* t, std::string* result, bool long_listing) {
+static void append_transport(const atransport* t, std::string* result,
+                             bool long_listing) {
     const char* serial = t->serial;
     if (!serial || !serial[0]) {
         serial = "(no serial number)";
@@ -886,6 +850,9 @@
         append_transport_info(result, "product:", t->product, false);
         append_transport_info(result, "model:", t->model, true);
         append_transport_info(result, "device:", t->device, false);
+        append_transport_info(result, "features:",
+                              android::base::Join(t->features(), ',').c_str(),
+                              false);
     }
     *result += '\n';
 }
@@ -893,7 +860,7 @@
 std::string list_transports(bool long_listing) {
     std::string result;
     adb_mutex_lock(&transport_lock);
-    for (atransport* t = transport_list.next; t != &transport_list; t = t->next) {
+    for (const auto t : transport_list) {
         append_transport(t, &result, long_listing);
     }
     adb_mutex_unlock(&transport_lock);
@@ -901,11 +868,10 @@
 }
 
 /* hack for osx */
-void close_usb_devices()
-{
+void close_usb_devices() {
     adb_mutex_lock(&transport_lock);
-    for (atransport* t = transport_list.next; t != &transport_list; t = t->next) {
-        if ( !t->kicked ) {
+    for (auto t : transport_list) {
+        if (!t->kicked) {
             t->kicked = 1;
             t->kick(t);
         }
@@ -914,47 +880,39 @@
 }
 #endif // ADB_HOST
 
-int register_socket_transport(int s, const char *serial, int port, int local)
-{
-    atransport *t = reinterpret_cast<atransport*>(calloc(1, sizeof(atransport)));
-    if (t == nullptr) {
-        return -1;
-    }
-
-    atransport *n;
-    char buff[32];
+int register_socket_transport(int s, const char *serial, int port, int local) {
+    atransport* t = new atransport();
 
     if (!serial) {
-        snprintf(buff, sizeof buff, "T-%p", t);
-        serial = buff;
+        char buf[32];
+        snprintf(buf, sizeof(buf), "T-%p", t);
+        serial = buf;
     }
+
     D("transport: %s init'ing for socket %d, on port %d\n", serial, s, port);
     if (init_socket_transport(t, s, port, local) < 0) {
-        free(t);
+        delete t;
         return -1;
     }
 
     adb_mutex_lock(&transport_lock);
-    for (n = pending_list.next; n != &pending_list; n = n->next) {
-        if (n->serial && !strcmp(serial, n->serial)) {
+    for (auto transport : pending_list) {
+        if (transport->serial && strcmp(serial, transport->serial) == 0) {
             adb_mutex_unlock(&transport_lock);
-            free(t);
+            delete t;
             return -1;
         }
     }
 
-    for (n = transport_list.next; n != &transport_list; n = n->next) {
-        if (n->serial && !strcmp(serial, n->serial)) {
+    for (auto transport : transport_list) {
+        if (transport->serial && strcmp(serial, transport->serial) == 0) {
             adb_mutex_unlock(&transport_lock);
-            free(t);
+            delete t;
             return -1;
         }
     }
 
-    t->next = &pending_list;
-    t->prev = pending_list.prev;
-    t->next->prev = t;
-    t->prev->next = t;
+    pending_list.push_front(t);
     t->serial = strdup(serial);
     adb_mutex_unlock(&transport_lock);
 
@@ -963,111 +921,82 @@
 }
 
 #if ADB_HOST
-atransport *find_transport(const char *serial)
-{
-    atransport *t;
+atransport *find_transport(const char *serial) {
+    atransport* result = nullptr;
 
     adb_mutex_lock(&transport_lock);
-    for(t = transport_list.next; t != &transport_list; t = t->next) {
-        if (t->serial && !strcmp(serial, t->serial)) {
+    for (auto& t : transport_list) {
+        if (t->serial && strcmp(serial, t->serial) == 0) {
+            result = t;
             break;
         }
-     }
+    }
     adb_mutex_unlock(&transport_lock);
 
-    if (t != &transport_list)
-        return t;
-    else
-        return 0;
+    return result;
 }
 
-void unregister_transport(atransport *t)
-{
+void kick_all_tcp_devices() {
     adb_mutex_lock(&transport_lock);
-    t->next->prev = t->prev;
-    t->prev->next = t->next;
-    adb_mutex_unlock(&transport_lock);
-
-    kick_transport(t);
-    transport_unref(t);
-}
-
-// unregisters all non-emulator TCP transports
-void unregister_all_tcp_transports()
-{
-    atransport *t, *next;
-    adb_mutex_lock(&transport_lock);
-    for (t = transport_list.next; t != &transport_list; t = next) {
-        next = t->next;
+    for (auto& t : transport_list) {
+        // TCP/IP devices have adb_port == 0.
         if (t->type == kTransportLocal && t->adb_port == 0) {
-            t->next->prev = t->prev;
-            t->prev->next = next;
-            // we cannot call kick_transport when holding transport_lock
-            if (!t->kicked)
-            {
-                t->kicked = 1;
-                t->kick(t);
-            }
-            transport_unref_locked(t);
+            // Kicking breaks the read_transport thread of this transport out of any read, then
+            // the read_transport thread will notify the main thread to make this transport
+            // offline. Then the main thread will notify the write_transport thread to exit.
+            // Finally, this transport will be closed and freed in the main thread.
+            kick_transport_locked(t);
         }
-     }
-
+    }
     adb_mutex_unlock(&transport_lock);
 }
 
 #endif
 
-void register_usb_transport(usb_handle *usb, const char *serial, const char *devpath, unsigned writeable)
-{
-    atransport *t = reinterpret_cast<atransport*>(calloc(1, sizeof(atransport)));
-    if (t == nullptr) fatal("cannot allocate USB atransport");
+void register_usb_transport(usb_handle* usb, const char* serial,
+                            const char* devpath, unsigned writeable) {
+    atransport* t = new atransport();
+
     D("transport: %p init'ing for usb_handle %p (sn='%s')\n", t, usb,
       serial ? serial : "");
-    init_usb_transport(t, usb, (writeable ? CS_OFFLINE : CS_NOPERM));
+    init_usb_transport(t, usb, (writeable ? kCsOffline : kCsNoPerm));
     if(serial) {
         t->serial = strdup(serial);
     }
-    if(devpath) {
+
+    if (devpath) {
         t->devpath = strdup(devpath);
     }
 
     adb_mutex_lock(&transport_lock);
-    t->next = &pending_list;
-    t->prev = pending_list.prev;
-    t->next->prev = t;
-    t->prev->next = t;
+    pending_list.push_front(t);
     adb_mutex_unlock(&transport_lock);
 
     register_transport(t);
 }
 
-/* this should only be used for transports with connection_state == CS_NOPERM */
-void unregister_usb_transport(usb_handle *usb)
-{
-    atransport *t;
+// This should only be used for transports with connection_state == kCsNoPerm.
+void unregister_usb_transport(usb_handle *usb) {
     adb_mutex_lock(&transport_lock);
-    for(t = transport_list.next; t != &transport_list; t = t->next) {
-        if (t->usb == usb && t->connection_state == CS_NOPERM) {
-            t->next->prev = t->prev;
-            t->prev->next = t->next;
-            break;
-        }
-     }
+    transport_list.remove_if([usb](atransport* t) {
+        return t->usb == usb && t->connection_state == kCsNoPerm;
+    });
     adb_mutex_unlock(&transport_lock);
 }
 
 #undef TRACE_TAG
 #define TRACE_TAG  TRACE_RWX
 
-int check_header(apacket *p)
+int check_header(apacket *p, atransport *t)
 {
     if(p->msg.magic != (p->msg.command ^ 0xffffffff)) {
         D("check_header(): invalid magic\n");
         return -1;
     }
 
-    if(p->msg.data_length > MAX_PAYLOAD) {
-        D("check_header(): %d > MAX_PAYLOAD\n", p->msg.data_length);
+    if(p->msg.data_length > t->get_max_payload()) {
+        D("check_header(): %u > atransport::max_payload = %zu\n",
+            p->msg.data_length, t->get_max_payload());
         return -1;
     }
 
diff --git a/adb/transport.h b/adb/transport.h
index 5b6fdac..3b56c55 100644
--- a/adb/transport.h
+++ b/adb/transport.h
@@ -19,30 +19,113 @@
 
 #include <sys/types.h>
 
+#include <list>
 #include <string>
+#include <unordered_set>
 
 #include "adb.h"
 
+typedef std::unordered_set<std::string> FeatureSet;
+
+const FeatureSet& supported_features();
+
+class atransport {
+public:
+    // TODO(danalbert): We expose waaaaaaay too much stuff because this was
+    // historically just a struct, but making the whole thing a more idiomatic
+    // class in one go is a very large change. Given how bad our testing is,
+    // it's better to do this piece by piece.
+
+    atransport() {
+        auth_fde = {};
+        transport_fde = {};
+        protocol_version = A_VERSION;
+        max_payload = MAX_PAYLOAD;
+    }
+
+    virtual ~atransport() {}
+
+    int (*read_from_remote)(apacket* p, atransport* t) = nullptr;
+    int (*write_to_remote)(apacket* p, atransport* t) = nullptr;
+    void (*close)(atransport* t) = nullptr;
+    void (*kick)(atransport* t) = nullptr;
+
+    int fd = -1;
+    int transport_socket = -1;
+    fdevent transport_fde;
+    size_t ref_count = 0;
+    uint32_t sync_token = 0;
+    ConnectionState connection_state = kCsOffline;
+    bool online = false;
+    TransportType type = kTransportAny;
+
+    // USB handle or socket fd as needed.
+    usb_handle* usb = nullptr;
+    int sfd = -1;
+
+    // Used to identify transports for clients.
+    char* serial = nullptr;
+    char* product = nullptr;
+    char* model = nullptr;
+    char* device = nullptr;
+    char* devpath = nullptr;
+    int adb_port = -1;  // Use for emulators (local transport)
+    bool kicked = false;
+
+    void* key = nullptr;
+    unsigned char token[TOKEN_SIZE] = {};
+    fdevent auth_fde;
+    size_t failed_auth_attempts = 0;
+
+    const char* connection_state_name() const;
+
+    void update_version(int version, size_t payload);
+    int get_protocol_version() const;
+    size_t get_max_payload() const;
+
+    inline const FeatureSet features() const {
+        return features_;
+    }
+
+    bool has_feature(const std::string& feature) const;
+    void add_feature(const std::string& feature);
+
+    // Returns true if both we and the other end of the transport support the
+    // feature.
+    bool CanUseFeature(const std::string& feature) const;
+
+    void AddDisconnect(adisconnect* disconnect);
+    void RemoveDisconnect(adisconnect* disconnect);
+    void RunDisconnects();
+
+private:
+    // A set of features transmitted in the banner with the initial connection.
+    // This is stored in the banner as 'features=feature0,feature1,etc'.
+    FeatureSet features_;
+    int protocol_version;
+    size_t max_payload;
+
+    // A list of adisconnect callbacks called when the transport is kicked.
+    std::list<adisconnect*> disconnects_;
+
+    DISALLOW_COPY_AND_ASSIGN(atransport);
+};
+
 /*
  * Obtain a transport from the available transports.
- * If state is != CS_ANY, only transports in that state are considered.
+ * If state is != kCsAny, only transports in that state are considered.
  * If serial is non-NULL then only the device with that serial will be chosen.
  * If no suitable transport is found, error is set.
  */
-atransport* acquire_one_transport(int state, transport_type ttype,
+atransport* acquire_one_transport(ConnectionState state, TransportType type,
                                   const char* serial, std::string* error_out);
-void add_transport_disconnect(atransport* t, adisconnect* dis);
-void remove_transport_disconnect(atransport* t, adisconnect* dis);
 void kick_transport(atransport* t);
-void run_transport_disconnects(atransport* t);
 void update_transports(void);
 
-/* transports are ref-counted
-** get_device_transport does an acquire on your behalf before returning
-*/
 void init_transport_registration(void);
 std::string list_transports(bool long_listing);
 atransport* find_transport(const char* serial);
+void kick_all_tcp_devices();
 
 void register_usb_transport(usb_handle* h, const char* serial,
                             const char* devpath, unsigned writeable);
@@ -50,14 +133,10 @@
 /* cause new transports to be init'd and added to the list */
 int register_socket_transport(int s, const char* serial, int port, int local);
 
-/* this should only be used for transports with connection_state == CS_NOPERM */
+// This should only be used for transports with connection_state == kCsNoPerm.
 void unregister_usb_transport(usb_handle* usb);
 
-/* these should only be used for the "adb disconnect" command */
-void unregister_transport(atransport* t);
-void unregister_all_tcp_transports();
-
-int check_header(apacket* p);
+int check_header(apacket* p, atransport* t);
 int check_data(apacket* p);
 
 /* for MacOS X cleanup */
diff --git a/adb/transport_local.cpp b/adb/transport_local.cpp
index b1deffd..6821cfc 100644
--- a/adb/transport_local.cpp
+++ b/adb/transport_local.cpp
@@ -26,6 +26,7 @@
 #include <sys/types.h>
 
 #include <base/stringprintf.h>
+#include <cutils/sockets.h>
 
 #if !ADB_HOST
 #include "cutils/properties.h"
@@ -33,6 +34,7 @@
 
 #include "adb.h"
 #include "adb_io.h"
+#include "adb_utils.h"
 
 #if ADB_HOST
 /* we keep a list of opened transports. The atransport struct knows to which
@@ -53,7 +55,7 @@
         return -1;
     }
 
-    if(check_header(p)) {
+    if(check_header(p, t)) {
         D("bad header: terminated (data)\n");
         return -1;
     }
@@ -83,23 +85,26 @@
     return 0;
 }
 
-
-int local_connect(int port) {
-    return local_connect_arbitrary_ports(port-1, port);
+void local_connect(int port) {
+    std::string dummy;
+    local_connect_arbitrary_ports(port-1, port, &dummy);
 }
 
-int local_connect_arbitrary_ports(int console_port, int adb_port)
-{
-    int  fd = -1;
+int local_connect_arbitrary_ports(int console_port, int adb_port, std::string* error) {
+    int fd = -1;
 
 #if ADB_HOST
+    if (find_emulator_transport_by_adb_port(adb_port) != nullptr) {
+        return -1;
+    }
+
     const char *host = getenv("ADBHOST");
     if (host) {
-        fd = socket_network_client(host, adb_port, SOCK_STREAM);
+        fd = network_connect(host, adb_port, SOCK_STREAM, 0, error);
     }
 #endif
     if (fd < 0) {
-        fd = socket_loopback_client(adb_port, SOCK_STREAM);
+        fd = network_loopback_client(adb_port, SOCK_STREAM, error);
     }
 
     if (fd >= 0) {
@@ -107,31 +112,34 @@
         close_on_exec(fd);
         disable_tcp_nagle(fd);
         std::string serial = android::base::StringPrintf("emulator-%d", console_port);
-        register_socket_transport(fd, serial.c_str(), adb_port, 1);
-        return 0;
+        if (register_socket_transport(fd, serial.c_str(), adb_port, 1) == 0) {
+            return 0;
+        }
+        adb_close(fd);
     }
     return -1;
 }
 
-
+#if ADB_HOST
 static void *client_socket_thread(void *x)
 {
-#if ADB_HOST
-    int  port  = DEFAULT_ADB_LOCAL_TRANSPORT_PORT;
-    int  count = ADB_LOCAL_TRANSPORT_MAX;
-
+    adb_thread_setname("client_socket_thread");
     D("transport: client_socket_thread() starting\n");
+    while (true) {
+        int port = DEFAULT_ADB_LOCAL_TRANSPORT_PORT;
+        int count = ADB_LOCAL_TRANSPORT_MAX;
 
-    /* try to connect to any number of running emulator instances     */
-    /* this is only done when ADB starts up. later, each new emulator */
-    /* will send a message to ADB to indicate that is is starting up  */
-    for ( ; count > 0; count--, port += 2 ) {
-        (void) local_connect(port);
+        // Try to connect to any number of running emulator instances.
+        for ( ; count > 0; count--, port += 2 ) {
+            local_connect(port);
+        }
+        sleep(1);
     }
-#endif
     return 0;
 }
 
+#else // ADB_HOST
+
 static void *server_socket_thread(void * arg)
 {
     int serverfd, fd;
@@ -139,13 +147,15 @@
     socklen_t alen;
     int port = (int) (uintptr_t) arg;
 
+    adb_thread_setname("server socket");
     D("transport: server_socket_thread() starting\n");
     serverfd = -1;
     for(;;) {
         if(serverfd == -1) {
-            serverfd = socket_inaddr_any_server(port, SOCK_STREAM);
+            std::string error;
+            serverfd = network_inaddr_any_server(port, SOCK_STREAM, &error);
             if(serverfd < 0) {
-                D("server: cannot bind socket yet: %s\n", strerror(errno));
+                D("server: cannot bind socket yet: %s\n", error.c_str());
                 adb_sleep_ms(1000);
                 continue;
             }
@@ -167,7 +177,6 @@
 }
 
 /* This is relevant only for ADB daemon running inside the emulator. */
-#if !ADB_HOST
 /*
  * Redefine open and write for qemu_pipe.h that contains inlined references
  * to those routines. We will redifine them back after qemu_pipe.h inclusion.
@@ -224,6 +233,7 @@
     char tmp[256];
     char con_name[32];
 
+    adb_thread_setname("qemu socket");
     D("transport: qemu_socket_thread() starting\n");
 
     /* adb QEMUD service connection request. */
@@ -234,9 +244,8 @@
     if (fd < 0) {
         /* This could be an older version of the emulator, that doesn't
          * implement adb QEMUD service. Fall back to the old TCP way. */
-        adb_thread_t thr;
         D("adb service is not available. Falling back to TCP socket.\n");
-        adb_thread_create(&thr, server_socket_thread, arg);
+        adb_thread_create(server_socket_thread, arg);
         return 0;
     }
 
@@ -279,34 +288,30 @@
 
 void local_init(int port)
 {
-    adb_thread_t thr;
     void* (*func)(void *);
+    const char* debug_name = "";
 
-    if(HOST) {
-        func = client_socket_thread;
-    } else {
 #if ADB_HOST
-        func = server_socket_thread;
+    func = client_socket_thread;
+    debug_name = "client";
 #else
-        /* For the adbd daemon in the system image we need to distinguish
-         * between the device, and the emulator. */
-        char is_qemu[PROPERTY_VALUE_MAX];
-        property_get("ro.kernel.qemu", is_qemu, "");
-        if (!strcmp(is_qemu, "1")) {
-            /* Running inside the emulator: use QEMUD pipe as the transport. */
-            func = qemu_socket_thread;
-        } else {
-            /* Running inside the device: use TCP socket as the transport. */
-            func = server_socket_thread;
-        }
-#endif // !ADB_HOST
+    /* For the adbd daemon in the system image we need to distinguish
+     * between the device, and the emulator. */
+    char is_qemu[PROPERTY_VALUE_MAX];
+    property_get("ro.kernel.qemu", is_qemu, "");
+    if (!strcmp(is_qemu, "1")) {
+        /* Running inside the emulator: use QEMUD pipe as the transport. */
+        func = qemu_socket_thread;
+    } else {
+        /* Running inside the device: use TCP socket as the transport. */
+        func = server_socket_thread;
     }
+    debug_name = "server";
+#endif // !ADB_HOST
 
-    D("transport: local %s init\n", HOST ? "client" : "server");
-
-    if(adb_thread_create(&thr, func, (void *) (uintptr_t) port)) {
-        fatal_errno("cannot create local socket %s thread",
-                    HOST ? "client" : "server");
+    D("transport: local %s init\n", debug_name);
+    if (!adb_thread_create(func, (void *) (uintptr_t) port)) {
+        fatal_errno("cannot create local socket %s thread", debug_name);
     }
 }
 
@@ -318,23 +323,25 @@
     adb_close(fd);
 
 #if ADB_HOST
-    if(HOST) {
-        int  nn;
-        adb_mutex_lock( &local_transports_lock );
-        for (nn = 0; nn < ADB_LOCAL_TRANSPORT_MAX; nn++) {
-            if (local_transports[nn] == t) {
-                local_transports[nn] = NULL;
-                break;
-            }
+    int  nn;
+    adb_mutex_lock( &local_transports_lock );
+    for (nn = 0; nn < ADB_LOCAL_TRANSPORT_MAX; nn++) {
+        if (local_transports[nn] == t) {
+            local_transports[nn] = NULL;
+            break;
         }
-        adb_mutex_unlock( &local_transports_lock );
     }
+    adb_mutex_unlock( &local_transports_lock );
 #endif
 }
 
 static void remote_close(atransport *t)
 {
-    adb_close(t->fd);
+    int fd = t->sfd;
+    if (fd != -1) {
+        t->sfd = -1;
+        adb_close(fd);
+    }
 }
 
 
@@ -390,12 +397,12 @@
     t->write_to_remote = remote_write;
     t->sfd = s;
     t->sync_token = 1;
-    t->connection_state = CS_OFFLINE;
+    t->connection_state = kCsOffline;
     t->type = kTransportLocal;
     t->adb_port = 0;
 
 #if ADB_HOST
-    if (HOST && local) {
+    if (local) {
         adb_mutex_lock( &local_transports_lock );
         {
             t->adb_port = adb_port;
diff --git a/adb/transport_test.cpp b/adb/transport_test.cpp
index 2b3fe3c..10872ac 100644
--- a/adb/transport_test.cpp
+++ b/adb/transport_test.cpp
@@ -20,34 +20,193 @@
 
 #include "adb.h"
 
+class TestTransport : public atransport {
+public:
+    bool operator==(const atransport& rhs) const {
+        EXPECT_EQ(read_from_remote, rhs.read_from_remote);
+        EXPECT_EQ(write_to_remote, rhs.write_to_remote);
+        EXPECT_EQ(close, rhs.close);
+        EXPECT_EQ(kick, rhs.kick);
+
+        EXPECT_EQ(fd, rhs.fd);
+        EXPECT_EQ(transport_socket, rhs.transport_socket);
+
+        EXPECT_EQ(
+            0, memcmp(&transport_fde, &rhs.transport_fde, sizeof(fdevent)));
+
+        EXPECT_EQ(ref_count, rhs.ref_count);
+        EXPECT_EQ(sync_token, rhs.sync_token);
+        EXPECT_EQ(connection_state, rhs.connection_state);
+        EXPECT_EQ(online, rhs.online);
+        EXPECT_EQ(type, rhs.type);
+
+        EXPECT_EQ(usb, rhs.usb);
+        EXPECT_EQ(sfd, rhs.sfd);
+
+        EXPECT_EQ(serial, rhs.serial);
+        EXPECT_EQ(product, rhs.product);
+        EXPECT_EQ(model, rhs.model);
+        EXPECT_EQ(device, rhs.device);
+        EXPECT_EQ(devpath, rhs.devpath);
+        EXPECT_EQ(adb_port, rhs.adb_port);
+        EXPECT_EQ(kicked, rhs.kicked);
+
+        EXPECT_EQ(key, rhs.key);
+        EXPECT_EQ(0, memcmp(token, rhs.token, TOKEN_SIZE));
+        EXPECT_EQ(0, memcmp(&auth_fde, &rhs.auth_fde, sizeof(fdevent)));
+        EXPECT_EQ(failed_auth_attempts, rhs.failed_auth_attempts);
+
+        EXPECT_EQ(features(), rhs.features());
+
+        return true;
+    }
+};
+
+class TransportSetup {
+public:
+  TransportSetup() {
+#ifdef _WIN32
+    // Use extern instead of including sysdeps.h which brings in various macros
+    // that conflict with APIs used in this file.
+    extern void adb_sysdeps_init(void);
+    adb_sysdeps_init();
+#else
+    // adb_sysdeps_init() is an inline function that we cannot link against.
+#endif
+  }
+};
+
+// Static initializer will call adb_sysdeps_init() before main() to initialize
+// the transport mutex before it is used in the tests. Alternatives would be to
+// use __attribute__((constructor)) here or to use that or a static initializer
+// for adb_sysdeps_init() itself in sysdeps_win32.cpp (caveats of unclear
+// init order), or to use a test fixture whose SetUp() could do the init once.
+static TransportSetup g_TransportSetup;
+
 TEST(transport, kick_transport) {
-  atransport t = {};
+  TestTransport t;
+
   // Mutate some member so we can test that the function is run.
   t.kick = [](atransport* trans) { trans->fd = 42; };
-  atransport expected = t;
+
+  TestTransport expected;
+  expected.kick = t.kick;
   expected.fd = 42;
   expected.kicked = 1;
+
   kick_transport(&t);
   ASSERT_EQ(42, t.fd);
   ASSERT_EQ(1, t.kicked);
-  ASSERT_EQ(0, memcmp(&expected, &t, sizeof(atransport)));
+  ASSERT_EQ(expected, t);
 }
 
 TEST(transport, kick_transport_already_kicked) {
   // Ensure that the transport is not modified if the transport has already been
   // kicked.
-  atransport t = {};
+  TestTransport t;
   t.kicked = 1;
   t.kick = [](atransport*) { FAIL() << "Kick should not have been called"; };
-  atransport expected = t;
+
+  TestTransport expected;
+  expected.kicked = 1;
+  expected.kick = t.kick;
+
   kick_transport(&t);
-  ASSERT_EQ(0, memcmp(&expected, &t, sizeof(atransport)));
+  ASSERT_EQ(expected, t);
 }
 
-// Disabled because the function currently segfaults for a zeroed atransport. I
-// want to make sure I understand how this is working at all before I try fixing
-// that.
-TEST(transport, DISABLED_run_transport_disconnects_zeroed_atransport) {
-  atransport t = {};
-  run_transport_disconnects(&t);
+static void DisconnectFunc(void* arg, atransport*) {
+    int* count = reinterpret_cast<int*>(arg);
+    ++*count;
+}
+
+TEST(transport, RunDisconnects) {
+    atransport t;
+    // RunDisconnects() can be called with an empty atransport.
+    t.RunDisconnects();
+
+    int count = 0;
+    adisconnect disconnect;
+    disconnect.func = DisconnectFunc;
+    disconnect.opaque = &count;
+    t.AddDisconnect(&disconnect);
+    t.RunDisconnects();
+    ASSERT_EQ(1, count);
+
+    // disconnect should have been removed automatically.
+    t.RunDisconnects();
+    ASSERT_EQ(1, count);
+
+    count = 0;
+    t.AddDisconnect(&disconnect);
+    t.RemoveDisconnect(&disconnect);
+    t.RunDisconnects();
+    ASSERT_EQ(0, count);
+}
+
+TEST(transport, add_feature) {
+    atransport t;
+    ASSERT_EQ(0U, t.features().size());
+
+    t.add_feature("foo");
+    ASSERT_EQ(1U, t.features().size());
+    ASSERT_TRUE(t.has_feature("foo"));
+
+    t.add_feature("bar");
+    ASSERT_EQ(2U, t.features().size());
+    ASSERT_TRUE(t.has_feature("foo"));
+    ASSERT_TRUE(t.has_feature("bar"));
+
+    t.add_feature("foo");
+    ASSERT_EQ(2U, t.features().size());
+    ASSERT_TRUE(t.has_feature("foo"));
+    ASSERT_TRUE(t.has_feature("bar"));
+}
+
+TEST(transport, parse_banner_no_features) {
+    atransport t;
+
+    parse_banner("host::", &t);
+
+    ASSERT_EQ(0U, t.features().size());
+    ASSERT_EQ(kCsHost, t.connection_state);
+
+    ASSERT_EQ(nullptr, t.product);
+    ASSERT_EQ(nullptr, t.model);
+    ASSERT_EQ(nullptr, t.device);
+}
+
+TEST(transport, parse_banner_product_features) {
+    atransport t;
+
+    const char banner[] =
+        "host::ro.product.name=foo;ro.product.model=bar;ro.product.device=baz;";
+    parse_banner(banner, &t);
+
+    ASSERT_EQ(kCsHost, t.connection_state);
+
+    ASSERT_EQ(0U, t.features().size());
+
+    ASSERT_EQ(std::string("foo"), t.product);
+    ASSERT_EQ(std::string("bar"), t.model);
+    ASSERT_EQ(std::string("baz"), t.device);
+}
+
+TEST(transport, parse_banner_features) {
+    atransport t;
+
+    const char banner[] =
+        "host::ro.product.name=foo;ro.product.model=bar;ro.product.device=baz;"
+        "features=woodly,doodly";
+    parse_banner(banner, &t);
+
+    ASSERT_EQ(kCsHost, t.connection_state);
+
+    ASSERT_EQ(2U, t.features().size());
+    ASSERT_TRUE(t.has_feature("woodly"));
+    ASSERT_TRUE(t.has_feature("doodly"));
+
+    ASSERT_EQ(std::string("foo"), t.product);
+    ASSERT_EQ(std::string("bar"), t.model);
+    ASSERT_EQ(std::string("baz"), t.device);
 }
diff --git a/adb/transport_usb.cpp b/adb/transport_usb.cpp
index cdabffe..96ccdad 100644
--- a/adb/transport_usb.cpp
+++ b/adb/transport_usb.cpp
@@ -32,7 +32,7 @@
         return -1;
     }
 
-    if(check_header(p)) {
+    if(check_header(p, t)) {
         D("remote usb: check_header failed\n");
         return -1;
     }
@@ -80,7 +80,7 @@
     usb_kick(t->usb);
 }
 
-void init_usb_transport(atransport *t, usb_handle *h, int state)
+void init_usb_transport(atransport *t, usb_handle *h, ConnectionState state)
 {
     D("transport: usb\n");
     t->close = remote_close;
@@ -91,12 +91,6 @@
     t->connection_state = state;
     t->type = kTransportUsb;
     t->usb = h;
-
-#if ADB_HOST
-    HOST = 1;
-#else
-    HOST = 0;
-#endif
 }
 
 #if ADB_HOST
diff --git a/adb/usb_linux.cpp b/adb/usb_linux.cpp
index 71baaee..65b8735 100644
--- a/adb/usb_linux.cpp
+++ b/adb/usb_linux.cpp
@@ -22,6 +22,7 @@
 #include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
+#include <linux/usb/ch9.h>
 #include <linux/usbdevice_fs.h>
 #include <linux/version.h>
 #include <stdio.h>
@@ -31,7 +32,12 @@
 #include <sys/time.h>
 #include <sys/types.h>
 #include <unistd.h>
-#include <linux/usb/ch9.h>
+
+#include <chrono>
+#include <condition_variable>
+#include <list>
+#include <mutex>
+#include <string>
 
 #include <base/file.h>
 #include <base/stringprintf.h>
@@ -40,110 +46,92 @@
 #include "adb.h"
 #include "transport.h"
 
+using namespace std::literals;
+
 /* usb scan debugging is waaaay too verbose */
 #define DBGX(x...)
 
-ADB_MUTEX_DEFINE( usb_lock );
+struct usb_handle {
+    ~usb_handle() {
+      if (fd != -1) unix_close(fd);
+    }
 
-struct usb_handle
-{
-    usb_handle *prev;
-    usb_handle *next;
-
-    char fname[64];
-    int desc;
+    std::string path;
+    int fd = -1;
     unsigned char ep_in;
     unsigned char ep_out;
 
     unsigned zero_mask;
-    unsigned writeable;
+    unsigned writeable = 1;
 
-    struct usbdevfs_urb urb_in;
-    struct usbdevfs_urb urb_out;
+    usbdevfs_urb urb_in;
+    usbdevfs_urb urb_out;
 
-    int urb_in_busy;
-    int urb_out_busy;
-    int dead;
+    bool urb_in_busy = false;
+    bool urb_out_busy = false;
+    bool dead = false;
 
-    adb_cond_t notify;
-    adb_mutex_t lock;
+    std::condition_variable cv;
+    std::mutex mutex;
 
     // for garbage collecting disconnected devices
-    int mark;
+    bool mark;
 
     // ID of thread currently in REAPURB
-    pthread_t reaper_thread;
+    pthread_t reaper_thread = 0;
 };
 
-static usb_handle handle_list = {
-    .prev = &handle_list,
-    .next = &handle_list,
-};
+static std::mutex g_usb_handles_mutex;
+static std::list<usb_handle*> g_usb_handles;
 
-static int known_device(const char *dev_name)
-{
-    usb_handle *usb;
-
-    adb_mutex_lock(&usb_lock);
-    for(usb = handle_list.next; usb != &handle_list; usb = usb->next){
-        if(!strcmp(usb->fname, dev_name)) {
+static int is_known_device(const char* dev_name) {
+    std::lock_guard<std::mutex> lock(g_usb_handles_mutex);
+    for (usb_handle* usb : g_usb_handles) {
+        if (usb->path == dev_name) {
             // set mark flag to indicate this device is still alive
-            usb->mark = 1;
-            adb_mutex_unlock(&usb_lock);
+            usb->mark = true;
             return 1;
         }
     }
-    adb_mutex_unlock(&usb_lock);
     return 0;
 }
 
-static void kick_disconnected_devices()
-{
-    usb_handle *usb;
-
-    adb_mutex_lock(&usb_lock);
+static void kick_disconnected_devices() {
+    std::lock_guard<std::mutex> lock(g_usb_handles_mutex);
     // kick any devices in the device list that were not found in the device scan
-    for(usb = handle_list.next; usb != &handle_list; usb = usb->next){
-        if (usb->mark == 0) {
+    for (usb_handle* usb : g_usb_handles) {
+        if (!usb->mark) {
             usb_kick(usb);
         } else {
-            usb->mark = 0;
+            usb->mark = false;
         }
     }
-    adb_mutex_unlock(&usb_lock);
-
 }
 
-static inline int badname(const char *name)
-{
-    while(*name) {
-        if(!isdigit(*name++)) return 1;
+static inline bool contains_non_digit(const char* name) {
+    while (*name) {
+        if (!isdigit(*name++)) return true;
     }
-    return 0;
+    return false;
 }
 
-static void find_usb_device(const char *base,
+static void find_usb_device(const std::string& base,
         void (*register_device_callback)
-                (const char *, const char *, unsigned char, unsigned char, int, int, unsigned))
+                (const char*, const char*, unsigned char, unsigned char, int, int, unsigned))
 {
-    char busname[32], devname[32];
-    unsigned char local_ep_in, local_ep_out;
-    DIR *busdir , *devdir ;
-    struct dirent *de;
-    int fd ;
+    std::unique_ptr<DIR, int(*)(DIR*)> bus_dir(opendir(base.c_str()), closedir);
+    if (!bus_dir) return;
 
-    busdir = opendir(base);
-    if(busdir == 0) return;
+    dirent* de;
+    while ((de = readdir(bus_dir.get())) != 0) {
+        if (contains_non_digit(de->d_name)) continue;
 
-    while((de = readdir(busdir)) != 0) {
-        if(badname(de->d_name)) continue;
+        std::string bus_name = base + "/" + de->d_name;
 
-        snprintf(busname, sizeof busname, "%s/%s", base, de->d_name);
-        devdir = opendir(busname);
-        if(devdir == 0) continue;
+        std::unique_ptr<DIR, int(*)(DIR*)> dev_dir(opendir(bus_name.c_str()), closedir);
+        if (!dev_dir) continue;
 
-//        DBGX("[ scanning %s ]\n", busname);
-        while((de = readdir(devdir))) {
+        while ((de = readdir(dev_dir.get()))) {
             unsigned char devdesc[4096];
             unsigned char* bufptr = devdesc;
             unsigned char* bufend;
@@ -153,28 +141,26 @@
             struct usb_endpoint_descriptor *ep1, *ep2;
             unsigned zero_mask = 0;
             unsigned vid, pid;
-            size_t desclength;
 
-            if(badname(de->d_name)) continue;
-            snprintf(devname, sizeof devname, "%s/%s", busname, de->d_name);
+            if (contains_non_digit(de->d_name)) continue;
 
-            if(known_device(devname)) {
-                DBGX("skipping %s\n", devname);
+            std::string dev_name = bus_name + "/" + de->d_name;
+            if (is_known_device(dev_name.c_str())) {
                 continue;
             }
 
-//            DBGX("[ scanning %s ]\n", devname);
-            if((fd = unix_open(devname, O_RDONLY | O_CLOEXEC)) < 0) {
+            int fd = unix_open(dev_name.c_str(), O_RDONLY | O_CLOEXEC);
+            if (fd == -1) {
                 continue;
             }
 
-            desclength = adb_read(fd, devdesc, sizeof(devdesc));
+            size_t desclength = unix_read(fd, devdesc, sizeof(devdesc));
             bufend = bufptr + desclength;
 
                 // should have device and configuration descriptors, and atleast two endpoints
             if (desclength < USB_DT_DEVICE_SIZE + USB_DT_CONFIG_SIZE) {
                 D("desclength %zu is too small\n", desclength);
-                adb_close(fd);
+                unix_close(fd);
                 continue;
             }
 
@@ -182,20 +168,20 @@
             bufptr += USB_DT_DEVICE_SIZE;
 
             if((device->bLength != USB_DT_DEVICE_SIZE) || (device->bDescriptorType != USB_DT_DEVICE)) {
-                adb_close(fd);
+                unix_close(fd);
                 continue;
             }
 
             vid = device->idVendor;
             pid = device->idProduct;
-            DBGX("[ %s is V:%04x P:%04x ]\n", devname, vid, pid);
+            DBGX("[ %s is V:%04x P:%04x ]\n", dev_name.c_str(), vid, pid);
 
                 // should have config descriptor next
             config = (struct usb_config_descriptor *)bufptr;
             bufptr += USB_DT_CONFIG_SIZE;
             if (config->bLength != USB_DT_CONFIG_SIZE || config->bDescriptorType != USB_DT_CONFIG) {
                 D("usb_config_descriptor not found\n");
-                adb_close(fd);
+                unix_close(fd);
                 continue;
             }
 
@@ -225,7 +211,7 @@
                         struct stat st;
                         char pathbuf[128];
                         char link[256];
-                        char *devpath = NULL;
+                        char *devpath = nullptr;
 
                         DBGX("looking for bulk endpoints\n");
                             // looks like ADB...
@@ -267,6 +253,7 @@
                         }
 
                             // we have a match.  now we just need to figure out which is in and which is out.
+                        unsigned char local_ep_in, local_ep_out;
                         if (ep1->bEndpointAddress & USB_ENDPOINT_DIR_MASK) {
                             local_ep_in = ep1->bEndpointAddress;
                             local_ep_out = ep2->bEndpointAddress;
@@ -277,14 +264,12 @@
 
                             // Determine the device path
                         if (!fstat(fd, &st) && S_ISCHR(st.st_mode)) {
-                            char *slash;
-                            ssize_t link_len;
                             snprintf(pathbuf, sizeof(pathbuf), "/sys/dev/char/%d:%d",
                                      major(st.st_rdev), minor(st.st_rdev));
-                            link_len = readlink(pathbuf, link, sizeof(link) - 1);
+                            ssize_t link_len = readlink(pathbuf, link, sizeof(link) - 1);
                             if (link_len > 0) {
                                 link[link_len] = '\0';
-                                slash = strrchr(link, '/');
+                                const char* slash = strrchr(link, '/');
                                 if (slash) {
                                     snprintf(pathbuf, sizeof(pathbuf),
                                              "usb:%s", slash + 1);
@@ -293,7 +278,7 @@
                             }
                         }
 
-                        register_device_callback(devname, devpath,
+                        register_device_callback(dev_name.c_str(), devpath,
                                 local_ep_in, local_ep_out,
                                 interface->bInterfaceNumber, device->iSerialNumber, zero_mask);
                         break;
@@ -303,77 +288,55 @@
                 }
             } // end of while
 
-            adb_close(fd);
-        } // end of devdir while
-        closedir(devdir);
-    } //end of busdir while
-    closedir(busdir);
+            unix_close(fd);
+        }
+    }
 }
 
-void usb_cleanup()
-{
-}
+static int usb_bulk_write(usb_handle* h, const void* data, int len) {
+    std::unique_lock<std::mutex> lock(h->mutex);
+    D("++ usb_bulk_write ++\n");
 
-static int usb_bulk_write(usb_handle *h, const void *data, int len)
-{
-    struct usbdevfs_urb *urb = &h->urb_out;
-    int res;
-    struct timeval tv;
-    struct timespec ts;
-
+    usbdevfs_urb* urb = &h->urb_out;
     memset(urb, 0, sizeof(*urb));
     urb->type = USBDEVFS_URB_TYPE_BULK;
     urb->endpoint = h->ep_out;
     urb->status = -1;
-    urb->buffer = (void*) data;
+    urb->buffer = const_cast<void*>(data);
     urb->buffer_length = len;
 
-    D("++ write ++\n");
-
-    adb_mutex_lock(&h->lock);
-    if(h->dead) {
-        res = -1;
-        goto fail;
-    }
-    do {
-        res = ioctl(h->desc, USBDEVFS_SUBMITURB, urb);
-    } while((res < 0) && (errno == EINTR));
-
-    if(res < 0) {
-        goto fail;
+    if (h->dead) {
+        errno = EINVAL;
+        return -1;
     }
 
-    res = -1;
-    h->urb_out_busy = 1;
-    for(;;) {
-        /* time out after five seconds */
-        gettimeofday(&tv, NULL);
-        ts.tv_sec = tv.tv_sec + 5;
-        ts.tv_nsec = tv.tv_usec * 1000L;
-        res = pthread_cond_timedwait(&h->notify, &h->lock, &ts);
-        if(res < 0 || h->dead) {
-            break;
+    if (TEMP_FAILURE_RETRY(ioctl(h->fd, USBDEVFS_SUBMITURB, urb)) == -1) {
+        return -1;
+    }
+
+    h->urb_out_busy = true;
+    while (true) {
+        auto now = std::chrono::system_clock::now();
+        if (h->cv.wait_until(lock, now + 5s) == std::cv_status::timeout || h->dead) {
+            // TODO: call USBDEVFS_DISCARDURB?
+            errno = ETIMEDOUT;
+            return -1;
         }
-        if(h->urb_out_busy == 0) {
-            if(urb->status == 0) {
-                res = urb->actual_length;
+        if (!h->urb_out_busy) {
+            if (urb->status != 0) {
+                errno = -urb->status;
+                return -1;
             }
-            break;
+            return urb->actual_length;
         }
     }
-fail:
-    adb_mutex_unlock(&h->lock);
-    D("-- write --\n");
-    return res;
 }
 
-static int usb_bulk_read(usb_handle *h, void *data, int len)
-{
-    struct usbdevfs_urb *urb = &h->urb_in;
-    struct usbdevfs_urb *out = NULL;
-    int res;
-
+static int usb_bulk_read(usb_handle* h, void* data, int len) {
+    std::unique_lock<std::mutex> lock(h->mutex);
     D("++ usb_bulk_read ++\n");
+
+    usbdevfs_urb* urb = &h->urb_in;
     memset(urb, 0, sizeof(*urb));
     urb->type = USBDEVFS_URB_TYPE_BULK;
     urb->endpoint = h->ep_in;
@@ -381,100 +344,76 @@
     urb->buffer = data;
     urb->buffer_length = len;
 
-
-    adb_mutex_lock(&h->lock);
-    if(h->dead) {
-        res = -1;
-        goto fail;
-    }
-    do {
-        res = ioctl(h->desc, USBDEVFS_SUBMITURB, urb);
-    } while((res < 0) && (errno == EINTR));
-
-    if(res < 0) {
-        goto fail;
+    if (h->dead) {
+        errno = EINVAL;
+        return -1;
     }
 
-    h->urb_in_busy = 1;
-    for(;;) {
+    if (TEMP_FAILURE_RETRY(ioctl(h->fd, USBDEVFS_SUBMITURB, urb)) == -1) {
+        return -1;
+    }
+
+    h->urb_in_busy = true;
+    while (true) {
         D("[ reap urb - wait ]\n");
         h->reaper_thread = pthread_self();
-        adb_mutex_unlock(&h->lock);
-        res = ioctl(h->desc, USBDEVFS_REAPURB, &out);
+        int fd = h->fd;
+        lock.unlock();
+
+        // This ioctl must not have TEMP_FAILURE_RETRY because we send SIGALRM to break out.
+        usbdevfs_urb* out = nullptr;
+        int res = ioctl(fd, USBDEVFS_REAPURB, &out);
         int saved_errno = errno;
-        adb_mutex_lock(&h->lock);
+
+        lock.lock();
         h->reaper_thread = 0;
-        if(h->dead) {
-            res = -1;
-            break;
+        if (h->dead) {
+            errno = EINVAL;
+            return -1;
         }
-        if(res < 0) {
-            if(saved_errno == EINTR) {
+        if (res < 0) {
+            if (saved_errno == EINTR) {
                 continue;
             }
             D("[ reap urb - error ]\n");
-            break;
+            errno = saved_errno;
+            return -1;
         }
-        D("[ urb @%p status = %d, actual = %d ]\n",
-            out, out->status, out->actual_length);
+        D("[ urb @%p status = %d, actual = %d ]\n", out, out->status, out->actual_length);
 
-        if(out == &h->urb_in) {
+        if (out == &h->urb_in) {
             D("[ reap urb - IN complete ]\n");
-            h->urb_in_busy = 0;
-            if(urb->status == 0) {
-                res = urb->actual_length;
-            } else {
-                res = -1;
+            h->urb_in_busy = false;
+            if (urb->status != 0) {
+                errno = -urb->status;
+                return -1;
             }
-            break;
+            return urb->actual_length;
         }
-        if(out == &h->urb_out) {
+        if (out == &h->urb_out) {
             D("[ reap urb - OUT compelete ]\n");
-            h->urb_out_busy = 0;
-            adb_cond_broadcast(&h->notify);
+            h->urb_out_busy = false;
+            h->cv.notify_all();
         }
     }
-fail:
-    adb_mutex_unlock(&h->lock);
-    D("-- usb_bulk_read --\n");
-    return res;
 }
 
 
 int usb_write(usb_handle *h, const void *_data, int len)
 {
-    unsigned char *data = (unsigned char*) _data;
-    int n;
-    int need_zero = 0;
-
     D("++ usb_write ++\n");
-    if(h->zero_mask) {
-            /* if we need 0-markers and our transfer
-            ** is an even multiple of the packet size,
-            ** we make note of it
-            */
-        if(!(len & h->zero_mask)) {
-            need_zero = 1;
-        }
+
+    unsigned char *data = (unsigned char*) _data;
+    int n = usb_bulk_write(h, data, len);
+    if (n != len) {
+        D("ERROR: n = %d, errno = %d (%s)\n", n, errno, strerror(errno));
+        return -1;
     }
 
-    while(len > 0) {
-        int xfer = (len > 4096) ? 4096 : len;
-
-        n = usb_bulk_write(h, data, xfer);
-        if(n != xfer) {
-            D("ERROR: n = %d, errno = %d (%s)\n",
-                n, errno, strerror(errno));
-            return -1;
-        }
-
-        len -= xfer;
-        data += xfer;
-    }
-
-    if(need_zero){
-        n = usb_bulk_write(h, _data, 0);
-        return n;
+    if (h->zero_mask && !(len & h->zero_mask)) {
+        // If we need 0-markers and our transfer is an even multiple of the packet size,
+        // then send a zero marker.
+        return usb_bulk_write(h, _data, 0);
     }
 
     D("-- usb_write --\n");
@@ -488,13 +427,13 @@
 
     D("++ usb_read ++\n");
     while(len > 0) {
-        int xfer = (len > 4096) ? 4096 : len;
+        int xfer = len;
 
-        D("[ usb read %d fd = %d], fname=%s\n", xfer, h->desc, h->fname);
+        D("[ usb read %d fd = %d], path=%s\n", xfer, h->fd, h->path.c_str());
         n = usb_bulk_read(h, data, xfer);
-        D("[ usb read %d ] = %d, fname=%s\n", xfer, n, h->fname);
+        D("[ usb read %d ] = %d, path=%s\n", xfer, n, h->path.c_str());
         if(n != xfer) {
-            if((errno == ETIMEDOUT) && (h->desc != -1)) {
+            if((errno == ETIMEDOUT) && (h->fd != -1)) {
                 D("[ timeout ]\n");
                 if(n > 0){
                     data += n;
@@ -515,12 +454,11 @@
     return 0;
 }
 
-void usb_kick(usb_handle *h)
-{
-    D("[ kicking %p (fd = %d) ]\n", h, h->desc);
-    adb_mutex_lock(&h->lock);
-    if(h->dead == 0) {
-        h->dead = 1;
+void usb_kick(usb_handle* h) {
+    std::lock_guard<std::mutex> lock(h->mutex);
+    D("[ kicking %p (fd = %d) ]\n", h, h->fd);
+    if (!h->dead) {
+        h->dead = true;
 
         if (h->writeable) {
             /* HACK ALERT!
@@ -536,34 +474,27 @@
             ** but this ensures that a reader blocked on REAPURB
             ** will get unblocked
             */
-            ioctl(h->desc, USBDEVFS_DISCARDURB, &h->urb_in);
-            ioctl(h->desc, USBDEVFS_DISCARDURB, &h->urb_out);
+            ioctl(h->fd, USBDEVFS_DISCARDURB, &h->urb_in);
+            ioctl(h->fd, USBDEVFS_DISCARDURB, &h->urb_out);
             h->urb_in.status = -ENODEV;
             h->urb_out.status = -ENODEV;
-            h->urb_in_busy = 0;
-            h->urb_out_busy = 0;
-            adb_cond_broadcast(&h->notify);
+            h->urb_in_busy = false;
+            h->urb_out_busy = false;
+            h->cv.notify_all();
         } else {
             unregister_usb_transport(h);
         }
     }
-    adb_mutex_unlock(&h->lock);
 }
 
-int usb_close(usb_handle *h)
-{
-    D("++ usb close ++\n");
-    adb_mutex_lock(&usb_lock);
-    h->next->prev = h->prev;
-    h->prev->next = h->next;
-    h->prev = 0;
-    h->next = 0;
+int usb_close(usb_handle* h) {
+    std::lock_guard<std::mutex> lock(g_usb_handles_mutex);
+    g_usb_handles.remove(h);
 
-    adb_close(h->desc);
-    D("-- usb closed %p (fd = %d) --\n", h, h->desc);
-    adb_mutex_unlock(&usb_lock);
+    D("-- usb close %p (fd = %d) --\n", h, h->fd);
 
-    free(h);
+    delete h;
+
     return 0;
 }
 
@@ -576,54 +507,44 @@
     // from the list when we're finally closed and everything will work out
     // fine.
     //
-    // If we have a usb_handle on the list 'o handles with a matching name, we
+    // If we have a usb_handle on the list of handles with a matching name, we
     // have no further work to do.
-    adb_mutex_lock(&usb_lock);
-    for (usb_handle* usb = handle_list.next; usb != &handle_list; usb = usb->next) {
-        if (!strcmp(usb->fname, dev_name)) {
-            adb_mutex_unlock(&usb_lock);
-            return;
+    {
+        std::lock_guard<std::mutex> lock(g_usb_handles_mutex);
+        for (usb_handle* usb: g_usb_handles) {
+            if (usb->path == dev_name) {
+                return;
+            }
         }
     }
-    adb_mutex_unlock(&usb_lock);
 
     D("[ usb located new device %s (%d/%d/%d) ]\n", dev_name, ep_in, ep_out, interface);
-    usb_handle* usb = reinterpret_cast<usb_handle*>(calloc(1, sizeof(usb_handle)));
-    if (usb == nullptr) fatal("couldn't allocate usb_handle");
-    strcpy(usb->fname, dev_name);
+    std::unique_ptr<usb_handle> usb(new usb_handle);
+    usb->path = dev_name;
     usb->ep_in = ep_in;
     usb->ep_out = ep_out;
     usb->zero_mask = zero_mask;
-    usb->writeable = 1;
 
-    adb_cond_init(&usb->notify, 0);
-    adb_mutex_init(&usb->lock, 0);
-    // Initialize mark to 1 so we don't get garbage collected after the device
-    // scan.
-    usb->mark = 1;
-    usb->reaper_thread = 0;
+    // Initialize mark so we don't get garbage collected after the device scan.
+    usb->mark = true;
 
-    usb->desc = unix_open(usb->fname, O_RDWR | O_CLOEXEC);
-    if (usb->desc == -1) {
+    usb->fd = unix_open(usb->path.c_str(), O_RDWR | O_CLOEXEC);
+    if (usb->fd == -1) {
         // Opening RW failed, so see if we have RO access.
-        usb->desc = unix_open(usb->fname, O_RDONLY | O_CLOEXEC);
-        if (usb->desc == -1) {
-            D("[ usb open %s failed: %s]\n", usb->fname, strerror(errno));
-            free(usb);
+        usb->fd = unix_open(usb->path.c_str(), O_RDONLY | O_CLOEXEC);
+        if (usb->fd == -1) {
+            D("[ usb open %s failed: %s]\n", usb->path.c_str(), strerror(errno));
             return;
         }
         usb->writeable = 0;
     }
 
-    D("[ usb opened %s%s, fd=%d]\n", usb->fname,
-      (usb->writeable ? "" : " (read-only)"), usb->desc);
+    D("[ usb opened %s%s, fd=%d]\n",
+      usb->path.c_str(), (usb->writeable ? "" : " (read-only)"), usb->fd);
 
     if (usb->writeable) {
-        if (ioctl(usb->desc, USBDEVFS_CLAIMINTERFACE, &interface) != 0) {
-            D("[ usb ioctl(%d, USBDEVFS_CLAIMINTERFACE) failed: %s]\n",
-              usb->desc, strerror(errno));
-            adb_close(usb->desc);
-            free(usb);
+        if (ioctl(usb->fd, USBDEVFS_CLAIMINTERFACE, &interface) != 0) {
+            D("[ usb ioctl(%d, USBDEVFS_CLAIMINTERFACE) failed: %s]\n", usb->fd, strerror(errno));
             return;
         }
     }
@@ -642,17 +563,16 @@
     serial = android::base::Trim(serial);
 
     // Add to the end of the active handles.
-    adb_mutex_lock(&usb_lock);
-    usb->next = &handle_list;
-    usb->prev = handle_list.prev;
-    usb->prev->next = usb;
-    usb->next->prev = usb;
-    adb_mutex_unlock(&usb_lock);
-
-    register_usb_transport(usb, serial.c_str(), dev_path, usb->writeable);
+    usb_handle* done_usb = usb.release();
+    {
+        std::lock_guard<std::mutex> lock(g_usb_handles_mutex);
+        g_usb_handles.push_back(done_usb);
+    }
+    register_usb_transport(done_usb, serial.c_str(), dev_path, done_usb->writeable);
 }
 
 static void* device_poll_thread(void* unused) {
+    adb_thread_setname("device poll");
     D("Created device thread\n");
     while (true) {
         // TODO: Use inotify.
@@ -663,22 +583,15 @@
     return nullptr;
 }
 
-static void sigalrm_handler(int signo) {
-    // don't need to do anything here
-}
-
-void usb_init()
-{
-    adb_thread_t tid;
-    struct sigaction    actions;
-
+void usb_init() {
+    struct sigaction actions;
     memset(&actions, 0, sizeof(actions));
     sigemptyset(&actions.sa_mask);
     actions.sa_flags = 0;
-    actions.sa_handler = sigalrm_handler;
-    sigaction(SIGALRM,& actions, NULL);
+    actions.sa_handler = [](int) {};
+    sigaction(SIGALRM, &actions, nullptr);
 
-    if(adb_thread_create(&tid, device_poll_thread, NULL)){
-        fatal_errno("cannot create input thread");
+    if (!adb_thread_create(device_poll_thread, nullptr)) {
+        fatal_errno("cannot create device_poll thread");
     }
 }
diff --git a/adb/usb_linux_client.cpp b/adb/usb_linux_client.cpp
index f3db346..e1d7594 100644
--- a/adb/usb_linux_client.cpp
+++ b/adb/usb_linux_client.cpp
@@ -92,7 +92,7 @@
     struct ss_func_desc ss_descs;
 } __attribute__((packed));
 
-struct func_desc fs_descriptors = {
+static struct func_desc fs_descriptors = {
     .intf = {
         .bLength = sizeof(fs_descriptors.intf),
         .bDescriptorType = USB_DT_INTERFACE,
@@ -119,7 +119,7 @@
     },
 };
 
-struct func_desc hs_descriptors = {
+static struct func_desc hs_descriptors = {
     .intf = {
         .bLength = sizeof(hs_descriptors.intf),
         .bDescriptorType = USB_DT_INTERFACE,
@@ -209,6 +209,8 @@
     struct usb_handle *usb = (struct usb_handle *)x;
     int fd;
 
+    adb_thread_setname("usb open");
+
     while (true) {
         // wait until the USB device needs opening
         adb_mutex_lock(&usb->lock);
@@ -246,7 +248,7 @@
     int n;
 
     D("about to write (fd=%d, len=%d)\n", h->fd, len);
-    n = adb_write(h->fd, data, len);
+    n = unix_write(h->fd, data, len);
     if(n != len) {
         D("ERROR: fd = %d, n = %d, errno = %d (%s)\n",
             h->fd, n, errno, strerror(errno));
@@ -258,14 +260,20 @@
 
 static int usb_adb_read(usb_handle *h, void *data, int len)
 {
-    int n;
-
     D("about to read (fd=%d, len=%d)\n", h->fd, len);
-    n = adb_read(h->fd, data, len);
-    if(n != len) {
-        D("ERROR: fd = %d, n = %d, errno = %d (%s)\n",
-            h->fd, n, errno, strerror(errno));
-        return -1;
+    while (len > 0) {
+        // The kernel implementation of adb_read in f_adb.c doesn't support
+        // reads larger then 4096 bytes. Read the data in 4096 byte chunks to
+        // avoid the issue. (The ffs implementation doesn't have this limit.)
+        int bytes_to_read = len < 4096 ? len : 4096;
+        int n = unix_read(h->fd, data, bytes_to_read);
+        if (n != bytes_to_read) {
+            D("ERROR: fd = %d, n = %d, errno = %d (%s)\n",
+                h->fd, n, errno, strerror(errno));
+            return -1;
+        }
+        len -= n;
+        data = ((char*)data) + n;
     }
     D("[ done fd=%d ]\n", h->fd);
     return 0;
@@ -275,7 +283,7 @@
 {
     D("usb_kick\n");
     adb_mutex_lock(&h->lock);
-    adb_close(h->fd);
+    unix_close(h->fd);
     h->fd = -1;
 
     // notify usb_adb_open_thread that we are disconnected
@@ -309,8 +317,7 @@
     }
 
     D("[ usb_init - starting thread ]\n");
-    adb_thread_t tid;
-    if(adb_thread_create(&tid, usb_adb_open_thread, h)){
+    if (!adb_thread_create(usb_adb_open_thread, h)) {
         fatal_errno("cannot create usb thread");
     }
 }
@@ -398,6 +405,8 @@
 {
     struct usb_handle *usb = (struct usb_handle *)x;
 
+    adb_thread_setname("usb ffs open");
+
     while (true) {
         // wait until the USB device needs opening
         adb_mutex_lock(&usb->lock);
@@ -426,17 +435,12 @@
 static int bulk_write(int bulk_in, const uint8_t* buf, size_t length)
 {
     size_t count = 0;
-    int ret;
 
-    do {
-        ret = adb_write(bulk_in, buf + count, length - count);
-        if (ret < 0) {
-            if (errno != EINTR)
-                return ret;
-        } else {
-            count += ret;
-        }
-    } while (count < length);
+    while (count < length) {
+        int ret = adb_write(bulk_in, buf + count, length - count);
+        if (ret < 0) return -1;
+        count += ret;
+    }
 
     D("[ bulk_write done fd=%d ]\n", bulk_in);
     return count;
@@ -457,20 +461,15 @@
 static int bulk_read(int bulk_out, uint8_t* buf, size_t length)
 {
     size_t count = 0;
-    int ret;
 
-    do {
-        ret = adb_read(bulk_out, buf + count, length - count);
+    while (count < length) {
+        int ret = adb_read(bulk_out, buf + count, length - count);
         if (ret < 0) {
-            if (errno != EINTR) {
-                D("[ bulk_read failed fd=%d length=%zu count=%zu ]\n",
-                                           bulk_out, length, count);
-                return ret;
-            }
-        } else {
-            count += ret;
+            D("[ bulk_read failed fd=%d length=%zu count=%zu ]\n", bulk_out, length, count);
+            return -1;
         }
-    } while (count < length);
+        count += ret;
+    }
 
     return count;
 }
@@ -493,11 +492,11 @@
 
     err = ioctl(h->bulk_in, FUNCTIONFS_CLEAR_HALT);
     if (err < 0)
-        D("[ kick: source (fd=%d) clear halt failed (%d) ]", h->bulk_in, errno);
+        D("[ kick: source (fd=%d) clear halt failed (%d) ]\n", h->bulk_in, errno);
 
     err = ioctl(h->bulk_out, FUNCTIONFS_CLEAR_HALT);
     if (err < 0)
-        D("[ kick: sink (fd=%d) clear halt failed (%d) ]", h->bulk_out, errno);
+        D("[ kick: sink (fd=%d) clear halt failed (%d) ]\n", h->bulk_out, errno);
 
     adb_mutex_lock(&h->lock);
 
@@ -531,8 +530,7 @@
     adb_mutex_init(&h->lock, 0);
 
     D("[ usb_init - starting thread ]\n");
-    adb_thread_t tid;
-    if (adb_thread_create(&tid, usb_ffs_open_thread, h)){
+    if (!adb_thread_create(usb_ffs_open_thread, h)) {
         fatal_errno("[ cannot create usb thread ]\n");
     }
 }
@@ -545,10 +543,6 @@
         usb_adb_init();
 }
 
-void usb_cleanup()
-{
-}
-
 int usb_write(usb_handle *h, const void *data, int len)
 {
     return h->write(h, data, len);
diff --git a/adb/usb_osx.cpp b/adb/usb_osx.cpp
index a795ce3..8037606 100644
--- a/adb/usb_osx.cpp
+++ b/adb/usb_osx.cpp
@@ -29,21 +29,28 @@
 #include <inttypes.h>
 #include <stdio.h>
 
+#include <base/logging.h>
+#include <base/stringprintf.h>
+
 #include "adb.h"
 #include "transport.h"
 
 #define  DBG   D
 
+// There's no strerror equivalent for the errors returned by IOKit.
+// https://developer.apple.com/library/mac/documentation/DeviceDrivers/Conceptual/AccessingHardware/AH_Handling_Errors/AH_Handling_Errors.html
+// Search the web for "IOReturn.h" to find a complete up to date list.
+
 static IONotificationPortRef    notificationPort = 0;
 static io_iterator_t            notificationIterator;
 
 struct usb_handle
 {
-    UInt8                     bulkIn;
-    UInt8                     bulkOut;
-    IOUSBInterfaceInterface   **interface;
-    io_object_t               usbNotification;
-    unsigned int              zero_mask;
+    UInt8 bulkIn;
+    UInt8 bulkOut;
+    IOUSBInterfaceInterface190** interface;
+    io_object_t usbNotification;
+    unsigned int zero_mask;
 };
 
 static CFRunLoopRef currentRunLoop = 0;
@@ -55,7 +62,7 @@
 static void AndroidInterfaceNotify(void *refCon, io_iterator_t iterator,
                                    natural_t messageType,
                                    void *messageArgument);
-static usb_handle* CheckInterface(IOUSBInterfaceInterface **iface,
+static usb_handle* CheckInterface(IOUSBInterfaceInterface190 **iface,
                                   UInt16 vendor, UInt16 product);
 
 static int
@@ -77,7 +84,7 @@
     matchingDict = IOServiceMatching(kIOUSBInterfaceClassName);
 
     if (!matchingDict) {
-        DBG("ERR: Couldn't create USB matching dictionary.\n");
+        LOG(ERROR) << "Couldn't create USB matching dictionary.";
         return -1;
     }
 
@@ -111,7 +118,7 @@
     IOUSBDeviceInterface197  **dev = NULL;
     HRESULT                  result;
     SInt32                   score;
-    UInt32                   locationId;
+    uint32_t                 locationId;
     UInt8                    if_class, subclass, protocol;
     UInt16                   vendor;
     UInt16                   product;
@@ -128,7 +135,7 @@
                                                &plugInInterface, &score);
         IOObjectRelease(usbInterface);
         if ((kIOReturnSuccess != kr) || (!plugInInterface)) {
-            DBG("ERR: Unable to create an interface plug-in (%08x)\n", kr);
+            LOG(ERROR) << "Unable to create an interface plug-in (" << std::hex << kr << ")";
             continue;
         }
 
@@ -139,7 +146,7 @@
         //* We only needed the plugin to get the interface, so discard it
         (*plugInInterface)->Release(plugInInterface);
         if (result || !iface) {
-            DBG("ERR: Couldn't query the interface (%08x)\n", (int) result);
+            LOG(ERROR) << "Couldn't query the interface (" << std::hex << result << ")";
             continue;
         }
 
@@ -148,7 +155,8 @@
         kr = (*iface)->GetInterfaceProtocol(iface, &protocol);
         if(if_class != ADB_CLASS || subclass != ADB_SUBCLASS || protocol != ADB_PROTOCOL) {
             // Ignore non-ADB devices.
-            DBG("Ignoring interface with incorrect class/subclass/protocol - %d, %d, %d\n", if_class, subclass, protocol);
+            LOG(DEBUG) << "Ignoring interface with incorrect class/subclass/protocol - " << if_class
+                       << ", " << subclass << ", " << protocol;
             (*iface)->Release(iface);
             continue;
         }
@@ -159,7 +167,7 @@
         //* Gotta love OS X
         kr = (*iface)->GetDevice(iface, &usbDevice);
         if (kIOReturnSuccess != kr || !usbDevice) {
-            DBG("ERR: Couldn't grab device from interface (%08x)\n", kr);
+            LOG(ERROR) << "Couldn't grab device from interface (" << std::hex << kr << ")";
             continue;
         }
 
@@ -173,7 +181,7 @@
         //* only needed this to find the plugin
         (void)IOObjectRelease(usbDevice);
         if ((kIOReturnSuccess != kr) || (!plugInInterface)) {
-            DBG("ERR: Unable to create a device plug-in (%08x)\n", kr);
+            LOG(ERROR) << "Unable to create a device plug-in (" << std::hex << kr << ")";
             continue;
         }
 
@@ -182,8 +190,7 @@
         //* only needed this to query the plugin
         (*plugInInterface)->Release(plugInInterface);
         if (result || !dev) {
-            DBG("ERR: Couldn't create a device interface (%08x)\n",
-                (int) result);
+            LOG(ERROR) << "Couldn't create a device interface (" << std::hex << result << ")";
             continue;
         }
 
@@ -193,74 +200,74 @@
         kr = (*dev)->GetDeviceProduct(dev, &product);
         kr = (*dev)->GetLocationID(dev, &locationId);
         if (kr == 0) {
-            snprintf(devpathBuf, sizeof(devpathBuf), "usb:%" PRIu32 "X",
-	             (unsigned int)locationId);
+            snprintf(devpathBuf, sizeof(devpathBuf), "usb:%" PRIu32 "X", locationId);
             devpath = devpathBuf;
         }
         kr = (*dev)->USBGetSerialNumberStringIndex(dev, &serialIndex);
 
-	if (serialIndex > 0) {
-		IOUSBDevRequest req;
-		UInt16          buffer[256];
-		UInt16          languages[128];
+        if (serialIndex > 0) {
+            IOUSBDevRequest req;
+            UInt16          buffer[256];
+            UInt16          languages[128];
 
-		memset(languages, 0, sizeof(languages));
+            memset(languages, 0, sizeof(languages));
 
-		req.bmRequestType =
-			USBmakebmRequestType(kUSBIn, kUSBStandard, kUSBDevice);
-		req.bRequest = kUSBRqGetDescriptor;
-		req.wValue = (kUSBStringDesc << 8) | 0;
-		req.wIndex = 0;
-		req.pData = languages;
-		req.wLength = sizeof(languages);
-		kr = (*dev)->DeviceRequest(dev, &req);
+            req.bmRequestType =
+                    USBmakebmRequestType(kUSBIn, kUSBStandard, kUSBDevice);
+            req.bRequest = kUSBRqGetDescriptor;
+            req.wValue = (kUSBStringDesc << 8) | 0;
+            req.wIndex = 0;
+            req.pData = languages;
+            req.wLength = sizeof(languages);
+            kr = (*dev)->DeviceRequest(dev, &req);
 
-		if (kr == kIOReturnSuccess && req.wLenDone > 0) {
+            if (kr == kIOReturnSuccess && req.wLenDone > 0) {
 
-			int langCount = (req.wLenDone - 2) / 2, lang;
+                int langCount = (req.wLenDone - 2) / 2, lang;
 
-			for (lang = 1; lang <= langCount; lang++) {
+                for (lang = 1; lang <= langCount; lang++) {
 
-                                memset(buffer, 0, sizeof(buffer));
-                                memset(&req, 0, sizeof(req));
+                    memset(buffer, 0, sizeof(buffer));
+                    memset(&req, 0, sizeof(req));
 
-				req.bmRequestType =
-					USBmakebmRequestType(kUSBIn, kUSBStandard, kUSBDevice);
-				req.bRequest = kUSBRqGetDescriptor;
-				req.wValue = (kUSBStringDesc << 8) | serialIndex;
-				req.wIndex = languages[lang];
-				req.pData = buffer;
-				req.wLength = sizeof(buffer);
-				kr = (*dev)->DeviceRequest(dev, &req);
+                    req.bmRequestType =
+                            USBmakebmRequestType(kUSBIn, kUSBStandard, kUSBDevice);
+                    req.bRequest = kUSBRqGetDescriptor;
+                    req.wValue = (kUSBStringDesc << 8) | serialIndex;
+                    req.wIndex = languages[lang];
+                    req.pData = buffer;
+                    req.wLength = sizeof(buffer);
+                    kr = (*dev)->DeviceRequest(dev, &req);
 
-				if (kr == kIOReturnSuccess && req.wLenDone > 0) {
-					int i, count;
+                    if (kr == kIOReturnSuccess && req.wLenDone > 0) {
+                        int i, count;
 
-					// skip first word, and copy the rest to the serial string,
-					// changing shorts to bytes.
-					count = (req.wLenDone - 1) / 2;
-					for (i = 0; i < count; i++)
-						serial[i] = buffer[i + 1];
-					serial[i] = 0;
-                                        break;
-				}
-			}
-		}
-	}
+                        // skip first word, and copy the rest to the serial string,
+                        // changing shorts to bytes.
+                        count = (req.wLenDone - 1) / 2;
+                        for (i = 0; i < count; i++)
+                                serial[i] = buffer[i + 1];
+                        serial[i] = 0;
+                        break;
+                    }
+                }
+            }
+        }
+
         (*dev)->Release(dev);
 
-        DBG("INFO: Found vid=%04x pid=%04x serial=%s\n", vendor, product,
-            serial);
+        LOG(INFO) << android::base::StringPrintf("Found vid=%04x pid=%04x serial=%s\n",
+                        vendor, product, serial);
 
-        usb_handle* handle = CheckInterface((IOUSBInterfaceInterface**)iface,
+        usb_handle* handle = CheckInterface((IOUSBInterfaceInterface190**)iface,
                                             vendor, product);
         if (handle == NULL) {
-            DBG("ERR: Could not find device interface: %08x\n", kr);
+            LOG(ERROR) << "Could not find device interface";
             (*iface)->Release(iface);
             continue;
         }
 
-        DBG("AndroidDeviceAdded calling register_usb_transport\n");
+        LOG(DEBUG) << "AndroidDeviceAdded calling register_usb_transport";
         register_usb_transport(handle, (serial[0] ? serial : NULL), devpath, 1);
 
         // Register for an interest notification of this device being removed.
@@ -274,7 +281,7 @@
                 &handle->usbNotification);
 
         if (kIOReturnSuccess != kr) {
-            DBG("ERR: Unable to create interest notification (%08x)\n", kr);
+            LOG(ERROR) << "Unable to create interest notification (" << std::hex << kr << ")";
         }
     }
 }
@@ -286,38 +293,49 @@
 
     if (messageType == kIOMessageServiceIsTerminated) {
         if (!handle) {
-            DBG("ERR: NULL handle\n");
+            LOG(ERROR) << "NULL handle";
             return;
         }
-        DBG("AndroidInterfaceNotify\n");
+        LOG(DEBUG) << "AndroidInterfaceNotify";
         IOObjectRelease(handle->usbNotification);
         usb_kick(handle);
     }
 }
 
+// Used to clear both the endpoints before starting.
+// When adb quits, we might clear the host endpoint but not the device.
+// So we make sure both sides are clear before starting up.
+static bool ClearPipeStallBothEnds(IOUSBInterfaceInterface190** interface, UInt8 bulkEp) {
+    IOReturn rc = (*interface)->ClearPipeStallBothEnds(interface, bulkEp);
+    if (rc != kIOReturnSuccess) {
+        LOG(ERROR) << "Could not clear pipe stall both ends: " << std::hex << rc;
+        return false;
+    }
+    return true;
+}
+
 //* TODO: simplify this further since we only register to get ADB interface
 //* subclass+protocol events
 static usb_handle*
-CheckInterface(IOUSBInterfaceInterface **interface, UInt16 vendor, UInt16 product)
+CheckInterface(IOUSBInterfaceInterface190 **interface, UInt16 vendor, UInt16 product)
 {
-    usb_handle*                 handle = NULL;
-    IOReturn                    kr;
-    UInt8  interfaceNumEndpoints, interfaceClass, interfaceSubClass, interfaceProtocol;
-    UInt8  endpoint;
-
+    usb_handle* handle;
+    IOReturn kr;
+    UInt8 interfaceNumEndpoints, interfaceClass, interfaceSubClass, interfaceProtocol;
+    UInt8 endpoint;
 
     //* Now open the interface.  This will cause the pipes associated with
     //* the endpoints in the interface descriptor to be instantiated
     kr = (*interface)->USBInterfaceOpen(interface);
     if (kr != kIOReturnSuccess) {
-        DBG("ERR: Could not open interface: (%08x)\n", kr);
+        LOG(ERROR) << "Could not open interface: " << std::hex << kr;
         return NULL;
     }
 
     //* Get the number of endpoints associated with this interface
     kr = (*interface)->GetNumEndpoints(interface, &interfaceNumEndpoints);
     if (kr != kIOReturnSuccess) {
-        DBG("ERR: Unable to get number of endpoints: (%08x)\n", kr);
+        LOG(ERROR) << "Unable to get number of endpoints: " << std::hex << kr;
         goto err_get_num_ep;
     }
 
@@ -325,22 +343,22 @@
     if ((*interface)->GetInterfaceClass(interface, &interfaceClass) != kIOReturnSuccess ||
             (*interface)->GetInterfaceSubClass(interface, &interfaceSubClass) != kIOReturnSuccess ||
             (*interface)->GetInterfaceProtocol(interface, &interfaceProtocol) != kIOReturnSuccess) {
-            DBG("ERR: Unable to get interface class, subclass and protocol\n");
+            LOG(ERROR) << "Unable to get interface class, subclass and protocol";
             goto err_get_interface_class;
     }
 
     //* check to make sure interface class, subclass and protocol match ADB
     //* avoid opening mass storage endpoints
-    if (!is_adb_interface(vendor, product, interfaceClass,
-                interfaceSubClass, interfaceProtocol))
+    if (!is_adb_interface(vendor, product, interfaceClass, interfaceSubClass, interfaceProtocol)) {
         goto err_bad_adb_interface;
+    }
 
     handle = reinterpret_cast<usb_handle*>(calloc(1, sizeof(usb_handle)));
     if (handle == nullptr) goto err_bad_adb_interface;
 
     //* Iterate over the endpoints for this interface and find the first
     //* bulk in/out pipes available.  These will be our read/write pipes.
-    for (endpoint = 0; endpoint <= interfaceNumEndpoints; endpoint++) {
+    for (endpoint = 1; endpoint <= interfaceNumEndpoints; endpoint++) {
         UInt8   transferType;
         UInt16  maxPacketSize;
         UInt8   interval;
@@ -349,22 +367,25 @@
 
         kr = (*interface)->GetPipeProperties(interface, endpoint, &direction,
                 &number, &transferType, &maxPacketSize, &interval);
-
-        if (kIOReturnSuccess == kr) {
-            if (kUSBBulk != transferType)
-                continue;
-
-            if (kUSBIn == direction)
-                handle->bulkIn = endpoint;
-
-            if (kUSBOut == direction)
-                handle->bulkOut = endpoint;
-
-            handle->zero_mask = maxPacketSize - 1;
-        } else {
-            DBG("ERR: FindDeviceInterface - could not get pipe properties\n");
+        if (kr != kIOReturnSuccess) {
+            LOG(ERROR) << "FindDeviceInterface - could not get pipe properties: "
+                       << std::hex << kr;
             goto err_get_pipe_props;
         }
+
+        if (kUSBBulk != transferType) continue;
+
+        if (kUSBIn == direction) {
+            handle->bulkIn = endpoint;
+            if (!ClearPipeStallBothEnds(interface, handle->bulkIn)) goto err_get_pipe_props;
+        }
+
+        if (kUSBOut == direction) {
+            handle->bulkOut = endpoint;
+            if (!ClearPipeStallBothEnds(interface, handle->bulkOut)) goto err_get_pipe_props;
+        }
+
+        handle->zero_mask = maxPacketSize - 1;
     }
 
     handle->interface = interface;
@@ -382,6 +403,7 @@
 
 void* RunLoopThread(void* unused)
 {
+    adb_thread_setname("RunLoop");
     InitUSB();
 
     currentRunLoop = CFRunLoopGetCurrent();
@@ -397,23 +419,28 @@
     IOObjectRelease(notificationIterator);
     IONotificationPortDestroy(notificationPort);
 
-    DBG("RunLoopThread done\n");
-    return NULL;    
+    LOG(DEBUG) << "RunLoopThread done";
+    return NULL;
 }
 
+static void usb_cleanup() {
+    LOG(DEBUG) << "usb_cleanup";
+    close_usb_devices();
+    if (currentRunLoop)
+        CFRunLoopStop(currentRunLoop);
+}
 
-static int initialized = 0;
-void usb_init()
-{
-    if (!initialized)
-    {
-        adb_thread_t    tid;
+void usb_init() {
+    static bool initialized = false;
+    if (!initialized) {
+        atexit(usb_cleanup);
 
         adb_mutex_init(&start_lock, NULL);
         adb_cond_init(&start_cond, NULL);
 
-        if(adb_thread_create(&tid, RunLoopThread, NULL))
-            fatal_errno("cannot create input thread");
+        if (!adb_thread_create(RunLoopThread, nullptr)) {
+            fatal_errno("cannot create RunLoop thread");
+        }
 
         // Wait for initialization to finish
         adb_mutex_lock(&start_lock);
@@ -423,18 +450,10 @@
         adb_mutex_destroy(&start_lock);
         adb_cond_destroy(&start_cond);
 
-        initialized = 1;
+        initialized = true;
     }
 }
 
-void usb_cleanup()
-{
-    DBG("usb_cleanup\n");
-    close_usb_devices();
-    if (currentRunLoop)
-        CFRunLoopStop(currentRunLoop);
-}
-
 int usb_write(usb_handle *handle, const void *buf, int len)
 {
     IOReturn    result;
@@ -446,18 +465,17 @@
         return -1;
 
     if (NULL == handle->interface) {
-        DBG("ERR: usb_write interface was null\n");
+        LOG(ERROR) << "usb_write interface was null";
         return -1;
     }
 
     if (0 == handle->bulkOut) {
-        DBG("ERR: bulkOut endpoint not assigned\n");
+        LOG(ERROR) << "bulkOut endpoint not assigned";
         return -1;
     }
 
     result =
-        (*handle->interface)->WritePipe(
-                              handle->interface, handle->bulkOut, (void *)buf, len);
+        (*handle->interface)->WritePipe(handle->interface, handle->bulkOut, (void *)buf, len);
 
     if ((result == 0) && (handle->zero_mask)) {
         /* we need 0-markers and our transfer */
@@ -471,7 +489,7 @@
     if (0 == result)
         return 0;
 
-    DBG("ERR: usb_write failed with status %d\n", result);
+    LOG(ERROR) << "usb_write failed with status: " << std::hex << result;
     return -1;
 }
 
@@ -489,19 +507,19 @@
     }
 
     if (NULL == handle->interface) {
-        DBG("ERR: usb_read interface was null\n");
+        LOG(ERROR) << "usb_read interface was null";
         return -1;
     }
 
     if (0 == handle->bulkIn) {
-        DBG("ERR: bulkIn endpoint not assigned\n");
+        LOG(ERROR) << "bulkIn endpoint not assigned";
         return -1;
     }
 
     result = (*handle->interface)->ReadPipe(handle->interface, handle->bulkIn, buf, &numBytes);
 
     if (kIOUSBPipeStalled == result) {
-        DBG(" Pipe stalled, clearing stall.\n");
+        LOG(ERROR) << "Pipe stalled, clearing stall.\n";
         (*handle->interface)->ClearPipeStall(handle->interface, handle->bulkIn);
         result = (*handle->interface)->ReadPipe(handle->interface, handle->bulkIn, buf, &numBytes);
     }
@@ -509,7 +527,7 @@
     if (kIOReturnSuccess == result)
         return 0;
     else {
-        DBG("ERR: usb_read failed with status %x\n", result);
+        LOG(ERROR) << "usb_read failed with status: " << std::hex << result;
     }
 
     return -1;
@@ -522,6 +540,7 @@
 
 void usb_kick(usb_handle *handle)
 {
+    LOG(INFO) << "Kicking handle";
     /* release the interface */
     if (!handle)
         return;
diff --git a/adb/usb_windows.cpp b/adb/usb_windows.cpp
index d2bd58c..ab36475 100644
--- a/adb/usb_windows.cpp
+++ b/adb/usb_windows.cpp
@@ -33,6 +33,10 @@
 /** Structure usb_handle describes our connection to the usb device via
   AdbWinApi.dll. This structure is returned from usb_open() routine and
   is expected in each subsequent call that is accessing the device.
+
+  Most members are protected by usb_lock, except for adb_{read,write}_pipe which
+  rely on AdbWinApi.dll's handle validation and AdbCloseHandle(endpoint)'s
+  ability to break a thread out of pipe IO.
 */
 struct usb_handle {
   /// Previous entry in the list of opened usb handles
@@ -86,6 +90,9 @@
 /// registers usb transport for them.
 void find_devices();
 
+/// Kicks all USB devices
+static void kick_devices();
+
 /// Entry point for thread that polls (every second) for new usb interfaces.
 /// This routine calls find_devices in infinite loop.
 void* device_poll_thread(void* unused);
@@ -93,9 +100,6 @@
 /// Initializes this module
 void usb_init();
 
-/// Cleans up this module
-void usb_cleanup();
-
 /// Opens usb interface (device) by interface (device) name.
 usb_handle* do_usb_open(const wchar_t* interface_name);
 
@@ -114,9 +118,6 @@
 /// Closes opened usb handle
 int usb_close(usb_handle* handle);
 
-/// Gets interface (device) name for an opened usb handle
-const char *usb_name(usb_handle* handle);
-
 int known_device_locked(const char* dev_name) {
   usb_handle* usb;
 
@@ -170,6 +171,7 @@
 }
 
 void* device_poll_thread(void* unused) {
+  adb_thread_setname("Device Poll");
   D("Created device thread\n");
 
   while(1) {
@@ -180,22 +182,100 @@
   return NULL;
 }
 
-void usb_init() {
-  adb_thread_t tid;
+static LRESULT CALLBACK _power_window_proc(HWND hwnd, UINT uMsg, WPARAM wParam,
+                                           LPARAM lParam) {
+  switch (uMsg) {
+  case WM_POWERBROADCAST:
+    switch (wParam) {
+    case PBT_APMRESUMEAUTOMATIC:
+      // Resuming from sleep or hibernation, so kick all existing USB devices
+      // and then allow the device_poll_thread to redetect USB devices from
+      // scratch. If we don't do this, existing USB devices will never respond
+      // to us because they'll be waiting for the connect/auth handshake.
+      D("Received (WM_POWERBROADCAST, PBT_APMRESUMEAUTOMATIC) notification, "
+        "so kicking all USB devices\n");
+      kick_devices();
+      return TRUE;
+    }
+  }
+  return DefWindowProcW(hwnd, uMsg, wParam, lParam);
+}
 
-  if(adb_thread_create(&tid, device_poll_thread, NULL)) {
-    fatal_errno("cannot create input thread");
+static void* _power_notification_thread(void* unused) {
+  // This uses a thread with its own window message pump to get power
+  // notifications. If adb runs from a non-interactive service account, this
+  // might not work (not sure). If that happens to not work, we could use
+  // heavyweight WMI APIs to get power notifications. But for the common case
+  // of a developer's interactive session, a window message pump is more
+  // appropriate.
+  D("Created power notification thread\n");
+  adb_thread_setname("Power Notifier");
+
+  // Window class names are process specific.
+  static const WCHAR kPowerNotificationWindowClassName[] =
+    L"PowerNotificationWindow";
+
+  // Get the HINSTANCE corresponding to the module that _power_window_proc
+  // is in (the main module).
+  const HINSTANCE instance = GetModuleHandleW(NULL);
+  if (!instance) {
+    // This is such a common API call that this should never fail.
+    fatal("GetModuleHandleW failed: %s",
+          SystemErrorCodeToString(GetLastError()).c_str());
+  }
+
+  WNDCLASSEXW wndclass;
+  memset(&wndclass, 0, sizeof(wndclass));
+  wndclass.cbSize = sizeof(wndclass);
+  wndclass.lpfnWndProc = _power_window_proc;
+  wndclass.hInstance = instance;
+  wndclass.lpszClassName = kPowerNotificationWindowClassName;
+  if (!RegisterClassExW(&wndclass)) {
+    fatal("RegisterClassExW failed: %s",
+          SystemErrorCodeToString(GetLastError()).c_str());
+  }
+
+  if (!CreateWindowExW(WS_EX_NOACTIVATE, kPowerNotificationWindowClassName,
+                       L"ADB Power Notification Window", WS_POPUP, 0, 0, 0, 0,
+                       NULL, NULL, instance, NULL)) {
+    fatal("CreateWindowExW failed: %s",
+          SystemErrorCodeToString(GetLastError()).c_str());
+  }
+
+  MSG msg;
+  while (GetMessageW(&msg, NULL, 0, 0)) {
+    TranslateMessage(&msg);
+    DispatchMessageW(&msg);
+  }
+
+  // GetMessageW() will return false if a quit message is posted. We don't
+  // do that, but it might be possible for that to occur when logging off or
+  // shutting down. Not a big deal since the whole process will be going away
+  // soon anyway.
+  D("Power notification thread exiting\n");
+
+  return NULL;
+}
+
+void usb_init() {
+  if (!adb_thread_create(device_poll_thread, nullptr)) {
+    fatal_errno("cannot create device poll thread");
+  }
+  if (!adb_thread_create(_power_notification_thread, nullptr)) {
+    fatal_errno("cannot create power notification thread");
   }
 }
 
-void usb_cleanup() {
-}
-
 usb_handle* do_usb_open(const wchar_t* interface_name) {
+  unsigned long name_len = 0;
+
   // Allocate our handle
-  usb_handle* ret = (usb_handle*)malloc(sizeof(usb_handle));
-  if (NULL == ret)
-    return NULL;
+  usb_handle* ret = (usb_handle*)calloc(1, sizeof(usb_handle));
+  if (NULL == ret) {
+    D("Could not allocate %u bytes for usb_handle: %s\n", sizeof(usb_handle),
+      strerror(errno));
+    goto fail;
+  }
 
   // Set linkers back to the handle
   ret->next = ret;
@@ -203,11 +283,10 @@
 
   // Create interface.
   ret->adb_interface = AdbCreateInterfaceByName(interface_name);
-
   if (NULL == ret->adb_interface) {
-    free(ret);
-    errno = GetLastError();
-    return NULL;
+    D("AdbCreateInterfaceByName failed: %s\n",
+      SystemErrorCodeToString(GetLastError()).c_str());
+    goto fail;
   }
 
   // Open read pipe (endpoint)
@@ -215,45 +294,60 @@
     AdbOpenDefaultBulkReadEndpoint(ret->adb_interface,
                                    AdbOpenAccessTypeReadWrite,
                                    AdbOpenSharingModeReadWrite);
-  if (NULL != ret->adb_read_pipe) {
-    // Open write pipe (endpoint)
-    ret->adb_write_pipe =
-      AdbOpenDefaultBulkWriteEndpoint(ret->adb_interface,
-                                      AdbOpenAccessTypeReadWrite,
-                                      AdbOpenSharingModeReadWrite);
-    if (NULL != ret->adb_write_pipe) {
-      // Save interface name
-      unsigned long name_len = 0;
-
-      // First get expected name length
-      AdbGetInterfaceName(ret->adb_interface,
-                          NULL,
-                          &name_len,
-                          true);
-      if (0 != name_len) {
-        ret->interface_name = (char*)malloc(name_len);
-
-        if (NULL != ret->interface_name) {
-          // Now save the name
-          if (AdbGetInterfaceName(ret->adb_interface,
-                                  ret->interface_name,
-                                  &name_len,
-                                  true)) {
-            // We're done at this point
-            return ret;
-          }
-        } else {
-          SetLastError(ERROR_OUTOFMEMORY);
-        }
-      }
-    }
+  if (NULL == ret->adb_read_pipe) {
+    D("AdbOpenDefaultBulkReadEndpoint failed: %s\n",
+      SystemErrorCodeToString(GetLastError()).c_str());
+    goto fail;
   }
 
-  // Something went wrong.
-  int saved_errno = GetLastError();
-  usb_cleanup_handle(ret);
-  free(ret);
-  SetLastError(saved_errno);
+  // Open write pipe (endpoint)
+  ret->adb_write_pipe =
+    AdbOpenDefaultBulkWriteEndpoint(ret->adb_interface,
+                                    AdbOpenAccessTypeReadWrite,
+                                    AdbOpenSharingModeReadWrite);
+  if (NULL == ret->adb_write_pipe) {
+    D("AdbOpenDefaultBulkWriteEndpoint failed: %s\n",
+      SystemErrorCodeToString(GetLastError()).c_str());
+    goto fail;
+  }
+
+  // Save interface name
+  // First get expected name length
+  AdbGetInterfaceName(ret->adb_interface,
+                      NULL,
+                      &name_len,
+                      true);
+  if (0 == name_len) {
+    D("AdbGetInterfaceName returned name length of zero: %s\n",
+      SystemErrorCodeToString(GetLastError()).c_str());
+    goto fail;
+  }
+
+  ret->interface_name = (char*)malloc(name_len);
+  if (NULL == ret->interface_name) {
+    D("Could not allocate %lu bytes for interface_name: %s\n", name_len,
+      strerror(errno));
+    goto fail;
+  }
+
+  // Now save the name
+  if (!AdbGetInterfaceName(ret->adb_interface,
+                           ret->interface_name,
+                           &name_len,
+                           true)) {
+    D("AdbGetInterfaceName failed: %s\n",
+      SystemErrorCodeToString(GetLastError()).c_str());
+    goto fail;
+  }
+
+  // We're done at this point
+  return ret;
+
+fail:
+  if (NULL != ret) {
+    usb_cleanup_handle(ret);
+    free(ret);
+  }
 
   return NULL;
 }
@@ -261,99 +355,130 @@
 int usb_write(usb_handle* handle, const void* data, int len) {
   unsigned long time_out = 5000;
   unsigned long written = 0;
-  int ret;
+  int err = 0;
 
   D("usb_write %d\n", len);
-  if (NULL != handle) {
-    // Perform write
-    ret = AdbWriteEndpointSync(handle->adb_write_pipe,
-                               (void*)data,
-                               (unsigned long)len,
-                               &written,
-                               time_out);
-    int saved_errno = GetLastError();
-
-    if (ret) {
-      // Make sure that we've written what we were asked to write
-      D("usb_write got: %ld, expected: %d\n", written, len);
-      if (written == (unsigned long)len) {
-        if(handle->zero_mask && (len & handle->zero_mask) == 0) {
-          // Send a zero length packet
-          AdbWriteEndpointSync(handle->adb_write_pipe,
-                               (void*)data,
-                               0,
-                               &written,
-                               time_out);
-        }
-        return 0;
-      }
-    } else {
-      // assume ERROR_INVALID_HANDLE indicates we are disconnected
-      if (saved_errno == ERROR_INVALID_HANDLE)
-        usb_kick(handle);
-    }
-    errno = saved_errno;
-  } else {
-    D("usb_write NULL handle\n");
-    SetLastError(ERROR_INVALID_HANDLE);
+  if (NULL == handle) {
+    D("usb_write was passed NULL handle\n");
+    err = EINVAL;
+    goto fail;
   }
 
-  D("usb_write failed: %d\n", errno);
+  // Perform write
+  if (!AdbWriteEndpointSync(handle->adb_write_pipe,
+                            (void*)data,
+                            (unsigned long)len,
+                            &written,
+                            time_out)) {
+    D("AdbWriteEndpointSync failed: %s\n",
+      SystemErrorCodeToString(GetLastError()).c_str());
+    err = EIO;
+    goto fail;
+  }
 
+  // Make sure that we've written what we were asked to write
+  D("usb_write got: %ld, expected: %d\n", written, len);
+  if (written != (unsigned long)len) {
+    // If this occurs, this code should be changed to repeatedly call
+    // AdbWriteEndpointSync() until all bytes are written.
+    D("AdbWriteEndpointSync was supposed to write %d, but only wrote %ld\n",
+      len, written);
+    err = EIO;
+    goto fail;
+  }
+
+  if (handle->zero_mask && (len & handle->zero_mask) == 0) {
+    // Send a zero length packet
+    if (!AdbWriteEndpointSync(handle->adb_write_pipe,
+                              (void*)data,
+                              0,
+                              &written,
+                              time_out)) {
+      D("AdbWriteEndpointSync of zero length packet failed: %s\n",
+        SystemErrorCodeToString(GetLastError()).c_str());
+      err = EIO;
+      goto fail;
+    }
+  }
+
+  return 0;
+
+fail:
+  // Any failure should cause us to kick the device instead of leaving it a
+  // zombie state with potential to hang.
+  if (NULL != handle) {
+    D("Kicking device due to error in usb_write\n");
+    usb_kick(handle);
+  }
+
+  D("usb_write failed\n");
+  errno = err;
   return -1;
 }
 
 int usb_read(usb_handle *handle, void* data, int len) {
   unsigned long time_out = 0;
   unsigned long read = 0;
-  int ret;
+  int err = 0;
 
   D("usb_read %d\n", len);
-  if (NULL != handle) {
-    while (len > 0) {
-      int xfer = (len > 4096) ? 4096 : len;
-
-      ret = AdbReadEndpointSync(handle->adb_read_pipe,
-                                  data,
-                                  (unsigned long)xfer,
-                                  &read,
-                                  time_out);
-      int saved_errno = GetLastError();
-      D("usb_write got: %ld, expected: %d, errno: %d\n", read, xfer, saved_errno);
-      if (ret) {
-        data = (char *)data + read;
-        len -= read;
-
-        if (len == 0)
-          return 0;
-      } else {
-        // assume ERROR_INVALID_HANDLE indicates we are disconnected
-        if (saved_errno == ERROR_INVALID_HANDLE)
-          usb_kick(handle);
-        break;
-      }
-      errno = saved_errno;
-    }
-  } else {
-    D("usb_read NULL handle\n");
-    SetLastError(ERROR_INVALID_HANDLE);
+  if (NULL == handle) {
+    D("usb_read was passed NULL handle\n");
+    err = EINVAL;
+    goto fail;
   }
 
-  D("usb_read failed: %d\n", errno);
+  while (len > 0) {
+    if (!AdbReadEndpointSync(handle->adb_read_pipe, data, len, &read,
+                             time_out)) {
+      D("AdbReadEndpointSync failed: %s\n",
+        SystemErrorCodeToString(GetLastError()).c_str());
+      err = EIO;
+      goto fail;
+    }
+    D("usb_read got: %ld, expected: %d\n", read, len);
 
+    data = (char *)data + read;
+    len -= read;
+  }
+
+  return 0;
+
+fail:
+  // Any failure should cause us to kick the device instead of leaving it a
+  // zombie state with potential to hang.
+  if (NULL != handle) {
+    D("Kicking device due to error in usb_read\n");
+    usb_kick(handle);
+  }
+
+  D("usb_read failed\n");
+  errno = err;
   return -1;
 }
 
+// Wrapper around AdbCloseHandle() that logs diagnostics.
+static void _adb_close_handle(ADBAPIHANDLE adb_handle) {
+  if (!AdbCloseHandle(adb_handle)) {
+    D("AdbCloseHandle(%p) failed: %s\n", adb_handle,
+      SystemErrorCodeToString(GetLastError()).c_str());
+  }
+}
+
 void usb_cleanup_handle(usb_handle* handle) {
+  D("usb_cleanup_handle\n");
   if (NULL != handle) {
     if (NULL != handle->interface_name)
       free(handle->interface_name);
+    // AdbCloseHandle(pipe) will break any threads out of pending IO calls and
+    // wait until the pipe no longer uses the interface. Then we can
+    // AdbCloseHandle() the interface.
     if (NULL != handle->adb_write_pipe)
-      AdbCloseHandle(handle->adb_write_pipe);
+      _adb_close_handle(handle->adb_write_pipe);
     if (NULL != handle->adb_read_pipe)
-      AdbCloseHandle(handle->adb_read_pipe);
+      _adb_close_handle(handle->adb_read_pipe);
     if (NULL != handle->adb_interface)
-      AdbCloseHandle(handle->adb_interface);
+      _adb_close_handle(handle->adb_interface);
 
     handle->interface_name = NULL;
     handle->adb_write_pipe = NULL;
@@ -362,16 +487,22 @@
   }
 }
 
+static void usb_kick_locked(usb_handle* handle) {
+  // The reason the lock must be acquired before calling this function is in
+  // case multiple threads are trying to kick the same device at the same time.
+  usb_cleanup_handle(handle);
+}
+
 void usb_kick(usb_handle* handle) {
+  D("usb_kick\n");
   if (NULL != handle) {
     adb_mutex_lock(&usb_lock);
 
-    usb_cleanup_handle(handle);
+    usb_kick_locked(handle);
 
     adb_mutex_unlock(&usb_lock);
   } else {
-    SetLastError(ERROR_INVALID_HANDLE);
-    errno = ERROR_INVALID_HANDLE;
+    errno = EINVAL;
   }
 }
 
@@ -399,16 +530,6 @@
   return 0;
 }
 
-const char *usb_name(usb_handle* handle) {
-  if (NULL == handle) {
-    SetLastError(ERROR_INVALID_HANDLE);
-    errno = ERROR_INVALID_HANDLE;
-    return NULL;
-  }
-
-  return (const char*)handle->interface_name;
-}
-
 int recognized_device(usb_handle* handle) {
   if (NULL == handle)
     return 0;
@@ -418,6 +539,8 @@
 
   if (!AdbGetUsbDeviceDescriptor(handle->adb_interface,
                                  &device_desc)) {
+    D("AdbGetUsbDeviceDescriptor failed: %s\n",
+      SystemErrorCodeToString(GetLastError()).c_str());
     return 0;
   }
 
@@ -426,6 +549,8 @@
 
   if (!AdbGetUsbInterfaceDescriptor(handle->adb_interface,
                                     &interf_desc)) {
+    D("AdbGetUsbInterfaceDescriptor failed: %s\n",
+      SystemErrorCodeToString(GetLastError()).c_str());
     return 0;
   }
 
@@ -442,6 +567,10 @@
       // assuming zero is a valid bulk endpoint ID
       if (AdbGetEndpointInformation(handle->adb_interface, 0, &endpoint_info)) {
         handle->zero_mask = endpoint_info.max_packet_size - 1;
+        D("device zero_mask: 0x%x\n", handle->zero_mask);
+      } else {
+        D("AdbGetEndpointInformation failed: %s\n",
+          SystemErrorCodeToString(GetLastError()).c_str());
       }
     }
 
@@ -463,8 +592,11 @@
   ADBAPIHANDLE enum_handle =
     AdbEnumInterfaces(usb_class_id, true, true, true);
 
-  if (NULL == enum_handle)
+  if (NULL == enum_handle) {
+    D("AdbEnumInterfaces failed: %s\n",
+      SystemErrorCodeToString(GetLastError()).c_str());
     return;
+  }
 
   while (AdbNextInterface(enum_handle, next_interface, &entry_buffer_size)) {
     // TODO: FIXME - temp hack converting wchar_t into char.
@@ -501,7 +633,8 @@
               free(handle);
             }
           } else {
-            D("cannot get serial number\n");
+            D("cannot get serial number: %s\n",
+              SystemErrorCodeToString(GetLastError()).c_str());
             usb_cleanup_handle(handle);
             free(handle);
           }
@@ -515,5 +648,21 @@
     entry_buffer_size = sizeof(entry_buffer);
   }
 
-  AdbCloseHandle(enum_handle);
+  if (GetLastError() != ERROR_NO_MORE_ITEMS) {
+    // Only ERROR_NO_MORE_ITEMS is expected at the end of enumeration.
+    D("AdbNextInterface failed: %s\n",
+      SystemErrorCodeToString(GetLastError()).c_str());
+  }
+
+  _adb_close_handle(enum_handle);
+}
+
+static void kick_devices() {
+  // Need to acquire lock to safely walk the list which might be modified
+  // by another thread.
+  adb_mutex_lock(&usb_lock);
+  for (usb_handle* usb = handle_list.next; usb != &handle_list; usb = usb->next) {
+    usb_kick_locked(usb);
+  }
+  adb_mutex_unlock(&usb_lock);
 }
diff --git a/adf/libadf/adf.c b/adf/libadf/adf.c
index 66c329c..c4d6681 100644
--- a/adf/libadf/adf.c
+++ b/adf/libadf/adf.c
@@ -87,7 +87,6 @@
 int adf_device_open(adf_id_t id, int flags, struct adf_device *dev)
 {
     char filename[64];
-    int err;
 
     dev->id = id;
 
diff --git a/base/Android.mk b/base/Android.mk
index ad85c6b..51dd736 100644
--- a/base/Android.mk
+++ b/base/Android.mk
@@ -18,15 +18,17 @@
 
 libbase_src_files := \
     file.cpp \
+    logging.cpp \
     stringprintf.cpp \
     strings.cpp \
+    test_utils.cpp \
 
 libbase_test_src_files := \
     file_test.cpp \
+    logging_test.cpp \
     stringprintf_test.cpp \
     strings_test.cpp \
     test_main.cpp \
-    test_utils.cpp \
 
 libbase_cppflags := \
     -Wall \
@@ -38,7 +40,7 @@
 include $(CLEAR_VARS)
 LOCAL_MODULE := libbase
 LOCAL_CLANG := true
-LOCAL_SRC_FILES := $(libbase_src_files) logging.cpp
+LOCAL_SRC_FILES := $(libbase_src_files)
 LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
 LOCAL_CPPFLAGS := $(libbase_cppflags)
 LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
@@ -61,14 +63,12 @@
 include $(CLEAR_VARS)
 LOCAL_MODULE := libbase
 LOCAL_SRC_FILES := $(libbase_src_files)
-ifneq ($(HOST_OS),windows)
-    LOCAL_SRC_FILES += logging.cpp
-endif
 LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
 LOCAL_CPPFLAGS := $(libbase_cppflags)
 LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
 LOCAL_STATIC_LIBRARIES := libcutils
 LOCAL_MULTILIB := both
+LOCAL_MODULE_HOST_OS := darwin linux windows
 include $(BUILD_HOST_STATIC_LIBRARY)
 
 include $(CLEAR_VARS)
@@ -85,7 +85,7 @@
 include $(CLEAR_VARS)
 LOCAL_MODULE := libbase_test
 LOCAL_CLANG := true
-LOCAL_SRC_FILES := $(libbase_test_src_files) logging_test.cpp
+LOCAL_SRC_FILES := $(libbase_test_src_files)
 LOCAL_C_INCLUDES := $(LOCAL_PATH)
 LOCAL_CPPFLAGS := $(libbase_cppflags)
 LOCAL_SHARED_LIBRARIES := libbase
@@ -97,9 +97,6 @@
 include $(CLEAR_VARS)
 LOCAL_MODULE := libbase_test
 LOCAL_SRC_FILES := $(libbase_test_src_files)
-ifneq ($(HOST_OS),windows)
-    LOCAL_SRC_FILES += logging_test.cpp
-endif
 LOCAL_C_INCLUDES := $(LOCAL_PATH)
 LOCAL_CPPFLAGS := $(libbase_cppflags)
 LOCAL_SHARED_LIBRARIES := libbase
diff --git a/base/file.cpp b/base/file.cpp
index 9a340b7..3468dcf 100644
--- a/base/file.cpp
+++ b/base/file.cpp
@@ -28,6 +28,10 @@
 #include "cutils/log.h"
 #include "utils/Compat.h"
 
+#if !defined(_WIN32)
+#define O_BINARY 0
+#endif
+
 namespace android {
 namespace base {
 
@@ -45,8 +49,7 @@
 bool ReadFileToString(const std::string& path, std::string* content) {
   content->clear();
 
-  int fd =
-      TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC | O_NOFOLLOW));
+  int fd = TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC | O_NOFOLLOW | O_BINARY));
   if (fd == -1) {
     return false;
   }
@@ -80,9 +83,8 @@
 #if !defined(_WIN32)
 bool WriteStringToFile(const std::string& content, const std::string& path,
                        mode_t mode, uid_t owner, gid_t group) {
-  int fd = TEMP_FAILURE_RETRY(
-      open(path.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW,
-           mode));
+  int flags = O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW | O_BINARY;
+  int fd = TEMP_FAILURE_RETRY(open(path.c_str(), flags, mode));
   if (fd == -1) {
     ALOGE("android::WriteStringToFile open failed: %s", strerror(errno));
     return false;
@@ -108,9 +110,8 @@
 #endif
 
 bool WriteStringToFile(const std::string& content, const std::string& path) {
-  int fd = TEMP_FAILURE_RETRY(
-      open(path.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW,
-           DEFFILEMODE));
+  int flags = O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW | O_BINARY;
+  int fd = TEMP_FAILURE_RETRY(open(path.c_str(), flags, DEFFILEMODE));
   if (fd == -1) {
     return false;
   }
diff --git a/base/file_test.cpp b/base/file_test.cpp
index 5445a0d..77b9268 100644
--- a/base/file_test.cpp
+++ b/base/file_test.cpp
@@ -24,7 +24,7 @@
 
 #include <string>
 
-#include "test_utils.h"
+#include "base/test_utils.h"
 
 TEST(file, ReadFileToString_ENOENT) {
   std::string s("hello");
@@ -34,73 +34,65 @@
   EXPECT_EQ("", s);  // s was cleared.
 }
 
-TEST(file, ReadFileToString_success) {
-  std::string s("hello");
-  ASSERT_TRUE(android::base::ReadFileToString("/proc/version", &s)) << errno;
-  EXPECT_GT(s.length(), 6U);
-  EXPECT_EQ('\n', s[s.length() - 1]);
-  s[5] = 0;
-  EXPECT_STREQ("Linux", s.c_str());
-}
-
-TEST(file, WriteStringToFile) {
+TEST(file, ReadFileToString_WriteStringToFile) {
   TemporaryFile tf;
   ASSERT_TRUE(tf.fd != -1);
-  ASSERT_TRUE(android::base::WriteStringToFile("abc", tf.filename)) << errno;
+  ASSERT_TRUE(android::base::WriteStringToFile("abc", tf.path))
+    << strerror(errno);
   std::string s;
-  ASSERT_TRUE(android::base::ReadFileToString(tf.filename, &s)) << errno;
+  ASSERT_TRUE(android::base::ReadFileToString(tf.path, &s))
+    << strerror(errno);
   EXPECT_EQ("abc", s);
 }
 
+// WriteStringToFile2 is explicitly for setting Unix permissions, which make no
+// sense on Windows.
+#if !defined(_WIN32)
 TEST(file, WriteStringToFile2) {
   TemporaryFile tf;
   ASSERT_TRUE(tf.fd != -1);
-  ASSERT_TRUE(android::base::WriteStringToFile("abc", tf.filename, 0660,
+  ASSERT_TRUE(android::base::WriteStringToFile("abc", tf.path, 0660,
                                                getuid(), getgid()))
-      << errno;
+      << strerror(errno);
   struct stat sb;
-  ASSERT_EQ(0, stat(tf.filename, &sb));
+  ASSERT_EQ(0, stat(tf.path, &sb));
   ASSERT_EQ(0660U, static_cast<unsigned int>(sb.st_mode & ~S_IFMT));
   ASSERT_EQ(getuid(), sb.st_uid);
   ASSERT_EQ(getgid(), sb.st_gid);
   std::string s;
-  ASSERT_TRUE(android::base::ReadFileToString(tf.filename, &s)) << errno;
+  ASSERT_TRUE(android::base::ReadFileToString(tf.path, &s))
+    << strerror(errno);
   EXPECT_EQ("abc", s);
 }
+#endif
 
 TEST(file, WriteStringToFd) {
   TemporaryFile tf;
   ASSERT_TRUE(tf.fd != -1);
   ASSERT_TRUE(android::base::WriteStringToFd("abc", tf.fd));
 
-  ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET)) << errno;
+  ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET)) << strerror(errno);
 
   std::string s;
-  ASSERT_TRUE(android::base::ReadFdToString(tf.fd, &s)) << errno;
+  ASSERT_TRUE(android::base::ReadFdToString(tf.fd, &s)) << strerror(errno);
   EXPECT_EQ("abc", s);
 }
 
-TEST(file, ReadFully) {
-  int fd = open("/proc/version", O_RDONLY);
-  ASSERT_NE(-1, fd) << strerror(errno);
-
-  char buf[1024];
-  memset(buf, 0, sizeof(buf));
-  ASSERT_TRUE(android::base::ReadFully(fd, buf, 5));
-  ASSERT_STREQ("Linux", buf);
-
-  ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
-
-  ASSERT_FALSE(android::base::ReadFully(fd, buf, sizeof(buf)));
-
-  close(fd);
-}
-
 TEST(file, WriteFully) {
   TemporaryFile tf;
   ASSERT_TRUE(tf.fd != -1);
   ASSERT_TRUE(android::base::WriteFully(tf.fd, "abc", 3));
+
+  ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET)) << strerror(errno);
+
   std::string s;
-  ASSERT_TRUE(android::base::ReadFileToString(tf.filename, &s)) << errno;
+  s.resize(3);
+  ASSERT_TRUE(android::base::ReadFully(tf.fd, &s[0], s.size()))
+    << strerror(errno);
   EXPECT_EQ("abc", s);
+
+  ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET)) << strerror(errno);
+
+  s.resize(1024);
+  ASSERT_FALSE(android::base::ReadFully(tf.fd, &s[0], s.size()));
 }
diff --git a/base/include/base/logging.h b/base/include/base/logging.h
index 230adb8..283a7bc 100644
--- a/base/include/base/logging.h
+++ b/base/include/base/logging.h
@@ -13,10 +13,18 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 #ifndef BASE_LOGGING_H
 #define BASE_LOGGING_H
 
+// NOTE: For Windows, you must include logging.h after windows.h to allow the
+// following code to suppress the evil ERROR macro:
+#ifdef _WIN32
+// windows.h includes wingdi.h which defines an evil macro ERROR.
+#ifdef ERROR
+#undef ERROR
+#endif
+#endif
+
 #include <functional>
 #include <memory>
 #include <ostream>
diff --git a/base/include/base/stringprintf.h b/base/include/base/stringprintf.h
index 195c1de..d68af87 100644
--- a/base/include/base/stringprintf.h
+++ b/base/include/base/stringprintf.h
@@ -23,16 +23,32 @@
 namespace android {
 namespace base {
 
+// These printf-like functions are implemented in terms of vsnprintf, so they
+// use the same attribute for compile-time format string checking. On Windows,
+// if the mingw version of vsnprintf is used, use `gnu_printf' which allows z
+// in %zd and PRIu64 (and related) to be recognized by the compile-time
+// checking.
+#define FORMAT_ARCHETYPE __printf__
+#ifdef __USE_MINGW_ANSI_STDIO
+#if __USE_MINGW_ANSI_STDIO
+#undef FORMAT_ARCHETYPE
+#define FORMAT_ARCHETYPE gnu_printf
+#endif
+#endif
+
 // Returns a string corresponding to printf-like formatting of the arguments.
 std::string StringPrintf(const char* fmt, ...)
-    __attribute__((__format__(__printf__, 1, 2)));
+    __attribute__((__format__(FORMAT_ARCHETYPE, 1, 2)));
 
 // Appends a printf-like formatting of the arguments to 'dst'.
 void StringAppendF(std::string* dst, const char* fmt, ...)
-    __attribute__((__format__(__printf__, 2, 3)));
+    __attribute__((__format__(FORMAT_ARCHETYPE, 2, 3)));
 
 // Appends a printf-like formatting of the arguments to 'dst'.
-void StringAppendV(std::string* dst, const char* format, va_list ap);
+void StringAppendV(std::string* dst, const char* format, va_list ap)
+    __attribute__((__format__(FORMAT_ARCHETYPE, 2, 0)));
+
+#undef FORMAT_ARCHETYPE
 
 }  // namespace base
 }  // namespace android
diff --git a/base/include/base/strings.h b/base/include/base/strings.h
index 5dbc5fb..638f845 100644
--- a/base/include/base/strings.h
+++ b/base/include/base/strings.h
@@ -17,6 +17,7 @@
 #ifndef BASE_STRINGS_H
 #define BASE_STRINGS_H
 
+#include <sstream>
 #include <string>
 #include <vector>
 
@@ -34,9 +35,24 @@
 // Trims whitespace off both ends of the given string.
 std::string Trim(const std::string& s);
 
-// Joins a vector of strings into a single string, using the given separator.
-template <typename StringT>
-std::string Join(const std::vector<StringT>& strings, char separator);
+// Joins a container of things into a single string, using the given separator.
+template <typename ContainerT>
+std::string Join(const ContainerT& things, char separator) {
+  if (things.empty()) {
+    return "";
+  }
+
+  std::ostringstream result;
+  result << *things.begin();
+  for (auto it = std::next(things.begin()); it != things.end(); ++it) {
+    result << separator << *it;
+  }
+  return result.str();
+}
+
+// We instantiate the common cases in strings.cpp.
+extern template std::string Join(const std::vector<std::string>&, char);
+extern template std::string Join(const std::vector<const char*>&, char);
 
 // Tests whether 's' starts with 'prefix'.
 bool StartsWith(const std::string& s, const char* prefix);
diff --git a/base/test_utils.h b/base/include/base/test_utils.h
similarity index 69%
copy from base/test_utils.h
copy to base/include/base/test_utils.h
index 132d3a7..402e0a5 100644
--- a/base/test_utils.h
+++ b/base/include/base/test_utils.h
@@ -17,16 +17,35 @@
 #ifndef TEST_UTILS_H
 #define TEST_UTILS_H
 
+#include <string>
+
+#include <base/macros.h>
+
 class TemporaryFile {
  public:
   TemporaryFile();
   ~TemporaryFile();
 
   int fd;
-  char filename[1024];
+  char path[1024];
 
  private:
-  void init(const char* tmp_dir);
+  void init(const std::string& tmp_dir);
+
+  DISALLOW_COPY_AND_ASSIGN(TemporaryFile);
+};
+
+class TemporaryDir {
+ public:
+  TemporaryDir();
+  ~TemporaryDir();
+
+  char path[1024];
+
+ private:
+  bool init(const std::string& tmp_dir);
+
+  DISALLOW_COPY_AND_ASSIGN(TemporaryDir);
 };
 
 #endif // TEST_UTILS_H
diff --git a/base/logging.cpp b/base/logging.cpp
index 0142b70..e9e06df 100644
--- a/base/logging.cpp
+++ b/base/logging.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+#ifdef _WIN32
+#include <windows.h>
+#endif
+
 #include "base/logging.h"
 
 #include <libgen.h>
@@ -27,12 +31,16 @@
 
 #include <iostream>
 #include <limits>
-#include <mutex>
 #include <sstream>
 #include <string>
 #include <utility>
 #include <vector>
 
+#ifndef _WIN32
+#include <mutex>
+#endif
+
+#include "base/macros.h"
 #include "base/strings.h"
 #include "cutils/threads.h"
 
@@ -45,10 +53,81 @@
 #include <unistd.h>
 #endif
 
+namespace {
+#ifndef _WIN32
+using std::mutex;
+using std::lock_guard;
+
+#if defined(__GLIBC__)
+const char* getprogname() {
+  return program_invocation_short_name;
+}
+#endif
+
+#else
+const char* getprogname() {
+  static bool first = true;
+  static char progname[MAX_PATH] = {};
+
+  if (first) {
+    CHAR longname[MAX_PATH];
+    DWORD nchars = GetModuleFileNameA(nullptr, longname, arraysize(longname));
+    if ((nchars >= arraysize(longname)) || (nchars == 0)) {
+      // String truncation or some other error.
+      strcpy(progname, "<unknown>");
+    } else {
+      strcpy(progname, basename(longname));
+    }
+    first = false;
+  }
+
+  return progname;
+}
+
+class mutex {
+ public:
+  mutex() {
+    InitializeCriticalSection(&critical_section_);
+  }
+  ~mutex() {
+    DeleteCriticalSection(&critical_section_);
+  }
+
+  void lock() {
+    EnterCriticalSection(&critical_section_);
+  }
+
+  void unlock() {
+    LeaveCriticalSection(&critical_section_);
+  }
+
+ private:
+  CRITICAL_SECTION critical_section_;
+};
+
+template <typename LockT>
+class lock_guard {
+ public:
+  explicit lock_guard(LockT& lock) : lock_(lock) {
+    lock_.lock();
+  }
+
+  ~lock_guard() {
+    lock_.unlock();
+  }
+
+ private:
+  LockT& lock_;
+
+  DISALLOW_COPY_AND_ASSIGN(lock_guard);
+};
+#endif
+} // namespace
+
 namespace android {
 namespace base {
 
-static std::mutex logging_lock;
+static mutex logging_lock;
 
 #ifdef __ANDROID__
 static LogFunction gLogger = LogdLogger();
@@ -60,12 +139,6 @@
 static LogSeverity gMinimumLogSeverity = INFO;
 static std::unique_ptr<std::string> gProgramInvocationName;
 
-#if defined(__GLIBC__)
-static const char* getprogname() {
-  return program_invocation_short_name;
-}
-#endif
-
 static const char* ProgramInvocationName() {
   if (gProgramInvocationName == nullptr) {
     gProgramInvocationName.reset(new std::string(getprogname()));
@@ -76,8 +149,9 @@
 
 void StderrLogger(LogId, LogSeverity severity, const char*, const char* file,
                   unsigned int line, const char* message) {
-  static const char* log_characters = "VDIWEF";
-  CHECK_EQ(strlen(log_characters), FATAL + 1U);
+  static const char log_characters[] = "VDIWEF";
+  static_assert(arraysize(log_characters) - 1 == FATAL + 1,
+                "Mismatch in size of log_characters and values in LogSeverity");
   char severity_char = log_characters[severity];
   fprintf(stderr, "%s %c %5d %5d %s:%u] %s\n", ProgramInvocationName(),
           severity_char, getpid(), gettid(), file, line, message);
@@ -126,6 +200,20 @@
   InitLogging(argv);
 }
 
+// TODO: make this public; it's independently useful.
+class ErrnoRestorer {
+ public:
+  ErrnoRestorer(int saved_errno) : saved_errno_(saved_errno) {
+  }
+
+  ~ErrnoRestorer() {
+    errno = saved_errno_;
+  }
+
+ private:
+  const int saved_errno_;
+};
+
 void InitLogging(char* argv[]) {
   if (gInitialized) {
     return;
@@ -182,23 +270,29 @@
 }
 
 void SetLogger(LogFunction&& logger) {
-  std::lock_guard<std::mutex> lock(logging_lock);
+  lock_guard<mutex> lock(logging_lock);
   gLogger = std::move(logger);
 }
 
+// We can't use basename(3) because this code runs on the Mac, which doesn't
+// have a non-modifying basename.
+static const char* GetFileBasename(const char* file) {
+  const char* last_slash = strrchr(file, '/');
+  return (last_slash == nullptr) ? file : last_slash + 1;
+}
+
 // This indirection greatly reduces the stack impact of having lots of
 // checks/logging in a function.
 class LogMessageData {
  public:
   LogMessageData(const char* file, unsigned int line, LogId id,
-                 LogSeverity severity, int error)
-      : file_(file),
+                 LogSeverity severity, int error, int saved_errno)
+      : file_(GetFileBasename(file)),
         line_number_(line),
         id_(id),
         severity_(severity),
-        error_(error) {
-    const char* last_slash = strrchr(file, '/');
-    file = (last_slash == nullptr) ? file : last_slash + 1;
+        error_(error),
+        errno_restorer_(saved_errno) {
   }
 
   const char* GetFile() const {
@@ -236,13 +330,14 @@
   const LogId id_;
   const LogSeverity severity_;
   const int error_;
+  ErrnoRestorer errno_restorer_;
 
   DISALLOW_COPY_AND_ASSIGN(LogMessageData);
 };
 
 LogMessage::LogMessage(const char* file, unsigned int line, LogId id,
                        LogSeverity severity, int error)
-    : data_(new LogMessageData(file, line, id, severity, error)) {
+    : data_(new LogMessageData(file, line, id, severity, error, errno)) {
 }
 
 LogMessage::~LogMessage() {
@@ -287,7 +382,7 @@
 void LogMessage::LogLine(const char* file, unsigned int line, LogId id,
                          LogSeverity severity, const char* message) {
   const char* tag = ProgramInvocationName();
-  std::lock_guard<std::mutex> lock(logging_lock);
+  lock_guard<mutex> lock(logging_lock);
   gLogger(id, severity, tag, file, line, message);
 }
 
diff --git a/base/logging_test.cpp b/base/logging_test.cpp
index d947c1d..c12dfa5 100644
--- a/base/logging_test.cpp
+++ b/base/logging_test.cpp
@@ -16,12 +16,14 @@
 
 #include "base/logging.h"
 
+#include <libgen.h>
+
 #include <regex>
 #include <string>
 
 #include "base/file.h"
 #include "base/stringprintf.h"
-#include "test_utils.h"
+#include "base/test_utils.h"
 
 #include <gtest/gtest.h>
 
@@ -76,15 +78,18 @@
                              const char* message) {
   static const char* log_characters = "VDIWEF";
   char log_char = log_characters[severity];
+  std::string holder(__FILE__);
   return android::base::StringPrintf(
-      "%c[[:space:]]+[[:digit:]]+[[:space:]]+[[:digit:]]+ " __FILE__
-      ":[[:digit:]]+] %s",
-      log_char, message);
+      "%c[[:space:]]+[[:digit:]]+[[:space:]]+[[:digit:]]+ %s:[[:digit:]]+] %s",
+      log_char, basename(&holder[0]), message);
 }
 
 TEST(logging, LOG) {
   ASSERT_DEATH(LOG(FATAL) << "foobar", "foobar");
 
+  // We can't usefully check the output of any of these on Windows because we
+  // don't have std::regex, but we can at least make sure we printed at least as
+  // many characters are in the log message.
   {
     CapturedStderr cap;
     LOG(WARNING) << "foobar";
@@ -92,10 +97,13 @@
 
     std::string output;
     android::base::ReadFdToString(cap.fd(), &output);
+    ASSERT_GT(output.length(), strlen("foobar"));
 
+#if !defined(_WIN32)
     std::regex message_regex(
         make_log_pattern(android::base::WARNING, "foobar"));
-    ASSERT_TRUE(std::regex_search(output, message_regex));
+    ASSERT_TRUE(std::regex_search(output, message_regex)) << output;
+#endif
   }
 
   {
@@ -105,10 +113,13 @@
 
     std::string output;
     android::base::ReadFdToString(cap.fd(), &output);
+    ASSERT_GT(output.length(), strlen("foobar"));
 
+#if !defined(_WIN32)
     std::regex message_regex(
         make_log_pattern(android::base::INFO, "foobar"));
-    ASSERT_TRUE(std::regex_search(output, message_regex));
+    ASSERT_TRUE(std::regex_search(output, message_regex)) << output;
+#endif
   }
 
   {
@@ -129,10 +140,13 @@
 
     std::string output;
     android::base::ReadFdToString(cap.fd(), &output);
+    ASSERT_GT(output.length(), strlen("foobar"));
 
+#if !defined(_WIN32)
     std::regex message_regex(
         make_log_pattern(android::base::DEBUG, "foobar"));
-    ASSERT_TRUE(std::regex_search(output, message_regex));
+    ASSERT_TRUE(std::regex_search(output, message_regex)) << output;
+#endif
   }
 }
 
@@ -145,10 +159,13 @@
 
     std::string output;
     android::base::ReadFdToString(cap.fd(), &output);
+    ASSERT_GT(output.length(), strlen("foobar"));
 
+#if !defined(_WIN32)
     std::regex message_regex(make_log_pattern(
         android::base::INFO, "foobar: No such file or directory"));
-    ASSERT_TRUE(std::regex_search(output, message_regex));
+    ASSERT_TRUE(std::regex_search(output, message_regex)) << output;
+#endif
   }
 }
 
@@ -161,11 +178,14 @@
 
     std::string output;
     android::base::ReadFdToString(cap.fd(), &output);
+    ASSERT_GT(output.length(), strlen("unimplemented"));
 
+#if !defined(_WIN32)
     std::string expected_message =
         android::base::StringPrintf("%s unimplemented ", __PRETTY_FUNCTION__);
     std::regex message_regex(
         make_log_pattern(android::base::ERROR, expected_message.c_str()));
-    ASSERT_TRUE(std::regex_search(output, message_regex));
+    ASSERT_TRUE(std::regex_search(output, message_regex)) << output;
+#endif
   }
 }
diff --git a/base/strings.cpp b/base/strings.cpp
index d3375d9..bac983b 100644
--- a/base/strings.cpp
+++ b/base/strings.cpp
@@ -79,25 +79,10 @@
   return s.substr(start_index, end_index - start_index + 1);
 }
 
-template <typename StringT>
-std::string Join(const std::vector<StringT>& strings, char separator) {
-  if (strings.empty()) {
-    return "";
-  }
-
-  std::string result(strings[0]);
-  for (size_t i = 1; i < strings.size(); ++i) {
-    result += separator;
-    result += strings[i];
-  }
-  return result;
-}
-
-// Explicit instantiations.
-template std::string Join<std::string>(const std::vector<std::string>& strings,
-                                       char separator);
-template std::string Join<const char*>(const std::vector<const char*>& strings,
-                                       char separator);
+// These cases are probably the norm, so we mark them extern in the header to
+// aid compile time and binary size.
+template std::string Join(const std::vector<std::string>&, char);
+template std::string Join(const std::vector<const char*>&, char);
 
 bool StartsWith(const std::string& s, const char* prefix) {
   return s.compare(0, strlen(prefix), prefix) == 0;
diff --git a/base/strings_test.cpp b/base/strings_test.cpp
index 46a1ab5..5f67575 100644
--- a/base/strings_test.cpp
+++ b/base/strings_test.cpp
@@ -20,6 +20,8 @@
 
 #include <string>
 #include <vector>
+#include <set>
+#include <unordered_set>
 
 TEST(strings, split_empty) {
   std::vector<std::string> parts = android::base::Split("", ",");
@@ -121,6 +123,17 @@
   ASSERT_EQ(",,,", android::base::Join(list, ','));
 }
 
+TEST(strings, join_simple_ints) {
+  std::set<int> list = {1, 2, 3};
+  ASSERT_EQ("1,2,3", android::base::Join(list, ','));
+}
+
+TEST(strings, join_unordered_set) {
+  std::unordered_set<int> list = {1, 2};
+  ASSERT_TRUE("1,2" == android::base::Join(list, ',') ||
+              "2,1" == android::base::Join(list, ','));
+}
+
 TEST(strings, startswith_empty) {
   ASSERT_FALSE(android::base::StartsWith("", "foo"));
   ASSERT_TRUE(android::base::StartsWith("", ""));
diff --git a/base/test_utils.cpp b/base/test_utils.cpp
index 1f6d3cf..22641e7 100644
--- a/base/test_utils.cpp
+++ b/base/test_utils.cpp
@@ -14,25 +14,89 @@
  * limitations under the License.
  */
 
-#include "test_utils.h"
+#include "base/logging.h"
+#include "base/test_utils.h"
+#include "utils/Compat.h" // For OS_PATH_SEPARATOR.
 
+#include <fcntl.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <sys/stat.h>
 #include <unistd.h>
 
-TemporaryFile::TemporaryFile() {
-  init("/data/local/tmp");
-  if (fd == -1) {
-    init("/tmp");
+#if defined(_WIN32)
+#include <windows.h>
+#include <direct.h>
+#endif
+
+#include <string>
+
+#ifdef _WIN32
+int mkstemp(char* template_name) {
+  if (_mktemp(template_name) == nullptr) {
+    return -1;
   }
+  // Use open() to match the close() that TemporaryFile's destructor does.
+  // Use O_BINARY to match base file APIs.
+  return open(template_name, O_CREAT | O_EXCL | O_RDWR | O_BINARY,
+              S_IRUSR | S_IWUSR);
+}
+
+char* mkdtemp(char* template_name) {
+  if (_mktemp(template_name) == nullptr) {
+    return nullptr;
+  }
+  if (_mkdir(template_name) == -1) {
+    return nullptr;
+  }
+  return template_name;
+}
+#endif
+
+static std::string GetSystemTempDir() {
+#if defined(__ANDROID__)
+  return "/data/local/tmp";
+#elif defined(_WIN32)
+  char tmp_dir[MAX_PATH];
+  DWORD result = GetTempPathA(sizeof(tmp_dir), tmp_dir);
+  CHECK_NE(result, 0ul) << "GetTempPathA failed, error: " << GetLastError();
+  CHECK_LT(result, sizeof(tmp_dir)) << "path truncated to: " << result;
+
+  // GetTempPath() returns a path with a trailing slash, but init()
+  // does not expect that, so remove it.
+  CHECK_EQ(tmp_dir[result - 1], '\\');
+  tmp_dir[result - 1] = '\0';
+  return tmp_dir;
+#else
+  return "/tmp";
+#endif
+}
+
+TemporaryFile::TemporaryFile() {
+  init(GetSystemTempDir());
 }
 
 TemporaryFile::~TemporaryFile() {
   close(fd);
-  unlink(filename);
+  unlink(path);
 }
 
-void TemporaryFile::init(const char* tmp_dir) {
-  snprintf(filename, sizeof(filename), "%s/TemporaryFile-XXXXXX", tmp_dir);
-  fd = mkstemp(filename);
+void TemporaryFile::init(const std::string& tmp_dir) {
+  snprintf(path, sizeof(path), "%s%cTemporaryFile-XXXXXX", tmp_dir.c_str(),
+           OS_PATH_SEPARATOR);
+  fd = mkstemp(path);
+}
+
+TemporaryDir::TemporaryDir() {
+  init(GetSystemTempDir());
+}
+
+TemporaryDir::~TemporaryDir() {
+  rmdir(path);
+}
+
+bool TemporaryDir::init(const std::string& tmp_dir) {
+  snprintf(path, sizeof(path), "%s%cTemporaryDir-XXXXXX", tmp_dir.c_str(),
+           OS_PATH_SEPARATOR);
+  return (mkdtemp(path) != nullptr);
 }
diff --git a/crash_reporter/.project_alias b/crash_reporter/.project_alias
new file mode 100644
index 0000000..0bc3798
--- /dev/null
+++ b/crash_reporter/.project_alias
@@ -0,0 +1 @@
+crash
diff --git a/crash_reporter/99-crash-reporter.rules b/crash_reporter/99-crash-reporter.rules
new file mode 100644
index 0000000..aea5b1c
--- /dev/null
+++ b/crash_reporter/99-crash-reporter.rules
@@ -0,0 +1,6 @@
+ACTION=="change", SUBSYSTEM=="drm", KERNEL=="card0", ENV{ERROR}=="1", RUN+="/sbin/crash_reporter --udev=KERNEL=card0:SUBSYSTEM=drm:ACTION=change"
+# For detecting cypress trackpad issue. Passing into crash_reporter SUBSYSTEM=i2c-cyapa since crash_reporter does not handle DRIVER string.
+ACTION=="change", SUBSYSTEM=="i2c", DRIVER=="cyapa", ENV{ERROR}=="1", RUN+="/sbin/crash_reporter --udev=SUBSYSTEM=i2c-cyapa:ACTION=change"
+# For detecting Atmel trackpad/touchscreen issue. Passing into crash_reporter SUBSYSTEM=i2c-atmel_mxt_ts since crash_reporter does not handle DRIVER string.
+ACTION=="change", SUBSYSTEM=="i2c", DRIVER=="atmel_mxt_ts", ENV{ERROR}=="1", RUN+="/sbin/crash_reporter --udev=SUBSYSTEM=i2c-atmel_mxt_ts:ACTION=change"
+ACTION=="add", SUBSYSTEM=="devcoredump", RUN+="/sbin/crash_reporter --udev=SUBSYSTEM=devcoredump:ACTION=add:KERNEL_NUMBER=%n"
diff --git a/crash_reporter/Android.mk b/crash_reporter/Android.mk
new file mode 100644
index 0000000..6b98af4
--- /dev/null
+++ b/crash_reporter/Android.mk
@@ -0,0 +1,129 @@
+# Copyright (C) 2015 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 := $(call my-dir)
+
+ifeq ($(HOST_OS),linux)
+
+crash_reporter_cpp_extension := .cc
+
+crash_reporter_src := crash_collector.cc \
+    kernel_collector.cc \
+    kernel_warning_collector.cc \
+    udev_collector.cc \
+    unclean_shutdown_collector.cc \
+    user_collector.cc
+
+crash_reporter_includes := external/gtest/include
+
+crash_reporter_test_src := crash_collector_test.cc \
+    crash_reporter_logs_test.cc \
+    kernel_collector_test.cc \
+    testrunner.cc \
+    udev_collector_test.cc \
+    unclean_shutdown_collector_test.cc \
+    user_collector_test.cc
+
+warn_collector_src := warn_collector.l
+
+# Crash reporter static library.
+# ========================================================
+include $(CLEAR_VARS)
+LOCAL_MODULE := libcrash
+LOCAL_CPP_EXTENSION := $(crash_reporter_cpp_extension)
+LOCAL_C_INCLUDES := $(crash_reporter_includes)
+LOCAL_RTTI_FLAG := -frtti
+LOCAL_SHARED_LIBRARIES := libchrome \
+    libchromeos \
+    libcutils \
+    libdbus \
+    libmetrics \
+    libpcrecpp
+LOCAL_SRC_FILES := $(crash_reporter_src)
+include $(BUILD_STATIC_LIBRARY)
+
+# Crash reporter client.
+# ========================================================
+include $(CLEAR_VARS)
+LOCAL_MODULE := crash_reporter
+LOCAL_CPP_EXTENSION := $(crash_reporter_cpp_extension)
+LOCAL_C_INCLUDES := $(crash_reporter_includes)
+LOCAL_REQUIRED_MODULES := core2md \
+    crash_reporter_logs.conf \
+    crash_sender \
+    dbus-send \
+    init.crash_reporter.rc
+LOCAL_RTTI_FLAG := -frtti
+LOCAL_SHARED_LIBRARIES := libchrome \
+    libchromeos \
+    libcutils \
+    libdbus \
+    libmetrics \
+    libpcrecpp
+LOCAL_SRC_FILES := crash_reporter.cc
+LOCAL_STATIC_LIBRARIES := libcrash
+include $(BUILD_EXECUTABLE)
+
+# Crash sender script.
+# ========================================================
+include $(CLEAR_VARS)
+LOCAL_MODULE := crash_sender
+LOCAL_MODULE_CLASS := EXECUTABLES
+LOCAL_MODULE_PATH := $(TARGET_OUT_EXECUTABLES)
+LOCAL_SRC_FILES := crash_sender
+include $(BUILD_PREBUILT)
+
+# Warn collector client.
+# ========================================================
+include $(CLEAR_VARS)
+LOCAL_MODULE := warn_collector
+LOCAL_CPP_EXTENSION := $(crash_reporter_cpp_extension)
+LOCAL_SHARED_LIBRARIES := libmetrics
+LOCAL_SRC_FILES := $(warn_collector_src)
+include $(BUILD_EXECUTABLE)
+
+# Crash reporter init script.
+# ========================================================
+ifdef TARGET_COPY_OUT_INITRCD
+include $(CLEAR_VARS)
+LOCAL_MODULE := init.crash_reporter.rc
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_INITRCD)
+LOCAL_SRC_FILES := init.crash_reporter.rc
+include $(BUILD_PREBUILT)
+endif
+
+# Crash reporter logs conf file.
+# ========================================================
+include $(CLEAR_VARS)
+LOCAL_MODULE := crash_reporter_logs.conf
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(PRODUCT_OUT)/system/etc
+LOCAL_SRC_FILES := crash_reporter_logs.conf
+include $(BUILD_PREBUILT)
+
+# Crash reporter tests.
+# ========================================================
+include $(CLEAR_VARS)
+LOCAL_MODULE := crash_reporter_tests
+LOCAL_CPP_EXTENSION := $(crash_reporter_cpp_extension)
+LOCAL_SHARED_LIBRARIES := libchrome \
+    libchromeos \
+    libdbus \
+    libpcrecpp
+LOCAL_SRC_FILES := $(crash_reporter_test_src)
+LOCAL_STATIC_LIBRARIES := libcrash libgmock
+include $(BUILD_NATIVE_TEST)
+
+endif # HOST_OS == linux
diff --git a/crash_reporter/OWNERS b/crash_reporter/OWNERS
new file mode 100644
index 0000000..96ea5b2
--- /dev/null
+++ b/crash_reporter/OWNERS
@@ -0,0 +1,2 @@
+set noparent
+vapier@chromium.org
diff --git a/crash_reporter/TEST_WARNING b/crash_reporter/TEST_WARNING
new file mode 100644
index 0000000..64ad2e9
--- /dev/null
+++ b/crash_reporter/TEST_WARNING
@@ -0,0 +1,31 @@
+Apr 31 25:25:25 localhost kernel: [117959.226729]  [<ffffffff810e16bf>] do_vfs_ioctl+0x469/0x4b3
+Apr 31 25:25:25 localhost kernel: [117959.226738]  [<ffffffff810d3117>] ? fsnotify_access+0x58/0x60
+Apr 31 25:25:25 localhost kernel: [117959.226747]  [<ffffffff810d3791>] ? vfs_read+0xad/0xd7
+Apr 31 25:25:25 localhost kernel: [117959.226756]  [<ffffffff810e175f>] sys_ioctl+0x56/0x7b
+Apr 31 25:25:25 localhost kernel: [117959.226765]  [<ffffffff810d37fe>] ? sys_read+0x43/0x73
+Apr 31 25:25:25 localhost kernel: [117959.226774]  [<ffffffff8146b7d2>] system_call_fastpath+0x16/0x1b
+Apr 31 25:25:25 localhost kernel: [117959.226782] ---[ end trace f16822cad7406cec ]---
+Apr 31 25:25:25 localhost kernel: [117959.231085] ------------[ cut here ]------------
+Apr 31 25:25:25 localhost kernel: [117959.231100] WARNING: at /mnt/host/source/src/third_party/kernel/files/drivers/gpu/drm/i915/intel_dp.c:351 intel_dp_check_edp+0x6b/0xb9()
+Apr 31 25:25:25 localhost kernel: [117959.231113] Hardware name: Link
+Apr 31 25:25:25 localhost kernel: [117959.231117] eDP powered off while attempting aux channel communication.
+Apr 31 25:25:25 localhost kernel: [117959.231240] Pid: 10508, comm: X Tainted: G        WC   3.4.0 #1
+Apr 31 25:25:25 localhost kernel: [117959.231247] Call Trace:
+Apr 31 25:25:25 localhost kernel: [117959.231393]  [<ffffffff810d3117>] ? fsnotify_access+0x58/0x60
+Apr 31 25:25:25 localhost kernel: [117959.231402]  [<ffffffff810d3791>] ? vfs_read+0xad/0xd7
+Apr 31 25:25:25 localhost kernel: [117959.231411]  [<ffffffff810e175f>] sys_ioctl+0x56/0x7b
+Apr 31 25:25:25 localhost kernel: [117959.231420]  [<ffffffff810d37fe>] ? sys_read+0x43/0x73
+Apr 31 25:25:25 localhost kernel: [117959.231431]  [<ffffffff8146b7d2>] system_call_fastpath+0x16/0x1b
+Apr 31 25:25:25 localhost kernel: [117959.231439] ---[ end trace f16822cad7406ced ]---
+Apr 31 25:25:25 localhost kernel: [117959.231450] ------------[ cut here ]------------
+Apr 31 25:25:25 localhost kernel: [117959.231458] BARNING: at /mnt/host/source/src/third_party/kernel/files/drivers/gpu/drm/i915/intel_dp.c:351 intel_dp_check_edp+0x6b/0xb9()
+Apr 31 25:25:25 localhost kernel: [117959.231458] ("BARNING" above is intentional)
+Apr 31 25:25:25 localhost kernel: [117959.231471] Hardware name: Link
+Apr 31 25:25:25 localhost kernel: [117959.231475] eDP powered off while attempting aux channel communication.
+Apr 31 25:25:25 localhost kernel: [117959.231482] Modules linked in: nls_iso8859_1 nls_cp437 vfat fat rfcomm i2c_dev ath9k_btcoex snd_hda_codec_hdmi snd_hda_codec_ca0132 mac80211 snd_hda_intel ath9k_common_btcoex snd_hda_codec ath9k_hw_btcoex aesni_intel cryptd snd_hwdep ath snd_pcm aes_x86_64 isl29018(C) memconsole snd_timer snd_page_alloc industrialio(C) cfg80211 rtc_cmos nm10_gpio zram(C) zsmalloc(C) lzo_decompress lzo_compress fuse nf_conntrack_ipv6 nf_defrag_ipv6 ip6table_filter ip6_tables xt_mark option usb_wwan cdc_ether usbnet ath3k btusb bluetooth uvcvideo videobuf2_core videodev videobuf2_vmalloc videobuf2_memops joydev
+Apr 31 25:25:25 localhost kernel: [117959.231588] Pid: 10508, comm: X Tainted: G        WC   3.4.0 #1
+Apr 31 25:25:25 localhost kernel: [117959.231595] Call Trace:
+Apr 31 25:25:25 localhost kernel: [117959.231601]  [<ffffffff8102a931>] warn_slowpath_common+0x83/0x9c
+Apr 31 25:25:25 localhost kernel: [117959.231610]  [<ffffffff8102a9ed>] warn_slowpath_fmt+0x46/0x48
+Apr 31 25:25:25 localhost kernel: [117959.231620]  [<ffffffff812af495>] intel_dp_check_edp+0x6b/0xb9
+Apr 31 25:25:25 localhost kernel: [117959.231629]  [<ffffffff8102a9ed>] ? warn_slowpath_fmt+
diff --git a/crash_reporter/crash_collector.cc b/crash_reporter/crash_collector.cc
new file mode 100644
index 0000000..77755f4
--- /dev/null
+++ b/crash_reporter/crash_collector.cc
@@ -0,0 +1,430 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "crash_collector.h"
+
+#include <dirent.h>
+#include <fcntl.h>  // For file creation modes.
+#include <inttypes.h>
+#include <linux/limits.h>  // PATH_MAX
+#include <pwd.h>  // For struct passwd.
+#include <sys/types.h>  // for mode_t.
+#include <sys/wait.h>  // For waitpid.
+#include <unistd.h>  // For execv and fork.
+
+#include <set>
+#include <utility>
+#include <vector>
+
+#include <base/files/file_util.h>
+#include <base/logging.h>
+#include <base/posix/eintr_wrapper.h>
+#include <base/strings/string_split.h>
+#include <base/strings/string_util.h>
+#include <base/strings/stringprintf.h>
+#include <chromeos/key_value_store.h>
+#include <chromeos/process.h>
+
+namespace {
+
+const char kCollectChromeFile[] =
+    "/mnt/stateful_partition/etc/collect_chrome_crashes";
+const char kCrashTestInProgressPath[] = "/tmp/crash-test-in-progress";
+const char kDefaultLogConfig[] = "/etc/crash_reporter_logs.conf";
+const char kDefaultUserName[] = "chronos";
+const char kLeaveCoreFile[] = "/root/.leave_core";
+const char kLsbRelease[] = "/etc/lsb-release";
+const char kShellPath[] = "/bin/sh";
+const char kSystemCrashPath[] = "/data/misc/crash_reporter/crash";
+const char kUploadVarPrefix[] = "upload_var_";
+const char kUploadFilePrefix[] = "upload_file_";
+
+// Key of the lsb-release entry containing the OS version.
+const char kLsbVersionKey[] = "CHROMEOS_RELEASE_VERSION";
+
+// Normally this path is not used.  Unfortunately, there are a few edge cases
+// where we need this.  Any process that runs as kDefaultUserName that crashes
+// is consider a "user crash".  That includes the initial Chrome browser that
+// runs the login screen.  If that blows up, there is no logged in user yet,
+// so there is no per-user dir for us to stash things in.  Instead we fallback
+// to this path as it is at least encrypted on a per-system basis.
+//
+// This also comes up when running autotests.  The GUI is sitting at the login
+// screen while tests are sshing in, changing users, and triggering crashes as
+// the user (purposefully).
+const char kFallbackUserCrashPath[] = "/home/chronos/crash";
+
+// Directory mode of the user crash spool directory.
+const mode_t kUserCrashPathMode = 0755;
+
+// Directory mode of the system crash spool directory.
+const mode_t kSystemCrashPathMode = 01755;
+
+const uid_t kRootOwner = 0;
+const uid_t kRootGroup = 0;
+
+}  // namespace
+
+// Maximum crash reports per crash spool directory.  Note that this is
+// a separate maximum from the maximum rate at which we upload these
+// diagnostics.  The higher this rate is, the more space we allow for
+// core files, minidumps, and kcrash logs, and equivalently the more
+// processor and I/O bandwidth we dedicate to handling these crashes when
+// many occur at once.  Also note that if core files are configured to
+// be left on the file system, we stop adding crashes when either the
+// number of core files or minidumps reaches this number.
+const int CrashCollector::kMaxCrashDirectorySize = 32;
+
+using base::FilePath;
+using base::StringPrintf;
+
+CrashCollector::CrashCollector()
+    : lsb_release_(kLsbRelease),
+      log_config_path_(kDefaultLogConfig) {
+}
+
+CrashCollector::~CrashCollector() {
+}
+
+void CrashCollector::Initialize(
+    CrashCollector::CountCrashFunction count_crash_function,
+    CrashCollector::IsFeedbackAllowedFunction is_feedback_allowed_function) {
+  CHECK(count_crash_function);
+  CHECK(is_feedback_allowed_function);
+
+  count_crash_function_ = count_crash_function;
+  is_feedback_allowed_function_ = is_feedback_allowed_function;
+}
+
+int CrashCollector::WriteNewFile(const FilePath &filename,
+                                 const char *data,
+                                 int size) {
+  int fd = HANDLE_EINTR(open(filename.value().c_str(),
+                             O_CREAT | O_WRONLY | O_TRUNC | O_EXCL, 0666));
+  if (fd < 0) {
+    return -1;
+  }
+
+  int rv = base::WriteFileDescriptor(fd, data, size) ? size : -1;
+  IGNORE_EINTR(close(fd));
+  return rv;
+}
+
+std::string CrashCollector::Sanitize(const std::string &name) {
+  // Make sure the sanitized name does not include any periods.
+  // The logic in crash_sender relies on this.
+  std::string result = name;
+  for (size_t i = 0; i < name.size(); ++i) {
+    if (!isalnum(result[i]) && result[i] != '_')
+      result[i] = '_';
+  }
+  return result;
+}
+
+std::string CrashCollector::FormatDumpBasename(const std::string &exec_name,
+                                               time_t timestamp,
+                                               pid_t pid) {
+  struct tm tm;
+  localtime_r(&timestamp, &tm);
+  std::string sanitized_exec_name = Sanitize(exec_name);
+  return StringPrintf("%s.%04d%02d%02d.%02d%02d%02d.%d",
+                      sanitized_exec_name.c_str(),
+                      tm.tm_year + 1900,
+                      tm.tm_mon + 1,
+                      tm.tm_mday,
+                      tm.tm_hour,
+                      tm.tm_min,
+                      tm.tm_sec,
+                      pid);
+}
+
+FilePath CrashCollector::GetCrashPath(const FilePath &crash_directory,
+                                      const std::string &basename,
+                                      const std::string &extension) {
+  return crash_directory.Append(StringPrintf("%s.%s",
+                                             basename.c_str(),
+                                             extension.c_str()));
+}
+
+FilePath CrashCollector::GetCrashDirectoryInfo(
+    mode_t *mode,
+    uid_t *directory_owner,
+    gid_t *directory_group) {
+  *mode = kSystemCrashPathMode;
+  *directory_owner = kRootOwner;
+  *directory_group = kRootGroup;
+  return FilePath(kSystemCrashPath);
+}
+
+bool CrashCollector::GetUserInfoFromName(const std::string &name,
+                                         uid_t *uid,
+                                         gid_t *gid) {
+  char storage[256];
+  struct passwd passwd_storage;
+  struct passwd *passwd_result = nullptr;
+
+  if (getpwnam_r(name.c_str(), &passwd_storage, storage, sizeof(storage),
+                 &passwd_result) != 0 || passwd_result == nullptr) {
+    LOG(ERROR) << "Cannot find user named " << name;
+    return false;
+  }
+
+  *uid = passwd_result->pw_uid;
+  *gid = passwd_result->pw_gid;
+  return true;
+}
+
+bool CrashCollector::GetCreatedCrashDirectoryByEuid(uid_t euid,
+                                                    FilePath *crash_directory,
+                                                    bool *out_of_capacity) {
+  if (out_of_capacity) *out_of_capacity = false;
+
+  // For testing.
+  if (!forced_crash_directory_.empty()) {
+    *crash_directory = forced_crash_directory_;
+    return true;
+  }
+
+  mode_t directory_mode;
+  uid_t directory_owner;
+  gid_t directory_group;
+  *crash_directory =
+      GetCrashDirectoryInfo(&directory_mode,
+                            &directory_owner,
+                            &directory_group);
+
+  if (!base::PathExists(*crash_directory)) {
+    // Create the spool directory with the appropriate mode (regardless of
+    // umask) and ownership.
+    mode_t old_mask = umask(0);
+    if (mkdir(crash_directory->value().c_str(), directory_mode) < 0 ||
+        chown(crash_directory->value().c_str(),
+              directory_owner,
+              directory_group) < 0) {
+      LOG(ERROR) << "Unable to create appropriate crash directory";
+      return false;
+    }
+    umask(old_mask);
+  }
+
+  if (!base::PathExists(*crash_directory)) {
+    LOG(ERROR) << "Unable to create crash directory "
+               << crash_directory->value().c_str();
+    return false;
+  }
+
+  if (!CheckHasCapacity(*crash_directory)) {
+    if (out_of_capacity) *out_of_capacity = true;
+    LOG(ERROR) << "Directory " << crash_directory->value()
+               << " is out of capacity.";
+    return false;
+  }
+
+  return true;
+}
+
+FilePath CrashCollector::GetProcessPath(pid_t pid) {
+  return FilePath(StringPrintf("/proc/%d", pid));
+}
+
+bool CrashCollector::GetSymlinkTarget(const FilePath &symlink,
+                                      FilePath *target) {
+  ssize_t max_size = 64;
+  std::vector<char> buffer;
+
+  while (true) {
+    buffer.resize(max_size + 1);
+    ssize_t size = readlink(symlink.value().c_str(), buffer.data(), max_size);
+    if (size < 0) {
+      int saved_errno = errno;
+      LOG(ERROR) << "Readlink failed on " << symlink.value() << " with "
+                 << saved_errno;
+      return false;
+    }
+
+    buffer[size] = 0;
+    if (size == max_size) {
+      max_size *= 2;
+      if (max_size > PATH_MAX) {
+        return false;
+      }
+      continue;
+    }
+    break;
+  }
+
+  *target = FilePath(buffer.data());
+  return true;
+}
+
+bool CrashCollector::GetExecutableBaseNameFromPid(pid_t pid,
+                                                 std::string *base_name) {
+  FilePath target;
+  FilePath process_path = GetProcessPath(pid);
+  FilePath exe_path = process_path.Append("exe");
+  if (!GetSymlinkTarget(exe_path, &target)) {
+    LOG(INFO) << "GetSymlinkTarget failed - Path " << process_path.value()
+              << " DirectoryExists: "
+              << base::DirectoryExists(process_path);
+    // Try to further diagnose exe readlink failure cause.
+    struct stat buf;
+    int stat_result = stat(exe_path.value().c_str(), &buf);
+    int saved_errno = errno;
+    if (stat_result < 0) {
+      LOG(INFO) << "stat " << exe_path.value() << " failed: " << stat_result
+                << " " << saved_errno;
+    } else {
+      LOG(INFO) << "stat " << exe_path.value() << " succeeded: st_mode="
+                << buf.st_mode;
+    }
+    return false;
+  }
+  *base_name = target.BaseName().value();
+  return true;
+}
+
+// Return true if the given crash directory has not already reached
+// maximum capacity.
+bool CrashCollector::CheckHasCapacity(const FilePath &crash_directory) {
+  DIR* dir = opendir(crash_directory.value().c_str());
+  if (!dir) {
+    LOG(WARNING) << "Unable to open crash directory "
+                 << crash_directory.value();
+    return false;
+  }
+  struct dirent ent_buf;
+  struct dirent* ent;
+  bool full = false;
+  std::set<std::string> basenames;
+  while (readdir_r(dir, &ent_buf, &ent) == 0 && ent) {
+    if ((strcmp(ent->d_name, ".") == 0) ||
+        (strcmp(ent->d_name, "..") == 0))
+      continue;
+
+    std::string filename(ent->d_name);
+    size_t last_dot = filename.rfind(".");
+    std::string basename;
+    // If there is a valid looking extension, use the base part of the
+    // name.  If the only dot is the first byte (aka a dot file), treat
+    // it as unique to avoid allowing a directory full of dot files
+    // from accumulating.
+    if (last_dot != std::string::npos && last_dot != 0)
+      basename = filename.substr(0, last_dot);
+    else
+      basename = filename;
+    basenames.insert(basename);
+
+    if (basenames.size() >= static_cast<size_t>(kMaxCrashDirectorySize)) {
+      LOG(WARNING) << "Crash directory " << crash_directory.value()
+                   << " already full with " << kMaxCrashDirectorySize
+                   << " pending reports";
+      full = true;
+      break;
+    }
+  }
+  closedir(dir);
+  return !full;
+}
+
+bool CrashCollector::GetLogContents(const FilePath &config_path,
+                                    const std::string &exec_name,
+                                    const FilePath &output_file) {
+  chromeos::KeyValueStore store;
+  if (!store.Load(config_path)) {
+    LOG(INFO) << "Unable to read log configuration file "
+              << config_path.value();
+    return false;
+  }
+
+  std::string command;
+  if (!store.GetString(exec_name, &command))
+    return false;
+
+  chromeos::ProcessImpl diag_process;
+  diag_process.AddArg(kShellPath);
+  diag_process.AddStringOption("-c", command);
+  diag_process.RedirectOutput(output_file.value());
+
+  const int result = diag_process.Run();
+  if (result != 0) {
+    LOG(INFO) << "Log command \"" << command << "\" exited with " << result;
+    return false;
+  }
+  return true;
+}
+
+void CrashCollector::AddCrashMetaData(const std::string &key,
+                                      const std::string &value) {
+  extra_metadata_.append(StringPrintf("%s=%s\n", key.c_str(), value.c_str()));
+}
+
+void CrashCollector::AddCrashMetaUploadFile(const std::string &key,
+                                            const std::string &path) {
+  if (!path.empty())
+    AddCrashMetaData(kUploadFilePrefix + key, path);
+}
+
+void CrashCollector::AddCrashMetaUploadData(const std::string &key,
+                                            const std::string &value) {
+  if (!value.empty())
+    AddCrashMetaData(kUploadVarPrefix + key, value);
+}
+
+void CrashCollector::WriteCrashMetaData(const FilePath &meta_path,
+                                        const std::string &exec_name,
+                                        const std::string &payload_path) {
+  chromeos::KeyValueStore store;
+  if (!store.Load(FilePath(lsb_release_))) {
+    LOG(ERROR) << "Problem parsing " << lsb_release_;
+    // Even though there was some failure, take as much as we could read.
+  }
+
+  std::string version("unknown");
+  if (!store.GetString(kLsbVersionKey, &version)) {
+    LOG(ERROR) << "Unable to read " << kLsbVersionKey << " from "
+               << lsb_release_;
+  }
+  int64_t payload_size = -1;
+  base::GetFileSize(FilePath(payload_path), &payload_size);
+  std::string meta_data = StringPrintf("%sexec_name=%s\n"
+                                       "ver=%s\n"
+                                       "payload=%s\n"
+                                       "payload_size=%" PRId64 "\n"
+                                       "done=1\n",
+                                       extra_metadata_.c_str(),
+                                       exec_name.c_str(),
+                                       version.c_str(),
+                                       payload_path.c_str(),
+                                       payload_size);
+  // We must use WriteNewFile instead of base::WriteFile as we
+  // do not want to write with root access to a symlink that an attacker
+  // might have created.
+  if (WriteNewFile(meta_path, meta_data.c_str(), meta_data.size()) < 0) {
+    LOG(ERROR) << "Unable to write " << meta_path.value();
+  }
+}
+
+bool CrashCollector::IsCrashTestInProgress() {
+  return base::PathExists(FilePath(kCrashTestInProgressPath));
+}
+
+bool CrashCollector::IsDeveloperImage() {
+  // If we're testing crash reporter itself, we don't want to special-case
+  // for developer images.
+  if (IsCrashTestInProgress())
+    return false;
+  return base::PathExists(FilePath(kLeaveCoreFile));
+}
diff --git a/crash_reporter/crash_collector.h b/crash_reporter/crash_collector.h
new file mode 100644
index 0000000..cfd76fd
--- /dev/null
+++ b/crash_reporter/crash_collector.h
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CRASH_REPORTER_CRASH_COLLECTOR_H_
+#define CRASH_REPORTER_CRASH_COLLECTOR_H_
+
+#include <sys/stat.h>
+
+#include <map>
+#include <string>
+
+#include <base/files/file_path.h>
+#include <base/macros.h>
+#include <gtest/gtest_prod.h>  // for FRIEND_TEST
+
+// User crash collector.
+class CrashCollector {
+ public:
+  typedef void (*CountCrashFunction)();
+  typedef bool (*IsFeedbackAllowedFunction)();
+
+  CrashCollector();
+
+  virtual ~CrashCollector();
+
+  // Initialize the crash collector for detection of crashes, given a
+  // crash counting function, and metrics collection enabled oracle.
+  void Initialize(CountCrashFunction count_crash,
+                  IsFeedbackAllowedFunction is_metrics_allowed);
+
+ protected:
+  friend class CrashCollectorTest;
+  FRIEND_TEST(ChromeCollectorTest, HandleCrash);
+  FRIEND_TEST(CrashCollectorTest, CheckHasCapacityCorrectBasename);
+  FRIEND_TEST(CrashCollectorTest, CheckHasCapacityStrangeNames);
+  FRIEND_TEST(CrashCollectorTest, CheckHasCapacityUsual);
+  FRIEND_TEST(CrashCollectorTest, GetCrashDirectoryInfo);
+  FRIEND_TEST(CrashCollectorTest, GetCrashPath);
+  FRIEND_TEST(CrashCollectorTest, GetLogContents);
+  FRIEND_TEST(CrashCollectorTest, ForkExecAndPipe);
+  FRIEND_TEST(CrashCollectorTest, FormatDumpBasename);
+  FRIEND_TEST(CrashCollectorTest, Initialize);
+  FRIEND_TEST(CrashCollectorTest, MetaData);
+  FRIEND_TEST(CrashCollectorTest, Sanitize);
+  FRIEND_TEST(CrashCollectorTest, WriteNewFile);
+  FRIEND_TEST(ForkExecAndPipeTest, Basic);
+  FRIEND_TEST(ForkExecAndPipeTest, NonZeroReturnValue);
+  FRIEND_TEST(ForkExecAndPipeTest, BadOutputFile);
+  FRIEND_TEST(ForkExecAndPipeTest, ExistingOutputFile);
+  FRIEND_TEST(ForkExecAndPipeTest, BadExecutable);
+  FRIEND_TEST(ForkExecAndPipeTest, StderrCaptured);
+  FRIEND_TEST(ForkExecAndPipeTest, NULLParam);
+  FRIEND_TEST(ForkExecAndPipeTest, NoParams);
+  FRIEND_TEST(ForkExecAndPipeTest, SegFaultHandling);
+
+  // Set maximum enqueued crashes in a crash directory.
+  static const int kMaxCrashDirectorySize;
+
+  // Writes |data| of |size| to |filename|, which must be a new file.
+  // If the file already exists or writing fails, return a negative value.
+  // Otherwise returns the number of bytes written.
+  int WriteNewFile(const base::FilePath &filename, const char *data, int size);
+
+  // Return a filename that has only [a-z0-1_] characters by mapping
+  // all others into '_'.
+  std::string Sanitize(const std::string &name);
+
+  // For testing, set the directory always returned by
+  // GetCreatedCrashDirectoryByEuid.
+  void ForceCrashDirectory(const base::FilePath &forced_directory) {
+    forced_crash_directory_ = forced_directory;
+  }
+
+  base::FilePath GetCrashDirectoryInfo(mode_t *mode,
+                                       uid_t *directory_owner,
+                                       gid_t *directory_group);
+  bool GetUserInfoFromName(const std::string &name,
+                           uid_t *uid,
+                           gid_t *gid);
+
+  // Determines the crash directory for given euid, and creates the
+  // directory if necessary with appropriate permissions.  If
+  // |out_of_capacity| is not nullptr, it is set to indicate if the call
+  // failed due to not having capacity in the crash directory. Returns
+  // true whether or not directory needed to be created, false on any
+  // failure.  If the crash directory is at capacity, returns false.
+  bool GetCreatedCrashDirectoryByEuid(uid_t euid,
+                                      base::FilePath *crash_file_path,
+                                      bool *out_of_capacity);
+
+  // Format crash name based on components.
+  std::string FormatDumpBasename(const std::string &exec_name,
+                                 time_t timestamp,
+                                 pid_t pid);
+
+  // Create a file path to a file in |crash_directory| with the given
+  // |basename| and |extension|.
+  base::FilePath GetCrashPath(const base::FilePath &crash_directory,
+                              const std::string &basename,
+                              const std::string &extension);
+
+  base::FilePath GetProcessPath(pid_t pid);
+  bool GetSymlinkTarget(const base::FilePath &symlink,
+                        base::FilePath *target);
+  bool GetExecutableBaseNameFromPid(pid_t pid,
+                                    std::string *base_name);
+
+  // Check given crash directory still has remaining capacity for another
+  // crash.
+  bool CheckHasCapacity(const base::FilePath &crash_directory);
+
+  // Write a log applicable to |exec_name| to |output_file| based on the
+  // log configuration file at |config_path|.
+  bool GetLogContents(const base::FilePath &config_path,
+                      const std::string &exec_name,
+                      const base::FilePath &output_file);
+
+  // Add non-standard meta data to the crash metadata file.  Call
+  // before calling WriteCrashMetaData.  Key must not contain "=" or
+  // "\n" characters.  Value must not contain "\n" characters.
+  void AddCrashMetaData(const std::string &key, const std::string &value);
+
+  // Add a file to be uploaded to the crash reporter server. The file must
+  // persist until the crash report is sent; ideally it should live in the same
+  // place as the .meta file, so it can be cleaned up automatically.
+  void AddCrashMetaUploadFile(const std::string &key, const std::string &path);
+
+  // Add non-standard meta data to the crash metadata file.
+  // Data added though this call will be uploaded to the crash reporter server,
+  // appearing as a form field.
+  void AddCrashMetaUploadData(const std::string &key, const std::string &value);
+
+  // Write a file of metadata about crash.
+  void WriteCrashMetaData(const base::FilePath &meta_path,
+                          const std::string &exec_name,
+                          const std::string &payload_path);
+
+  // Returns true if the a crash test is currently running.
+  bool IsCrashTestInProgress();
+  // Returns true if we should consider ourselves to be running on a
+  // developer image.
+  bool IsDeveloperImage();
+
+  CountCrashFunction count_crash_function_;
+  IsFeedbackAllowedFunction is_feedback_allowed_function_;
+  std::string extra_metadata_;
+  base::FilePath forced_crash_directory_;
+  std::string lsb_release_;
+  base::FilePath log_config_path_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(CrashCollector);
+};
+
+#endif  // CRASH_REPORTER_CRASH_COLLECTOR_H_
diff --git a/crash_reporter/crash_collector_test.cc b/crash_reporter/crash_collector_test.cc
new file mode 100644
index 0000000..32cbe9f
--- /dev/null
+++ b/crash_reporter/crash_collector_test.cc
@@ -0,0 +1,251 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "crash_collector_test.h"
+
+#include <unistd.h>
+#include <utility>
+
+#include <base/files/file_util.h>
+#include <base/strings/string_util.h>
+#include <base/strings/stringprintf.h>
+#include <chromeos/syslog_logging.h>
+#include <gtest/gtest.h>
+
+#include "crash_collector.h"
+
+using base::FilePath;
+using base::StringPrintf;
+using chromeos::FindLog;
+using ::testing::Invoke;
+using ::testing::Return;
+
+namespace {
+
+void CountCrash() {
+  ADD_FAILURE();
+}
+
+bool IsMetrics() {
+  ADD_FAILURE();
+  return false;
+}
+
+}  // namespace
+
+class CrashCollectorTest : public ::testing::Test {
+ public:
+  void SetUp() {
+    EXPECT_CALL(collector_, SetUpDBus()).WillRepeatedly(Return());
+
+    collector_.Initialize(CountCrash, IsMetrics);
+    test_dir_ = FilePath("test");
+    base::CreateDirectory(test_dir_);
+    chromeos::ClearLog();
+  }
+
+  void TearDown() {
+    base::DeleteFile(test_dir_, true);
+  }
+
+  bool CheckHasCapacity();
+
+ protected:
+  CrashCollectorMock collector_;
+  FilePath test_dir_;
+};
+
+TEST_F(CrashCollectorTest, Initialize) {
+  ASSERT_TRUE(CountCrash == collector_.count_crash_function_);
+  ASSERT_TRUE(IsMetrics == collector_.is_feedback_allowed_function_);
+}
+
+TEST_F(CrashCollectorTest, WriteNewFile) {
+  FilePath test_file = test_dir_.Append("test_new");
+  const char kBuffer[] = "buffer";
+  EXPECT_EQ(strlen(kBuffer),
+            collector_.WriteNewFile(test_file,
+                                    kBuffer,
+                                    strlen(kBuffer)));
+  EXPECT_LT(collector_.WriteNewFile(test_file,
+                                    kBuffer,
+                                    strlen(kBuffer)), 0);
+}
+
+TEST_F(CrashCollectorTest, Sanitize) {
+  EXPECT_EQ("chrome", collector_.Sanitize("chrome"));
+  EXPECT_EQ("CHROME", collector_.Sanitize("CHROME"));
+  EXPECT_EQ("1chrome2", collector_.Sanitize("1chrome2"));
+  EXPECT_EQ("chrome__deleted_", collector_.Sanitize("chrome (deleted)"));
+  EXPECT_EQ("foo_bar", collector_.Sanitize("foo.bar"));
+  EXPECT_EQ("", collector_.Sanitize(""));
+  EXPECT_EQ("_", collector_.Sanitize(" "));
+}
+
+TEST_F(CrashCollectorTest, FormatDumpBasename) {
+  struct tm tm = {0};
+  tm.tm_sec = 15;
+  tm.tm_min = 50;
+  tm.tm_hour = 13;
+  tm.tm_mday = 23;
+  tm.tm_mon = 4;
+  tm.tm_year = 110;
+  tm.tm_isdst = -1;
+  std::string basename =
+      collector_.FormatDumpBasename("foo", mktime(&tm), 100);
+  ASSERT_EQ("foo.20100523.135015.100", basename);
+}
+
+TEST_F(CrashCollectorTest, GetCrashPath) {
+  EXPECT_EQ("/var/spool/crash/myprog.20100101.1200.1234.core",
+            collector_.GetCrashPath(FilePath("/var/spool/crash"),
+                                    "myprog.20100101.1200.1234",
+                                    "core").value());
+  EXPECT_EQ("/home/chronos/user/crash/chrome.20100101.1200.1234.dmp",
+            collector_.GetCrashPath(FilePath("/home/chronos/user/crash"),
+                                    "chrome.20100101.1200.1234",
+                                    "dmp").value());
+}
+
+
+bool CrashCollectorTest::CheckHasCapacity() {
+  static const char kFullMessage[] = "Crash directory test already full";
+  bool has_capacity = collector_.CheckHasCapacity(test_dir_);
+  bool has_message = FindLog(kFullMessage);
+  EXPECT_EQ(has_message, !has_capacity);
+  return has_capacity;
+}
+
+TEST_F(CrashCollectorTest, CheckHasCapacityUsual) {
+  // Test kMaxCrashDirectorySize - 1 non-meta files can be added.
+  for (int i = 0; i < CrashCollector::kMaxCrashDirectorySize - 1; ++i) {
+    base::WriteFile(test_dir_.Append(StringPrintf("file%d.core", i)), "", 0);
+    EXPECT_TRUE(CheckHasCapacity());
+  }
+
+  // Test an additional kMaxCrashDirectorySize - 1 meta files fit.
+  for (int i = 0; i < CrashCollector::kMaxCrashDirectorySize - 1; ++i) {
+    base::WriteFile(test_dir_.Append(StringPrintf("file%d.meta", i)), "", 0);
+    EXPECT_TRUE(CheckHasCapacity());
+  }
+
+  // Test an additional kMaxCrashDirectorySize meta files don't fit.
+  for (int i = 0; i < CrashCollector::kMaxCrashDirectorySize; ++i) {
+    base::WriteFile(test_dir_.Append(StringPrintf("overage%d.meta", i)), "", 0);
+    EXPECT_FALSE(CheckHasCapacity());
+  }
+}
+
+TEST_F(CrashCollectorTest, CheckHasCapacityCorrectBasename) {
+  // Test kMaxCrashDirectorySize - 1 files can be added.
+  for (int i = 0; i < CrashCollector::kMaxCrashDirectorySize - 1; ++i) {
+    base::WriteFile(test_dir_.Append(StringPrintf("file.%d.core", i)), "", 0);
+    EXPECT_TRUE(CheckHasCapacity());
+  }
+  base::WriteFile(test_dir_.Append("file.last.core"), "", 0);
+  EXPECT_FALSE(CheckHasCapacity());
+}
+
+TEST_F(CrashCollectorTest, CheckHasCapacityStrangeNames) {
+  // Test many files with different extensions and same base fit.
+  for (int i = 0; i < 5 * CrashCollector::kMaxCrashDirectorySize; ++i) {
+    base::WriteFile(test_dir_.Append(StringPrintf("a.%d", i)), "", 0);
+    EXPECT_TRUE(CheckHasCapacity());
+  }
+  // Test dot files are treated as individual files.
+  for (int i = 0; i < CrashCollector::kMaxCrashDirectorySize - 2; ++i) {
+    base::WriteFile(test_dir_.Append(StringPrintf(".file%d", i)), "", 0);
+    EXPECT_TRUE(CheckHasCapacity());
+  }
+  base::WriteFile(test_dir_.Append("normal.meta"), "", 0);
+  EXPECT_FALSE(CheckHasCapacity());
+}
+
+TEST_F(CrashCollectorTest, MetaData) {
+  const char kMetaFileBasename[] = "generated.meta";
+  FilePath meta_file = test_dir_.Append(kMetaFileBasename);
+  FilePath lsb_release = test_dir_.Append("lsb-release");
+  FilePath payload_file = test_dir_.Append("payload-file");
+  std::string contents;
+  collector_.lsb_release_ = lsb_release.value();
+  const char kLsbContents[] =
+      "CHROMEOS_RELEASE_BOARD=lumpy\n"
+      "CHROMEOS_RELEASE_VERSION=6727.0.2015_01_26_0853\n"
+      "CHROMEOS_RELEASE_NAME=Chromium OS\n";
+  ASSERT_TRUE(base::WriteFile(lsb_release, kLsbContents, strlen(kLsbContents)));
+  const char kPayload[] = "foo";
+  ASSERT_TRUE(base::WriteFile(payload_file, kPayload, strlen(kPayload)));
+  collector_.AddCrashMetaData("foo", "bar");
+  collector_.WriteCrashMetaData(meta_file, "kernel", payload_file.value());
+  EXPECT_TRUE(base::ReadFileToString(meta_file, &contents));
+  const char kExpectedMeta[] =
+      "foo=bar\n"
+      "exec_name=kernel\n"
+      "ver=6727.0.2015_01_26_0853\n"
+      "payload=test/payload-file\n"
+      "payload_size=3\n"
+      "done=1\n";
+  EXPECT_EQ(kExpectedMeta, contents);
+
+  // Test target of symlink is not overwritten.
+  payload_file = test_dir_.Append("payload2-file");
+  ASSERT_TRUE(base::WriteFile(payload_file, kPayload, strlen(kPayload)));
+  FilePath meta_symlink_path = test_dir_.Append("symlink.meta");
+  ASSERT_EQ(0,
+            symlink(kMetaFileBasename,
+                    meta_symlink_path.value().c_str()));
+  ASSERT_TRUE(base::PathExists(meta_symlink_path));
+  chromeos::ClearLog();
+  collector_.WriteCrashMetaData(meta_symlink_path,
+                                "kernel",
+                                payload_file.value());
+  // Target metadata contents should have stayed the same.
+  contents.clear();
+  EXPECT_TRUE(base::ReadFileToString(meta_file, &contents));
+  EXPECT_EQ(kExpectedMeta, contents);
+  EXPECT_TRUE(FindLog("Unable to write"));
+
+  // Test target of dangling symlink is not created.
+  base::DeleteFile(meta_file, false);
+  ASSERT_FALSE(base::PathExists(meta_file));
+  chromeos::ClearLog();
+  collector_.WriteCrashMetaData(meta_symlink_path, "kernel",
+                                payload_file.value());
+  EXPECT_FALSE(base::PathExists(meta_file));
+  EXPECT_TRUE(FindLog("Unable to write"));
+}
+
+TEST_F(CrashCollectorTest, GetLogContents) {
+  FilePath config_file = test_dir_.Append("crash_config");
+  FilePath output_file = test_dir_.Append("crash_log");
+  const char kConfigContents[] =
+      "foobar=echo hello there | \\\n  sed -e \"s/there/world/\"";
+  ASSERT_TRUE(
+      base::WriteFile(config_file, kConfigContents, strlen(kConfigContents)));
+  base::DeleteFile(FilePath(output_file), false);
+  EXPECT_FALSE(collector_.GetLogContents(config_file,
+                                         "barfoo",
+                                         output_file));
+  EXPECT_FALSE(base::PathExists(output_file));
+  base::DeleteFile(FilePath(output_file), false);
+  EXPECT_TRUE(collector_.GetLogContents(config_file,
+                                        "foobar",
+                                        output_file));
+  ASSERT_TRUE(base::PathExists(output_file));
+  std::string contents;
+  EXPECT_TRUE(base::ReadFileToString(output_file, &contents));
+  EXPECT_EQ("hello world\n", contents);
+}
diff --git a/crash_reporter/crash_collector_test.h b/crash_reporter/crash_collector_test.h
new file mode 100644
index 0000000..cfbb97b
--- /dev/null
+++ b/crash_reporter/crash_collector_test.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2013 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 CRASH_REPORTER_CRASH_COLLECTOR_TEST_H_
+#define CRASH_REPORTER_CRASH_COLLECTOR_TEST_H_
+
+#include "crash_collector.h"
+
+#include <map>
+#include <string>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+class CrashCollectorMock : public CrashCollector {
+ public:
+  MOCK_METHOD0(SetUpDBus, void());
+  MOCK_METHOD1(GetActiveUserSessions,
+               bool(std::map<std::string, std::string> *sessions));
+};
+
+#endif  // CRASH_REPORTER_CRASH_COLLECTOR_TEST_H_
diff --git a/crash_reporter/crash_reporter.cc b/crash_reporter/crash_reporter.cc
new file mode 100644
index 0000000..72eeeda
--- /dev/null
+++ b/crash_reporter/crash_reporter.cc
@@ -0,0 +1,315 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fcntl.h>  // for open
+
+#include <string>
+#include <vector>
+
+#include <base/files/file_util.h>
+#include <base/logging.h>
+#include <base/strings/string_split.h>
+#include <base/strings/string_util.h>
+#include <base/strings/stringprintf.h>
+#include <chromeos/flag_helper.h>
+#include <chromeos/process.h>
+#include <chromeos/syslog_logging.h>
+#include <metrics/metrics_library.h>
+
+#include "kernel_collector.h"
+#include "kernel_warning_collector.h"
+#include "udev_collector.h"
+#include "unclean_shutdown_collector.h"
+#include "user_collector.h"
+
+static const char kCrashCounterHistogram[] = "Logging.CrashCounter";
+static const char kUserCrashSignal[] =
+    "org.chromium.CrashReporter.UserCrash";
+static const char kKernelCrashDetected[] = "/var/run/kernel-crash-detected";
+static const char kUncleanShutdownDetected[] =
+    "/var/run/unclean-shutdown-detected";
+
+// Enumeration of kinds of crashes to be used in the CrashCounter histogram.
+enum CrashKinds {
+  kCrashKindUncleanShutdown = 1,
+  kCrashKindUser = 2,
+  kCrashKindKernel = 3,
+  kCrashKindUdev = 4,
+  kCrashKindKernelWarning = 5,
+  kCrashKindMax
+};
+
+static MetricsLibrary s_metrics_lib;
+
+using base::FilePath;
+using base::StringPrintf;
+
+static bool IsFeedbackAllowed() {
+  return s_metrics_lib.AreMetricsEnabled();
+}
+
+static bool TouchFile(const FilePath &file_path) {
+  return base::WriteFile(file_path, "", 0) == 0;
+}
+
+static void SendCrashMetrics(CrashKinds type, const char* name) {
+  // TODO(kmixter): We can remove this histogram as part of
+  // crosbug.com/11163.
+  s_metrics_lib.SendEnumToUMA(kCrashCounterHistogram, type, kCrashKindMax);
+  s_metrics_lib.SendCrashToUMA(name);
+}
+
+static void CountKernelCrash() {
+  SendCrashMetrics(kCrashKindKernel, "kernel");
+}
+
+static void CountUdevCrash() {
+  SendCrashMetrics(kCrashKindUdev, "udevcrash");
+}
+
+static void CountUncleanShutdown() {
+  SendCrashMetrics(kCrashKindUncleanShutdown, "uncleanshutdown");
+}
+
+static void CountUserCrash() {
+  SendCrashMetrics(kCrashKindUser, "user");
+  // Announce through D-Bus whenever a user crash happens. This is
+  // used by the metrics daemon to log active use time between
+  // crashes.
+  //
+  // We run in the background in case dbus-daemon itself is crashed
+  // and not responding.  This allows us to not block and potentially
+  // deadlock on a dbus-daemon crash.  If dbus-daemon crashes without
+  // restarting, each crash will fork off a lot of dbus-send
+  // processes.  Such a system is in a unusable state and will need
+  // to be restarted anyway.
+  //
+  // Note: This will mean that the dbus-send process will become a zombie and
+  // reparent to init for reaping, but that's OK -- see above.
+
+  chromeos::ProcessImpl dbus_send;
+  dbus_send.AddArg("/system/bin/dbus-send");
+  dbus_send.AddArg("--type=signal");
+  dbus_send.AddArg("--system");
+  dbus_send.AddArg("/");
+  dbus_send.AddArg(kUserCrashSignal);
+  bool status = dbus_send.Start();
+  if (status) {
+    dbus_send.Release();
+  } else {
+    PLOG(WARNING) << "Sending UserCrash DBus signal failed";
+  }
+}
+
+
+static int Initialize(KernelCollector *kernel_collector,
+                      UserCollector *user_collector,
+                      UncleanShutdownCollector *unclean_shutdown_collector,
+                      const bool unclean_check,
+                      const bool clean_shutdown) {
+  CHECK(!clean_shutdown) << "Incompatible options";
+
+  bool was_kernel_crash = false;
+  bool was_unclean_shutdown = false;
+  kernel_collector->Enable();
+  if (kernel_collector->is_enabled()) {
+    was_kernel_crash = kernel_collector->Collect();
+  }
+
+  if (unclean_check) {
+    was_unclean_shutdown = unclean_shutdown_collector->Collect();
+  }
+
+  // Touch a file to notify the metrics daemon that a kernel
+  // crash has been detected so that it can log the time since
+  // the last kernel crash.
+  if (IsFeedbackAllowed()) {
+    if (was_kernel_crash) {
+      TouchFile(FilePath(kKernelCrashDetected));
+    } else if (was_unclean_shutdown) {
+      // We only count an unclean shutdown if it did not come with
+      // an associated kernel crash.
+      TouchFile(FilePath(kUncleanShutdownDetected));
+    }
+  }
+
+  // Must enable the unclean shutdown collector *after* collecting.
+  unclean_shutdown_collector->Enable();
+  user_collector->Enable();
+
+  return 0;
+}
+
+static int HandleUserCrash(UserCollector *user_collector,
+                           const std::string& user, const bool crash_test) {
+  // Handle a specific user space crash.
+  CHECK(!user.empty()) << "--user= must be set";
+
+  // Make it possible to test what happens when we crash while
+  // handling a crash.
+  if (crash_test) {
+    *(volatile char *)0 = 0;
+    return 0;
+  }
+
+  // Accumulate logs to help in diagnosing failures during user collection.
+  chromeos::LogToString(true);
+  // Handle the crash, get the name of the process from procfs.
+  bool handled = user_collector->HandleCrash(user, nullptr);
+  chromeos::LogToString(false);
+  if (!handled)
+    return 1;
+  return 0;
+}
+
+static int HandleUdevCrash(UdevCollector *udev_collector,
+                           const std::string& udev_event) {
+  // Handle a crash indicated by a udev event.
+  CHECK(!udev_event.empty()) << "--udev= must be set";
+
+  // Accumulate logs to help in diagnosing failures during user collection.
+  chromeos::LogToString(true);
+  bool handled = udev_collector->HandleCrash(udev_event);
+  chromeos::LogToString(false);
+  if (!handled)
+    return 1;
+  return 0;
+}
+
+static int HandleKernelWarning(KernelWarningCollector
+                               *kernel_warning_collector) {
+  // Accumulate logs to help in diagnosing failures during collection.
+  chromeos::LogToString(true);
+  bool handled = kernel_warning_collector->Collect();
+  chromeos::LogToString(false);
+  if (!handled)
+    return 1;
+  return 0;
+}
+
+// Interactive/diagnostics mode for generating kernel crash signatures.
+static int GenerateKernelSignature(KernelCollector *kernel_collector,
+                                   const std::string& kernel_signature_file) {
+  std::string kcrash_contents;
+  std::string signature;
+  if (!base::ReadFileToString(FilePath(kernel_signature_file),
+                              &kcrash_contents)) {
+    fprintf(stderr, "Could not read file.\n");
+    return 1;
+  }
+  if (!kernel_collector->ComputeKernelStackSignature(
+          kcrash_contents,
+          &signature,
+          true)) {
+    fprintf(stderr, "Signature could not be generated.\n");
+    return 1;
+  }
+  printf("Kernel crash signature is \"%s\".\n", signature.c_str());
+  return 0;
+}
+
+// Ensure stdout, stdin, and stderr are open file descriptors.  If
+// they are not, any code which writes to stderr/stdout may write out
+// to files opened during execution.  In particular, when
+// crash_reporter is run by the kernel coredump pipe handler (via
+// kthread_create/kernel_execve), it will not have file table entries
+// 1 and 2 (stdout and stderr) populated.  We populate them here.
+static void OpenStandardFileDescriptors() {
+  int new_fd = -1;
+  // We open /dev/null to fill in any of the standard [0, 2] file
+  // descriptors.  We leave these open for the duration of the
+  // process.  This works because open returns the lowest numbered
+  // invalid fd.
+  do {
+    new_fd = open("/dev/null", 0);
+    CHECK_GE(new_fd, 0) << "Unable to open /dev/null";
+  } while (new_fd >= 0 && new_fd <= 2);
+  close(new_fd);
+}
+
+int main(int argc, char *argv[]) {
+  DEFINE_bool(init, false, "Initialize crash logging");
+  DEFINE_bool(clean_shutdown, false, "Signal clean shutdown");
+  DEFINE_string(generate_kernel_signature, "",
+                "Generate signature from given kcrash file");
+  DEFINE_bool(crash_test, false, "Crash test");
+  DEFINE_string(user, "", "User crash info (pid:signal:exec_name)");
+  DEFINE_bool(unclean_check, true, "Check for unclean shutdown");
+  DEFINE_string(udev, "", "Udev event description (type:device:subsystem)");
+  DEFINE_bool(kernel_warning, false, "Report collected kernel warning");
+  DEFINE_string(pid, "", "PID of crashing process");
+  DEFINE_string(uid, "", "UID of crashing process");
+  DEFINE_string(exe, "", "Executable name of crashing process");
+  DEFINE_bool(core2md_failure, false, "Core2md failure test");
+  DEFINE_bool(directory_failure, false, "Spool directory failure test");
+  DEFINE_string(filter_in, "",
+                "Ignore all crashes but this for testing");
+
+  OpenStandardFileDescriptors();
+  FilePath my_path = base::MakeAbsoluteFilePath(FilePath(argv[0]));
+  s_metrics_lib.Init();
+  chromeos::FlagHelper::Init(argc, argv, "Chromium OS Crash Reporter");
+  chromeos::OpenLog(my_path.BaseName().value().c_str(), true);
+  chromeos::InitLog(chromeos::kLogToSyslog);
+
+  KernelCollector kernel_collector;
+  kernel_collector.Initialize(CountKernelCrash, IsFeedbackAllowed);
+  UserCollector user_collector;
+  user_collector.Initialize(CountUserCrash,
+                            my_path.value(),
+                            IsFeedbackAllowed,
+                            true,  // generate_diagnostics
+                            FLAGS_core2md_failure,
+                            FLAGS_directory_failure,
+                            FLAGS_filter_in);
+  UncleanShutdownCollector unclean_shutdown_collector;
+  unclean_shutdown_collector.Initialize(CountUncleanShutdown,
+                                        IsFeedbackAllowed);
+  UdevCollector udev_collector;
+  udev_collector.Initialize(CountUdevCrash, IsFeedbackAllowed);
+
+  KernelWarningCollector kernel_warning_collector;
+  kernel_warning_collector.Initialize(CountUdevCrash, IsFeedbackAllowed);
+
+  if (FLAGS_init) {
+    return Initialize(&kernel_collector,
+                      &user_collector,
+                      &unclean_shutdown_collector,
+                      FLAGS_unclean_check,
+                      FLAGS_clean_shutdown);
+  }
+
+  if (FLAGS_clean_shutdown) {
+    unclean_shutdown_collector.Disable();
+    user_collector.Disable();
+    return 0;
+  }
+
+  if (!FLAGS_generate_kernel_signature.empty()) {
+    return GenerateKernelSignature(&kernel_collector,
+                                   FLAGS_generate_kernel_signature);
+  }
+
+  if (!FLAGS_udev.empty()) {
+    return HandleUdevCrash(&udev_collector, FLAGS_udev);
+  }
+
+  if (FLAGS_kernel_warning) {
+    return HandleKernelWarning(&kernel_warning_collector);
+  }
+
+  return HandleUserCrash(&user_collector, FLAGS_user, FLAGS_crash_test);
+}
diff --git a/crash_reporter/crash_reporter_logs.conf b/crash_reporter/crash_reporter_logs.conf
new file mode 100644
index 0000000..7db308c
--- /dev/null
+++ b/crash_reporter/crash_reporter_logs.conf
@@ -0,0 +1,122 @@
+# Copyright (C) 2012 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# This file is parsed by chromeos::KeyValueStore. It has the format:
+#
+# <basename>=<shell command>\n
+#
+# Commands may be split across multiple lines using trailing backslashes.
+#
+# When an executable named <basename> crashes, the corresponding command is
+# executed and its standard output and standard error are attached to the crash
+# report.
+#
+# Use caution in modifying this file. Only run common Unix commands here, as
+# these commands will be run when a crash has recently occurred and we should
+# avoid running anything that might cause another crash. Similarly, these
+# commands block notification of the crash to parent processes, so commands
+# should execute quickly.
+
+update_engine=cat $(ls -1tr /var/log/update_engine | tail -5 | \
+  sed s.^./var/log/update_engine/.) | tail -c 50000
+
+# The cros_installer output is logged into the update engine log file,
+# so it is handled in the same way as update_engine.
+cros_installer=cat $(ls -1tr /var/log/update_engine | tail -5 | \
+  sed s.^./var/log/update_engine/.) | tail -c 50000
+
+# Dump the last 20 lines of the last two files in Chrome's system and user log
+# directories, along with the last 20 messages from the session manager.
+chrome=\
+  for f in $(ls -1rt /var/log/chrome/chrome_[0-9]* | tail -2) \
+    $(ls -1rt /home/chronos/u-*/log/chrome_[0-9]* 2>/dev/null | tail -2); do \
+    echo "===$f (tail)==="; \
+    tail -20 $f; \
+    echo EOF; \
+    echo; \
+  done; \
+  echo "===session_manager (tail)==="; \
+  awk '$3 ~ "^session_manager\[" { print }' /var/log/messages | tail -20; \
+  echo EOF
+
+# The following rule is used for generating additional diagnostics when
+# collection of user crashes fails.  This output should not be too large
+# as it is stored in memory.  The output format specified for 'ps' is the
+# same as with the "u" ("user-oriented") option, except it doesn't show
+# the commands' arguments (i.e. "comm" instead of "command").
+crash_reporter-user-collection=\
+  echo "===ps output==="; \
+  ps axw -o user,pid,%cpu,%mem,vsz,rss,tname,stat,start_time,bsdtime,comm | \
+    tail -c 25000; \
+  echo "===meminfo==="; \
+  cat /proc/meminfo
+
+# This rule is similar to the crash_reporter-user-collection rule, except it is
+# run for kernel errors reported through udev events.
+crash_reporter-udev-collection-change-card0-drm=\
+  for dri in /sys/kernel/debug/dri/*; do \
+    echo "===$dri/i915_error_state==="; \
+    cat $dri/i915_error_state; \
+  done
+
+# When trackpad driver cyapa detects some abnormal behavior, we collect
+# additional logs from kernel messages.
+crash_reporter-udev-collection-change--i2c-cyapa=\
+  /usr/sbin/kernel_log_collector.sh cyapa 30
+# When trackpad/touchscreen driver atmel_mxt_ts detects some abnormal behavior,
+# we collect additional logs from kernel messages.
+crash_reporter-udev-collection-change--i2c-atmel_mxt_ts=\
+  /usr/sbin/kernel_log_collector.sh atmel 30
+# When touch device noise are detected, we collect relevant logs.
+# (crosbug.com/p/16788)
+crash_reporter-udev-collection---TouchNoise=cat /var/log/touch_noise.log
+# Periodically collect touch event log for debugging (crosbug.com/p/17244)
+crash_reporter-udev-collection---TouchEvent=cat /var/log/touch_event.log
+
+# Collect the last 50 lines of /var/log/messages and /var/log/net.log for
+# intel wifi driver (iwlwifi) for debugging purpose.
+crash_reporter-udev-collection-devcoredump-iwlwifi=\
+  echo "===/var/log/messages==="; \
+  tail -n 50 /var/log/messages; \
+  echo "===/var/log/net.log==="; \
+  tail -n 50 /var/log/net.log; \
+  echo EOF
+
+# Dump the last 50 lines of the last two powerd log files -- if the job has
+# already restarted, we want to see the end of the previous instance's logs.
+powerd=\
+  for f in $(ls -1tr /var/log/power_manager/powerd.[0-9]* | tail -2); do \
+    echo "===$(basename $f) (tail)==="; \
+    tail -50 $f; \
+    echo EOF; \
+  done
+# If power_supply_info aborts (due to e.g. a bad battery), its failure message
+# could end up in various places depending on which process was running it.
+# Attach the end of powerd's log since it might've also logged the underlying
+# problem.
+power_supply_info=\
+  echo "===powerd.LATEST (tail)==="; \
+  tail -50 /var/log/power_manager/powerd.LATEST; \
+  echo EOF
+# powerd_setuid_helper gets run by powerd, so its stdout/stderr will be mixed in
+# with powerd's stdout/stderr.
+powerd_setuid_helper=\
+  echo "===powerd.OUT (tail)==="; \
+  tail -50 /var/log/powerd.out; \
+  echo EOF
+
+# The following rules are only for testing purposes.
+crash_log_test=echo hello world
+crash_log_recursion_test=sleep 1 && \
+  /usr/local/autotest/tests/crash_log_recursion_test
diff --git a/crash_reporter/crash_reporter_logs_test.cc b/crash_reporter/crash_reporter_logs_test.cc
new file mode 100644
index 0000000..c9ca02d
--- /dev/null
+++ b/crash_reporter/crash_reporter_logs_test.cc
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2015 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>
+
+#include <base/files/file_path.h>
+#include <chromeos/key_value_store.h>
+#include <gtest/gtest.h>
+
+namespace {
+
+// Name of the checked-in configuration file containing log-collection commands.
+const char kConfigFile[] = "crash_reporter_logs.conf";
+
+// Executable name for Chrome. kConfigFile is expected to contain this entry.
+const char kChromeExecName[] = "chrome";
+
+}  // namespace
+
+// Tests that the config file is parsable and that Chrome is listed.
+TEST(CrashReporterLogsTest, ReadConfig) {
+  chromeos::KeyValueStore store;
+  ASSERT_TRUE(store.Load(base::FilePath(kConfigFile)));
+  std::string command;
+  EXPECT_TRUE(store.GetString(kChromeExecName, &command));
+  EXPECT_FALSE(command.empty());
+}
diff --git a/crash_reporter/crash_sender b/crash_reporter/crash_sender
new file mode 100755
index 0000000..fa2f8fc
--- /dev/null
+++ b/crash_reporter/crash_sender
@@ -0,0 +1,723 @@
+#!/bin/sh
+
+# Copyright (C) 2010 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set -e
+
+# Default product ID in crash report (used if GOOGLE_CRASH_* is undefined).
+CHROMEOS_PRODUCT=ChromeOS
+
+# File whose existence implies crash reports may be sent, and whose
+# contents includes our machine's anonymized guid.
+CONSENT_ID="/home/chronos/Consent To Send Stats"
+
+# Crash sender lock in case the sender is already running.
+CRASH_SENDER_LOCK="/var/lock/crash_sender"
+
+# Path to file that indicates a crash test is currently running.
+CRASH_TEST_IN_PROGRESS_FILE="/tmp/crash-test-in-progress"
+
+# Path to find which is required for computing the crash rate.
+FIND="/usr/bin/find"
+
+# Set this to 1 in the environment to allow uploading crash reports
+# for unofficial versions.
+FORCE_OFFICIAL=${FORCE_OFFICIAL:-0}
+
+# Path to hardware class description.
+HWCLASS_PATH="/sys/devices/platform/chromeos_acpi/HWID"
+
+# Path to file that indicates this is a developer image.
+LEAVE_CORE_FILE="/root/.leave_core"
+
+# Path to list_proxies.
+LIST_PROXIES="/usr/bin/list_proxies"
+
+# Maximum crashes to send per day.
+MAX_CRASH_RATE=${MAX_CRASH_RATE:-32}
+
+# Path to metrics_client.
+METRICS_CLIENT="/usr/bin/metrics_client"
+
+# File whose existence mocks crash sending.  If empty we pretend the
+# crash sending was successful, otherwise unsuccessful.
+MOCK_CRASH_SENDING="/tmp/mock-crash-sending"
+
+# Set this to 1 in the environment to pretend to have booted in developer
+# mode.  This is used by autotests.
+MOCK_DEVELOPER_MODE=${MOCK_DEVELOPER_MODE:-0}
+
+# Ignore PAUSE_CRASH_SENDING file if set.
+OVERRIDE_PAUSE_SENDING=${OVERRIDE_PAUSE_SENDING:-0}
+
+# File whose existence causes crash sending to be delayed (for testing).
+# Must be stateful to enable testing kernel crashes.
+PAUSE_CRASH_SENDING="/var/lib/crash_sender_paused"
+
+# URL to send official build crash reports to.
+REPORT_UPLOAD_PROD_URL="https://clients2.google.com/cr/report"
+
+# Path to a directory of restricted certificates which includes
+# a certificate for ${REPORT_UPLOAD_PROD_URL}.
+RESTRICTED_CERTIFICATES_PATH="/usr/share/chromeos-ca-certificates"
+
+# File whose existence implies we're running and not to start again.
+RUN_FILE="/var/run/crash_sender.pid"
+
+# Maximum time to sleep between sends.
+SECONDS_SEND_SPREAD=${SECONDS_SEND_SPREAD:-600}
+
+# Set this to 1 to allow uploading of device coredumps.
+DEVCOREDUMP_UPLOAD_FLAG_FILE=\
+"/var/lib/crash_reporter/device_coredump_upload_allowed"
+
+# The syslog tag for all logging we emit.
+TAG="$(basename $0)[$$]"
+
+# Directory to store timestamp files indicating the uploads in the past 24
+# hours.
+TIMESTAMPS_DIR="/var/lib/crash_sender"
+
+# Temp directory for this process.
+TMP_DIR=""
+
+# Chrome's crash report log file.
+CHROME_CRASH_LOG="/var/log/chrome/Crash Reports/uploads.log"
+
+lecho() {
+  logger -t "${TAG}" "$@"
+}
+
+# Returns true if mock is enabled.
+is_mock() {
+  [ -f "${MOCK_CRASH_SENDING}" ] && return 0
+  return 1
+}
+
+is_mock_successful() {
+  local mock_in=$(cat "${MOCK_CRASH_SENDING}")
+  [ "${mock_in}" = "" ] && return 0  # empty file means success
+  return 1
+}
+
+cleanup() {
+  if [ -n "${TMP_DIR}" ]; then
+    rm -rf "${TMP_DIR}"
+  fi
+  rm -f "${RUN_FILE}"
+  crash_done
+}
+
+crash_done() {
+  if is_mock; then
+    # For testing purposes, emit a message to log so that we
+    # know when the test has received all the messages from this run.
+    lecho "crash_sender done."
+  fi
+}
+
+is_official_image() {
+  [ ${FORCE_OFFICIAL} -ne 0 ] && return 0
+  grep ^CHROMEOS_RELEASE_DESCRIPTION /etc/lsb-release | grep -q Official
+}
+
+# Returns 0 if the a crash test is currently running.  NOTE: Mirrors
+# crash_collector.cc:CrashCollector::IsCrashTestInProgress().
+is_crash_test_in_progress() {
+  [ -f "${CRASH_TEST_IN_PROGRESS_FILE}" ] && return 0
+  return 1
+}
+
+# Returns 0 if we should consider ourselves to be running on a developer
+# image.  NOTE: Mirrors crash_collector.cc:CrashCollector::IsDeveloperImage().
+is_developer_image() {
+  # If we're testing crash reporter itself, we don't want to special-case
+  # for developer images.
+  is_crash_test_in_progress && return 1
+  [ -f "${LEAVE_CORE_FILE}" ] && return 0
+  return 1
+}
+
+# Returns 0 if we should consider ourselves to be running on a test image.
+is_test_image() {
+  # If we're testing crash reporter itself, we don't want to special-case
+  # for test images.
+  is_crash_test_in_progress && return 1
+  case $(get_channel) in
+  test*) return 0;;
+  esac
+  return 1
+}
+
+# Returns 0 if the machine booted up in developer mode.
+is_developer_mode() {
+  [ ${MOCK_DEVELOPER_MODE} -ne 0 ] && return 0
+  # If we're testing crash reporter itself, we don't want to special-case
+  # for developer mode.
+  is_crash_test_in_progress && return 1
+  crossystem "devsw_boot?1"  # exit status will be accurate
+}
+
+# Return 0 if the uploading of device coredumps is allowed.
+is_device_coredump_upload_allowed() {
+  [ -f "${DEVCOREDUMP_UPLOAD_FLAG_FILE}" ] && return 0
+  return 1
+}
+
+# Generate a uniform random number in 0..max-1.
+generate_uniform_random() {
+  local max=$1
+  local random="$(od -An -N4 -tu /dev/urandom)"
+  echo $((random % max))
+}
+
+# Check if sending a crash now does not exceed the maximum 24hr rate and
+# commit to doing so, if not.
+check_rate() {
+  mkdir -p ${TIMESTAMPS_DIR}
+  # Only consider minidumps written in the past 24 hours by removing all older.
+  ${FIND} "${TIMESTAMPS_DIR}" -mindepth 1 -mmin +$((24 * 60)) \
+      -exec rm -- '{}' ';'
+  local sends_in_24hrs=$(echo "${TIMESTAMPS_DIR}"/* | wc -w)
+  lecho "Current send rate: ${sends_in_24hrs}sends/24hrs"
+  if [ ${sends_in_24hrs} -ge ${MAX_CRASH_RATE} ]; then
+    lecho "Cannot send more crashes:"
+    lecho "  current ${sends_in_24hrs}send/24hrs >= " \
+          "max ${MAX_CRASH_RATE}send/24hrs"
+    return 1
+  fi
+  mktemp "${TIMESTAMPS_DIR}"/XXXX > /dev/null
+  return 0
+}
+
+# Gets the base part of a crash report file, such as name.01234.5678.9012 from
+# name.01234.5678.9012.meta or name.01234.5678.9012.log.tar.xz.  We make sure
+# "name" is sanitized in CrashCollector::Sanitize to not include any periods.
+get_base() {
+  echo "$1" | cut -d. -f-4
+}
+
+get_extension() {
+  local extension="${1##*.}"
+  local filename="${1%.*}"
+  # For gzipped file, we ignore .gz and get the real extension
+  if [ "${extension}" = "gz" ]; then
+    echo "${filename##*.}"
+  else
+    echo "${extension}"
+  fi
+}
+
+# Return which kind of report the given metadata file relates to
+get_kind() {
+  local payload="$(get_key_value "$1" "payload")"
+  if [ ! -r "${payload}" ]; then
+    lecho "Missing payload: ${payload}"
+    echo "undefined"
+    return
+  fi
+  local kind="$(get_extension "${payload}")"
+  if [ "${kind}" = "dmp" ]; then
+    echo "minidump"
+    return
+  fi
+  echo "${kind}"
+}
+
+get_key_value() {
+  local file="$1" key="$2" value
+
+  if [ -f "${file}" ]; then
+    # Return the first entry.  There shouldn't be more than one anyways.
+    # Substr at length($1) + 2 skips past the key and following = sign (awk
+    # uses 1-based indexes), but preserves embedded = characters.
+    value=$(sed -n "/^${key}[[:space:]]*=/{s:^[^=]*=::p;q}" "${file}")
+  fi
+
+  echo "${value:-undefined}"
+}
+
+get_keys() {
+  local file="$1" regex="$2"
+
+  awk -F'[[:space:]=]' -vregex="${regex}" \
+      'match($1, regex) { print $1 }' "${file}"
+}
+
+# Return the board name.
+get_board() {
+  get_key_value "/etc/lsb-release" "CHROMEOS_RELEASE_BOARD"
+}
+
+# Return the channel name (sans "-channel" suffix).
+get_channel() {
+  get_key_value "/etc/lsb-release" "CHROMEOS_RELEASE_TRACK" |
+    sed 's:-channel$::'
+}
+
+# Return the hardware class or "undefined".
+get_hardware_class() {
+  if [ -r "${HWCLASS_PATH}" ]; then
+    cat "${HWCLASS_PATH}"
+  elif crossystem hwid > /dev/null 2>&1; then
+    echo "$(crossystem hwid)"
+  else
+    echo "undefined"
+  fi
+}
+
+send_crash() {
+  local meta_path="$1"
+  local report_payload="$(get_key_value "${meta_path}" "payload")"
+  local kind="$(get_kind "${meta_path}")"
+  local exec_name="$(get_key_value "${meta_path}" "exec_name")"
+  local url="${REPORT_UPLOAD_PROD_URL}"
+  local chromeos_version="$(get_key_value "${meta_path}" "ver")"
+  local board="$(get_board)"
+  local hwclass="$(get_hardware_class)"
+  local write_payload_size="$(get_key_value "${meta_path}" "payload_size")"
+  local log="$(get_key_value "${meta_path}" "log")"
+  local sig="$(get_key_value "${meta_path}" "sig")"
+  local send_payload_size="$(stat --printf=%s "${report_payload}" 2>/dev/null)"
+  local product="$(get_key_value "${meta_path}" "upload_var_prod")"
+  local version="$(get_key_value "${meta_path}" "upload_var_ver")"
+  local upload_prefix="$(get_key_value "${meta_path}" "upload_prefix")"
+  local guid
+
+  set -- \
+    -F "write_payload_size=${write_payload_size}" \
+    -F "send_payload_size=${send_payload_size}"
+  if [ "${sig}" != "undefined" ]; then
+    set -- "$@" \
+      -F "sig=${sig}" \
+      -F "sig2=${sig}"
+  fi
+  if [ -r "${report_payload}" ]; then
+    set -- "$@" \
+      -F "upload_file_${kind}=@${report_payload}"
+  fi
+  if [ "${log}" != "undefined" -a -r "${log}" ]; then
+    set -- "$@" \
+      -F "log=@${log}"
+  fi
+
+  if [ "${upload_prefix}" = "undefined" ]; then
+    upload_prefix=""
+  fi
+
+  # Grab any variable that begins with upload_.
+  local v
+  for k in $(get_keys "${meta_path}" "^upload_"); do
+    v="$(get_key_value "${meta_path}" "${k}")"
+    case ${k} in
+      # Product & version are handled separately.
+      upload_var_prod) ;;
+      upload_var_ver) ;;
+      upload_var_*)
+        set -- "$@" -F "${upload_prefix}${k#upload_var_}=${v}"
+        ;;
+      upload_file_*)
+        if [ -r "${v}" ]; then
+          set -- "$@" -F "${upload_prefix}${k#upload_file_}=@${v}"
+        fi
+        ;;
+    esac
+  done
+
+  # When uploading Chrome reports we need to report the right product and
+  # version. If the meta file does not specify it, use GOOGLE_CRASH_ID
+  # as the product and GOOGLE_CRASH_VERSION_ID as the version.
+  if [ "${product}" = "undefined" ]; then
+    product="$(get_key_value /etc/os-release 'GOOGLE_CRASH_ID')"
+  fi
+  if [ "${version}" = "undefined" ]; then
+    version="$(get_key_value /etc/os-release 'GOOGLE_CRASH_VERSION_ID')"
+  fi
+
+  # If GOOGLE_CRASH_* is undefined, we look for ID and VERSION_ID in
+  # /etc/os-release.
+  if [ "${product}" = "undefined" ]; then
+    product="$(get_key_value /etc/os-release 'ID')"
+  fi
+  if [ "${version}" = "undefined" ]; then
+    version="$(get_key_value /etc/os-release 'VERSION_ID')"
+  fi
+
+  # If ID or VERSION_ID is undefined, we use the default product name
+  # and CHROMEOS_RELEASE_VERSION from /etc/lsb-release.
+  if [ "${product}" = "undefined" ]; then
+    product="${CHROMEOS_PRODUCT}"
+  fi
+  if [ "${version}" = "undefined" ]; then
+    version="${chromeos_version}"
+  fi
+
+  local image_type
+  if is_test_image; then
+    image_type="test"
+  elif is_developer_image; then
+    image_type="dev"
+  elif [ ${FORCE_OFFICIAL} -ne 0 ]; then
+    image_type="force-official"
+  elif is_mock && ! is_mock_successful; then
+    image_type="mock-fail"
+  fi
+
+  local boot_mode
+  if ! crossystem "cros_debug" > /dev/null 2>&1; then
+    # Sanity-check failed that makes sure crossystem exists.
+    lecho "Cannot determine boot mode due to error running crossystem command"
+    boot_mode="missing-crossystem"
+  elif is_developer_mode; then
+    boot_mode="dev"
+  fi
+
+  # Need to strip dashes ourselves as Chrome preserves it in the file
+  # nowadays.  This is also what the Chrome breakpad client does.
+  guid=$(tr -d '-' < "${CONSENT_ID}")
+
+  local error_type="$(get_key_value "${meta_path}" "error_type")"
+  [ "${error_type}" = "undefined" ] && error_type=
+
+  lecho "Sending crash:"
+  if [ "${product}" != "${CHROMEOS_PRODUCT}" ]; then
+    lecho "  Sending crash report on behalf of ${product}"
+  fi
+  lecho "  Metadata: ${meta_path} (${kind})"
+  lecho "  Payload: ${report_payload}"
+  lecho "  Version: ${version}"
+  [ -n "${image_type}" ] && lecho "  Image type: ${image_type}"
+  [ -n "${boot_mode}" ] && lecho "  Boot mode: ${boot_mode}"
+  if is_mock; then
+    lecho "  Product: ${product}"
+    lecho "  URL: ${url}"
+    lecho "  Board: ${board}"
+    lecho "  HWClass: ${hwclass}"
+    lecho "  write_payload_size: ${write_payload_size}"
+    lecho "  send_payload_size: ${send_payload_size}"
+    if [ "${log}" != "undefined" ]; then
+      lecho "  log: @${log}"
+    fi
+    if [ "${sig}" != "undefined" ]; then
+      lecho "  sig: ${sig}"
+    fi
+  fi
+  lecho "  Exec name: ${exec_name}"
+  [ -n "${error_type}" ] && lecho "  Error type: ${error_type}"
+  if is_mock; then
+    if ! is_mock_successful; then
+      lecho "Mocking unsuccessful send"
+      return 1
+    fi
+    lecho "Mocking successful send"
+    return 0
+  fi
+
+  # Read in the first proxy, if any, for a given URL.  NOTE: The
+  # double-quotes are necessary due to a bug in dash with the "local"
+  # builtin command and values that have spaces in them (see
+  # "https://bugs.launchpad.net/ubuntu/+source/dash/+bug/139097").
+  if [ -f "${LIST_PROXIES}" ]; then
+    local proxy ret
+    proxy=$("${LIST_PROXIES}" --quiet "${url}")
+    ret=$?
+    if [ ${ret} -ne 0 ]; then
+      proxy=''
+      lecho -psyslog.warn \
+        "Listing proxies failed with exit code ${ret}"
+    else
+      proxy=$(echo "${proxy}" | head -1)
+    fi
+  fi
+  # if a direct connection should be used, unset the proxy variable.
+  [ "${proxy}" = "direct://" ] && proxy=
+  local report_id="${TMP_DIR}/report_id"
+  local curl_stderr="${TMP_DIR}/curl_stderr"
+
+  set +e
+  curl "${url}" -v ${proxy:+--proxy "$proxy"} \
+    --capath "${RESTRICTED_CERTIFICATES_PATH}" --ciphers HIGH \
+    -F "prod=${product}" \
+    -F "ver=${version}" \
+    -F "board=${board}" \
+    -F "hwclass=${hwclass}" \
+    -F "exec_name=${exec_name}" \
+    ${image_type:+-F "image_type=${image_type}"} \
+    ${boot_mode:+-F "boot_mode=${boot_mode}"} \
+    ${error_type:+-F "error_type=${error_type}"} \
+    -F "guid=${guid}" \
+    -o "${report_id}" \
+    "$@" \
+    2>"${curl_stderr}"
+  curl_result=$?
+  set -e
+
+  if [ ${curl_result} -eq 0 ]; then
+    local id="$(cat "${report_id}")"
+    local product_name
+    local timestamp="$(date +%s)"
+    case ${product} in
+    Chrome_ChromeOS)
+      if is_official_image; then
+        product_name="Chrome"
+      else
+        product_name="Chromium"
+      fi
+      ;;
+    *)
+      if is_official_image; then
+        product_name="ChromeOS"
+      else
+        product_name="ChromiumOS"
+      fi
+      ;;
+    esac
+    printf '%s,%s,%s\n' \
+      "${timestamp}" "${id}" "${product_name}" >> "${CHROME_CRASH_LOG}"
+    lecho "Crash report receipt ID ${id}"
+  else
+    lecho "Crash sending failed with exit code ${curl_result}: " \
+      "$(cat "${curl_stderr}")"
+  fi
+
+  rm -f "${report_id}"
+
+  return ${curl_result}
+}
+
+# *.meta files always end with done=1 so we can tell if they are complete.
+is_complete_metadata() {
+  grep -q "done=1" "$1"
+}
+
+# Remove the given report path.
+remove_report() {
+  local base="${1%.*}"
+  rm -f -- "${base}".*
+}
+
+# Send all crashes from the given directory.  This applies even when we're on a
+# 3G connection (see crosbug.com/3304 for discussion).
+send_crashes() {
+  local dir="$1"
+
+  if [ ! -d "${dir}" ]; then
+    return
+  fi
+
+  # Consider any old files which still have no corresponding meta file
+  # as orphaned, and remove them.
+  for old_file in $(${FIND} "${dir}" -mindepth 1 \
+                    -mmin +$((24 * 60)) -type f); do
+    if [ ! -e "$(get_base "${old_file}").meta" ]; then
+      lecho "Removing old orphaned file: ${old_file}."
+      rm -f -- "${old_file}"
+    fi
+  done
+
+  # Look through all metadata (*.meta) files, oldest first.  That way, the rate
+  # limit does not stall old crashes if there's a high amount of new crashes
+  # coming in.
+  # For each crash report, first evaluate conditions that might lead to its
+  # removal to honor user choice and to free disk space as soon as possible,
+  # then decide whether it should be sent right now or kept for later sending.
+  for meta_path in $(ls -1tr "${dir}"/*.meta 2>/dev/null); do
+    lecho "Considering metadata ${meta_path}."
+
+    local kind=$(get_kind "${meta_path}")
+    if [ "${kind}" != "minidump" ] && \
+       [ "${kind}" != "kcrash" ] && \
+       [ "${kind}" != "log" ] &&
+       [ "${kind}" != "devcore" ]; then
+      lecho "Unknown report kind ${kind}.  Removing report."
+      remove_report "${meta_path}"
+      continue
+    fi
+
+    if ! is_complete_metadata "${meta_path}"; then
+      # This report is incomplete, so if it's old, just remove it.
+      local old_meta=$(${FIND} "${dir}" -mindepth 1 -name \
+        $(basename "${meta_path}") -mmin +$((24 * 60)) -type f)
+      if [ -n "${old_meta}" ]; then
+        lecho "Removing old incomplete metadata."
+        remove_report "${meta_path}"
+      else
+        lecho "Ignoring recent incomplete metadata."
+      fi
+      continue
+    fi
+
+    # Ignore device coredump if device coredump uploading is not allowed.
+    if [ "${kind}" = "devcore" ] && ! is_device_coredump_upload_allowed; then
+      lecho "Ignoring device coredump. Device coredump upload not allowed."
+      continue
+    fi
+
+    if ! is_mock && ! is_official_image; then
+      lecho "Not an official OS version.  Removing crash."
+      remove_report "${meta_path}"
+      continue
+    fi
+
+    # Don't send crash reports from previous sessions while we're in guest mode
+    # to avoid the impression that crash reporting was enabled, which it isn't.
+    # (Don't exit right now because subsequent reports may be candidates for
+    # deletion.)
+    if ${METRICS_CLIENT} -g; then
+      lecho "Guest mode has been entered.  Delaying crash sending until exited."
+      continue
+    fi
+
+    # Remove existing crashes in case user consent has not (yet) been given or
+    # has been revoked.  This must come after the guest mode check because
+    # ${METRICS_CLIENT} always returns "not consented" in guest mode.
+    if ! ${METRICS_CLIENT} -c; then
+      lecho "Crash reporting is disabled.  Removing crash."
+      remove_report "${meta_path}"
+      continue
+    fi
+
+    # Skip report if the upload rate is exceeded.  (Don't exit right now because
+    # subsequent reports may be candidates for deletion.)
+    if ! check_rate; then
+      lecho "Sending ${meta_path} would exceed rate.  Leaving for later."
+      continue
+    fi
+
+    # The .meta file should be written *after* all to-be-uploaded files that it
+    # references.  Nevertheless, as a safeguard, a hold-off time of thirty
+    # seconds after writing the .meta file is ensured.  Also, sending of crash
+    # reports is spread out randomly by up to SECONDS_SEND_SPREAD.  Thus, for
+    # the sleep call the greater of the two delays is used.
+    local now=$(date +%s)
+    local holdoff_time=$(($(stat --format=%Y "${meta_path}") + 30 - ${now}))
+    local spread_time=$(generate_uniform_random "${SECONDS_SEND_SPREAD}")
+    local sleep_time
+    if [ ${spread_time} -gt ${holdoff_time} ]; then
+      sleep_time="${spread_time}"
+    else
+      sleep_time="${holdoff_time}"
+    fi
+    lecho "Scheduled to send in ${sleep_time}s."
+    if ! is_mock; then
+      if ! sleep "${sleep_time}"; then
+          lecho "Sleep failed"
+          return 1
+      fi
+    fi
+
+    # Try to upload.
+    if ! send_crash "${meta_path}"; then
+      lecho "Problem sending ${meta_path}, not removing."
+      continue
+    fi
+
+    # Send was successful, now remove.
+    lecho "Successfully sent crash ${meta_path} and removing."
+    remove_report "${meta_path}"
+  done
+}
+
+usage() {
+  cat <<EOF
+Usage: crash_sender [options]
+
+Options:
+ -e <var>=<val>     Set env |var| to |val| (only some vars)
+EOF
+  exit ${1:-1}
+}
+
+parseargs() {
+  # Parse the command line arguments.
+  while [ $# -gt 0 ]; do
+    case $1 in
+    -e)
+      shift
+      case $1 in
+      FORCE_OFFICIAL=*|\
+      MAX_CRASH_RATE=*|\
+      MOCK_DEVELOPER_MODE=*|\
+      OVERRIDE_PAUSE_SENDING=*|\
+      SECONDS_SEND_SPREAD=*)
+        export "$1"
+        ;;
+      *)
+        lecho "Unknown var passed to -e: $1"
+        exit 1
+        ;;
+      esac
+      ;;
+    -h)
+      usage 0
+      ;;
+    *)
+      lecho "Unknown options: $*"
+      exit 1
+      ;;
+    esac
+    shift
+  done
+}
+
+main() {
+  trap cleanup EXIT INT TERM
+
+  parseargs "$@"
+
+  if [ -e "${PAUSE_CRASH_SENDING}" ] && \
+     [ ${OVERRIDE_PAUSE_SENDING} -eq 0 ]; then
+    lecho "Exiting early due to ${PAUSE_CRASH_SENDING}."
+    exit 1
+  fi
+
+  if is_test_image; then
+    lecho "Exiting early due to test image."
+    exit 1
+  fi
+
+  # We don't perform checks on this because we have a master lock with the
+  # CRASH_SENDER_LOCK file.  This pid file is for the system to keep track
+  # (like with autotests) that we're still running.
+  echo $$ > "${RUN_FILE}"
+
+  for dependency in "${FIND}" "${METRICS_CLIENT}" \
+                    "${RESTRICTED_CERTIFICATES_PATH}"; do
+    if [ ! -x "${dependency}" ]; then
+      lecho "Fatal: Crash sending disabled: ${dependency} not found."
+      exit 1
+    fi
+  done
+
+  TMP_DIR="$(mktemp -d /tmp/crash_sender.XXXXXX)"
+
+  # Send system-wide crashes
+  send_crashes "/var/spool/crash"
+
+  # Send user-specific crashes
+  local d
+  for d in /home/chronos/crash /home/chronos/u-*/crash; do
+    send_crashes "${d}"
+  done
+}
+
+(
+if ! flock -n 9; then
+  lecho "Already running; quitting."
+  crash_done
+  exit 1
+fi
+main "$@"
+) 9>"${CRASH_SENDER_LOCK}"
diff --git a/crash_reporter/dbus_bindings/org.chromium.LibCrosService.xml b/crash_reporter/dbus_bindings/org.chromium.LibCrosService.xml
new file mode 100644
index 0000000..64b8b84
--- /dev/null
+++ b/crash_reporter/dbus_bindings/org.chromium.LibCrosService.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<node name="/org/chromium/LibCrosService"
+      xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0">
+  <interface name="org.chromium.LibCrosServiceInterface">
+    <method name="ResolveNetworkProxy">
+      <arg name="source_url" type="s" direction="in"/>
+      <arg name="signal_interface" type="s" direction="in"/>
+      <arg name="signal_name" type="s" direction="in"/>
+      <annotation name="org.chromium.DBus.Method.Kind" value="simple"/>
+    </method>
+  </interface>
+  <interface name="org.chromium.CrashReporterLibcrosProxyResolvedInterface">
+    <signal name="ProxyResolved">
+      <arg name="source_url" type="s" direction="out"/>
+      <arg name="proxy_info" type="s" direction="out"/>
+      <arg name="error_message" type="s" direction="out"/>
+    </signal>
+  </interface>
+</node>
diff --git a/crash_reporter/init.crash_reporter.rc b/crash_reporter/init.crash_reporter.rc
new file mode 100644
index 0000000..6882b77
--- /dev/null
+++ b/crash_reporter/init.crash_reporter.rc
@@ -0,0 +1,18 @@
+on property:crash_reporter.coredump.enabled=1
+    write /proc/sys/kernel/core_pattern \
+          "|/system/bin/crash_reporter --user=%P:%s:%u:%e"
+
+on property:crash_reporter.coredump.enabled=0
+    write /proc/sys/kernel/core_pattern "core"
+
+on boot
+    # Allow catching multiple unrelated concurrent crashes, but use a finite
+    # number to prevent infinitely recursing on crash handling.
+    write /proc/sys/kernel/core_pipe_limit 4
+
+    # Create crash directories.
+    mkdir /data/misc/crash_reporter 0700 root root
+
+service crash_reporter /system/bin/crash_reporter --init
+    class late_start
+    oneshot
diff --git a/crash_reporter/init/crash-reporter.conf b/crash_reporter/init/crash-reporter.conf
new file mode 100644
index 0000000..19f2cdb
--- /dev/null
+++ b/crash_reporter/init/crash-reporter.conf
@@ -0,0 +1,27 @@
+# Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+description     "Initialize crash reporting services"
+author          "chromium-os-dev@chromium.org"
+
+# This job merely initializes its service and then terminates; the
+# actual checking and reporting of crash dumps is triggered by an
+# hourly cron job.
+start on starting system-services
+
+pre-start script
+  mkdir -p /var/spool
+
+  # Only allow device coredumps on a "developer system".
+  if ! is_developer_end_user; then
+    # consumer end-user - disable device coredumps, if driver exists.
+    echo 1 > /sys/class/devcoredump/disabled || true
+  fi
+end script
+
+# crash_reporter uses argv[0] as part of the command line for
+# /proc/sys/kernel/core_pattern.  That command line is invoked by
+# the kernel, and can't rely on PATH, so argv[0] must be a full
+# path; we invoke it as such here.
+exec /sbin/crash_reporter --init
diff --git a/crash_reporter/init/crash-sender.conf b/crash_reporter/init/crash-sender.conf
new file mode 100644
index 0000000..892186f
--- /dev/null
+++ b/crash_reporter/init/crash-sender.conf
@@ -0,0 +1,11 @@
+# Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+description     "Run the crash sender periodically"
+author          "chromium-os-dev@chromium.org"
+
+start on starting system-services
+stop on stopping system-services
+
+exec periodic_scheduler 3600 14400 crash_sender /sbin/crash_sender
diff --git a/crash_reporter/init/warn-collector.conf b/crash_reporter/init/warn-collector.conf
new file mode 100644
index 0000000..3be80da
--- /dev/null
+++ b/crash_reporter/init/warn-collector.conf
@@ -0,0 +1,12 @@
+# Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+description "Runs a daemon which collects and reports kernel warnings"
+author      "chromium-os-dev@chromium.org"
+
+start on started system-services
+stop on stopping system-services
+respawn
+
+exec warn_collector
diff --git a/crash_reporter/kernel_collector.cc b/crash_reporter/kernel_collector.cc
new file mode 100644
index 0000000..12b00b9
--- /dev/null
+++ b/crash_reporter/kernel_collector.cc
@@ -0,0 +1,603 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernel_collector.h"
+
+#include <map>
+#include <sys/stat.h>
+
+#include <base/files/file_util.h>
+#include <base/logging.h>
+#include <base/strings/string_util.h>
+#include <base/strings/stringprintf.h>
+
+using base::FilePath;
+using base::StringPrintf;
+
+namespace {
+
+const char kDefaultKernelStackSignature[] = "kernel-UnspecifiedStackSignature";
+const char kDumpParentPath[] = "/dev";
+const char kDumpPath[] = "/dev/pstore";
+const char kDumpFormat[] = "dmesg-ramoops-%zu";
+const char kKernelExecName[] = "kernel";
+// Maximum number of records to examine in the kDumpPath.
+const size_t kMaxDumpRecords = 100;
+const pid_t kKernelPid = 0;
+const char kKernelSignatureKey[] = "sig";
+// Byte length of maximum human readable portion of a kernel crash signature.
+const int kMaxHumanStringLength = 40;
+const uid_t kRootUid = 0;
+// Time in seconds from the final kernel log message for a call stack
+// to count towards the signature of the kcrash.
+const int kSignatureTimestampWindow = 2;
+// Kernel log timestamp regular expression.
+const char kTimestampRegex[] = "^<.*>\\[\\s*(\\d+\\.\\d+)\\]";
+
+//
+// These regular expressions enable to us capture the PC in a backtrace.
+// The backtrace is obtained through dmesg or the kernel's preserved/kcrashmem
+// feature.
+//
+// For ARM we see:
+//   "<5>[   39.458982] PC is at write_breakme+0xd0/0x1b4"
+// For MIPS we see:
+//   "<5>[ 3378.552000] epc   : 804010f0 lkdtm_do_action+0x68/0x3f8"
+// For x86:
+//   "<0>[   37.474699] EIP: [<790ed488>] write_breakme+0x80/0x108
+//    SS:ESP 0068:e9dd3efc"
+//
+const char* const kPCRegex[] = {
+  0,
+  " PC is at ([^\\+ ]+).*",
+  " epc\\s+:\\s+\\S+\\s+([^\\+ ]+).*",  // MIPS has an exception program counter
+  " EIP: \\[<.*>\\] ([^\\+ ]+).*",  // X86 uses EIP for the program counter
+  " RIP  \\[<.*>\\] ([^\\+ ]+).*",  // X86_64 uses RIP for the program counter
+};
+
+COMPILE_ASSERT(arraysize(kPCRegex) == KernelCollector::kArchCount,
+               missing_arch_pc_regexp);
+
+}  // namespace
+
+KernelCollector::KernelCollector()
+    : is_enabled_(false),
+      ramoops_dump_path_(kDumpPath),
+      records_(0),
+      // We expect crash dumps in the format of architecture we are built for.
+      arch_(GetCompilerArch()) {
+}
+
+KernelCollector::~KernelCollector() {
+}
+
+void KernelCollector::OverridePreservedDumpPath(const FilePath &file_path) {
+  ramoops_dump_path_ = file_path;
+}
+
+bool KernelCollector::ReadRecordToString(std::string *contents,
+                                         size_t current_record,
+                                         bool *record_found) {
+  // A record is a ramoops dump. It has an associated size of "record_size".
+  std::string record;
+  std::string captured;
+
+  // Ramoops appends a header to a crash which contains ==== followed by a
+  // timestamp. Ignore the header.
+  pcrecpp::RE record_re(
+      "====\\d+\\.\\d+\n(.*)",
+      pcrecpp::RE_Options().set_multiline(true).set_dotall(true));
+
+  pcrecpp::RE sanity_check_re("\n<\\d+>\\[\\s*(\\d+\\.\\d+)\\]");
+
+  FilePath ramoops_record;
+  GetRamoopsRecordPath(&ramoops_record, current_record);
+  if (!base::ReadFileToString(ramoops_record, &record)) {
+    LOG(ERROR) << "Unable to open " << ramoops_record.value();
+    return false;
+  }
+
+  *record_found = false;
+  if (record_re.FullMatch(record, &captured)) {
+    // Found a ramoops header, so strip the header and append the rest.
+    contents->append(captured);
+    *record_found = true;
+  } else if (sanity_check_re.PartialMatch(record.substr(0, 1024))) {
+    // pstore compression has been added since kernel 3.12. In order to
+    // decompress dmesg correctly, ramoops driver has to strip the header
+    // before handing over the record to the pstore driver, so we don't
+    // need to do it here anymore. However, the sanity check is needed because
+    // sometimes a pstore record is just a chunk of uninitialized memory which
+    // is not the result of a kernel crash. See crbug.com/443764
+    contents->append(record);
+    *record_found = true;
+  } else {
+    LOG(WARNING) << "Found invalid record at " << ramoops_record.value();
+  }
+
+  // Remove the record from pstore after it's found.
+  if (*record_found)
+    base::DeleteFile(ramoops_record, false);
+
+  return true;
+}
+
+void KernelCollector::GetRamoopsRecordPath(FilePath *path,
+                                           size_t record) {
+  // Disable error "format not a string literal, argument types not checked"
+  // because this is valid, but GNU apparently doesn't bother checking a const
+  // format string.
+  #pragma GCC diagnostic push
+  #pragma GCC diagnostic ignored "-Wformat-nonliteral"
+  *path = ramoops_dump_path_.Append(StringPrintf(kDumpFormat, record));
+  #pragma GCC diagnostic pop
+}
+
+bool KernelCollector::LoadParameters() {
+  // Discover how many ramoops records are being exported by the driver.
+  size_t count;
+
+  for (count = 0; count < kMaxDumpRecords; ++count) {
+    FilePath ramoops_record;
+    GetRamoopsRecordPath(&ramoops_record, count);
+
+    if (!base::PathExists(ramoops_record))
+      break;
+  }
+
+  records_ = count;
+  return (records_ > 0);
+}
+
+bool KernelCollector::LoadPreservedDump(std::string *contents) {
+  // Load dumps from the preserved memory and save them in contents.
+  // Since the system is set to restart on oops we won't actually ever have
+  // multiple records (only 0 or 1), but check in case we don't restart on
+  // oops in the future.
+  bool any_records_found = false;
+  bool record_found = false;
+  // clear contents since ReadFileToString actually appends to the string.
+  contents->clear();
+
+  for (size_t i = 0; i < records_; ++i) {
+    if (!ReadRecordToString(contents, i, &record_found)) {
+      break;
+    }
+    if (record_found) {
+      any_records_found = true;
+    }
+  }
+
+  if (!any_records_found) {
+    LOG(ERROR) << "No valid records found in " << ramoops_dump_path_.value();
+    return false;
+  }
+
+  return true;
+}
+
+void KernelCollector::StripSensitiveData(std::string *kernel_dump) {
+  // Strip any data that the user might not want sent up to the crash servers.
+  // We'll read in from kernel_dump and also place our output there.
+  //
+  // At the moment, the only sensitive data we strip is MAC addresses.
+
+  // Get rid of things that look like MAC addresses, since they could possibly
+  // give information about where someone has been.  This is strings that look
+  // like this: 11:22:33:44:55:66
+  // Complications:
+  // - Within a given kernel_dump, want to be able to tell when the same MAC
+  //   was used more than once.  Thus, we'll consistently replace the first
+  //   MAC found with 00:00:00:00:00:01, the second with ...:02, etc.
+  // - ACPI commands look like MAC addresses.  We'll specifically avoid getting
+  //   rid of those.
+  std::ostringstream result;
+  std::string pre_mac_str;
+  std::string mac_str;
+  std::map<std::string, std::string> mac_map;
+  pcrecpp::StringPiece input(*kernel_dump);
+
+  // This RE will find the next MAC address and can return us the data preceding
+  // the MAC and the MAC itself.
+  pcrecpp::RE mac_re("(.*?)("
+                     "[0-9a-fA-F][0-9a-fA-F]:"
+                     "[0-9a-fA-F][0-9a-fA-F]:"
+                     "[0-9a-fA-F][0-9a-fA-F]:"
+                     "[0-9a-fA-F][0-9a-fA-F]:"
+                     "[0-9a-fA-F][0-9a-fA-F]:"
+                     "[0-9a-fA-F][0-9a-fA-F])",
+                     pcrecpp::RE_Options()
+                       .set_multiline(true)
+                       .set_dotall(true));
+
+  // This RE will identify when the 'pre_mac_str' shows that the MAC address
+  // was really an ACPI cmd.  The full string looks like this:
+  //   ata1.00: ACPI cmd ef/10:03:00:00:00:a0 (SET FEATURES) filtered out
+  pcrecpp::RE acpi_re("ACPI cmd ef/$",
+                      pcrecpp::RE_Options()
+                        .set_multiline(true)
+                        .set_dotall(true));
+
+  // Keep consuming, building up a result string as we go.
+  while (mac_re.Consume(&input, &pre_mac_str, &mac_str)) {
+    if (acpi_re.PartialMatch(pre_mac_str)) {
+      // We really saw an ACPI command; add to result w/ no stripping.
+      result << pre_mac_str << mac_str;
+    } else {
+      // Found a MAC address; look up in our hash for the mapping.
+      std::string replacement_mac = mac_map[mac_str];
+      if (replacement_mac == "") {
+        // It wasn't present, so build up a replacement string.
+        int mac_id = mac_map.size();
+
+        // Handle up to 2^32 unique MAC address; overkill, but doesn't hurt.
+        replacement_mac = StringPrintf("00:00:%02x:%02x:%02x:%02x",
+                                       (mac_id & 0xff000000) >> 24,
+                                       (mac_id & 0x00ff0000) >> 16,
+                                       (mac_id & 0x0000ff00) >> 8,
+                                       (mac_id & 0x000000ff));
+        mac_map[mac_str] = replacement_mac;
+      }
+
+      // Dump the string before the MAC and the fake MAC address into result.
+      result << pre_mac_str << replacement_mac;
+    }
+  }
+
+  // One last bit of data might still be in the input.
+  result << input;
+
+  // We'll just assign right back to kernel_dump.
+  *kernel_dump = result.str();
+}
+
+bool KernelCollector::DumpDirMounted() {
+  struct stat st_parent;
+  if (stat(kDumpParentPath, &st_parent)) {
+    PLOG(WARNING) << "Could not stat " << kDumpParentPath;
+    return false;
+  }
+
+  struct stat st_dump;
+  if (stat(kDumpPath, &st_dump)) {
+    PLOG(WARNING) << "Could not stat " << kDumpPath;
+    return false;
+  }
+
+  if (st_parent.st_dev == st_dump.st_dev) {
+    LOG(WARNING) << "Dump dir " << kDumpPath << " not mounted";
+    return false;
+  }
+
+  return true;
+}
+
+bool KernelCollector::Enable() {
+  if (arch_ == kArchUnknown || arch_ >= kArchCount ||
+      kPCRegex[arch_] == nullptr) {
+    LOG(WARNING) << "KernelCollector does not understand this architecture";
+    return false;
+  }
+
+  if (!DumpDirMounted()) {
+    LOG(WARNING) << "Kernel does not support crash dumping";
+    return false;
+  }
+
+  // To enable crashes, we will eventually need to set
+  // the chnv bit in BIOS, but it does not yet work.
+  LOG(INFO) << "Enabling kernel crash handling";
+  is_enabled_ = true;
+  return true;
+}
+
+// Hash a string to a number.  We define our own hash function to not
+// be dependent on a C++ library that might change.  This function
+// uses basically the same approach as tr1/functional_hash.h but with
+// a larger prime number (16127 vs 131).
+static unsigned HashString(const std::string &input) {
+  unsigned hash = 0;
+  for (size_t i = 0; i < input.length(); ++i)
+    hash = hash * 16127 + input[i];
+  return hash;
+}
+
+void KernelCollector::ProcessStackTrace(
+    pcrecpp::StringPiece kernel_dump,
+    bool print_diagnostics,
+    unsigned *hash,
+    float *last_stack_timestamp,
+    bool *is_watchdog_crash) {
+  pcrecpp::RE line_re("(.+)", pcrecpp::MULTILINE());
+  pcrecpp::RE stack_trace_start_re(std::string(kTimestampRegex) +
+        " (Call Trace|Backtrace):$");
+
+  // Match lines such as the following and grab out "function_name".
+  // The ? may or may not be present.
+  //
+  // For ARM:
+  // <4>[ 3498.731164] [<c0057220>] ? (function_name+0x20/0x2c) from
+  // [<c018062c>] (foo_bar+0xdc/0x1bc)
+  //
+  // For MIPS:
+  // <5>[ 3378.656000] [<804010f0>] lkdtm_do_action+0x68/0x3f8
+  //
+  // For X86:
+  // <4>[ 6066.849504]  [<7937bcee>] ? function_name+0x66/0x6c
+  //
+  pcrecpp::RE stack_entry_re(std::string(kTimestampRegex) +
+    "\\s+\\[<[[:xdigit:]]+>\\]"      // Matches "  [<7937bcee>]"
+    "([\\s\\?(]+)"                   // Matches " ? (" (ARM) or " ? " (X86)
+    "([^\\+ )]+)");                  // Matches until delimiter reached
+  std::string line;
+  std::string hashable;
+  std::string previous_hashable;
+  bool is_watchdog = false;
+
+  *hash = 0;
+  *last_stack_timestamp = 0;
+
+  // Find the last and second-to-last stack traces.  The latter is used when
+  // the panic is from a watchdog timeout.
+  while (line_re.FindAndConsume(&kernel_dump, &line)) {
+    std::string certainty;
+    std::string function_name;
+    if (stack_trace_start_re.PartialMatch(line, last_stack_timestamp)) {
+      if (print_diagnostics) {
+        printf("Stack trace starting.%s\n",
+               hashable.empty() ? "" : "  Saving prior trace.");
+      }
+      previous_hashable = hashable;
+      hashable.clear();
+      is_watchdog = false;
+    } else if (stack_entry_re.PartialMatch(line,
+                                           last_stack_timestamp,
+                                           &certainty,
+                                           &function_name)) {
+      bool is_certain = certainty.find('?') == std::string::npos;
+      if (print_diagnostics) {
+        printf("@%f: stack entry for %s (%s)\n",
+               *last_stack_timestamp,
+               function_name.c_str(),
+               is_certain ? "certain" : "uncertain");
+      }
+      // Do not include any uncertain (prefixed by '?') frames in our hash.
+      if (!is_certain)
+        continue;
+      if (!hashable.empty())
+        hashable.append("|");
+      if (function_name == "watchdog_timer_fn" ||
+          function_name == "watchdog") {
+        is_watchdog = true;
+      }
+      hashable.append(function_name);
+    }
+  }
+
+  // If the last stack trace contains a watchdog function we assume the panic
+  // is from the watchdog timer, and we hash the previous stack trace rather
+  // than the last one, assuming that the previous stack is that of the hung
+  // thread.
+  //
+  // In addition, if the hashable is empty (meaning all frames are uncertain,
+  // for whatever reason) also use the previous frame, as it cannot be any
+  // worse.
+  if (is_watchdog || hashable.empty()) {
+    hashable = previous_hashable;
+  }
+
+  *hash = HashString(hashable);
+  *is_watchdog_crash = is_watchdog;
+
+  if (print_diagnostics) {
+    printf("Hash based on stack trace: \"%s\" at %f.\n",
+           hashable.c_str(), *last_stack_timestamp);
+  }
+}
+
+// static
+KernelCollector::ArchKind KernelCollector::GetCompilerArch() {
+#if defined(COMPILER_GCC) && defined(ARCH_CPU_ARM_FAMILY)
+  return kArchArm;
+#elif defined(COMPILER_GCC) && defined(ARCH_CPU_MIPS_FAMILY)
+  return kArchMips;
+#elif defined(COMPILER_GCC) && defined(ARCH_CPU_X86_64)
+  return kArchX86_64;
+#elif defined(COMPILER_GCC) && defined(ARCH_CPU_X86_FAMILY)
+  return kArchX86;
+#else
+  return kArchUnknown;
+#endif
+}
+
+bool KernelCollector::FindCrashingFunction(
+  pcrecpp::StringPiece kernel_dump,
+  bool print_diagnostics,
+  float stack_trace_timestamp,
+  std::string *crashing_function) {
+  float timestamp = 0;
+
+  // Use the correct regex for this architecture.
+  pcrecpp::RE eip_re(std::string(kTimestampRegex) + kPCRegex[arch_],
+                     pcrecpp::MULTILINE());
+
+  while (eip_re.FindAndConsume(&kernel_dump, &timestamp, crashing_function)) {
+    if (print_diagnostics) {
+      printf("@%f: found crashing function %s\n",
+             timestamp,
+             crashing_function->c_str());
+    }
+  }
+  if (timestamp == 0) {
+    if (print_diagnostics) {
+      printf("Found no crashing function.\n");
+    }
+    return false;
+  }
+  if (stack_trace_timestamp != 0 &&
+      abs(static_cast<int>(stack_trace_timestamp - timestamp))
+        > kSignatureTimestampWindow) {
+    if (print_diagnostics) {
+      printf("Found crashing function but not within window.\n");
+    }
+    return false;
+  }
+  if (print_diagnostics) {
+    printf("Found crashing function %s\n", crashing_function->c_str());
+  }
+  return true;
+}
+
+bool KernelCollector::FindPanicMessage(pcrecpp::StringPiece kernel_dump,
+                                       bool print_diagnostics,
+                                       std::string *panic_message) {
+  // Match lines such as the following and grab out "Fatal exception"
+  // <0>[  342.841135] Kernel panic - not syncing: Fatal exception
+  pcrecpp::RE kernel_panic_re(std::string(kTimestampRegex) +
+                              " Kernel panic[^\\:]*\\:\\s*(.*)",
+                              pcrecpp::MULTILINE());
+  float timestamp = 0;
+  while (kernel_panic_re.FindAndConsume(&kernel_dump,
+                                        &timestamp,
+                                        panic_message)) {
+    if (print_diagnostics) {
+      printf("@%f: panic message %s\n",
+             timestamp,
+             panic_message->c_str());
+    }
+  }
+  if (timestamp == 0) {
+    if (print_diagnostics) {
+      printf("Found no panic message.\n");
+    }
+    return false;
+  }
+  return true;
+}
+
+bool KernelCollector::ComputeKernelStackSignature(
+    const std::string &kernel_dump,
+    std::string *kernel_signature,
+    bool print_diagnostics) {
+  unsigned stack_hash = 0;
+  float last_stack_timestamp = 0;
+  std::string human_string;
+  bool is_watchdog_crash;
+
+  ProcessStackTrace(kernel_dump,
+                    print_diagnostics,
+                    &stack_hash,
+                    &last_stack_timestamp,
+                    &is_watchdog_crash);
+
+  if (!FindCrashingFunction(kernel_dump,
+                            print_diagnostics,
+                            last_stack_timestamp,
+                            &human_string)) {
+    if (!FindPanicMessage(kernel_dump, print_diagnostics, &human_string)) {
+      if (print_diagnostics) {
+        printf("Found no human readable string, using empty string.\n");
+      }
+      human_string.clear();
+    }
+  }
+
+  if (human_string.empty() && stack_hash == 0) {
+    if (print_diagnostics) {
+      printf("Found neither a stack nor a human readable string, failing.\n");
+    }
+    return false;
+  }
+
+  human_string = human_string.substr(0, kMaxHumanStringLength);
+  *kernel_signature = StringPrintf("%s-%s%s-%08X",
+                                   kKernelExecName,
+                                   (is_watchdog_crash ? "(HANG)-" : ""),
+                                   human_string.c_str(),
+                                   stack_hash);
+  return true;
+}
+
+bool KernelCollector::Collect() {
+  std::string kernel_dump;
+  FilePath root_crash_directory;
+
+  if (!LoadParameters()) {
+    return false;
+  }
+  if (!LoadPreservedDump(&kernel_dump)) {
+    return false;
+  }
+  StripSensitiveData(&kernel_dump);
+  if (kernel_dump.empty()) {
+    return false;
+  }
+  std::string signature;
+  if (!ComputeKernelStackSignature(kernel_dump, &signature, false)) {
+    signature = kDefaultKernelStackSignature;
+  }
+
+  std::string reason = "handling";
+  bool feedback = true;
+  if (IsDeveloperImage()) {
+    reason = "developer build - always dumping";
+    feedback = true;
+  } else if (!is_feedback_allowed_function_()) {
+    reason = "ignoring - no consent";
+    feedback = false;
+  }
+
+  LOG(INFO) << "Received prior crash notification from "
+            << "kernel (signature " << signature << ") (" << reason << ")";
+
+  if (feedback) {
+    count_crash_function_();
+
+    if (!GetCreatedCrashDirectoryByEuid(kRootUid,
+                                        &root_crash_directory,
+                                        nullptr)) {
+      return true;
+    }
+
+    std::string dump_basename =
+        FormatDumpBasename(kKernelExecName, time(nullptr), kKernelPid);
+    FilePath kernel_crash_path = root_crash_directory.Append(
+        StringPrintf("%s.kcrash", dump_basename.c_str()));
+
+    // We must use WriteNewFile instead of base::WriteFile as we
+    // do not want to write with root access to a symlink that an attacker
+    // might have created.
+    if (WriteNewFile(kernel_crash_path,
+                     kernel_dump.data(),
+                     kernel_dump.length()) !=
+        static_cast<int>(kernel_dump.length())) {
+      LOG(INFO) << "Failed to write kernel dump to "
+                << kernel_crash_path.value().c_str();
+      return true;
+    }
+
+    AddCrashMetaData(kKernelSignatureKey, signature);
+    WriteCrashMetaData(
+        root_crash_directory.Append(
+            StringPrintf("%s.meta", dump_basename.c_str())),
+        kKernelExecName,
+        kernel_crash_path.value());
+
+    LOG(INFO) << "Stored kcrash to " << kernel_crash_path.value();
+  }
+
+  return true;
+}
diff --git a/crash_reporter/kernel_collector.h b/crash_reporter/kernel_collector.h
new file mode 100644
index 0000000..206ee26
--- /dev/null
+++ b/crash_reporter/kernel_collector.h
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CRASH_REPORTER_KERNEL_COLLECTOR_H_
+#define CRASH_REPORTER_KERNEL_COLLECTOR_H_
+
+#include <pcrecpp.h>
+
+#include <string>
+
+#include <base/files/file_path.h>
+#include <base/macros.h>
+#include <gtest/gtest_prod.h>  // for FRIEND_TEST
+
+#include "crash_collector.h"
+
+// Kernel crash collector.
+class KernelCollector : public CrashCollector {
+ public:
+  // Enumeration to specify architecture type.
+  enum ArchKind {
+    kArchUnknown,
+    kArchArm,
+    kArchMips,
+    kArchX86,
+    kArchX86_64,
+
+    kArchCount  // Number of architectures.
+  };
+
+  KernelCollector();
+
+  ~KernelCollector() override;
+
+  void OverridePreservedDumpPath(const base::FilePath &file_path);
+
+  // Enable collection.
+  bool Enable();
+
+  // Returns true if the kernel collection currently enabled.
+  bool is_enabled() const { return is_enabled_; }
+
+  // Collect any preserved kernel crash dump. Returns true if there was
+  // a dump (even if there were problems storing the dump), false otherwise.
+  bool Collect();
+
+  // Compute a stack signature string from a kernel dump.
+  bool ComputeKernelStackSignature(const std::string &kernel_dump,
+                                   std::string *kernel_signature,
+                                   bool print_diagnostics);
+
+  // Set the architecture of the crash dumps we are looking at.
+  void set_arch(ArchKind arch) { arch_ = arch; }
+  ArchKind arch() const { return arch_; }
+
+ private:
+  friend class KernelCollectorTest;
+  FRIEND_TEST(KernelCollectorTest, LoadPreservedDump);
+  FRIEND_TEST(KernelCollectorTest, StripSensitiveDataBasic);
+  FRIEND_TEST(KernelCollectorTest, StripSensitiveDataBulk);
+  FRIEND_TEST(KernelCollectorTest, StripSensitiveDataSample);
+  FRIEND_TEST(KernelCollectorTest, CollectOK);
+
+  virtual bool DumpDirMounted();
+
+  bool LoadPreservedDump(std::string *contents);
+  void StripSensitiveData(std::string *kernel_dump);
+
+  void GetRamoopsRecordPath(base::FilePath *path, size_t record);
+  bool LoadParameters();
+  bool HasMoreRecords();
+
+  // Read a record to string, modified from file_utils since that didn't
+  // provide a way to restrict the read length.
+  // Return value indicates (only) error state:
+  //  * false when we get an error (can't read from dump location).
+  //  * true if no error occured.
+  // Not finding a valid record is not an error state and is signaled by the
+  // record_found output parameter.
+  bool ReadRecordToString(std::string *contents,
+                          size_t current_record,
+                          bool *record_found);
+
+  void ProcessStackTrace(pcrecpp::StringPiece kernel_dump,
+                         bool print_diagnostics,
+                         unsigned *hash,
+                         float *last_stack_timestamp,
+                         bool *is_watchdog_crash);
+  bool FindCrashingFunction(pcrecpp::StringPiece kernel_dump,
+                            bool print_diagnostics,
+                            float stack_trace_timestamp,
+                            std::string *crashing_function);
+  bool FindPanicMessage(pcrecpp::StringPiece kernel_dump,
+                        bool print_diagnostics,
+                        std::string *panic_message);
+
+  // Returns the architecture kind for which we are built.
+  static ArchKind GetCompilerArch();
+
+  bool is_enabled_;
+  base::FilePath ramoops_dump_path_;
+  size_t records_;
+
+  // The architecture of kernel dump strings we are working with.
+  ArchKind arch_;
+
+  DISALLOW_COPY_AND_ASSIGN(KernelCollector);
+};
+
+#endif  // CRASH_REPORTER_KERNEL_COLLECTOR_H_
diff --git a/crash_reporter/kernel_collector_test.cc b/crash_reporter/kernel_collector_test.cc
new file mode 100644
index 0000000..e690b77
--- /dev/null
+++ b/crash_reporter/kernel_collector_test.cc
@@ -0,0 +1,686 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernel_collector_test.h"
+
+#include <unistd.h>
+
+#include <base/files/file_util.h>
+#include <base/files/scoped_temp_dir.h>
+#include <base/strings/string_util.h>
+#include <base/strings/stringprintf.h>
+#include <chromeos/syslog_logging.h>
+#include <gtest/gtest.h>
+
+using base::FilePath;
+using base::StringPrintf;
+using chromeos::FindLog;
+using chromeos::GetLog;
+
+namespace {
+
+int s_crashes = 0;
+bool s_metrics = false;
+
+void CountCrash() {
+  ++s_crashes;
+}
+
+bool IsMetrics() {
+  return s_metrics;
+}
+
+}  // namespace
+
+class KernelCollectorTest : public ::testing::Test {
+ protected:
+  void WriteStringToFile(const FilePath &file_path,
+                         const char *data) {
+    ASSERT_EQ(strlen(data), base::WriteFile(file_path, data, strlen(data)));
+  }
+
+  void SetUpSuccessfulCollect();
+  void ComputeKernelStackSignatureCommon();
+
+  const FilePath &kcrash_file() const { return test_kcrash_; }
+  const FilePath &test_crash_directory() const { return test_crash_directory_; }
+
+  KernelCollectorMock collector_;
+
+ private:
+  void SetUp() override {
+    s_crashes = 0;
+    s_metrics = true;
+
+    EXPECT_CALL(collector_, SetUpDBus()).WillRepeatedly(testing::Return());
+
+    collector_.Initialize(CountCrash, IsMetrics);
+    ASSERT_TRUE(scoped_temp_dir_.CreateUniqueTempDir());
+    test_kcrash_ = scoped_temp_dir_.path().Append("kcrash");
+    ASSERT_TRUE(base::CreateDirectory(test_kcrash_));
+    collector_.OverridePreservedDumpPath(test_kcrash_);
+
+    test_kcrash_ = test_kcrash_.Append("dmesg-ramoops-0");
+    ASSERT_FALSE(base::PathExists(test_kcrash_));
+
+    test_crash_directory_ = scoped_temp_dir_.path().Append("crash_directory");
+    ASSERT_TRUE(base::CreateDirectory(test_crash_directory_));
+    chromeos::ClearLog();
+  }
+
+  FilePath test_kcrash_;
+  FilePath test_crash_directory_;
+  base::ScopedTempDir scoped_temp_dir_;
+};
+
+TEST_F(KernelCollectorTest, ComputeKernelStackSignatureBase) {
+  // Make sure the normal build architecture is detected
+  EXPECT_NE(KernelCollector::kArchUnknown, collector_.arch());
+}
+
+TEST_F(KernelCollectorTest, LoadPreservedDump) {
+  ASSERT_FALSE(base::PathExists(kcrash_file()));
+  std::string dump;
+  dump.clear();
+
+  WriteStringToFile(kcrash_file(),
+      "CrashRecordWithoutRamoopsHeader\n<6>[    0.078852]");
+  ASSERT_TRUE(collector_.LoadParameters());
+  ASSERT_TRUE(collector_.LoadPreservedDump(&dump));
+  ASSERT_EQ("CrashRecordWithoutRamoopsHeader\n<6>[    0.078852]", dump);
+
+  WriteStringToFile(kcrash_file(), "====1.1\nsomething");
+  ASSERT_TRUE(collector_.LoadParameters());
+  ASSERT_TRUE(collector_.LoadPreservedDump(&dump));
+  ASSERT_EQ("something", dump);
+
+  WriteStringToFile(kcrash_file(), "\x01\x02\xfe\xff random blob");
+  ASSERT_TRUE(collector_.LoadParameters());
+  ASSERT_FALSE(collector_.LoadPreservedDump(&dump));
+  ASSERT_EQ("", dump);
+}
+
+TEST_F(KernelCollectorTest, EnableMissingKernel) {
+  ASSERT_FALSE(collector_.Enable());
+  ASSERT_FALSE(collector_.is_enabled());
+  ASSERT_TRUE(FindLog(
+      "Kernel does not support crash dumping"));
+  ASSERT_EQ(s_crashes, 0);
+}
+
+TEST_F(KernelCollectorTest, EnableOK) {
+  WriteStringToFile(kcrash_file(), "");
+  EXPECT_CALL(collector_, DumpDirMounted()).WillOnce(::testing::Return(true));
+  ASSERT_TRUE(collector_.Enable());
+  ASSERT_TRUE(collector_.is_enabled());
+  ASSERT_TRUE(FindLog("Enabling kernel crash handling"));
+  ASSERT_EQ(s_crashes, 0);
+}
+
+TEST_F(KernelCollectorTest, StripSensitiveDataBasic) {
+  // Basic tests of StripSensitiveData...
+
+  // Make sure we work OK with a string w/ no MAC addresses.
+  const std::string kCrashWithNoMacsOrig =
+      "<7>[111566.131728] PM: Entering mem sleep\n";
+  std::string crash_with_no_macs(kCrashWithNoMacsOrig);
+  collector_.StripSensitiveData(&crash_with_no_macs);
+  EXPECT_EQ(kCrashWithNoMacsOrig, crash_with_no_macs);
+
+  // Make sure that we handle the case where there's nothing before/after the
+  // MAC address.
+  const std::string kJustAMacOrig =
+      "11:22:33:44:55:66";
+  const std::string kJustAMacStripped =
+      "00:00:00:00:00:01";
+  std::string just_a_mac(kJustAMacOrig);
+  collector_.StripSensitiveData(&just_a_mac);
+  EXPECT_EQ(kJustAMacStripped, just_a_mac);
+
+  // Test MAC addresses crammed together to make sure it gets both of them.
+  //
+  // I'm not sure that the code does ideal on these two test cases (they don't
+  // look like two MAC addresses to me), but since we don't see them I think
+  // it's OK to behave as shown here.
+  const std::string kCrammedMacs1Orig =
+      "11:22:33:44:55:66:11:22:33:44:55:66";
+  const std::string kCrammedMacs1Stripped =
+      "00:00:00:00:00:01:00:00:00:00:00:01";
+  std::string crammed_macs_1(kCrammedMacs1Orig);
+  collector_.StripSensitiveData(&crammed_macs_1);
+  EXPECT_EQ(kCrammedMacs1Stripped, crammed_macs_1);
+
+  const std::string kCrammedMacs2Orig =
+      "11:22:33:44:55:6611:22:33:44:55:66";
+  const std::string kCrammedMacs2Stripped =
+      "00:00:00:00:00:0100:00:00:00:00:01";
+  std::string crammed_macs_2(kCrammedMacs2Orig);
+  collector_.StripSensitiveData(&crammed_macs_2);
+  EXPECT_EQ(kCrammedMacs2Stripped, crammed_macs_2);
+
+  // Test case-sensitiveness (we shouldn't be case-senstive).
+  const std::string kCapsMacOrig =
+      "AA:BB:CC:DD:EE:FF";
+  const std::string kCapsMacStripped =
+      "00:00:00:00:00:01";
+  std::string caps_mac(kCapsMacOrig);
+  collector_.StripSensitiveData(&caps_mac);
+  EXPECT_EQ(kCapsMacStripped, caps_mac);
+
+  const std::string kLowerMacOrig =
+      "aa:bb:cc:dd:ee:ff";
+  const std::string kLowerMacStripped =
+      "00:00:00:00:00:01";
+  std::string lower_mac(kLowerMacOrig);
+  collector_.StripSensitiveData(&lower_mac);
+  EXPECT_EQ(kLowerMacStripped, lower_mac);
+}
+
+TEST_F(KernelCollectorTest, StripSensitiveDataBulk) {
+  // Test calling StripSensitiveData w/ lots of MAC addresses in the "log".
+
+  // Test that stripping code handles more than 256 unique MAC addresses, since
+  // that overflows past the last byte...
+  // We'll write up some code that generates 258 unique MAC addresses.  Sorta
+  // cheating since the code is very similar to the current code in
+  // StripSensitiveData(), but would catch if someone changed that later.
+  std::string lotsa_macs_orig;
+  std::string lotsa_macs_stripped;
+  int i;
+  for (i = 0; i < 258; i++) {
+    lotsa_macs_orig += StringPrintf(" 11:11:11:11:%02X:%02x",
+                                  (i & 0xff00) >> 8, i & 0x00ff);
+    lotsa_macs_stripped += StringPrintf(" 00:00:00:00:%02X:%02x",
+                                     ((i+1) & 0xff00) >> 8, (i+1) & 0x00ff);
+  }
+  std::string lotsa_macs(lotsa_macs_orig);
+  collector_.StripSensitiveData(&lotsa_macs);
+  EXPECT_EQ(lotsa_macs_stripped, lotsa_macs);
+}
+
+TEST_F(KernelCollectorTest, StripSensitiveDataSample) {
+  // Test calling StripSensitiveData w/ some actual lines from a real crash;
+  // included two MAC addresses (though replaced them with some bogusness).
+  const std::string kCrashWithMacsOrig =
+      "<6>[111567.195339] ata1.00: ACPI cmd ef/10:03:00:00:00:a0 (SET FEATURES)"
+        " filtered out\n"
+      "<7>[108539.540144] wlan0: authenticate with 11:22:33:44:55:66 (try 1)\n"
+      "<7>[108539.554973] wlan0: associate with 11:22:33:44:55:66 (try 1)\n"
+      "<6>[110136.587583] usb0: register 'QCUSBNet2k' at usb-0000:00:1d.7-2,"
+        " QCUSBNet Ethernet Device, 99:88:77:66:55:44\n"
+      "<7>[110964.314648] wlan0: deauthenticated from 11:22:33:44:55:66"
+        " (Reason: 6)\n"
+      "<7>[110964.325057] phy0: Removed STA 11:22:33:44:55:66\n"
+      "<7>[110964.325115] phy0: Destroyed STA 11:22:33:44:55:66\n"
+      "<6>[110969.219172] usb0: register 'QCUSBNet2k' at usb-0000:00:1d.7-2,"
+        " QCUSBNet Ethernet Device, 99:88:77:66:55:44\n"
+      "<7>[111566.131728] PM: Entering mem sleep\n";
+  const std::string kCrashWithMacsStripped =
+      "<6>[111567.195339] ata1.00: ACPI cmd ef/10:03:00:00:00:a0 (SET FEATURES)"
+        " filtered out\n"
+      "<7>[108539.540144] wlan0: authenticate with 00:00:00:00:00:01 (try 1)\n"
+      "<7>[108539.554973] wlan0: associate with 00:00:00:00:00:01 (try 1)\n"
+      "<6>[110136.587583] usb0: register 'QCUSBNet2k' at usb-0000:00:1d.7-2,"
+        " QCUSBNet Ethernet Device, 00:00:00:00:00:02\n"
+      "<7>[110964.314648] wlan0: deauthenticated from 00:00:00:00:00:01"
+        " (Reason: 6)\n"
+      "<7>[110964.325057] phy0: Removed STA 00:00:00:00:00:01\n"
+      "<7>[110964.325115] phy0: Destroyed STA 00:00:00:00:00:01\n"
+      "<6>[110969.219172] usb0: register 'QCUSBNet2k' at usb-0000:00:1d.7-2,"
+        " QCUSBNet Ethernet Device, 00:00:00:00:00:02\n"
+      "<7>[111566.131728] PM: Entering mem sleep\n";
+  std::string crash_with_macs(kCrashWithMacsOrig);
+  collector_.StripSensitiveData(&crash_with_macs);
+  EXPECT_EQ(kCrashWithMacsStripped, crash_with_macs);
+}
+
+TEST_F(KernelCollectorTest, CollectPreservedFileMissing) {
+  ASSERT_FALSE(collector_.Collect());
+  ASSERT_FALSE(FindLog("Stored kcrash to "));
+  ASSERT_EQ(0, s_crashes);
+}
+
+TEST_F(KernelCollectorTest, CollectBadDirectory) {
+  WriteStringToFile(kcrash_file(), "====1.1\nsomething");
+  ASSERT_TRUE(collector_.Collect());
+  ASSERT_TRUE(FindLog("Unable to create appropriate crash directory"))
+      << "Did not find expected error string in log: {\n"
+      << GetLog() << "}";
+  ASSERT_EQ(1, s_crashes);
+}
+
+void KernelCollectorTest::SetUpSuccessfulCollect() {
+  collector_.ForceCrashDirectory(test_crash_directory());
+  WriteStringToFile(kcrash_file(), "====1.1\nsomething");
+  ASSERT_EQ(0, s_crashes);
+}
+
+TEST_F(KernelCollectorTest, CollectOptedOut) {
+  SetUpSuccessfulCollect();
+  s_metrics = false;
+  ASSERT_TRUE(collector_.Collect());
+  ASSERT_TRUE(FindLog("(ignoring - no consent)"));
+  ASSERT_EQ(0, s_crashes);
+}
+
+TEST_F(KernelCollectorTest, CollectOK) {
+  SetUpSuccessfulCollect();
+  ASSERT_TRUE(collector_.Collect());
+  ASSERT_EQ(1, s_crashes);
+  ASSERT_TRUE(FindLog("(handling)"));
+  static const char kNamePrefix[] = "Stored kcrash to ";
+  std::string log = chromeos::GetLog();
+  size_t pos = log.find(kNamePrefix);
+  ASSERT_NE(std::string::npos, pos)
+      << "Did not find string \"" << kNamePrefix << "\" in log: {\n"
+      << log << "}";
+  pos += strlen(kNamePrefix);
+  std::string filename = log.substr(pos, std::string::npos);
+  // Take the name up until \n
+  size_t end_pos = filename.find_first_of("\n");
+  ASSERT_NE(std::string::npos, end_pos);
+  filename = filename.substr(0, end_pos);
+  ASSERT_EQ(0, filename.find(test_crash_directory().value()));
+  ASSERT_TRUE(base::PathExists(FilePath(filename)));
+  std::string contents;
+  ASSERT_TRUE(base::ReadFileToString(FilePath(filename), &contents));
+  ASSERT_EQ("something", contents);
+}
+
+// Perform tests which are common across architectures
+void KernelCollectorTest::ComputeKernelStackSignatureCommon() {
+  std::string signature;
+
+  const char kStackButNoPC[] =
+      "<4>[ 6066.829029]  [<790340af>] __do_softirq+0xa6/0x143\n";
+  EXPECT_TRUE(
+      collector_.ComputeKernelStackSignature(kStackButNoPC, &signature, false));
+  EXPECT_EQ("kernel--83615F0A", signature);
+
+  const char kMissingEverything[] =
+      "<4>[ 6066.829029]  [<790340af>] ? __do_softirq+0xa6/0x143\n";
+  EXPECT_FALSE(
+      collector_.ComputeKernelStackSignature(kMissingEverything,
+                                             &signature,
+                                             false));
+
+  // Long message.
+  const char kTruncatedMessage[] =
+      "<0>[   87.485611] Kernel panic - not syncing: 01234567890123456789"
+          "01234567890123456789X\n";
+  EXPECT_TRUE(
+      collector_.ComputeKernelStackSignature(kTruncatedMessage,
+                                             &signature,
+                                             false));
+  EXPECT_EQ("kernel-0123456789012345678901234567890123456789-00000000",
+            signature);
+}
+
+TEST_F(KernelCollectorTest, ComputeKernelStackSignatureARM) {
+  const char kBugToPanic[] =
+      "<5>[  123.412524] Modules linked in:\n"
+      "<5>[  123.412534] CPU: 0    Tainted: G        W    "
+          "(2.6.37-01030-g51cee64 #153)\n"
+      "<5>[  123.412552] PC is at write_breakme+0xd0/0x1b4\n"
+      "<5>[  123.412560] LR is at write_breakme+0xc8/0x1b4\n"
+      "<5>[  123.412569] pc : [<c0058220>]    lr : [<c005821c>]    "
+          "psr: 60000013\n"
+      "<5>[  123.412574] sp : f4e0ded8  ip : c04d104c  fp : 000e45e0\n"
+      "<5>[  123.412581] r10: 400ff000  r9 : f4e0c000  r8 : 00000004\n"
+      "<5>[  123.412589] r7 : f4e0df80  r6 : f4820c80  r5 : 00000004  "
+          "r4 : f4e0dee8\n"
+      "<5>[  123.412598] r3 : 00000000  r2 : f4e0decc  r1 : c05f88a9  "
+          "r0 : 00000039\n"
+      "<5>[  123.412608] Flags: nZCv  IRQs on  FIQs on  Mode SVC_32  ISA "
+          "ARM  Segment user\n"
+      "<5>[  123.412617] Control: 10c53c7d  Table: 34dcc04a  DAC: 00000015\n"
+      "<0>[  123.412626] Process bash (pid: 1014, stack limit = 0xf4e0c2f8)\n"
+      "<0>[  123.412634] Stack: (0xf4e0ded8 to 0xf4e0e000)\n"
+      "<0>[  123.412641] dec0:                                              "
+          "         f4e0dee8 c0183678\n"
+      "<0>[  123.412654] dee0: 00000000 00000000 00677562 0000081f c06a6a78 "
+          "400ff000 f4e0dfb0 00000000\n"
+      "<0>[  123.412666] df00: bec7ab44 000b1719 bec7ab0c c004f498 bec7a314 "
+          "c024acc8 00000001 c018359c\n"
+      "<0>[  123.412679] df20: f4e0df34 c04d10fc f5803c80 271beb39 000e45e0 "
+          "f5803c80 c018359c c017bfe0\n"
+      "<0>[  123.412691] df40: 00000004 f4820c80 400ff000 f4e0df80 00000004 "
+          "f4e0c000 00000000 c01383e4\n"
+      "<0>[  123.412703] df60: f4820c80 400ff000 f4820c80 400ff000 00000000 "
+          "00000000 00000004 c0138578\n"
+      "<0>[  123.412715] df80: 00000000 00000000 00000004 00000000 00000004 "
+          "402f95d0 00000004 00000004\n"
+      "<0>[  123.412727] dfa0: c0054984 c00547c0 00000004 402f95d0 00000001 "
+          "400ff000 00000004 00000000\n"
+      "<0>[  123.412739] dfc0: 00000004 402f95d0 00000004 00000004 400ff000 "
+          "000c194c bec7ab58 000e45e0\n"
+      "<0>[  123.412751] dfe0: 00000000 bec7aad8 40232520 40284e9c 60000010 "
+          "00000001 00000000 00000000\n"
+      "<5>[   39.496577] Backtrace:\n"
+      "<5>[  123.412782] [<c0058220>] (__bug+0x20/0x2c) from [<c0183678>] "
+          "(write_breakme+0xdc/0x1bc)\n"
+      "<5>[  123.412798] [<c0183678>] (write_breakme+0xdc/0x1bc) from "
+          "[<c017bfe0>] (proc_reg_write+0x88/0x9c)\n";
+  std::string signature;
+
+  collector_.set_arch(KernelCollector::kArchArm);
+  EXPECT_TRUE(
+      collector_.ComputeKernelStackSignature(kBugToPanic, &signature, false));
+  EXPECT_EQ("kernel-write_breakme-97D3E92F", signature);
+
+  ComputeKernelStackSignatureCommon();
+}
+
+TEST_F(KernelCollectorTest, ComputeKernelStackSignatureMIPS) {
+  const char kBugToPanic[] =
+      "<5>[ 3378.472000] lkdtm: Performing direct entry BUG\n"
+      "<5>[ 3378.476000] Kernel bug detected[#1]:\n"
+      "<5>[ 3378.484000] CPU: 0 PID: 185 Comm: dash Not tainted 3.14.0 #1\n"
+      "<5>[ 3378.488000] task: 8fed5220 ti: 8ec4a000 task.ti: 8ec4a000\n"
+      "<5>[ 3378.496000] $ 0   : 00000000 804018b8 804010f0 7785b507\n"
+      "<5>[ 3378.500000] $ 4   : 8061ab64 81204478 81205b20 00000000\n"
+      "<5>[ 3378.508000] $ 8   : 80830000 20746365 72746e65 55422079\n"
+      "<5>[ 3378.512000] $12   : 8ec4be94 000000fc 00000000 00000048\n"
+      "<5>[ 3378.520000] $16   : 00000004 8ef54000 80710000 00000002\n"
+      "<5>[ 3378.528000] $20   : 7765b6d4 00000004 7fffffff 00000002\n"
+      "<5>[ 3378.532000] $24   : 00000001 803dc0dc                  \n"
+      "<5>[ 3378.540000] $28   : 8ec4a000 8ec4be20 7775438d 804018b8\n"
+      "<5>[ 3378.544000] Hi    : 00000000\n"
+      "<5>[ 3378.548000] Lo    : 49bf8080\n"
+      "<5>[ 3378.552000] epc   : 804010f0 lkdtm_do_action+0x68/0x3f8\n"
+      "<5>[ 3378.560000]     Not tainted\n"
+      "<5>[ 3378.564000] ra    : 804018b8 direct_entry+0x110/0x154\n"
+      "<5>[ 3378.568000] Status: 3100dc03 KERNEL EXL IE \n"
+      "<5>[ 3378.572000] Cause : 10800024\n"
+      "<5>[ 3378.576000] PrId  : 0001a120 (MIPS interAptiv (multi))\n"
+      "<5>[ 3378.580000] Modules linked in: uinput cfg80211 nf_conntrack_ipv6 "
+          "nf_defrag_ipv6 ip6table_filter ip6_tables pcnet32 mii fuse "
+          "ppp_async ppp_generic slhc tun\n"
+      "<5>[ 3378.600000] Process dash (pid: 185, threadinfo=8ec4a000, "
+          "task=8fed5220, tls=77632490)\n"
+      "<5>[ 3378.608000] Stack : 00000006 ffffff9c 00000000 00000000 00000000 "
+          "00000000 8083454a 00000022\n"
+      "<5>          7765baa1 00001fee 80710000 8ef54000 8ec4bf08 00000002 "
+          "7765b6d4 00000004\n"
+      "<5>          7fffffff 00000002 7775438d 805e5158 7fffffff 00000002 "
+          "00000000 7785b507\n"
+      "<5>          806a96bc 00000004 8ef54000 8ec4bf08 00000002 804018b8 "
+          "80710000 806a98bc\n"
+      "<5>          00000002 00000020 00000004 8d515600 77756450 00000004 "
+          "8ec4bf08 802377e4\n"
+      "<5>          ...\n"
+      "<5>[ 3378.652000] Call Trace:\n"
+      "<5>[ 3378.656000] [<804010f0>] lkdtm_do_action+0x68/0x3f8\n"
+      "<5>[ 3378.660000] [<804018b8>] direct_entry+0x110/0x154\n"
+      "<5>[ 3378.664000] [<802377e4>] vfs_write+0xe0/0x1bc\n"
+      "<5>[ 3378.672000] [<80237f90>] SyS_write+0x78/0xf8\n"
+      "<5>[ 3378.676000] [<80111888>] handle_sys+0x128/0x14c\n"
+      "<5>[ 3378.680000] \n"
+      "<5>[ 3378.684000] \n"
+      "<5>Code: 3c04806b  0c1793aa  248494f0 <000c000d> 3c04806b  248494fc  "
+          "0c04cc7f  2405017a  08100514 \n"
+      "<5>[ 3378.696000] ---[ end trace 75067432f24bbc93 ]---\n";
+  std::string signature;
+
+  collector_.set_arch(KernelCollector::kArchMips);
+  EXPECT_TRUE(
+      collector_.ComputeKernelStackSignature(kBugToPanic, &signature, false));
+  EXPECT_EQ("kernel-lkdtm_do_action-5E600A6B", signature);
+
+  ComputeKernelStackSignatureCommon();
+}
+
+TEST_F(KernelCollectorTest, ComputeKernelStackSignatureX86) {
+  const char kBugToPanic[] =
+      "<4>[ 6066.829029]  [<79039d16>] ? run_timer_softirq+0x165/0x1e6\n"
+      "<4>[ 6066.829029]  [<790340af>] ignore_old_stack+0xa6/0x143\n"
+      "<0>[ 6066.829029] EIP: [<b82d7c15>] ieee80211_stop_tx_ba_session+"
+          "0xa3/0xb5 [mac80211] SS:ESP 0068:7951febc\n"
+      "<0>[ 6066.829029] CR2: 00000000323038a7\n"
+      "<4>[ 6066.845422] ---[ end trace 12b058bb46c43500 ]---\n"
+      "<0>[ 6066.845747] Kernel panic - not syncing: Fatal exception "
+          "in interrupt\n"
+      "<0>[ 6066.846902] Call Trace:\n"
+      "<4>[ 6066.846902]  [<7937a07b>] ? printk+0x14/0x19\n"
+      "<4>[ 6066.949779]  [<79379fc1>] panic+0x3e/0xe4\n"
+      "<4>[ 6066.949971]  [<7937c5c5>] oops_end+0x73/0x81\n"
+      "<4>[ 6066.950208]  [<7901b260>] no_context+0x10d/0x117\n";
+  std::string signature;
+
+  collector_.set_arch(KernelCollector::kArchX86);
+  EXPECT_TRUE(
+      collector_.ComputeKernelStackSignature(kBugToPanic, &signature, false));
+  EXPECT_EQ("kernel-ieee80211_stop_tx_ba_session-DE253569", signature);
+
+  const char kPCButNoStack[] =
+      "<0>[ 6066.829029] EIP: [<b82d7c15>] ieee80211_stop_tx_ba_session+";
+  EXPECT_TRUE(
+      collector_.ComputeKernelStackSignature(kPCButNoStack, &signature, false));
+  EXPECT_EQ("kernel-ieee80211_stop_tx_ba_session-00000000", signature);
+
+  const char kBreakmeBug[] =
+      "<4>[  180.492137]  [<790970c6>] ? handle_mm_fault+0x67f/0x96d\n"
+      "<4>[  180.492137]  [<790dcdfe>] ? proc_reg_write+0x5f/0x73\n"
+      "<4>[  180.492137]  [<790e2224>] ? write_breakme+0x0/0x108\n"
+      "<4>[  180.492137]  [<790dcd9f>] ? proc_reg_write+0x0/0x73\n"
+      "<4>[  180.492137]  [<790ac0aa>] vfs_write+0x85/0xe4\n"
+      "<0>[  180.492137] Code: c6 44 05 b2 00 89 d8 e8 0c ef 09 00 85 c0 75 "
+      "0b c7 00 00 00 00 00 e9 8e 00 00 00 ba e6 75 4b 79 89 d8 e8 f1 ee 09 "
+      "00 85 c0 75 04 <0f> 0b eb fe ba 58 47 49 79 89 d8 e8 dd ee 09 00 85 "
+      "c0 75 0a 68\n"
+      "<0>[  180.492137] EIP: [<790e22a4>] write_breakme+0x80/0x108 SS:ESP "
+          "0068:aa3e9efc\n"
+      "<4>[  180.501800] ---[ end trace 2a6b72965e1b1523 ]---\n"
+      "<0>[  180.502026] Kernel panic - not syncing: Fatal exception\n"
+      "<4>[  180.502026] Call Trace:\n"
+      "<4>[  180.502806]  [<79379aba>] ? printk+0x14/0x1a\n"
+      "<4>[  180.503033]  [<79379a00>] panic+0x3e/0xe4\n"
+      "<4>[  180.503287]  [<7937c005>] oops_end+0x73/0x81\n"
+      "<4>[  180.503520]  [<790055dd>] die+0x58/0x5e\n"
+      "<4>[  180.503538]  [<7937b96c>] do_trap+0x8e/0xa7\n"
+      "<4>[  180.503555]  [<79003d70>] ? do_invalid_op+0x0/0x80\n";
+
+  EXPECT_TRUE(
+      collector_.ComputeKernelStackSignature(kBreakmeBug, &signature, false));
+  EXPECT_EQ("kernel-write_breakme-122AB3CD", signature);
+
+  const char kPCLineTooOld[] =
+      "<4>[  174.492137]  [<790970c6>] ignored_function+0x67f/0x96d\n"
+      "<4>[  175.492137]  [<790970c6>] ignored_function2+0x67f/0x96d\n"
+      "<0>[  174.492137] EIP: [<790e22a4>] write_breakme+0x80/0x108 SS:ESP "
+          "0068:aa3e9efc\n"
+      "<4>[  180.501800] ---[ end trace 2a6b72965e1b1523 ]---\n"
+      "<4>[  180.502026] Call Trace:\n"
+      "<0>[  180.502026] Kernel panic - not syncing: Fatal exception\n"
+      "<4>[  180.502806]  [<79379aba>] printk+0x14/0x1a\n";
+
+  EXPECT_TRUE(
+      collector_.ComputeKernelStackSignature(kPCLineTooOld, &signature, false));
+  EXPECT_EQ("kernel-Fatal exception-ED4C84FE", signature);
+
+  // Panic without EIP line.
+  const char kExamplePanicOnly[] =
+      "<0>[   87.485611] Kernel panic - not syncing: Testing panic\n"
+      "<4>[   87.485630] Pid: 2825, comm: bash Tainted: G         "
+          "C 2.6.32.23+drm33.10 #1\n"
+      "<4>[   87.485639] Call Trace:\n"
+      "<4>[   87.485660]  [<8133f71d>] ? printk+0x14/0x17\n"
+      "<4>[   87.485674]  [<8133f663>] panic+0x3e/0xe4\n"
+      "<4>[   87.485689]  [<810d062e>] write_breakme+0xaa/0x124\n";
+  EXPECT_TRUE(
+      collector_.ComputeKernelStackSignature(kExamplePanicOnly,
+                                             &signature,
+                                             false));
+  EXPECT_EQ("kernel-Testing panic-E0FC3552", signature);
+
+  // Panic from hung task.
+  const char kHungTaskBreakMe[] =
+      "<3>[  720.459157] INFO: task bash:2287 blocked blah blah\n"
+      "<5>[  720.459282] Call Trace:\n"
+      "<5>[  720.459307]  [<810a457b>] ? __dentry_open+0x186/0x23e\n"
+      "<5>[  720.459323]  [<810b9c71>] ? mntput_no_expire+0x29/0xe2\n"
+      "<5>[  720.459336]  [<810b9d48>] ? mntput+0x1e/0x20\n"
+      "<5>[  720.459350]  [<810ad135>] ? path_put+0x1a/0x1d\n"
+      "<5>[  720.459366]  [<8137cacc>] schedule+0x4d/0x4f\n"
+      "<5>[  720.459379]  [<8137ccfb>] schedule_timeout+0x26/0xaf\n"
+      "<5>[  720.459394]  [<8102127e>] ? should_resched+0xd/0x27\n"
+      "<5>[  720.459409]  [<81174d1f>] ? _copy_from_user+0x3c/0x50\n"
+      "<5>[  720.459423]  [<8137cd9e>] "
+      "schedule_timeout_uninterruptible+0x1a/0x1c\n"
+      "<5>[  720.459438]  [<810dee63>] write_breakme+0xb3/0x178\n"
+      "<5>[  720.459453]  [<810dedb0>] ? meminfo_proc_show+0x2f2/0x2f2\n"
+      "<5>[  720.459467]  [<810d94ae>] proc_reg_write+0x6d/0x87\n"
+      "<5>[  720.459481]  [<810d9441>] ? proc_reg_poll+0x76/0x76\n"
+      "<5>[  720.459493]  [<810a5e9e>] vfs_write+0x79/0xa5\n"
+      "<5>[  720.459505]  [<810a6011>] sys_write+0x40/0x65\n"
+      "<5>[  720.459519]  [<8137e677>] sysenter_do_call+0x12/0x26\n"
+      "<0>[  720.459530] Kernel panic - not syncing: hung_task: blocked tasks\n"
+      "<5>[  720.459768] Pid: 31, comm: khungtaskd Tainted: "
+      "G         C  3.0.8 #1\n"
+      "<5>[  720.459998] Call Trace:\n"
+      "<5>[  720.460140]  [<81378a35>] panic+0x53/0x14a\n"
+      "<5>[  720.460312]  [<8105f875>] watchdog+0x15b/0x1a0\n"
+      "<5>[  720.460495]  [<8105f71a>] ? hung_task_panic+0x16/0x16\n"
+      "<5>[  720.460693]  [<81043af3>] kthread+0x67/0x6c\n"
+      "<5>[  720.460862]  [<81043a8c>] ? __init_kthread_worker+0x2d/0x2d\n"
+      "<5>[  720.461106]  [<8137eb9e>] kernel_thread_helper+0x6/0x10\n";
+
+  EXPECT_TRUE(
+      collector_.ComputeKernelStackSignature(kHungTaskBreakMe,
+                                             &signature,
+                                             false));
+
+  EXPECT_EQ("kernel-(HANG)-hung_task: blocked tasks-600B37EA", signature);
+
+  // Panic with all question marks in the last stack trace.
+  const char kUncertainStackTrace[] =
+      "<0>[56279.689669] ------------[ cut here ]------------\n"
+      "<2>[56279.689677] kernel BUG at /build/x86-alex/tmp/portage/"
+      "sys-kernel/chromeos-kernel-0.0.1-r516/work/chromeos-kernel-0.0.1/"
+      "kernel/timer.c:844!\n"
+      "<0>[56279.689683] invalid opcode: 0000 [#1] SMP \n"
+      "<0>[56279.689688] last sysfs file: /sys/power/state\n"
+      "<5>[56279.689692] Modules linked in: nls_iso8859_1 nls_cp437 vfat fat "
+      "gobi usbnet tsl2583(C) industrialio(C) snd_hda_codec_realtek "
+      "snd_hda_intel i2c_dev snd_hda_codec snd_hwdep qcserial snd_pcm usb_wwan "
+      "i2c_i801 snd_timer nm10_gpio snd_page_alloc rtc_cmos fuse "
+      "nf_conntrack_ipv6 nf_defrag_ipv6 uvcvideo videodev ip6table_filter "
+      "ath9k ip6_tables ipv6 mac80211 ath9k_common ath9k_hw ath cfg80211 "
+      "xt_mark\n"
+      "<5>[56279.689731] \n"
+      "<5>[56279.689738] Pid: 24607, comm: powerd_suspend Tainted: G        "
+      "WC  2.6.38.3+ #1 SAMSUNG ELECTRONICS CO., LTD. Alex/G100          \n"
+      "<5>[56279.689748] EIP: 0060:[<8103e3ea>] EFLAGS: 00210286 CPU: 3\n"
+      "<5>[56279.689758] EIP is at add_timer+0xd/0x1b\n"
+      "<5>[56279.689762] EAX: f5e00684 EBX: f5e003c0 ECX: 00000002 EDX: "
+      "00200246\n"
+      "<5>[56279.689767] ESI: f5e003c0 EDI: d28bc03c EBP: d2be5e40 ESP: "
+      "d2be5e40\n"
+      "<5>[56279.689772]  DS: 007b ES: 007b FS: 00d8 GS: 00e0 SS: 0068\n"
+      "<0>[56279.689778] Process powerd_suspend (pid: 24607, ti=d2be4000 "
+      "task=f5dc9b60 task.ti=d2be4000)\n"
+      "<0>[56279.689782] Stack:\n"
+      "<5>[56279.689785]  d2be5e4c f8dccced f4ac02c0 d2be5e70 f8ddc752 "
+      "f5e003c0 f4ac0458 f4ac092c\n"
+      "<5>[56279.689797]  f4ac043c f4ac02c0 f4ac0000 f4ac007c d2be5e7c "
+      "f8dd4a33 f4ac0164 d2be5e94\n"
+      "<5>[56279.689809]  f87e0304 f69ff0cc f4ac0164 f87e02a4 f4ac0164 "
+      "d2be5eb0 81248968 00000000\n"
+      "<0>[56279.689821] Call Trace:\n"
+      "<5>[56279.689840]  [<f8dccced>] ieee80211_sta_restart+0x25/0x8c "
+      "[mac80211]\n"
+      "<5>[56279.689854]  [<f8ddc752>] ieee80211_reconfig+0x2e9/0x339 "
+      "[mac80211]\n"
+      "<5>[56279.689869]  [<f8dd4a33>] ieee80211_aes_cmac+0x182d/0x184e "
+      "[mac80211]\n"
+      "<5>[56279.689883]  [<f87e0304>] cfg80211_get_dev_from_info+0x29b/0x2c0 "
+      "[cfg80211]\n"
+      "<5>[56279.689895]  [<f87e02a4>] ? "
+      "cfg80211_get_dev_from_info+0x23b/0x2c0 [cfg80211]\n"
+      "<5>[56279.689904]  [<81248968>] legacy_resume+0x25/0x5d\n"
+      "<5>[56279.689910]  [<812490ae>] device_resume+0xdd/0x110\n"
+      "<5>[56279.689917]  [<812491c2>] dpm_resume_end+0xe1/0x271\n"
+      "<5>[56279.689925]  [<81060481>] suspend_devices_and_enter+0x18b/0x1de\n"
+      "<5>[56279.689932]  [<810605ba>] enter_state+0xe6/0x132\n"
+      "<5>[56279.689939]  [<8105fd4b>] state_store+0x91/0x9d\n"
+      "<5>[56279.689945]  [<8105fcba>] ? state_store+0x0/0x9d\n"
+      "<5>[56279.689953]  [<81178fb1>] kobj_attr_store+0x16/0x22\n"
+      "<5>[56279.689961]  [<810eea5e>] sysfs_write_file+0xc1/0xec\n"
+      "<5>[56279.689969]  [<810af443>] vfs_write+0x8f/0x101\n"
+      "<5>[56279.689975]  [<810ee99d>] ? sysfs_write_file+0x0/0xec\n"
+      "<5>[56279.689982]  [<810af556>] sys_write+0x40/0x65\n"
+      "<5>[56279.689989]  [<81002d57>] sysenter_do_call+0x12/0x26\n"
+      "<0>[56279.689993] Code: c1 d3 e2 4a 89 55 f4 f7 d2 21 f2 6a 00 31 c9 89 "
+      "d8 e8 6e fd ff ff 5a 8d 65 f8 5b 5e 5d c3 55 89 e5 3e 8d 74 26 00 83 38 "
+      "00 74 04 <0f> 0b eb fe 8b 50 08 e8 6f ff ff ff 5d c3 55 89 e5 3e 8d 74 "
+      "26 \n"
+      "<0>[56279.690009] EIP: [<8103e3ea>] add_timer+0xd/0x1b SS:ESP "
+      "0068:d2be5e40\n"
+      "<4>[56279.690113] ---[ end trace b71141bb67c6032a ]---\n"
+      "<7>[56279.694069] wlan0: deauthenticated from 00:00:00:00:00:01 "
+      "(Reason: 6)\n"
+      "<0>[56279.703465] Kernel panic - not syncing: Fatal exception\n"
+      "<5>[56279.703471] Pid: 24607, comm: powerd_suspend Tainted: G      D "
+      "WC  2.6.38.3+ #1\n"
+      "<5>[56279.703475] Call Trace:\n"
+      "<5>[56279.703483]  [<8136648c>] ? panic+0x55/0x152\n"
+      "<5>[56279.703491]  [<810057fa>] ? oops_end+0x73/0x81\n"
+      "<5>[56279.703497]  [<81005a44>] ? die+0xed/0xf5\n"
+      "<5>[56279.703503]  [<810033cb>] ? do_trap+0x7a/0x80\n"
+      "<5>[56279.703509]  [<8100369b>] ? do_invalid_op+0x0/0x80\n"
+      "<5>[56279.703515]  [<81003711>] ? do_invalid_op+0x76/0x80\n"
+      "<5>[56279.703522]  [<8103e3ea>] ? add_timer+0xd/0x1b\n"
+      "<5>[56279.703529]  [<81025e23>] ? check_preempt_curr+0x2e/0x69\n"
+      "<5>[56279.703536]  [<8102ef28>] ? ttwu_post_activation+0x5a/0x11b\n"
+      "<5>[56279.703543]  [<8102fa8d>] ? try_to_wake_up+0x213/0x21d\n"
+      "<5>[56279.703550]  [<81368b7f>] ? error_code+0x67/0x6c\n"
+      "<5>[56279.703557]  [<8103e3ea>] ? add_timer+0xd/0x1b\n"
+      "<5>[56279.703577]  [<f8dccced>] ? ieee80211_sta_restart+0x25/0x8c "
+      "[mac80211]\n"
+      "<5>[56279.703591]  [<f8ddc752>] ? ieee80211_reconfig+0x2e9/0x339 "
+      "[mac80211]\n"
+      "<5>[56279.703605]  [<f8dd4a33>] ? ieee80211_aes_cmac+0x182d/0x184e "
+      "[mac80211]\n"
+      "<5>[56279.703618]  [<f87e0304>] ? "
+      "cfg80211_get_dev_from_info+0x29b/0x2c0 [cfg80211]\n"
+      "<5>[56279.703630]  [<f87e02a4>] ? "
+      "cfg80211_get_dev_from_info+0x23b/0x2c0 [cfg80211]\n"
+      "<5>[56279.703637]  [<81248968>] ? legacy_resume+0x25/0x5d\n"
+      "<5>[56279.703643]  [<812490ae>] ? device_resume+0xdd/0x110\n"
+      "<5>[56279.703649]  [<812491c2>] ? dpm_resume_end+0xe1/0x271\n"
+      "<5>[56279.703657]  [<81060481>] ? "
+      "suspend_devices_and_enter+0x18b/0x1de\n"
+      "<5>[56279.703663]  [<810605ba>] ? enter_state+0xe6/0x132\n"
+      "<5>[56279.703670]  [<8105fd4b>] ? state_store+0x91/0x9d\n"
+      "<5>[56279.703676]  [<8105fcba>] ? state_store+0x0/0x9d\n"
+      "<5>[56279.703683]  [<81178fb1>] ? kobj_attr_store+0x16/0x22\n"
+      "<5>[56279.703690]  [<810eea5e>] ? sysfs_write_file+0xc1/0xec\n"
+      "<5>[56279.703697]  [<810af443>] ? vfs_write+0x8f/0x101\n"
+      "<5>[56279.703703]  [<810ee99d>] ? sysfs_write_file+0x0/0xec\n"
+      "<5>[56279.703709]  [<810af556>] ? sys_write+0x40/0x65\n"
+      "<5>[56279.703716]  [<81002d57>] ? sysenter_do_call+0x12/0x26\n";
+
+  EXPECT_TRUE(
+      collector_.ComputeKernelStackSignature(kUncertainStackTrace,
+                                             &signature,
+                                             false));
+  // The first trace contains only uncertain entries and its hash is 00000000,
+  // so, if we used that, the signature would be kernel-add_timer-00000000.
+  // Instead we use the second-to-last trace for the hash.
+  EXPECT_EQ("kernel-add_timer-B5178878", signature);
+
+  ComputeKernelStackSignatureCommon();
+}
diff --git a/crash_reporter/kernel_collector_test.h b/crash_reporter/kernel_collector_test.h
new file mode 100644
index 0000000..f689e7d
--- /dev/null
+++ b/crash_reporter/kernel_collector_test.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2014 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 CRASH_REPORTER_KERNEL_COLLECTOR_TEST_H_
+#define CRASH_REPORTER_KERNEL_COLLECTOR_TEST_H_
+
+#include "kernel_collector.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+class KernelCollectorMock : public KernelCollector {
+ public:
+  MOCK_METHOD0(DumpDirMounted, bool());
+  MOCK_METHOD0(SetUpDBus, void());
+};
+
+#endif  // CRASH_REPORTER_KERNEL_COLLECTOR_TEST_H_
diff --git a/crash_reporter/kernel_log_collector.sh b/crash_reporter/kernel_log_collector.sh
new file mode 100644
index 0000000..82512c2
--- /dev/null
+++ b/crash_reporter/kernel_log_collector.sh
@@ -0,0 +1,59 @@
+#!/bin/sh
+
+# Copyright (C) 2013 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.
+
+# Usage example: "kernel_log_collector.sh XXX YYY"
+# This script searches logs in the /var/log/messages which have the keyword XXX.
+# And only those logs which are within the last YYY seconds of the latest log
+# that has the keyword XXX are printed.
+
+# Kernel log has the possible formats:
+# 2013-06-14T16:31:40.514513-07:00 localhost kernel: [    2.682472] MSG MSG ...
+# 2013-06-19T20:38:58.661826+00:00 localhost kernel: [    1.668092] MSG MSG ...
+
+search_key=$1
+time_duration=$2
+msg_pattern="^[0-9-]*T[0-9:.+-]* localhost kernel"
+
+die() {
+  echo "kernel_log_collector: $*" >&2
+  exit 1
+}
+
+get_timestamp() {
+  timestamp="$(echo $1 | cut -d " " -f 1)"
+  timestamp="$(date -d "${timestamp}" +%s)" || exit $?
+  echo "${timestamp}"
+}
+
+last_line=$(grep "${msg_pattern}" /var/log/messages | grep -- "${search_key}" | tail -n 1)
+
+if [ -n "${last_line}" ]; then
+  if ! allowed_timestamp=$(get_timestamp "${last_line}"); then
+    die "coule not get timestamp from: ${last_line}"
+  fi
+  : $(( allowed_timestamp -= ${time_duration} ))
+  grep "${msg_pattern}" /var/log/messages | grep -- "${search_key}" | while read line; do
+    if ! timestamp=$(get_timestamp "${line}"); then
+      die "could not get timestamp from: ${line}"
+    fi
+    if [ ${timestamp} -gt ${allowed_timestamp} ]; then
+      echo "${line}"
+    fi
+  done
+fi
+
+echo "END-OF-LOG"
+
diff --git a/crash_reporter/kernel_warning_collector.cc b/crash_reporter/kernel_warning_collector.cc
new file mode 100644
index 0000000..e28e8fd
--- /dev/null
+++ b/crash_reporter/kernel_warning_collector.cc
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernel_warning_collector.h"
+
+#include <base/files/file_util.h>
+#include <base/logging.h>
+#include <base/strings/string_number_conversions.h>
+#include <base/strings/string_util.h>
+#include <base/strings/stringprintf.h>
+
+namespace {
+const char kExecName[] = "kernel-warning";
+const char kKernelWarningSignatureKey[] = "sig";
+const char kKernelWarningPath[] = "/var/run/kwarn/warning";
+const pid_t kKernelPid = 0;
+const uid_t kRootUid = 0;
+}  // namespace
+
+using base::FilePath;
+using base::StringPrintf;
+
+KernelWarningCollector::KernelWarningCollector() {
+}
+
+KernelWarningCollector::~KernelWarningCollector() {
+}
+
+bool KernelWarningCollector::LoadKernelWarning(std::string *content,
+                                               std::string *signature) {
+  FilePath kernel_warning_path(kKernelWarningPath);
+  if (!base::ReadFileToString(kernel_warning_path, content)) {
+    LOG(ERROR) << "Could not open " << kKernelWarningPath;
+    return false;
+  }
+  // The signature is in the first line.
+  std::string::size_type end_position = content->find('\n');
+  if (end_position == std::string::npos) {
+    LOG(ERROR) << "unexpected kernel warning format";
+    return false;
+  }
+  *signature = content->substr(0, end_position);
+  return true;
+}
+
+bool KernelWarningCollector::Collect() {
+  std::string reason = "normal collection";
+  bool feedback = true;
+  if (IsDeveloperImage()) {
+    reason = "always collect from developer builds";
+    feedback = true;
+  } else if (!is_feedback_allowed_function_()) {
+    reason = "no user consent";
+    feedback = false;
+  }
+
+  LOG(INFO) << "Processing kernel warning: " << reason;
+
+  if (!feedback) {
+    return true;
+  }
+
+  std::string kernel_warning;
+  std::string warning_signature;
+  if (!LoadKernelWarning(&kernel_warning, &warning_signature)) {
+    return true;
+  }
+
+  FilePath root_crash_directory;
+  if (!GetCreatedCrashDirectoryByEuid(kRootUid, &root_crash_directory,
+                                      nullptr)) {
+    return true;
+  }
+
+  std::string dump_basename =
+      FormatDumpBasename(kExecName, time(nullptr), kKernelPid);
+  FilePath kernel_crash_path = root_crash_directory.Append(
+      StringPrintf("%s.kcrash", dump_basename.c_str()));
+
+  // We must use WriteNewFile instead of base::WriteFile as we
+  // do not want to write with root access to a symlink that an attacker
+  // might have created.
+  if (WriteNewFile(kernel_crash_path,
+                   kernel_warning.data(),
+                   kernel_warning.length()) !=
+      static_cast<int>(kernel_warning.length())) {
+    LOG(INFO) << "Failed to write kernel warning to "
+              << kernel_crash_path.value().c_str();
+    return true;
+  }
+
+  AddCrashMetaData(kKernelWarningSignatureKey, warning_signature);
+  WriteCrashMetaData(
+      root_crash_directory.Append(
+          StringPrintf("%s.meta", dump_basename.c_str())),
+    kExecName, kernel_crash_path.value());
+
+  LOG(INFO) << "Stored kernel warning into " << kernel_crash_path.value();
+  return true;
+}
diff --git a/crash_reporter/kernel_warning_collector.h b/crash_reporter/kernel_warning_collector.h
new file mode 100644
index 0000000..5ccb780
--- /dev/null
+++ b/crash_reporter/kernel_warning_collector.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2013 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 CRASH_REPORTER_KERNEL_WARNING_COLLECTOR_H_
+#define CRASH_REPORTER_KERNEL_WARNING_COLLECTOR_H_
+
+#include <string>
+
+#include <base/macros.h>
+#include <gtest/gtest_prod.h>  // for FRIEND_TEST
+
+#include "crash_collector.h"
+
+// Kernel warning collector.
+class KernelWarningCollector : public CrashCollector {
+ public:
+  KernelWarningCollector();
+
+  ~KernelWarningCollector() override;
+
+  // Collects warning.
+  bool Collect();
+
+ private:
+  friend class KernelWarningCollectorTest;
+  FRIEND_TEST(KernelWarningCollectorTest, CollectOK);
+
+  // Reads the full content of the kernel warn dump and its signature.
+  bool LoadKernelWarning(std::string *content, std::string *signature);
+
+  DISALLOW_COPY_AND_ASSIGN(KernelWarningCollector);
+};
+
+#endif  // CRASH_REPORTER_KERNEL_WARNING_COLLECTOR_H_
diff --git a/crash_reporter/list_proxies.cc b/crash_reporter/list_proxies.cc
new file mode 100644
index 0000000..a39441d
--- /dev/null
+++ b/crash_reporter/list_proxies.cc
@@ -0,0 +1,303 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <sysexits.h>
+#include <unistd.h>  // for isatty()
+
+#include <string>
+#include <vector>
+
+#include <base/cancelable_callback.h>
+#include <base/command_line.h>
+#include <base/files/file_util.h>
+#include <base/memory/weak_ptr.h>
+#include <base/strings/string_number_conversions.h>
+#include <base/strings/string_tokenizer.h>
+#include <base/strings/string_util.h>
+#include <base/values.h>
+#include <chromeos/daemons/dbus_daemon.h>
+#include <chromeos/syslog_logging.h>
+
+#include "libcrosservice/dbus-proxies.h"
+
+using std::unique_ptr;
+
+namespace {
+
+const char kLibCrosProxyResolvedSignalInterface[] =
+    "org.chromium.CrashReporterLibcrosProxyResolvedInterface";
+const char kLibCrosProxyResolvedName[] = "ProxyResolved";
+const char kLibCrosServiceName[] = "org.chromium.LibCrosService";
+const char kNoProxy[] = "direct://";
+
+const int kTimeoutDefaultSeconds = 5;
+
+const char kHelp[] = "help";
+const char kQuiet[] = "quiet";
+const char kTimeout[] = "timeout";
+const char kVerbose[] = "verbose";
+// Help message to show when the --help command line switch is specified.
+const char kHelpMessage[] =
+    "Chromium OS Crash helper: proxy lister\n"
+    "\n"
+    "Available Switches:\n"
+    "  --quiet      Only print the proxies\n"
+    "  --verbose    Print additional messages even when not run from a TTY\n"
+    "  --timeout=N  Set timeout for browser resolving proxies (default is 5)\n"
+    "  --help       Show this help.\n";
+
+// Copied from src/update_engine/chrome_browser_proxy_resolver.cc
+// Parses the browser's answer for resolved proxies.  It returns a
+// list of strings, each of which is a resolved proxy.
+std::vector<std::string> ParseProxyString(const std::string& input) {
+  std::vector<std::string> ret;
+  // Some of this code taken from
+  // http://src.chromium.org/svn/trunk/src/net/proxy/proxy_server.cc and
+  // http://src.chromium.org/svn/trunk/src/net/proxy/proxy_list.cc
+  base::StringTokenizer entry_tok(input, ";");
+  while (entry_tok.GetNext()) {
+    std::string token = entry_tok.token();
+    base::TrimWhitespaceASCII(token, base::TRIM_ALL, &token);
+
+    // Start by finding the first space (if any).
+    std::string::iterator space;
+    for (space = token.begin(); space != token.end(); ++space) {
+      if (IsAsciiWhitespace(*space)) {
+        break;
+      }
+    }
+
+    std::string scheme = std::string(token.begin(), space);
+    base::StringToLowerASCII(&scheme);
+    // Chrome uses "socks" to mean socks4 and "proxy" to mean http.
+    if (scheme == "socks")
+      scheme += "4";
+    else if (scheme == "proxy")
+      scheme = "http";
+    else if (scheme != "https" &&
+             scheme != "socks4" &&
+             scheme != "socks5" &&
+             scheme != "direct")
+      continue;  // Invalid proxy scheme
+
+    std::string host_and_port = std::string(space, token.end());
+    base::TrimWhitespaceASCII(host_and_port, base::TRIM_ALL, &host_and_port);
+    if (scheme != "direct" && host_and_port.empty())
+      continue;  // Must supply host/port when non-direct proxy used.
+    ret.push_back(scheme + "://" + host_and_port);
+  }
+  if (ret.empty() || *ret.rbegin() != kNoProxy)
+    ret.push_back(kNoProxy);
+  return ret;
+}
+
+// A class for interfacing with Chrome to resolve proxies for a given source
+// url.  The class is initialized with the given source url to check, the
+// signal interface and name that Chrome will reply to, and how long to wait
+// for the resolve request to timeout.  Once initialized, the Run() function
+// must be called, which blocks on the D-Bus call to Chrome.  The call returns
+// after either the timeout or the proxy has been resolved.  The resolved
+// proxies can then be accessed through the proxies() function.
+class ProxyResolver : public chromeos::DBusDaemon {
+ public:
+  ProxyResolver(const std::string& source_url,
+                const std::string& signal_interface,
+                const std::string& signal_name,
+                base::TimeDelta timeout)
+      : source_url_(source_url),
+        signal_interface_(signal_interface),
+        signal_name_(signal_name),
+        timeout_(timeout),
+        weak_ptr_factory_(this),
+        timeout_callback_(base::Bind(&ProxyResolver::HandleBrowserTimeout,
+                                     weak_ptr_factory_.GetWeakPtr())) {}
+
+  ~ProxyResolver() override {}
+
+  const std::vector<std::string>& proxies() {
+    return proxies_;
+  }
+
+  int Run() override {
+    // Add task for if the browser proxy call times out.
+    base::MessageLoop::current()->PostDelayedTask(
+        FROM_HERE,
+        timeout_callback_.callback(),
+        timeout_);
+
+    return chromeos::DBusDaemon::Run();
+  }
+
+ protected:
+  // If the browser times out, quit the run loop.
+  void HandleBrowserTimeout() {
+    LOG(ERROR) << "Timeout while waiting for browser to resolve proxy";
+    Quit();
+  }
+
+  // If the signal handler connects successfully, call the browser's
+  // ResolveNetworkProxy D-Bus method.  Otherwise, don't do anything and let
+  // the timeout task quit the run loop.
+  void HandleDBusSignalConnected(const std::string& interface,
+                                 const std::string& signal,
+                                 bool success) {
+    if (!success) {
+      LOG(ERROR) << "Could not connect to signal " << interface << "."
+                 << signal;
+      timeout_callback_.Cancel();
+      Quit();
+      return;
+    }
+
+    chromeos::ErrorPtr error;
+    call_proxy_->ResolveNetworkProxy(source_url_,
+                                     signal_interface_,
+                                     signal_name_,
+                                     &error);
+
+    if (error) {
+      LOG(ERROR) << "Call to ResolveNetworkProxy failed: "
+                 << error->GetMessage();
+      timeout_callback_.Cancel();
+      Quit();
+    }
+  }
+
+  // Handle incoming ProxyResolved signal.
+  void HandleProxyResolvedSignal(const std::string& source_url,
+                                 const std::string& proxy_info,
+                                 const std::string& error_message) {
+    timeout_callback_.Cancel();
+    proxies_ = ParseProxyString(proxy_info);
+    LOG(INFO) << "Found proxies via browser signal: "
+              << JoinString(proxies_, 'x');
+
+    Quit();
+  }
+
+  int OnInit() override {
+    int return_code = chromeos::DBusDaemon::OnInit();
+    if (return_code != EX_OK)
+      return return_code;
+
+    // Initialize D-Bus proxies.
+    call_proxy_.reset(
+        new org::chromium::LibCrosServiceInterfaceProxy(bus_,
+                                                        kLibCrosServiceName));
+    signal_proxy_.reset(
+        new org::chromium::CrashReporterLibcrosProxyResolvedInterfaceProxy(
+            bus_,
+            kLibCrosServiceName));
+
+    // Set up the D-Bus signal handler.
+    // TODO(crbug.com/446115): Update ResolveNetworkProxy call to use an
+    //     asynchronous return value rather than a return signal.
+    signal_proxy_->RegisterProxyResolvedSignalHandler(
+        base::Bind(&ProxyResolver::HandleProxyResolvedSignal,
+                   weak_ptr_factory_.GetWeakPtr()),
+        base::Bind(&ProxyResolver::HandleDBusSignalConnected,
+                   weak_ptr_factory_.GetWeakPtr()));
+
+    return EX_OK;
+  }
+
+ private:
+  unique_ptr<org::chromium::LibCrosServiceInterfaceProxy> call_proxy_;
+  unique_ptr<org::chromium::CrashReporterLibcrosProxyResolvedInterfaceProxy>
+      signal_proxy_;
+
+  const std::string source_url_;
+  const std::string signal_interface_;
+  const std::string signal_name_;
+  base::TimeDelta timeout_;
+
+  std::vector<std::string> proxies_;
+  base::WeakPtrFactory<ProxyResolver> weak_ptr_factory_;
+
+  base::CancelableClosure timeout_callback_;
+
+  DISALLOW_COPY_AND_ASSIGN(ProxyResolver);
+};
+
+static bool ShowBrowserProxies(std::string url, base::TimeDelta timeout) {
+  // Initialize and run the proxy resolver to watch for signals.
+  ProxyResolver resolver(url,
+                         kLibCrosProxyResolvedSignalInterface,
+                         kLibCrosProxyResolvedName,
+                         timeout);
+  resolver.Run();
+
+  std::vector<std::string> proxies = resolver.proxies();
+
+  // If proxies is empty, then the timeout was reached waiting for the proxy
+  // resolved signal.  If no proxies are defined, proxies will be populated
+  // with "direct://".
+  if (proxies.empty())
+    return false;
+
+  for (const auto& proxy : proxies) {
+    printf("%s\n", proxy.c_str());
+  }
+  return true;
+}
+
+}  // namespace
+
+int main(int argc, char *argv[]) {
+  base::CommandLine::Init(argc, argv);
+  base::CommandLine* cl = base::CommandLine::ForCurrentProcess();
+
+  if (cl->HasSwitch(kHelp)) {
+    LOG(INFO) << kHelpMessage;
+    return 0;
+  }
+
+  bool quiet = cl->HasSwitch(kQuiet);
+  bool verbose = cl->HasSwitch(kVerbose);
+
+  int timeout = kTimeoutDefaultSeconds;
+  std::string str_timeout = cl->GetSwitchValueASCII(kTimeout);
+  if (!str_timeout.empty() && !base::StringToInt(str_timeout, &timeout)) {
+    LOG(ERROR) << "Invalid timeout value: " << str_timeout;
+    return 1;
+  }
+
+  // Default to logging to syslog.
+  int init_flags = chromeos::kLogToSyslog;
+  // Log to stderr if a TTY (and "-quiet" wasn't passed), or if "-verbose"
+  // was passed.
+
+  if ((!quiet && isatty(STDERR_FILENO)) || verbose)
+    init_flags |= chromeos::kLogToStderr;
+  chromeos::InitLog(init_flags);
+
+  std::string url;
+  base::CommandLine::StringVector urls = cl->GetArgs();
+  if (!urls.empty()) {
+    url = urls[0];
+    LOG(INFO) << "Resolving proxies for URL: " << url;
+  } else {
+    LOG(INFO) << "Resolving proxies without URL";
+  }
+
+  if (!ShowBrowserProxies(url, base::TimeDelta::FromSeconds(timeout))) {
+    LOG(ERROR) << "Error resolving proxies via the browser";
+    LOG(INFO) << "Assuming direct proxy";
+    printf("%s\n", kNoProxy);
+  }
+
+  return 0;
+}
diff --git a/base/test_utils.h b/crash_reporter/testrunner.cc
similarity index 73%
rename from base/test_utils.h
rename to crash_reporter/testrunner.cc
index 132d3a7..a8c717e 100644
--- a/base/test_utils.h
+++ b/crash_reporter/testrunner.cc
@@ -14,19 +14,10 @@
  * limitations under the License.
  */
 
-#ifndef TEST_UTILS_H
-#define TEST_UTILS_H
+#include <chromeos/test_helpers.h>
+#include <gtest/gtest.h>
 
-class TemporaryFile {
- public:
-  TemporaryFile();
-  ~TemporaryFile();
-
-  int fd;
-  char filename[1024];
-
- private:
-  void init(const char* tmp_dir);
-};
-
-#endif // TEST_UTILS_H
+int main(int argc, char** argv) {
+  SetUpTests(&argc, argv, true);
+  return RUN_ALL_TESTS();
+}
diff --git a/crash_reporter/udev_collector.cc b/crash_reporter/udev_collector.cc
new file mode 100644
index 0000000..576fdbd
--- /dev/null
+++ b/crash_reporter/udev_collector.cc
@@ -0,0 +1,244 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "udev_collector.h"
+
+#include <map>
+#include <utility>
+#include <vector>
+
+#include <base/files/file_enumerator.h>
+#include <base/files/file_util.h>
+#include <base/logging.h>
+#include <base/strings/string_number_conversions.h>
+#include <base/strings/string_split.h>
+#include <base/strings/string_util.h>
+#include <base/strings/stringprintf.h>
+#include <chromeos/process.h>
+
+using base::FilePath;
+
+namespace {
+
+const char kCollectUdevSignature[] = "crash_reporter-udev-collection";
+const char kGzipPath[] = "/bin/gzip";
+const char kUdevExecName[] = "udev";
+const char kUdevSignatureKey[] = "sig";
+const char kUdevSubsystemDevCoredump[] = "devcoredump";
+const char kDefaultDevCoredumpDirectory[] = "/sys/class/devcoredump";
+const char kDevCoredumpFilePrefixFormat[] = "devcoredump_%s";
+
+}  // namespace
+
+UdevCollector::UdevCollector()
+    : dev_coredump_directory_(kDefaultDevCoredumpDirectory) {}
+
+UdevCollector::~UdevCollector() {}
+
+bool UdevCollector::HandleCrash(const std::string &udev_event) {
+  if (IsDeveloperImage()) {
+    LOG(INFO) << "developer image - collect udev crash info.";
+  } else if (is_feedback_allowed_function_()) {
+    LOG(INFO) << "Consent given - collect udev crash info.";
+  } else {
+    LOG(INFO) << "Ignoring - Non-developer image and no consent given.";
+    return false;
+  }
+
+  // Process the udev event string.
+  // First get all the key-value pairs.
+  std::vector<std::pair<std::string, std::string>> udev_event_keyval;
+  base::SplitStringIntoKeyValuePairs(udev_event, '=', ':', &udev_event_keyval);
+  std::vector<std::pair<std::string, std::string>>::const_iterator iter;
+  std::map<std::string, std::string> udev_event_map;
+  for (iter = udev_event_keyval.begin();
+       iter != udev_event_keyval.end();
+       ++iter) {
+    udev_event_map[iter->first] = iter->second;
+  }
+
+  // Make sure the crash directory exists, or create it if it doesn't.
+  FilePath crash_directory;
+  if (!GetCreatedCrashDirectoryByEuid(0, &crash_directory, nullptr)) {
+    LOG(ERROR) << "Could not get crash directory.";
+    return false;
+  }
+
+  if (udev_event_map["SUBSYSTEM"] == kUdevSubsystemDevCoredump) {
+    int instance_number;
+    if (!base::StringToInt(udev_event_map["KERNEL_NUMBER"], &instance_number)) {
+      LOG(ERROR) << "Invalid kernel number: "
+                 << udev_event_map["KERNEL_NUMBER"];
+      return false;
+    }
+    return ProcessDevCoredump(crash_directory, instance_number);
+  }
+
+  return ProcessUdevCrashLogs(crash_directory,
+                              udev_event_map["ACTION"],
+                              udev_event_map["KERNEL"],
+                              udev_event_map["SUBSYSTEM"]);
+}
+
+bool UdevCollector::ProcessUdevCrashLogs(const FilePath& crash_directory,
+                                         const std::string& action,
+                                         const std::string& kernel,
+                                         const std::string& subsystem) {
+  // Construct the basename string for crash_reporter_logs.conf:
+  //   "crash_reporter-udev-collection-[action]-[name]-[subsystem]"
+  // If a udev field is not provided, "" is used in its place, e.g.:
+  //   "crash_reporter-udev-collection-[action]--[subsystem]"
+  // Hence, "" is used as a wildcard name string.
+  // TODO(sque, crosbug.com/32238): Implement wildcard checking.
+  std::string basename = action + "-" + kernel + "-" + subsystem;
+  std::string udev_log_name = std::string(kCollectUdevSignature) + '-' +
+                              basename;
+
+  // Create the destination path.
+  std::string log_file_name =
+      FormatDumpBasename(basename, time(nullptr), 0);
+  FilePath crash_path = GetCrashPath(crash_directory, log_file_name, "log");
+
+  // Handle the crash.
+  bool result = GetLogContents(log_config_path_, udev_log_name, crash_path);
+  if (!result) {
+    LOG(ERROR) << "Error reading udev log info " << udev_log_name;
+    return false;
+  }
+
+  // Compress the output using gzip.
+  chromeos::ProcessImpl gzip_process;
+  gzip_process.AddArg(kGzipPath);
+  gzip_process.AddArg(crash_path.value());
+  int process_result = gzip_process.Run();
+  FilePath crash_path_zipped = FilePath(crash_path.value() + ".gz");
+  // If the zip file was not created, use the uncompressed file.
+  if (process_result != 0 || !base::PathExists(crash_path_zipped))
+    LOG(ERROR) << "Could not create zip file " << crash_path_zipped.value();
+  else
+    crash_path = crash_path_zipped;
+
+  std::string exec_name = std::string(kUdevExecName) + "-" + subsystem;
+  AddCrashMetaData(kUdevSignatureKey, udev_log_name);
+  WriteCrashMetaData(GetCrashPath(crash_directory, log_file_name, "meta"),
+                     exec_name, crash_path.value());
+  return true;
+}
+
+bool UdevCollector::ProcessDevCoredump(const FilePath& crash_directory,
+                                       int instance_number) {
+  FilePath coredump_path =
+      FilePath(base::StringPrintf("%s/devcd%d/data",
+                                  dev_coredump_directory_.c_str(),
+                                  instance_number));
+  if (!base::PathExists(coredump_path)) {
+    LOG(ERROR) << "Device coredump file " << coredump_path.value()
+               << " does not exist";
+    return false;
+  }
+
+  // Add coredump file to the crash directory.
+  if (!AppendDevCoredump(crash_directory, coredump_path, instance_number)) {
+    ClearDevCoredump(coredump_path);
+    return false;
+  }
+
+  // Clear the coredump data to allow generation of future device coredumps
+  // without having to wait for the 5-minutes timeout.
+  return ClearDevCoredump(coredump_path);
+}
+
+bool UdevCollector::AppendDevCoredump(const FilePath& crash_directory,
+                                      const FilePath& coredump_path,
+                                      int instance_number) {
+  // Retrieve the driver name of the failing device.
+  std::string driver_name = GetFailingDeviceDriverName(instance_number);
+  if (driver_name.empty()) {
+    LOG(ERROR) << "Failed to obtain driver name for instance: "
+               << instance_number;
+    return false;
+  }
+
+  std::string coredump_prefix =
+      base::StringPrintf(kDevCoredumpFilePrefixFormat, driver_name.c_str());
+
+  std::string dump_basename = FormatDumpBasename(coredump_prefix,
+                                                 time(nullptr),
+                                                 instance_number);
+  FilePath core_path = GetCrashPath(crash_directory, dump_basename, "devcore");
+  FilePath log_path = GetCrashPath(crash_directory, dump_basename, "log");
+  FilePath meta_path = GetCrashPath(crash_directory, dump_basename, "meta");
+
+  // Collect coredump data.
+  if (!base::CopyFile(coredump_path, core_path)) {
+    LOG(ERROR) << "Failed to copy device coredumpm file from "
+               << coredump_path.value() << " to " << core_path.value();
+    return false;
+  }
+
+  // Collect additional logs if one is specified in the config file.
+  std::string udev_log_name = std::string(kCollectUdevSignature) + '-' +
+      kUdevSubsystemDevCoredump + '-' + driver_name;
+  bool result = GetLogContents(log_config_path_, udev_log_name, log_path);
+  if (result) {
+    AddCrashMetaUploadFile("logs", log_path.value());
+  }
+
+  WriteCrashMetaData(meta_path, coredump_prefix, core_path.value());
+
+  return true;
+}
+
+bool UdevCollector::ClearDevCoredump(const FilePath& coredump_path) {
+  if (!base::WriteFile(coredump_path, "0", 1)) {
+    LOG(ERROR) << "Failed to delete the coredump data file "
+               << coredump_path.value();
+    return false;
+  }
+  return true;
+}
+
+std::string UdevCollector::GetFailingDeviceDriverName(int instance_number) {
+  FilePath failing_uevent_path =
+      FilePath(base::StringPrintf("%s/devcd%d/failing_device/uevent",
+                                  dev_coredump_directory_.c_str(),
+                                  instance_number));
+  if (!base::PathExists(failing_uevent_path)) {
+    LOG(ERROR) << "Failing uevent path " << failing_uevent_path.value()
+               << " does not exist";
+    return "";
+  }
+
+  std::string uevent_content;
+  if (!base::ReadFileToString(failing_uevent_path, &uevent_content)) {
+    LOG(ERROR) << "Failed to read uevent file " << failing_uevent_path.value();
+    return "";
+  }
+
+  // Parse uevent file contents as key-value pairs.
+  std::vector<std::pair<std::string, std::string>> uevent_keyval;
+  base::SplitStringIntoKeyValuePairs(uevent_content, '=', '\n', &uevent_keyval);
+  std::vector<std::pair<std::string, std::string>>::const_iterator iter;
+  for (iter = uevent_keyval.begin();
+       iter != uevent_keyval.end();
+       ++iter) {
+    if (iter->first == "DRIVER") {
+      return iter->second;
+    }
+  }
+
+  return "";
+}
diff --git a/crash_reporter/udev_collector.h b/crash_reporter/udev_collector.h
new file mode 100644
index 0000000..e267b75
--- /dev/null
+++ b/crash_reporter/udev_collector.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CRASH_REPORTER_UDEV_COLLECTOR_H_
+#define CRASH_REPORTER_UDEV_COLLECTOR_H_
+
+#include <string>
+
+#include <base/files/file_path.h>
+#include <base/macros.h>
+#include <gtest/gtest_prod.h>  // for FRIEND_TEST
+
+#include "crash_collector.h"
+
+// Udev crash collector.
+class UdevCollector : public CrashCollector {
+ public:
+  UdevCollector();
+
+  ~UdevCollector() override;
+
+  // The udev event string should be formatted as follows:
+  //   "ACTION=[action]:KERNEL=[name]:SUBSYSTEM=[subsystem]"
+  // The values don't have to be in any particular order. One or more of them
+  // could be omitted, in which case it would be treated as a wildcard (*).
+  bool HandleCrash(const std::string& udev_event);
+
+ protected:
+  std::string dev_coredump_directory_;
+
+ private:
+  friend class UdevCollectorTest;
+
+  // Process udev crash logs, collecting log files according to the config
+  // file (crash_reporter_logs.conf).
+  bool ProcessUdevCrashLogs(const base::FilePath& crash_directory,
+                            const std::string& action,
+                            const std::string& kernel,
+                            const std::string& subsystem);
+  // Process device coredump, collecting device coredump file.
+  // |instance_number| is the kernel number of the virtual device for the device
+  // coredump instance.
+  bool ProcessDevCoredump(const base::FilePath& crash_directory,
+                          int instance_number);
+  // Copy device coredump file to crash directory, and perform necessary
+  // coredump file management.
+  bool AppendDevCoredump(const base::FilePath& crash_directory,
+                         const base::FilePath& coredump_path,
+                         int instance_number);
+  // Clear the device coredump file by performing a dummy write to it.
+  bool ClearDevCoredump(const base::FilePath& coredump_path);
+  // Return the driver name of the device that generates the coredump.
+  std::string GetFailingDeviceDriverName(int instance_number);
+
+  // Mutator for unit testing.
+  void set_log_config_path(const std::string& path) {
+    log_config_path_ = base::FilePath(path);
+  }
+
+  DISALLOW_COPY_AND_ASSIGN(UdevCollector);
+};
+
+#endif  // CRASH_REPORTER_UDEV_COLLECTOR_H_
diff --git a/crash_reporter/udev_collector_test.cc b/crash_reporter/udev_collector_test.cc
new file mode 100644
index 0000000..a6643fb
--- /dev/null
+++ b/crash_reporter/udev_collector_test.cc
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <base/files/file_enumerator.h>
+#include <base/files/file_util.h>
+#include <base/files/scoped_temp_dir.h>
+#include <base/strings/stringprintf.h>
+#include <chromeos/syslog_logging.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "udev_collector.h"
+
+using base::FilePath;
+
+namespace {
+
+// Dummy log config file name.
+const char kLogConfigFileName[] = "log_config_file";
+
+// Dummy directory for storing device coredumps.
+const char kDevCoredumpDirectory[] = "devcoredump";
+
+// A bunch of random rules to put into the dummy log config file.
+const char kLogConfigFileContents[] =
+    "crash_reporter-udev-collection-change-card0-drm=echo change card0 drm\n"
+    "crash_reporter-udev-collection-add-state0-cpu=echo change state0 cpu\n"
+    "crash_reporter-udev-collection-devcoredump-iwlwifi=echo devcoredump\n"
+    "cros_installer=echo not for udev";
+
+const char kCrashLogFilePattern[] = "*.log.gz";
+const char kDevCoredumpFilePattern[] = "*.devcore";
+
+// Dummy content for device coredump data file.
+const char kDevCoredumpDataContents[] = "coredump";
+
+// Content for failing device's uevent file.
+const char kFailingDeviceUeventContents[] = "DRIVER=iwlwifi\n";
+
+void CountCrash() {}
+
+bool s_consent_given = true;
+
+bool IsMetrics() {
+  return s_consent_given;
+}
+
+// Returns the number of files found in the given path that matches the
+// specified file name pattern.
+int GetNumFiles(const FilePath& path, const std::string& file_pattern) {
+  base::FileEnumerator enumerator(path, false, base::FileEnumerator::FILES,
+                                  file_pattern);
+  int num_files = 0;
+  for (FilePath file_path = enumerator.Next();
+       !file_path.value().empty();
+       file_path = enumerator.Next()) {
+    num_files++;
+  }
+  return num_files;
+}
+
+}  // namespace
+
+class UdevCollectorMock : public UdevCollector {
+ public:
+  MOCK_METHOD0(SetUpDBus, void());
+};
+
+class UdevCollectorTest : public ::testing::Test {
+ protected:
+  base::ScopedTempDir temp_dir_generator_;
+
+  void HandleCrash(const std::string &udev_event) {
+    collector_.HandleCrash(udev_event);
+  }
+
+  void GenerateDevCoredump(const std::string& device_name) {
+    // Generate coredump data file.
+    ASSERT_TRUE(CreateDirectory(
+        FilePath(base::StringPrintf("%s/%s",
+                                    collector_.dev_coredump_directory_.c_str(),
+                                    device_name.c_str()))));
+    FilePath data_path =
+        FilePath(base::StringPrintf("%s/%s/data",
+                                    collector_.dev_coredump_directory_.c_str(),
+                                    device_name.c_str()));
+    ASSERT_EQ(strlen(kDevCoredumpDataContents),
+              base::WriteFile(data_path,
+                              kDevCoredumpDataContents,
+                              strlen(kDevCoredumpDataContents)));
+    // Generate uevent file for failing device.
+    ASSERT_TRUE(CreateDirectory(
+        FilePath(base::StringPrintf("%s/%s/failing_device",
+                                    collector_.dev_coredump_directory_.c_str(),
+                                    device_name.c_str()))));
+    FilePath uevent_path =
+        FilePath(base::StringPrintf("%s/%s/failing_device/uevent",
+                                    collector_.dev_coredump_directory_.c_str(),
+                                    device_name.c_str()));
+    ASSERT_EQ(strlen(kFailingDeviceUeventContents),
+              base::WriteFile(uevent_path,
+                              kFailingDeviceUeventContents,
+                              strlen(kFailingDeviceUeventContents)));
+  }
+
+ private:
+  void SetUp() override {
+    s_consent_given = true;
+
+    EXPECT_CALL(collector_, SetUpDBus()).WillRepeatedly(testing::Return());
+
+    collector_.Initialize(CountCrash, IsMetrics);
+
+    ASSERT_TRUE(temp_dir_generator_.CreateUniqueTempDir());
+
+    FilePath log_config_path =
+        temp_dir_generator_.path().Append(kLogConfigFileName);
+    collector_.log_config_path_ = log_config_path;
+    collector_.ForceCrashDirectory(temp_dir_generator_.path());
+
+    FilePath dev_coredump_path =
+        temp_dir_generator_.path().Append(kDevCoredumpDirectory);
+    collector_.dev_coredump_directory_ = dev_coredump_path.value();
+
+    // Write to a dummy log config file.
+    ASSERT_EQ(strlen(kLogConfigFileContents),
+              base::WriteFile(log_config_path,
+                              kLogConfigFileContents,
+                              strlen(kLogConfigFileContents)));
+
+    chromeos::ClearLog();
+  }
+
+  UdevCollectorMock collector_;
+};
+
+TEST_F(UdevCollectorTest, TestNoConsent) {
+  s_consent_given = false;
+  HandleCrash("ACTION=change:KERNEL=card0:SUBSYSTEM=drm");
+  EXPECT_EQ(0, GetNumFiles(temp_dir_generator_.path(), kCrashLogFilePattern));
+}
+
+TEST_F(UdevCollectorTest, TestNoMatch) {
+  // No rule should match this.
+  HandleCrash("ACTION=change:KERNEL=foo:SUBSYSTEM=bar");
+  EXPECT_EQ(0, GetNumFiles(temp_dir_generator_.path(), kCrashLogFilePattern));
+}
+
+TEST_F(UdevCollectorTest, TestMatches) {
+  // Try multiple udev events in sequence.  The number of log files generated
+  // should increase.
+  HandleCrash("ACTION=change:KERNEL=card0:SUBSYSTEM=drm");
+  EXPECT_EQ(1, GetNumFiles(temp_dir_generator_.path(), kCrashLogFilePattern));
+  HandleCrash("ACTION=add:KERNEL=state0:SUBSYSTEM=cpu");
+  EXPECT_EQ(2, GetNumFiles(temp_dir_generator_.path(), kCrashLogFilePattern));
+}
+
+TEST_F(UdevCollectorTest, TestDevCoredump) {
+  GenerateDevCoredump("devcd0");
+  HandleCrash("ACTION=add:KERNEL_NUMBER=0:SUBSYSTEM=devcoredump");
+  EXPECT_EQ(1, GetNumFiles(temp_dir_generator_.path(),
+                           kDevCoredumpFilePattern));
+  GenerateDevCoredump("devcd1");
+  HandleCrash("ACTION=add:KERNEL_NUMBER=1:SUBSYSTEM=devcoredump");
+  EXPECT_EQ(2, GetNumFiles(temp_dir_generator_.path(),
+                           kDevCoredumpFilePattern));
+}
+
+// TODO(sque, crosbug.com/32238) - test wildcard cases, multiple identical udev
+// events.
diff --git a/crash_reporter/unclean_shutdown_collector.cc b/crash_reporter/unclean_shutdown_collector.cc
new file mode 100644
index 0000000..8a092ec
--- /dev/null
+++ b/crash_reporter/unclean_shutdown_collector.cc
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "unclean_shutdown_collector.h"
+
+#include <base/files/file_util.h>
+#include <base/logging.h>
+
+static const char kUncleanShutdownFile[] =
+    "/var/lib/crash_reporter/pending_clean_shutdown";
+
+// Files created by power manager used for crash reporting.
+static const char kPowerdTracePath[] = "/var/lib/power_manager";
+// Presence of this file indicates that the system was suspended
+static const char kPowerdSuspended[] = "powerd_suspended";
+
+using base::FilePath;
+
+UncleanShutdownCollector::UncleanShutdownCollector()
+    : unclean_shutdown_file_(kUncleanShutdownFile),
+      powerd_trace_path_(kPowerdTracePath),
+      powerd_suspended_file_(powerd_trace_path_.Append(kPowerdSuspended)) {
+}
+
+UncleanShutdownCollector::~UncleanShutdownCollector() {
+}
+
+bool UncleanShutdownCollector::Enable() {
+  FilePath file_path(unclean_shutdown_file_);
+  base::CreateDirectory(file_path.DirName());
+  if (base::WriteFile(file_path, "", 0) != 0) {
+    LOG(ERROR) << "Unable to create shutdown check file";
+    return false;
+  }
+  return true;
+}
+
+bool UncleanShutdownCollector::DeleteUncleanShutdownFiles() {
+  if (!base::DeleteFile(FilePath(unclean_shutdown_file_), false)) {
+    LOG(ERROR) << "Failed to delete unclean shutdown file "
+               << unclean_shutdown_file_;
+    return false;
+  }
+  // Delete power manager state file if it exists.
+  base::DeleteFile(powerd_suspended_file_, false);
+  return true;
+}
+
+bool UncleanShutdownCollector::Collect() {
+  FilePath unclean_file_path(unclean_shutdown_file_);
+  if (!base::PathExists(unclean_file_path)) {
+    return false;
+  }
+  LOG(WARNING) << "Last shutdown was not clean";
+  if (DeadBatteryCausedUncleanShutdown()) {
+    DeleteUncleanShutdownFiles();
+    return false;
+  }
+  DeleteUncleanShutdownFiles();
+
+  if (is_feedback_allowed_function_()) {
+    count_crash_function_();
+  }
+  return true;
+}
+
+bool UncleanShutdownCollector::Disable() {
+  LOG(INFO) << "Clean shutdown signalled";
+  return DeleteUncleanShutdownFiles();
+}
+
+bool UncleanShutdownCollector::DeadBatteryCausedUncleanShutdown() {
+  // Check for case of battery running out while suspended.
+  if (base::PathExists(powerd_suspended_file_)) {
+    LOG(INFO) << "Unclean shutdown occurred while suspended. Not counting "
+              << "toward unclean shutdown statistic.";
+    return true;
+  }
+  return false;
+}
diff --git a/crash_reporter/unclean_shutdown_collector.h b/crash_reporter/unclean_shutdown_collector.h
new file mode 100644
index 0000000..5bc9968
--- /dev/null
+++ b/crash_reporter/unclean_shutdown_collector.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CRASH_REPORTER_UNCLEAN_SHUTDOWN_COLLECTOR_H_
+#define CRASH_REPORTER_UNCLEAN_SHUTDOWN_COLLECTOR_H_
+
+#include <string>
+
+#include <base/files/file_path.h>
+#include <base/macros.h>
+#include <gtest/gtest_prod.h>  // for FRIEND_TEST
+
+#include "crash_collector.h"
+
+// Unclean shutdown collector.
+class UncleanShutdownCollector : public CrashCollector {
+ public:
+  UncleanShutdownCollector();
+  ~UncleanShutdownCollector() override;
+
+  // Enable collection - signal that a boot has started.
+  bool Enable();
+
+  // Collect if there is was an unclean shutdown. Returns true if
+  // there was, false otherwise.
+  bool Collect();
+
+  // Disable collection - signal that the system has been shutdown cleanly.
+  bool Disable();
+
+ private:
+  friend class UncleanShutdownCollectorTest;
+  FRIEND_TEST(UncleanShutdownCollectorTest, EnableCannotWrite);
+  FRIEND_TEST(UncleanShutdownCollectorTest, CollectDeadBatterySuspended);
+
+  bool DeleteUncleanShutdownFiles();
+
+  // Check for unclean shutdown due to battery running out by analyzing powerd
+  // trace files.
+  bool DeadBatteryCausedUncleanShutdown();
+
+  const char *unclean_shutdown_file_;
+  base::FilePath powerd_trace_path_;
+  base::FilePath powerd_suspended_file_;
+
+  DISALLOW_COPY_AND_ASSIGN(UncleanShutdownCollector);
+};
+
+#endif  // CRASH_REPORTER_UNCLEAN_SHUTDOWN_COLLECTOR_H_
diff --git a/crash_reporter/unclean_shutdown_collector_test.cc b/crash_reporter/unclean_shutdown_collector_test.cc
new file mode 100644
index 0000000..c5c0662
--- /dev/null
+++ b/crash_reporter/unclean_shutdown_collector_test.cc
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "unclean_shutdown_collector.h"
+
+#include <unistd.h>
+
+#include <base/files/file_util.h>
+#include <base/strings/string_util.h>
+#include <chromeos/syslog_logging.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+using base::FilePath;
+using ::chromeos::FindLog;
+
+namespace {
+
+int s_crashes = 0;
+bool s_metrics = true;
+
+const char kTestDirectory[] = "test";
+const char kTestSuspended[] = "test/suspended";
+const char kTestUnclean[] = "test/unclean";
+
+void CountCrash() {
+  ++s_crashes;
+}
+
+bool IsMetrics() {
+  return s_metrics;
+}
+
+}  // namespace
+
+class UncleanShutdownCollectorMock : public UncleanShutdownCollector {
+ public:
+  MOCK_METHOD0(SetUpDBus, void());
+};
+
+class UncleanShutdownCollectorTest : public ::testing::Test {
+  void SetUp() {
+    s_crashes = 0;
+
+    EXPECT_CALL(collector_, SetUpDBus()).WillRepeatedly(testing::Return());
+
+    collector_.Initialize(CountCrash,
+                          IsMetrics);
+    rmdir(kTestDirectory);
+    test_unclean_ = FilePath(kTestUnclean);
+    collector_.unclean_shutdown_file_ = kTestUnclean;
+    base::DeleteFile(test_unclean_, true);
+    // Set up an alternate power manager state file as well
+    collector_.powerd_suspended_file_ = FilePath(kTestSuspended);
+    chromeos::ClearLog();
+  }
+
+ protected:
+  void WriteStringToFile(const FilePath &file_path,
+                         const char *data) {
+    ASSERT_EQ(strlen(data), base::WriteFile(file_path, data, strlen(data)));
+  }
+
+  UncleanShutdownCollectorMock collector_;
+  FilePath test_unclean_;
+};
+
+TEST_F(UncleanShutdownCollectorTest, EnableWithoutParent) {
+  ASSERT_TRUE(collector_.Enable());
+  ASSERT_TRUE(base::PathExists(test_unclean_));
+}
+
+TEST_F(UncleanShutdownCollectorTest, EnableWithParent) {
+  mkdir(kTestDirectory, 0777);
+  ASSERT_TRUE(collector_.Enable());
+  ASSERT_TRUE(base::PathExists(test_unclean_));
+}
+
+TEST_F(UncleanShutdownCollectorTest, EnableCannotWrite) {
+  collector_.unclean_shutdown_file_ = "/bad/path";
+  ASSERT_FALSE(collector_.Enable());
+  ASSERT_TRUE(FindLog("Unable to create shutdown check file"));
+}
+
+TEST_F(UncleanShutdownCollectorTest, CollectTrue) {
+  ASSERT_TRUE(collector_.Enable());
+  ASSERT_TRUE(base::PathExists(test_unclean_));
+  ASSERT_TRUE(collector_.Collect());
+  ASSERT_FALSE(base::PathExists(test_unclean_));
+  ASSERT_EQ(1, s_crashes);
+  ASSERT_TRUE(FindLog("Last shutdown was not clean"));
+}
+
+TEST_F(UncleanShutdownCollectorTest, CollectFalse) {
+  ASSERT_FALSE(collector_.Collect());
+  ASSERT_EQ(0, s_crashes);
+}
+
+TEST_F(UncleanShutdownCollectorTest, CollectDeadBatterySuspended) {
+  ASSERT_TRUE(collector_.Enable());
+  ASSERT_TRUE(base::PathExists(test_unclean_));
+  base::WriteFile(collector_.powerd_suspended_file_, "", 0);
+  ASSERT_FALSE(collector_.Collect());
+  ASSERT_FALSE(base::PathExists(test_unclean_));
+  ASSERT_FALSE(base::PathExists(collector_.powerd_suspended_file_));
+  ASSERT_EQ(0, s_crashes);
+  ASSERT_TRUE(FindLog("Unclean shutdown occurred while suspended."));
+}
+
+TEST_F(UncleanShutdownCollectorTest, Disable) {
+  ASSERT_TRUE(collector_.Enable());
+  ASSERT_TRUE(base::PathExists(test_unclean_));
+  ASSERT_TRUE(collector_.Disable());
+  ASSERT_FALSE(base::PathExists(test_unclean_));
+  ASSERT_FALSE(collector_.Collect());
+}
+
+TEST_F(UncleanShutdownCollectorTest, DisableWhenNotEnabled) {
+  ASSERT_TRUE(collector_.Disable());
+}
+
+TEST_F(UncleanShutdownCollectorTest, CantDisable) {
+  mkdir(kTestDirectory, 0700);
+  if (mkdir(kTestUnclean, 0700)) {
+    ASSERT_EQ(EEXIST, errno)
+        << "Error while creating directory '" << kTestUnclean
+        << "': " << strerror(errno);
+  }
+  ASSERT_EQ(0, base::WriteFile(test_unclean_.Append("foo"), "", 0))
+      << "Error while creating empty file '"
+      << test_unclean_.Append("foo").value() << "': " << strerror(errno);
+  ASSERT_FALSE(collector_.Disable());
+  rmdir(kTestUnclean);
+}
diff --git a/crash_reporter/user_collector.cc b/crash_reporter/user_collector.cc
new file mode 100644
index 0000000..61ccc37
--- /dev/null
+++ b/crash_reporter/user_collector.cc
@@ -0,0 +1,582 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "user_collector.h"
+
+#include <elf.h>
+#include <fcntl.h>
+#include <grp.h>  // For struct group.
+#include <pcrecpp.h>
+#include <pwd.h>  // For struct passwd.
+#include <stdint.h>
+#include <sys/cdefs.h>  // For __WORDSIZE
+#include <sys/types.h>  // For getpwuid_r, getgrnam_r, WEXITSTATUS.
+#include <unistd.h>  // For setgroups
+
+#include <string>
+#include <vector>
+
+#include <base/files/file_util.h>
+#include <base/logging.h>
+#include <base/posix/eintr_wrapper.h>
+#include <base/strings/string_split.h>
+#include <base/strings/string_util.h>
+#include <base/strings/stringprintf.h>
+#include <chromeos/process.h>
+#include <chromeos/syslog_logging.h>
+#include <cutils/properties.h>
+#include <private/android_filesystem_config.h>
+
+static const char kCollectionErrorSignature[] =
+    "crash_reporter-user-collection";
+static const char kCorePatternProperty[] = "crash_reporter.coredump.enabled";
+static const char kCoreToMinidumpConverterPath[] = "/system/bin/core2md";
+
+static const char kStatePrefix[] = "State:\t";
+
+static const char kCoreTempFolder[] = "/data/misc/crash_reporter/tmp";
+
+// Define an otherwise invalid value that represents an unknown UID.
+static const uid_t kUnknownUid = -1;
+
+const char *UserCollector::kUserId = "Uid:\t";
+const char *UserCollector::kGroupId = "Gid:\t";
+
+using base::FilePath;
+using base::StringPrintf;
+
+UserCollector::UserCollector()
+    : generate_diagnostics_(false),
+      initialized_(false) {
+}
+
+void UserCollector::Initialize(
+    UserCollector::CountCrashFunction count_crash_function,
+    const std::string &our_path,
+    UserCollector::IsFeedbackAllowedFunction is_feedback_allowed_function,
+    bool generate_diagnostics,
+    bool core2md_failure,
+    bool directory_failure,
+    const std::string &filter_in) {
+  CrashCollector::Initialize(count_crash_function,
+                             is_feedback_allowed_function);
+  our_path_ = our_path;
+  initialized_ = true;
+  generate_diagnostics_ = generate_diagnostics;
+  core2md_failure_ = core2md_failure;
+  directory_failure_ = directory_failure;
+  filter_in_ = filter_in;
+
+  gid_t groups[] = { AID_SYSTEM, AID_DBUS };
+  if (setgroups(arraysize(groups), groups) != 0) {
+    PLOG(FATAL) << "Unable to set groups to system and dbus";
+  }
+}
+
+UserCollector::~UserCollector() {
+}
+
+std::string UserCollector::GetErrorTypeSignature(ErrorType error_type) const {
+  switch (error_type) {
+    case kErrorSystemIssue:
+      return "system-issue";
+    case kErrorReadCoreData:
+      return "read-core-data";
+    case kErrorUnusableProcFiles:
+      return "unusable-proc-files";
+    case kErrorInvalidCoreFile:
+      return "invalid-core-file";
+    case kErrorUnsupported32BitCoreFile:
+      return "unsupported-32bit-core-file";
+    case kErrorCore2MinidumpConversion:
+      return "core2md-conversion";
+    default:
+      return "";
+  }
+}
+
+// Return the string that should be used for the kernel's core_pattern file.
+// Note that if you change the format of the enabled pattern, you'll probably
+// also need to change the ParseCrashAttributes() function below, the
+// user_collector_test.cc unittest, and the logging_UserCrash.py autotest.
+std::string UserCollector::GetPattern(bool enabled) const {
+  if (enabled) {
+    // Combine the four crash attributes into one parameter to try to reduce
+    // the size of the invocation line for crash_reporter, since the kernel
+    // has a fixed-sized (128B) buffer for it (before parameter expansion).
+    // Note that the kernel does not support quoted arguments in core_pattern.
+    return StringPrintf("|%s --user=%%P:%%s:%%u:%%e", our_path_.c_str());
+  } else {
+    return "core";
+  }
+}
+
+bool UserCollector::SetUpInternal(bool enabled) {
+  CHECK(initialized_);
+  LOG(INFO) << (enabled ? "Enabling" : "Disabling") << " user crash handling";
+
+  property_set(kCorePatternProperty, enabled ? "1" : "0");
+
+  return true;
+}
+
+bool UserCollector::GetFirstLineWithPrefix(
+    const std::vector<std::string> &lines,
+    const char *prefix, std::string *line) {
+  std::vector<std::string>::const_iterator line_iterator;
+  for (line_iterator = lines.begin(); line_iterator != lines.end();
+       ++line_iterator) {
+    if (line_iterator->find(prefix) == 0) {
+      *line = *line_iterator;
+      return true;
+    }
+  }
+  return false;
+}
+
+bool UserCollector::GetIdFromStatus(
+    const char *prefix, IdKind kind,
+    const std::vector<std::string> &status_lines, int *id) {
+  // From fs/proc/array.c:task_state(), this file contains:
+  // \nUid:\t<uid>\t<euid>\t<suid>\t<fsuid>\n
+  std::string id_line;
+  if (!GetFirstLineWithPrefix(status_lines, prefix, &id_line)) {
+    return false;
+  }
+  std::string id_substring = id_line.substr(strlen(prefix), std::string::npos);
+  std::vector<std::string> ids;
+  base::SplitString(id_substring, '\t', &ids);
+  if (ids.size() != kIdMax || kind < 0 || kind >= kIdMax) {
+    return false;
+  }
+  const char *number = ids[kind].c_str();
+  char *end_number = nullptr;
+  *id = strtol(number, &end_number, 10);
+  if (*end_number != '\0') {
+    return false;
+  }
+  return true;
+}
+
+bool UserCollector::GetStateFromStatus(
+    const std::vector<std::string> &status_lines, std::string *state) {
+  std::string state_line;
+  if (!GetFirstLineWithPrefix(status_lines, kStatePrefix, &state_line)) {
+    return false;
+  }
+  *state = state_line.substr(strlen(kStatePrefix), std::string::npos);
+  return true;
+}
+
+void UserCollector::EnqueueCollectionErrorLog(pid_t pid,
+                                              ErrorType error_type,
+                                              const std::string &exec) {
+  FilePath crash_path;
+  LOG(INFO) << "Writing conversion problems as separate crash report.";
+  if (!GetCreatedCrashDirectoryByEuid(0, &crash_path, nullptr)) {
+    LOG(ERROR) << "Could not even get log directory; out of space?";
+    return;
+  }
+  AddCrashMetaData("sig", kCollectionErrorSignature);
+  AddCrashMetaData("error_type", GetErrorTypeSignature(error_type));
+  std::string dump_basename = FormatDumpBasename(exec, time(nullptr), pid);
+  std::string error_log = chromeos::GetLog();
+  FilePath diag_log_path = GetCrashPath(crash_path, dump_basename, "diaglog");
+  if (GetLogContents(FilePath(log_config_path_), kCollectionErrorSignature,
+                     diag_log_path)) {
+    // We load the contents of diag_log into memory and append it to
+    // the error log.  We cannot just append to files because we need
+    // to always create new files to prevent attack.
+    std::string diag_log_contents;
+    base::ReadFileToString(diag_log_path, &diag_log_contents);
+    error_log.append(diag_log_contents);
+    base::DeleteFile(diag_log_path, false);
+  }
+  FilePath log_path = GetCrashPath(crash_path, dump_basename, "log");
+  FilePath meta_path = GetCrashPath(crash_path, dump_basename, "meta");
+  // We must use WriteNewFile instead of base::WriteFile as we do
+  // not want to write with root access to a symlink that an attacker
+  // might have created.
+  if (WriteNewFile(log_path, error_log.data(), error_log.length()) < 0) {
+    LOG(ERROR) << "Error writing new file " << log_path.value();
+    return;
+  }
+  WriteCrashMetaData(meta_path, exec, log_path.value());
+}
+
+bool UserCollector::CopyOffProcFiles(pid_t pid,
+                                     const FilePath &container_dir) {
+  if (!base::CreateDirectory(container_dir)) {
+    PLOG(ERROR) << "Could not create " << container_dir.value().c_str();
+    return false;
+  }
+  FilePath process_path = GetProcessPath(pid);
+  if (!base::PathExists(process_path)) {
+    LOG(ERROR) << "Path " << process_path.value() << " does not exist";
+    return false;
+  }
+  static const char *proc_files[] = {
+    "auxv",
+    "cmdline",
+    "environ",
+    "maps",
+    "status"
+  };
+  for (unsigned i = 0; i < arraysize(proc_files); ++i) {
+    if (!base::CopyFile(process_path.Append(proc_files[i]),
+                        container_dir.Append(proc_files[i]))) {
+      LOG(ERROR) << "Could not copy " << proc_files[i] << " file";
+      return false;
+    }
+  }
+  return true;
+}
+
+bool UserCollector::ValidateProcFiles(const FilePath &container_dir) const {
+  // Check if the maps file is empty, which could be due to the crashed
+  // process being reaped by the kernel before finishing a core dump.
+  int64_t file_size = 0;
+  if (!base::GetFileSize(container_dir.Append("maps"), &file_size)) {
+    LOG(ERROR) << "Could not get the size of maps file";
+    return false;
+  }
+  if (file_size == 0) {
+    LOG(ERROR) << "maps file is empty";
+    return false;
+  }
+  return true;
+}
+
+UserCollector::ErrorType UserCollector::ValidateCoreFile(
+    const FilePath &core_path) const {
+  int fd = HANDLE_EINTR(open(core_path.value().c_str(), O_RDONLY));
+  if (fd < 0) {
+    PLOG(ERROR) << "Could not open core file " << core_path.value();
+    return kErrorInvalidCoreFile;
+  }
+
+  char e_ident[EI_NIDENT];
+  bool read_ok = base::ReadFromFD(fd, e_ident, sizeof(e_ident));
+  IGNORE_EINTR(close(fd));
+  if (!read_ok) {
+    LOG(ERROR) << "Could not read header of core file";
+    return kErrorInvalidCoreFile;
+  }
+
+  if (e_ident[EI_MAG0] != ELFMAG0 || e_ident[EI_MAG1] != ELFMAG1 ||
+      e_ident[EI_MAG2] != ELFMAG2 || e_ident[EI_MAG3] != ELFMAG3) {
+    LOG(ERROR) << "Invalid core file";
+    return kErrorInvalidCoreFile;
+  }
+
+#if __WORDSIZE == 64
+  // TODO(benchan, mkrebs): Remove this check once core2md can
+  // handles both 32-bit and 64-bit ELF on a 64-bit platform.
+  if (e_ident[EI_CLASS] == ELFCLASS32) {
+    LOG(ERROR) << "Conversion of 32-bit core file on 64-bit platform is "
+               << "currently not supported";
+    return kErrorUnsupported32BitCoreFile;
+  }
+#endif
+
+  return kErrorNone;
+}
+
+bool UserCollector::GetCreatedCrashDirectory(pid_t pid, uid_t supplied_ruid,
+                                             FilePath *crash_file_path,
+                                             bool *out_of_capacity) {
+  FilePath process_path = GetProcessPath(pid);
+  std::string status;
+  if (directory_failure_) {
+    LOG(ERROR) << "Purposefully failing to create spool directory";
+    return false;
+  }
+
+  uid_t uid;
+  if (base::ReadFileToString(process_path.Append("status"), &status)) {
+    std::vector<std::string> status_lines;
+    base::SplitString(status, '\n', &status_lines);
+
+    std::string process_state;
+    if (!GetStateFromStatus(status_lines, &process_state)) {
+      LOG(ERROR) << "Could not find process state in status file";
+      return false;
+    }
+    LOG(INFO) << "State of crashed process [" << pid << "]: " << process_state;
+
+    // Get effective UID of crashing process.
+    int id;
+    if (!GetIdFromStatus(kUserId, kIdEffective, status_lines, &id)) {
+      LOG(ERROR) << "Could not find euid in status file";
+      return false;
+    }
+    uid = id;
+  } else if (supplied_ruid != kUnknownUid) {
+    LOG(INFO) << "Using supplied UID " << supplied_ruid
+              << " for crashed process [" << pid
+              << "] due to error reading status file";
+    uid = supplied_ruid;
+  } else {
+    LOG(ERROR) << "Could not read status file and kernel did not supply UID";
+    LOG(INFO) << "Path " << process_path.value() << " DirectoryExists: "
+              << base::DirectoryExists(process_path);
+    return false;
+  }
+
+  if (!GetCreatedCrashDirectoryByEuid(uid, crash_file_path, out_of_capacity)) {
+    LOG(ERROR) << "Could not create crash directory";
+    return false;
+  }
+  return true;
+}
+
+bool UserCollector::CopyStdinToCoreFile(const FilePath &core_path) {
+  // Copy off all stdin to a core file.
+  FilePath stdin_path("/proc/self/fd/0");
+  if (base::CopyFile(stdin_path, core_path)) {
+    return true;
+  }
+
+  PLOG(ERROR) << "Could not write core file";
+  // If the file system was full, make sure we remove any remnants.
+  base::DeleteFile(core_path, false);
+  return false;
+}
+
+bool UserCollector::RunCoreToMinidump(const FilePath &core_path,
+                                      const FilePath &procfs_directory,
+                                      const FilePath &minidump_path,
+                                      const FilePath &temp_directory) {
+  FilePath output_path = temp_directory.Append("output");
+  chromeos::ProcessImpl core2md;
+  core2md.RedirectOutput(output_path.value());
+  core2md.AddArg(kCoreToMinidumpConverterPath);
+  core2md.AddArg(core_path.value());
+  core2md.AddArg(procfs_directory.value());
+
+  if (!core2md_failure_) {
+    core2md.AddArg(minidump_path.value());
+  } else {
+    // To test how core2md errors are propagaged, cause an error
+    // by forgetting a required argument.
+  }
+
+  int errorlevel = core2md.Run();
+
+  std::string output;
+  base::ReadFileToString(output_path, &output);
+  if (errorlevel != 0) {
+    LOG(ERROR) << "Problem during " << kCoreToMinidumpConverterPath
+               << " [result=" << errorlevel << "]: " << output;
+    return false;
+  }
+
+  if (!base::PathExists(minidump_path)) {
+    LOG(ERROR) << "Minidump file " << minidump_path.value()
+               << " was not created";
+    return false;
+  }
+  return true;
+}
+
+UserCollector::ErrorType UserCollector::ConvertCoreToMinidump(
+    pid_t pid,
+    const FilePath &container_dir,
+    const FilePath &core_path,
+    const FilePath &minidump_path) {
+  // If proc files are unuable, we continue to read the core file from stdin,
+  // but only skip the core-to-minidump conversion, so that we may still use
+  // the core file for debugging.
+  bool proc_files_usable =
+      CopyOffProcFiles(pid, container_dir) && ValidateProcFiles(container_dir);
+
+  if (!CopyStdinToCoreFile(core_path)) {
+    return kErrorReadCoreData;
+  }
+
+  if (!proc_files_usable) {
+    LOG(INFO) << "Skipped converting core file to minidump due to "
+              << "unusable proc files";
+    return kErrorUnusableProcFiles;
+  }
+
+  ErrorType error = ValidateCoreFile(core_path);
+  if (error != kErrorNone) {
+    return error;
+  }
+
+  if (!RunCoreToMinidump(core_path,
+                         container_dir,  // procfs directory
+                         minidump_path,
+                         container_dir)) {  // temporary directory
+    return kErrorCore2MinidumpConversion;
+  }
+
+  LOG(INFO) << "Stored minidump to " << minidump_path.value();
+  return kErrorNone;
+}
+
+UserCollector::ErrorType UserCollector::ConvertAndEnqueueCrash(
+    pid_t pid, const std::string &exec, uid_t supplied_ruid,
+    bool *out_of_capacity) {
+  FilePath crash_path;
+  if (!GetCreatedCrashDirectory(pid, supplied_ruid, &crash_path,
+      out_of_capacity)) {
+    LOG(ERROR) << "Unable to find/create process-specific crash path";
+    return kErrorSystemIssue;
+  }
+
+  // Directory like /tmp/crash_reporter/1234 which contains the
+  // procfs entries and other temporary files used during conversion.
+  FilePath container_dir(StringPrintf("%s/%d", kCoreTempFolder, pid));
+  // Delete a pre-existing directory from crash reporter that may have
+  // been left around for diagnostics from a failed conversion attempt.
+  // If we don't, existing files can cause forking to fail.
+  base::DeleteFile(container_dir, true);
+  std::string dump_basename = FormatDumpBasename(exec, time(nullptr), pid);
+  FilePath core_path = GetCrashPath(crash_path, dump_basename, "core");
+  FilePath meta_path = GetCrashPath(crash_path, dump_basename, "meta");
+  FilePath minidump_path = GetCrashPath(crash_path, dump_basename, "dmp");
+  FilePath log_path = GetCrashPath(crash_path, dump_basename, "log");
+
+  if (GetLogContents(FilePath(log_config_path_), exec, log_path))
+    AddCrashMetaData("log", log_path.value());
+
+  ErrorType error_type =
+      ConvertCoreToMinidump(pid, container_dir, core_path, minidump_path);
+  if (error_type != kErrorNone) {
+    LOG(INFO) << "Leaving core file at " << core_path.value()
+              << " due to conversion error";
+    return error_type;
+  }
+
+  // Here we commit to sending this file.  We must not return false
+  // after this point or we will generate a log report as well as a
+  // crash report.
+  WriteCrashMetaData(meta_path,
+                     exec,
+                     minidump_path.value());
+
+  if (!IsDeveloperImage()) {
+    base::DeleteFile(core_path, false);
+  } else {
+    LOG(INFO) << "Leaving core file at " << core_path.value()
+              << " due to developer image";
+  }
+
+  base::DeleteFile(container_dir, true);
+  return kErrorNone;
+}
+
+bool UserCollector::ParseCrashAttributes(const std::string &crash_attributes,
+                                         pid_t *pid, int *signal, uid_t *uid,
+                                         std::string *kernel_supplied_name) {
+  pcrecpp::RE re("(\\d+):(\\d+):(\\d+):(.*)");
+  if (re.FullMatch(crash_attributes, pid, signal, uid, kernel_supplied_name))
+    return true;
+
+  LOG(INFO) << "Falling back to parsing crash attributes '"
+            << crash_attributes << "' without UID";
+  pcrecpp::RE re_without_uid("(\\d+):(\\d+):(.*)");
+  *uid = kUnknownUid;
+  return re_without_uid.FullMatch(crash_attributes, pid, signal,
+      kernel_supplied_name);
+}
+
+bool UserCollector::ShouldDump(bool has_owner_consent,
+                               bool is_developer,
+                               std::string *reason) {
+  reason->clear();
+
+  // For developer builds, we always want to keep the crash reports unless
+  // we're testing the crash facilities themselves.  This overrides
+  // feedback.  Crash sending still obeys consent.
+  if (is_developer) {
+    *reason = "developer build - not testing - always dumping";
+    return true;
+  }
+
+  if (!has_owner_consent) {
+    *reason = "ignoring - no consent";
+    return false;
+  }
+
+  *reason = "handling";
+  return true;
+}
+
+bool UserCollector::HandleCrash(const std::string &crash_attributes,
+                                const char *force_exec) {
+  CHECK(initialized_);
+  pid_t pid = 0;
+  int signal = 0;
+  uid_t supplied_ruid = kUnknownUid;
+  std::string kernel_supplied_name;
+
+  if (!ParseCrashAttributes(crash_attributes, &pid, &signal, &supplied_ruid,
+                            &kernel_supplied_name)) {
+    LOG(ERROR) << "Invalid parameter: --user=" <<  crash_attributes;
+    return false;
+  }
+
+  std::string exec;
+  if (force_exec) {
+    exec.assign(force_exec);
+  } else if (!GetExecutableBaseNameFromPid(pid, &exec)) {
+    // If we cannot find the exec name, use the kernel supplied name.
+    // We don't always use the kernel's since it truncates the name to
+    // 16 characters.
+    exec = StringPrintf("supplied_%s", kernel_supplied_name.c_str());
+  }
+
+  // Allow us to test the crash reporting mechanism successfully even if
+  // other parts of the system crash.
+  if (!filter_in_.empty() &&
+      (filter_in_ == "none" ||
+       filter_in_ != exec)) {
+    // We use a different format message to make it more obvious in tests
+    // which crashes are test generated and which are real.
+    LOG(WARNING) << "Ignoring crash from " << exec << "[" << pid << "] while "
+                 << "filter_in=" << filter_in_ << ".";
+    return true;
+  }
+
+  std::string reason;
+  bool dump = ShouldDump(is_feedback_allowed_function_(),
+                         IsDeveloperImage(),
+                         &reason);
+
+  LOG(WARNING) << "Received crash notification for " << exec << "[" << pid
+               << "] sig " << signal << ", user " << supplied_ruid
+               << " (" << reason << ")";
+
+  if (dump) {
+    count_crash_function_();
+
+    if (generate_diagnostics_) {
+      bool out_of_capacity = false;
+      ErrorType error_type =
+          ConvertAndEnqueueCrash(pid, exec, supplied_ruid, &out_of_capacity);
+      if (error_type != kErrorNone) {
+        if (!out_of_capacity)
+          EnqueueCollectionErrorLog(pid, error_type, exec);
+        return false;
+      }
+    }
+  }
+
+  return true;
+}
diff --git a/crash_reporter/user_collector.h b/crash_reporter/user_collector.h
new file mode 100644
index 0000000..8c38aa2
--- /dev/null
+++ b/crash_reporter/user_collector.h
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CRASH_REPORTER_USER_COLLECTOR_H_
+#define CRASH_REPORTER_USER_COLLECTOR_H_
+
+#include <string>
+#include <vector>
+
+#include <base/files/file_path.h>
+#include <base/macros.h>
+#include <gtest/gtest_prod.h>  // for FRIEND_TEST
+
+#include "crash_collector.h"
+
+class SystemLogging;
+
+// User crash collector.
+class UserCollector : public CrashCollector {
+ public:
+  UserCollector();
+
+  // Initialize the user crash collector for detection of crashes,
+  // given a crash counting function, the path to this executable,
+  // metrics collection enabled oracle, and system logger facility.
+  // Crash detection/reporting is not enabled until Enable is called.
+  // |generate_diagnostics| is used to indicate whether or not to try
+  // to generate a minidump from crashes.
+  void Initialize(CountCrashFunction count_crash,
+                  const std::string &our_path,
+                  IsFeedbackAllowedFunction is_metrics_allowed,
+                  bool generate_diagnostics,
+                  bool core2md_failure,
+                  bool directory_failure,
+                  const std::string &filter_in);
+
+  ~UserCollector() override;
+
+  // Enable collection.
+  bool Enable() { return SetUpInternal(true); }
+
+  // Disable collection.
+  bool Disable() { return SetUpInternal(false); }
+
+  // Handle a specific user crash.  Returns true on success.
+  bool HandleCrash(const std::string &crash_attributes,
+                   const char *force_exec);
+
+ private:
+  friend class UserCollectorTest;
+  FRIEND_TEST(UserCollectorTest, CopyOffProcFilesBadPath);
+  FRIEND_TEST(UserCollectorTest, CopyOffProcFilesBadPid);
+  FRIEND_TEST(UserCollectorTest, CopyOffProcFilesOK);
+  FRIEND_TEST(UserCollectorTest, GetExecutableBaseNameFromPid);
+  FRIEND_TEST(UserCollectorTest, GetFirstLineWithPrefix);
+  FRIEND_TEST(UserCollectorTest, GetIdFromStatus);
+  FRIEND_TEST(UserCollectorTest, GetStateFromStatus);
+  FRIEND_TEST(UserCollectorTest, GetProcessPath);
+  FRIEND_TEST(UserCollectorTest, GetSymlinkTarget);
+  FRIEND_TEST(UserCollectorTest, GetUserInfoFromName);
+  FRIEND_TEST(UserCollectorTest, ParseCrashAttributes);
+  FRIEND_TEST(UserCollectorTest, ShouldDumpChromeOverridesDeveloperImage);
+  FRIEND_TEST(UserCollectorTest, ShouldDumpDeveloperImageOverridesConsent);
+  FRIEND_TEST(UserCollectorTest, ShouldDumpUseConsentProductionImage);
+  FRIEND_TEST(UserCollectorTest, ValidateProcFiles);
+  FRIEND_TEST(UserCollectorTest, ValidateCoreFile);
+
+  // Enumeration to pass to GetIdFromStatus.  Must match the order
+  // that the kernel lists IDs in the status file.
+  enum IdKind {
+    kIdReal = 0,  // uid and gid
+    kIdEffective = 1,  // euid and egid
+    kIdSet = 2,  // suid and sgid
+    kIdFileSystem = 3,  // fsuid and fsgid
+    kIdMax
+  };
+
+  enum ErrorType {
+    kErrorNone,
+    kErrorSystemIssue,
+    kErrorReadCoreData,
+    kErrorUnusableProcFiles,
+    kErrorInvalidCoreFile,
+    kErrorUnsupported32BitCoreFile,
+    kErrorCore2MinidumpConversion,
+  };
+
+  static const int kForkProblem = 255;
+
+  // Returns an error type signature for a given |error_type| value,
+  // which is reported to the crash server along with the
+  // crash_reporter-user-collection signature.
+  std::string GetErrorTypeSignature(ErrorType error_type) const;
+
+  std::string GetPattern(bool enabled) const;
+  bool SetUpInternal(bool enabled);
+
+  // Returns, via |line|, the first line in |lines| that starts with |prefix|.
+  // Returns true if a line is found, or false otherwise.
+  bool GetFirstLineWithPrefix(const std::vector<std::string> &lines,
+                              const char *prefix, std::string *line);
+
+  // Returns the identifier of |kind|, via |id|, found in |status_lines| on
+  // the line starting with |prefix|. |status_lines| contains the lines in
+  // the status file. Returns true if the identifier can be determined.
+  bool GetIdFromStatus(const char *prefix,
+                       IdKind kind,
+                       const std::vector<std::string> &status_lines,
+                       int *id);
+
+  // Returns the process state, via |state|, found in |status_lines|, which
+  // contains the lines in the status file. Returns true if the process state
+  // can be determined.
+  bool GetStateFromStatus(const std::vector<std::string> &status_lines,
+                          std::string *state);
+
+  void LogCollectionError(const std::string &error_message);
+  void EnqueueCollectionErrorLog(pid_t pid, ErrorType error_type,
+                                 const std::string &exec_name);
+
+  bool CopyOffProcFiles(pid_t pid, const base::FilePath &container_dir);
+
+  // Validates the proc files at |container_dir| and returns true if they
+  // are usable for the core-to-minidump conversion later. For instance, if
+  // a process is reaped by the kernel before the copying of its proc files
+  // takes place, some proc files like /proc/<pid>/maps may contain nothing
+  // and thus become unusable.
+  bool ValidateProcFiles(const base::FilePath &container_dir) const;
+
+  // Validates the core file at |core_path| and returns kErrorNone if
+  // the file contains the ELF magic bytes and an ELF class that matches the
+  // platform (i.e. 32-bit ELF on a 32-bit platform or 64-bit ELF on a 64-bit
+  // platform), which is due to the limitation in core2md. It returns an error
+  // type otherwise.
+  ErrorType ValidateCoreFile(const base::FilePath &core_path) const;
+
+  // Determines the crash directory for given pid based on pid's owner,
+  // and creates the directory if necessary with appropriate permissions.
+  // Returns true whether or not directory needed to be created, false on
+  // any failure.
+  bool GetCreatedCrashDirectory(pid_t pid, uid_t supplied_ruid,
+                                base::FilePath *crash_file_path,
+                                bool *out_of_capacity);
+  bool CopyStdinToCoreFile(const base::FilePath &core_path);
+  bool RunCoreToMinidump(const base::FilePath &core_path,
+                         const base::FilePath &procfs_directory,
+                         const base::FilePath &minidump_path,
+                         const base::FilePath &temp_directory);
+  ErrorType ConvertCoreToMinidump(pid_t pid,
+                                  const base::FilePath &container_dir,
+                                  const base::FilePath &core_path,
+                                  const base::FilePath &minidump_path);
+  ErrorType ConvertAndEnqueueCrash(pid_t pid, const std::string &exec_name,
+                                   uid_t supplied_ruid, bool *out_of_capacity);
+  bool ParseCrashAttributes(const std::string &crash_attributes,
+                            pid_t *pid, int *signal, uid_t *uid,
+                            std::string *kernel_supplied_name);
+
+  bool ShouldDump(bool has_owner_consent,
+                  bool is_developer,
+                  std::string *reason);
+
+  bool generate_diagnostics_;
+  std::string our_path_;
+  bool initialized_;
+
+  bool core2md_failure_;
+  bool directory_failure_;
+  std::string filter_in_;
+
+  static const char *kUserId;
+  static const char *kGroupId;
+
+  DISALLOW_COPY_AND_ASSIGN(UserCollector);
+};
+
+#endif  // CRASH_REPORTER_USER_COLLECTOR_H_
diff --git a/crash_reporter/user_collector_test.cc b/crash_reporter/user_collector_test.cc
new file mode 100644
index 0000000..4419e7c
--- /dev/null
+++ b/crash_reporter/user_collector_test.cc
@@ -0,0 +1,459 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "user_collector.h"
+
+#include <elf.h>
+#include <sys/cdefs.h>  // For __WORDSIZE
+#include <unistd.h>
+
+#include <base/files/file_util.h>
+#include <base/files/scoped_temp_dir.h>
+#include <base/strings/string_split.h>
+#include <chromeos/syslog_logging.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+using base::FilePath;
+using chromeos::FindLog;
+
+namespace {
+
+int s_crashes = 0;
+bool s_metrics = false;
+
+const char kFilePath[] = "/my/path";
+
+// Keep in sync with UserCollector::ShouldDump.
+const char kChromeIgnoreMsg[] =
+    "ignoring call by kernel - chrome crash; "
+    "waiting for chrome to call us directly";
+
+void CountCrash() {
+  ++s_crashes;
+}
+
+bool IsMetrics() {
+  return s_metrics;
+}
+
+}  // namespace
+
+class UserCollectorMock : public UserCollector {
+ public:
+  MOCK_METHOD0(SetUpDBus, void());
+};
+
+class UserCollectorTest : public ::testing::Test {
+  void SetUp() {
+    s_crashes = 0;
+
+    EXPECT_CALL(collector_, SetUpDBus()).WillRepeatedly(testing::Return());
+
+    collector_.Initialize(CountCrash,
+                          kFilePath,
+                          IsMetrics,
+                          false,
+                          false,
+                          false,
+                          "");
+    base::DeleteFile(FilePath("test"), true);
+    mkdir("test", 0777);
+    pid_ = getpid();
+    chromeos::ClearLog();
+  }
+
+ protected:
+  void ExpectFileEquals(const char *golden,
+                        const FilePath &file_path) {
+    std::string contents;
+    EXPECT_TRUE(base::ReadFileToString(file_path, &contents));
+    EXPECT_EQ(golden, contents);
+  }
+
+  std::vector<std::string> SplitLines(const std::string &lines) const {
+    std::vector<std::string> result;
+    base::SplitString(lines, '\n', &result);
+    return result;
+  }
+
+  UserCollectorMock collector_;
+  pid_t pid_;
+};
+
+TEST_F(UserCollectorTest, ParseCrashAttributes) {
+  pid_t pid;
+  int signal;
+  uid_t uid;
+  std::string exec_name;
+  EXPECT_TRUE(collector_.ParseCrashAttributes("123456:11:1000:foobar",
+      &pid, &signal, &uid, &exec_name));
+  EXPECT_EQ(123456, pid);
+  EXPECT_EQ(11, signal);
+  EXPECT_EQ(1000, uid);
+  EXPECT_EQ("foobar", exec_name);
+  EXPECT_TRUE(collector_.ParseCrashAttributes("4321:6:barfoo",
+      &pid, &signal, &uid, &exec_name));
+  EXPECT_EQ(4321, pid);
+  EXPECT_EQ(6, signal);
+  EXPECT_EQ(-1, uid);
+  EXPECT_EQ("barfoo", exec_name);
+
+  EXPECT_FALSE(collector_.ParseCrashAttributes("123456:11",
+      &pid, &signal, &uid, &exec_name));
+
+  EXPECT_TRUE(collector_.ParseCrashAttributes("123456:11:exec:extra",
+      &pid, &signal, &uid, &exec_name));
+  EXPECT_EQ("exec:extra", exec_name);
+
+  EXPECT_FALSE(collector_.ParseCrashAttributes("12345p:11:foobar",
+      &pid, &signal, &uid, &exec_name));
+
+  EXPECT_FALSE(collector_.ParseCrashAttributes("123456:1 :foobar",
+      &pid, &signal, &uid, &exec_name));
+
+  EXPECT_FALSE(collector_.ParseCrashAttributes("123456::foobar",
+      &pid, &signal, &uid, &exec_name));
+}
+
+TEST_F(UserCollectorTest, ShouldDumpDeveloperImageOverridesConsent) {
+  std::string reason;
+  EXPECT_TRUE(collector_.ShouldDump(false, true, &reason));
+  EXPECT_EQ("developer build - not testing - always dumping", reason);
+
+  // When running a crash test, behave as normal.
+  EXPECT_FALSE(collector_.ShouldDump(false, false, &reason));
+  EXPECT_EQ("ignoring - no consent", reason);
+}
+
+TEST_F(UserCollectorTest, ShouldDumpUseConsentProductionImage) {
+  std::string result;
+  EXPECT_FALSE(collector_.ShouldDump(false, false, &result));
+  EXPECT_EQ("ignoring - no consent", result);
+
+  EXPECT_TRUE(collector_.ShouldDump(true, false, &result));
+  EXPECT_EQ("handling", result);
+}
+
+TEST_F(UserCollectorTest, HandleCrashWithoutConsent) {
+  s_metrics = false;
+  collector_.HandleCrash("20:10:ignored", "foobar");
+  EXPECT_TRUE(FindLog(
+      "Received crash notification for foobar[20] sig 10"));
+  ASSERT_EQ(s_crashes, 0);
+}
+
+TEST_F(UserCollectorTest, HandleNonChromeCrashWithConsent) {
+  s_metrics = true;
+  collector_.HandleCrash("5:2:ignored", "chromeos-wm");
+  EXPECT_TRUE(FindLog(
+      "Received crash notification for chromeos-wm[5] sig 2"));
+  ASSERT_EQ(s_crashes, 1);
+}
+
+TEST_F(UserCollectorTest, HandleChromeCrashWithConsent) {
+  s_metrics = true;
+  collector_.HandleCrash("5:2:ignored", "chrome");
+  EXPECT_TRUE(FindLog(
+      "Received crash notification for chrome[5] sig 2"));
+  EXPECT_TRUE(FindLog(kChromeIgnoreMsg));
+  ASSERT_EQ(s_crashes, 0);
+}
+
+TEST_F(UserCollectorTest, HandleSuppliedChromeCrashWithConsent) {
+  s_metrics = true;
+  collector_.HandleCrash("0:2:chrome", nullptr);
+  EXPECT_TRUE(FindLog(
+      "Received crash notification for supplied_chrome[0] sig 2"));
+  EXPECT_TRUE(FindLog(kChromeIgnoreMsg));
+  ASSERT_EQ(s_crashes, 0);
+}
+
+TEST_F(UserCollectorTest, GetProcessPath) {
+  FilePath path = collector_.GetProcessPath(100);
+  ASSERT_EQ("/proc/100", path.value());
+}
+
+TEST_F(UserCollectorTest, GetSymlinkTarget) {
+  FilePath result;
+  ASSERT_FALSE(collector_.GetSymlinkTarget(FilePath("/does_not_exist"),
+                                           &result));
+  ASSERT_TRUE(FindLog(
+      "Readlink failed on /does_not_exist with 2"));
+  std::string long_link;
+  for (int i = 0; i < 50; ++i)
+    long_link += "0123456789";
+  long_link += "/gold";
+
+  for (size_t len = 1; len <= long_link.size(); ++len) {
+    std::string this_link;
+    static const char kLink[] = "test/this_link";
+    this_link.assign(long_link.c_str(), len);
+    ASSERT_EQ(len, this_link.size());
+    unlink(kLink);
+    ASSERT_EQ(0, symlink(this_link.c_str(), kLink));
+    ASSERT_TRUE(collector_.GetSymlinkTarget(FilePath(kLink), &result));
+    ASSERT_EQ(this_link, result.value());
+  }
+}
+
+TEST_F(UserCollectorTest, GetExecutableBaseNameFromPid) {
+  std::string base_name;
+  EXPECT_FALSE(collector_.GetExecutableBaseNameFromPid(0, &base_name));
+  EXPECT_TRUE(FindLog(
+      "Readlink failed on /proc/0/exe with 2"));
+  EXPECT_TRUE(FindLog(
+      "GetSymlinkTarget failed - Path /proc/0 DirectoryExists: 0"));
+  EXPECT_TRUE(FindLog("stat /proc/0/exe failed: -1 2"));
+
+  chromeos::ClearLog();
+  pid_t my_pid = getpid();
+  EXPECT_TRUE(collector_.GetExecutableBaseNameFromPid(my_pid, &base_name));
+  EXPECT_FALSE(FindLog("Readlink failed"));
+  EXPECT_EQ("crash_reporter_test", base_name);
+}
+
+TEST_F(UserCollectorTest, GetFirstLineWithPrefix) {
+  std::vector<std::string> lines;
+  std::string line;
+
+  EXPECT_FALSE(collector_.GetFirstLineWithPrefix(lines, "Name:", &line));
+  EXPECT_EQ("", line);
+
+  lines.push_back("Name:\tls");
+  lines.push_back("State:\tR (running)");
+  lines.push_back(" Foo:\t1000");
+
+  line.clear();
+  EXPECT_TRUE(collector_.GetFirstLineWithPrefix(lines, "Name:", &line));
+  EXPECT_EQ(lines[0], line);
+
+  line.clear();
+  EXPECT_TRUE(collector_.GetFirstLineWithPrefix(lines, "State:", &line));
+  EXPECT_EQ(lines[1], line);
+
+  line.clear();
+  EXPECT_FALSE(collector_.GetFirstLineWithPrefix(lines, "Foo:", &line));
+  EXPECT_EQ("", line);
+
+  line.clear();
+  EXPECT_TRUE(collector_.GetFirstLineWithPrefix(lines, " Foo:", &line));
+  EXPECT_EQ(lines[2], line);
+
+  line.clear();
+  EXPECT_FALSE(collector_.GetFirstLineWithPrefix(lines, "Bar:", &line));
+  EXPECT_EQ("", line);
+}
+
+TEST_F(UserCollectorTest, GetIdFromStatus) {
+  int id = 1;
+  EXPECT_FALSE(collector_.GetIdFromStatus(UserCollector::kUserId,
+                                          UserCollector::kIdEffective,
+                                          SplitLines("nothing here"),
+                                          &id));
+  EXPECT_EQ(id, 1);
+
+  // Not enough parameters.
+  EXPECT_FALSE(collector_.GetIdFromStatus(UserCollector::kUserId,
+                                          UserCollector::kIdReal,
+                                          SplitLines("line 1\nUid:\t1\n"),
+                                          &id));
+
+  const std::vector<std::string> valid_contents =
+      SplitLines("\nUid:\t1\t2\t3\t4\nGid:\t5\t6\t7\t8\n");
+  EXPECT_TRUE(collector_.GetIdFromStatus(UserCollector::kUserId,
+                                         UserCollector::kIdReal,
+                                         valid_contents,
+                                         &id));
+  EXPECT_EQ(1, id);
+
+  EXPECT_TRUE(collector_.GetIdFromStatus(UserCollector::kUserId,
+                                         UserCollector::kIdEffective,
+                                         valid_contents,
+                                         &id));
+  EXPECT_EQ(2, id);
+
+  EXPECT_TRUE(collector_.GetIdFromStatus(UserCollector::kUserId,
+                                         UserCollector::kIdFileSystem,
+                                         valid_contents,
+                                         &id));
+  EXPECT_EQ(4, id);
+
+  EXPECT_TRUE(collector_.GetIdFromStatus(UserCollector::kGroupId,
+                                         UserCollector::kIdEffective,
+                                         valid_contents,
+                                         &id));
+  EXPECT_EQ(6, id);
+
+  EXPECT_TRUE(collector_.GetIdFromStatus(UserCollector::kGroupId,
+                                         UserCollector::kIdSet,
+                                         valid_contents,
+                                         &id));
+  EXPECT_EQ(7, id);
+
+  EXPECT_FALSE(collector_.GetIdFromStatus(UserCollector::kGroupId,
+                                          UserCollector::IdKind(5),
+                                          valid_contents,
+                                          &id));
+  EXPECT_FALSE(collector_.GetIdFromStatus(UserCollector::kGroupId,
+                                          UserCollector::IdKind(-1),
+                                          valid_contents,
+                                          &id));
+
+  // Fail if junk after number
+  EXPECT_FALSE(collector_.GetIdFromStatus(UserCollector::kUserId,
+                                          UserCollector::kIdReal,
+                                          SplitLines("Uid:\t1f\t2\t3\t4\n"),
+                                          &id));
+  EXPECT_TRUE(collector_.GetIdFromStatus(UserCollector::kUserId,
+                                         UserCollector::kIdReal,
+                                         SplitLines("Uid:\t1\t2\t3\t4\n"),
+                                         &id));
+  EXPECT_EQ(1, id);
+
+  // Fail if more than 4 numbers.
+  EXPECT_FALSE(collector_.GetIdFromStatus(UserCollector::kUserId,
+                                          UserCollector::kIdReal,
+                                          SplitLines("Uid:\t1\t2\t3\t4\t5\n"),
+                                          &id));
+}
+
+TEST_F(UserCollectorTest, GetStateFromStatus) {
+  std::string state;
+  EXPECT_FALSE(collector_.GetStateFromStatus(SplitLines("nothing here"),
+                                             &state));
+  EXPECT_EQ("", state);
+
+  EXPECT_TRUE(collector_.GetStateFromStatus(SplitLines("State:\tR (running)"),
+                                            &state));
+  EXPECT_EQ("R (running)", state);
+
+  EXPECT_TRUE(collector_.GetStateFromStatus(
+      SplitLines("Name:\tls\nState:\tZ (zombie)\n"), &state));
+  EXPECT_EQ("Z (zombie)", state);
+}
+
+TEST_F(UserCollectorTest, GetUserInfoFromName) {
+  gid_t gid = 100;
+  uid_t uid = 100;
+  EXPECT_TRUE(collector_.GetUserInfoFromName("root", &uid, &gid));
+  EXPECT_EQ(0, uid);
+  EXPECT_EQ(0, gid);
+}
+
+TEST_F(UserCollectorTest, CopyOffProcFilesBadPath) {
+  // Try a path that is not writable.
+  ASSERT_FALSE(collector_.CopyOffProcFiles(pid_, FilePath("/bad/path")));
+  EXPECT_TRUE(FindLog("Could not create /bad/path"));
+}
+
+TEST_F(UserCollectorTest, CopyOffProcFilesBadPid) {
+  FilePath container_path("test/container");
+  ASSERT_FALSE(collector_.CopyOffProcFiles(0, container_path));
+  EXPECT_TRUE(FindLog("Path /proc/0 does not exist"));
+}
+
+TEST_F(UserCollectorTest, CopyOffProcFilesOK) {
+  FilePath container_path("test/container");
+  ASSERT_TRUE(collector_.CopyOffProcFiles(pid_, container_path));
+  EXPECT_FALSE(FindLog("Could not copy"));
+  static struct {
+    const char *name;
+    bool exists;
+  } expectations[] = {
+    { "auxv", true },
+    { "cmdline", true },
+    { "environ", true },
+    { "maps", true },
+    { "mem", false },
+    { "mounts", false },
+    { "sched", false },
+    { "status", true }
+  };
+  for (unsigned i = 0; i < sizeof(expectations)/sizeof(expectations[0]); ++i) {
+    EXPECT_EQ(expectations[i].exists,
+              base::PathExists(
+                  container_path.Append(expectations[i].name)));
+  }
+}
+
+TEST_F(UserCollectorTest, ValidateProcFiles) {
+  base::ScopedTempDir temp_dir;
+  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+  FilePath container_dir = temp_dir.path();
+
+  // maps file not exists (i.e. GetFileSize fails)
+  EXPECT_FALSE(collector_.ValidateProcFiles(container_dir));
+
+  // maps file is empty
+  FilePath maps_file = container_dir.Append("maps");
+  ASSERT_EQ(0, base::WriteFile(maps_file, nullptr, 0));
+  ASSERT_TRUE(base::PathExists(maps_file));
+  EXPECT_FALSE(collector_.ValidateProcFiles(container_dir));
+
+  // maps file is not empty
+  const char data[] = "test data";
+  ASSERT_EQ(sizeof(data), base::WriteFile(maps_file, data, sizeof(data)));
+  ASSERT_TRUE(base::PathExists(maps_file));
+  EXPECT_TRUE(collector_.ValidateProcFiles(container_dir));
+}
+
+TEST_F(UserCollectorTest, ValidateCoreFile) {
+  base::ScopedTempDir temp_dir;
+  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+  FilePath container_dir = temp_dir.path();
+  FilePath core_file = container_dir.Append("core");
+
+  // Core file does not exist
+  EXPECT_EQ(UserCollector::kErrorInvalidCoreFile,
+            collector_.ValidateCoreFile(core_file));
+  char e_ident[EI_NIDENT];
+  e_ident[EI_MAG0] = ELFMAG0;
+  e_ident[EI_MAG1] = ELFMAG1;
+  e_ident[EI_MAG2] = ELFMAG2;
+  e_ident[EI_MAG3] = ELFMAG3;
+#if __WORDSIZE == 32
+  e_ident[EI_CLASS] = ELFCLASS32;
+#elif __WORDSIZE == 64
+  e_ident[EI_CLASS] = ELFCLASS64;
+#else
+#error Unknown/unsupported value of __WORDSIZE.
+#endif
+
+  // Core file has the expected header
+  ASSERT_TRUE(base::WriteFile(core_file, e_ident, sizeof(e_ident)));
+  EXPECT_EQ(UserCollector::kErrorNone,
+            collector_.ValidateCoreFile(core_file));
+
+#if __WORDSIZE == 64
+  // 32-bit core file on 64-bit platform
+  e_ident[EI_CLASS] = ELFCLASS32;
+  ASSERT_TRUE(base::WriteFile(core_file, e_ident, sizeof(e_ident)));
+  EXPECT_EQ(UserCollector::kErrorUnsupported32BitCoreFile,
+            collector_.ValidateCoreFile(core_file));
+  e_ident[EI_CLASS] = ELFCLASS64;
+#endif
+
+  // Invalid core files
+  ASSERT_TRUE(base::WriteFile(core_file, e_ident, sizeof(e_ident) - 1));
+  EXPECT_EQ(UserCollector::kErrorInvalidCoreFile,
+            collector_.ValidateCoreFile(core_file));
+
+  e_ident[EI_MAG0] = 0;
+  ASSERT_TRUE(base::WriteFile(core_file, e_ident, sizeof(e_ident)));
+  EXPECT_EQ(UserCollector::kErrorInvalidCoreFile,
+            collector_.ValidateCoreFile(core_file));
+}
diff --git a/crash_reporter/warn_collector.l b/crash_reporter/warn_collector.l
new file mode 100644
index 0000000..70ab25c
--- /dev/null
+++ b/crash_reporter/warn_collector.l
@@ -0,0 +1,335 @@
+/*
+ * Copyright (C) 2013 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 flex program reads /var/log/messages as it grows and saves kernel
+ * warnings to files.  It keeps track of warnings it has seen (based on
+ * file/line only, ignoring differences in the stack trace), and reports only
+ * the first warning of each kind, but maintains a count of all warnings by
+ * using their hashes as buckets in a UMA sparse histogram.  It also invokes
+ * the crash collector, which collects the warnings and prepares them for later
+ * shipment to the crash server.
+ */
+
+%option noyywrap
+
+%{
+#include <fcntl.h>
+#include <inttypes.h>
+#include <pwd.h>
+#include <stdarg.h>
+#include <sys/inotify.h>
+#include <sys/select.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "metrics/c_metrics_library.h"
+
+int WarnStart(void);
+void WarnEnd(void);
+void WarnInput(char *buf, yy_size_t *result, size_t max_size);
+
+#define YY_INPUT(buf, result, max_size) WarnInput(buf, &result, max_size)
+
+%}
+
+/* Define a few useful regular expressions. */
+
+D               [0-9]
+PREFIX          .*" kernel: [ "*{D}+"."{D}+"]"
+CUT_HERE        {PREFIX}" ------------[ cut here".*
+WARNING         {PREFIX}" WARNING: at "
+END_TRACE       {PREFIX}" ---[ end trace".*
+
+/* Use exclusive start conditions. */
+%x PRE_WARN WARN
+
+%%
+ /* The scanner itself. */
+
+^{CUT_HERE}\n{WARNING}          BEGIN(PRE_WARN);
+.|\n                            /* ignore all other input in state 0 */
+<PRE_WARN>[^ ]+.[^ ]+\n         if (WarnStart()) {
+                                  /* yytext is
+                                     "file:line func+offset/offset()\n" */
+                                  BEGIN(WARN); ECHO;
+                                } else {
+                                  BEGIN(0);
+                                }
+
+ /* Assume the warning ends at the "end trace" line */
+<WARN>^{END_TRACE}\n            ECHO; BEGIN(0); WarnEnd();
+<WARN>^.*\n                     ECHO;
+
+%%
+
+#define HASH_BITMAP_SIZE        (1 << 15)  /* size in bits */
+#define HASH_BITMAP_MASK        (HASH_BITMAP_SIZE - 1)
+
+const char warn_hist_name[] = "Platform.KernelWarningHashes";
+uint32_t hash_bitmap[HASH_BITMAP_SIZE / 32];
+CMetricsLibrary metrics_library;
+
+const char *prog_name;          /* the name of this program */
+int yyin_fd;                    /* instead of FILE *yyin to avoid buffering */
+int i_fd;                       /* for inotify, to detect file changes */
+int testing;                    /* 1 if running test */
+int filter;                     /* 1 when using as filter (for development) */
+int fifo;                       /* 1 when reading from fifo (for devel) */
+int draining;                   /* 1 when draining renamed log file */
+
+const char *msg_path = "/var/log/messages";
+const char warn_dump_dir[]  = "/var/run/kwarn";
+const char *warn_dump_path = "/var/run/kwarn/warning";
+const char *crash_reporter_command;
+
+__attribute__((__format__(__printf__, 1, 2)))
+static void Die(const char *format, ...) {
+  va_list ap;
+  va_start(ap, format);
+  fprintf(stderr, "%s: ", prog_name);
+  vfprintf(stderr, format, ap);
+  exit(1);
+}
+
+static void RunCrashReporter(void) {
+  int status = system(crash_reporter_command);
+  if (status != 0)
+    Die("%s exited with status %d\n", crash_reporter_command, status);
+}
+
+static uint32_t StringHash(const char *string) {
+  uint32_t hash = 0;
+  while (*string != '\0') {
+    hash = (hash << 5) + hash + *string++;
+  }
+  return hash;
+}
+
+/* We expect only a handful of different warnings per boot session, so the
+ * probability of a collision is very low, and statistically it won't matter
+ * (unless warnings with the same hash also happens in tandem, which is even
+ * rarer).
+ */
+static int HashSeen(uint32_t hash) {
+  int word_index = (hash & HASH_BITMAP_MASK) / 32;
+  int bit_index = (hash & HASH_BITMAP_MASK) % 32;
+  return hash_bitmap[word_index] & 1 << bit_index;
+}
+
+static void SetHashSeen(uint32_t hash) {
+  int word_index = (hash & HASH_BITMAP_MASK) / 32;
+  int bit_index = (hash & HASH_BITMAP_MASK) % 32;
+  hash_bitmap[word_index] |= 1 << bit_index;
+}
+
+#pragma GCC diagnostic ignored "-Wwrite-strings"
+int WarnStart(void) {
+  uint32_t hash;
+  char *spacep;
+
+  if (filter)
+    return 1;
+
+  hash = StringHash(yytext);
+  if (!(testing || fifo || filter)) {
+    CMetricsLibrarySendSparseToUMA(metrics_library, warn_hist_name, (int) hash);
+  }
+  if (HashSeen(hash))
+    return 0;
+  SetHashSeen(hash);
+
+  yyout = fopen(warn_dump_path, "w");
+  if (yyout == NULL)
+    Die("fopen %s failed: %s\n", warn_dump_path, strerror(errno));
+  spacep = strchr(yytext, ' ');
+  if (spacep == NULL || spacep[1] == '\0')
+    spacep = "unknown-function";
+  fprintf(yyout, "%08x-%s\n", hash, spacep + 1);
+  return 1;
+}
+
+void WarnEnd(void) {
+  if (filter)
+    return;
+  fclose(yyout);
+  yyout = stdout;               /* for debugging */
+  RunCrashReporter();
+}
+
+static void WarnOpenInput(const char *path) {
+  yyin_fd = open(path, O_RDONLY);
+  if (yyin_fd < 0)
+    Die("could not open %s: %s\n", path, strerror(errno));
+  if (!fifo) {
+    /* Go directly to the end of the file.  We don't want to parse the same
+     * warnings multiple times on reboot/restart.  We might miss some
+     * warnings, but so be it---it's too hard to keep track reliably of the
+     * last parsed position in the syslog.
+     */
+    if (lseek(yyin_fd, 0, SEEK_END) < 0)
+      Die("could not lseek %s: %s\n", path, strerror(errno));
+    /* Set up notification of file growth and rename. */
+    i_fd = inotify_init();
+    if (i_fd < 0)
+      Die("inotify_init: %s\n", strerror(errno));
+    if (inotify_add_watch(i_fd, path, IN_MODIFY | IN_MOVE_SELF) < 0)
+      Die("inotify_add_watch: %s\n", strerror(errno));
+  }
+}
+
+/* We replace the default YY_INPUT() for the following reasons:
+ *
+ * 1.  We want to read data as soon as it becomes available, but the default
+ * YY_INPUT() uses buffered I/O.
+ *
+ * 2.  We want to block on end of input and wait for the file to grow.
+ *
+ * 3.  We want to detect log rotation, and reopen the input file as needed.
+ */
+void WarnInput(char *buf, yy_size_t *result, size_t max_size) {
+  while (1) {
+    ssize_t ret = read(yyin_fd, buf, max_size);
+    if (ret < 0)
+      Die("read: %s", strerror(errno));
+    *result = ret;
+    if (*result > 0 || fifo || filter)
+      return;
+    if (draining) {
+      /* Assume we're done with this log, and move to next
+       * log.  Rsyslogd may keep writing to the old log file
+       * for a while, but we don't care since we don't have
+       * to be exact.
+       */
+      close(yyin_fd);
+      if (YYSTATE == WARN) {
+        /* Be conservative in case we lose the warn
+         * terminator during the switch---or we may
+         * collect personally identifiable information.
+         */
+        WarnEnd();
+      }
+      BEGIN(0);        /* see above comment */
+      sleep(1);        /* avoid race with log rotator */
+      WarnOpenInput(msg_path);
+      draining = 0;
+      continue;
+    }
+    /* Nothing left to read, so we must wait. */
+    struct inotify_event event;
+    while (1) {
+      int n = read(i_fd, &event, sizeof(event));
+      if (n <= 0) {
+        if (errno == EINTR)
+          continue;
+        else
+          Die("inotify: %s\n", strerror(errno));
+      } else
+        break;
+    }
+    if (event.mask & IN_MOVE_SELF) {
+      /* The file has been renamed.  Before switching
+       * to the new one, we process any remaining
+       * content of this file.
+       */
+      draining = 1;
+    }
+  }
+}
+
+int main(int argc, char **argv) {
+  int result;
+  struct passwd *user;
+  prog_name = argv[0];
+
+  if (argc == 2 && strcmp(argv[1], "--test") == 0)
+    testing = 1;
+  else if (argc == 2 && strcmp(argv[1], "--filter") == 0)
+    filter = 1;
+  else if (argc == 2 && strcmp(argv[1], "--fifo") == 0) {
+    fifo = 1;
+  } else if (argc != 1) {
+    fprintf(stderr,
+            "usage: %s [single-flag]\n"
+            "flags (for testing only):\n"
+            "--fifo\tinput is fifo \"fifo\", output is stdout\n"
+            "--filter\tinput is stdin, output is stdout\n"
+            "--test\trun self-test\n",
+            prog_name);
+    exit(1);
+  }
+
+  metrics_library = CMetricsLibraryNew();
+  CMetricsLibraryInit(metrics_library);
+
+  crash_reporter_command = testing ?
+    "./warn_collector_test_reporter.sh" :
+    "/sbin/crash_reporter --kernel_warning";
+
+  /* When filtering with --filter (for development) use stdin for input.
+   * Otherwise read input from a file or a fifo.
+   */
+  yyin_fd = fileno(stdin);
+  if (testing) {
+    msg_path = "messages";
+    warn_dump_path = "warning";
+  }
+  if (fifo) {
+    msg_path = "fifo";
+  }
+  if (!filter) {
+    WarnOpenInput(msg_path);
+  }
+
+  /* Create directory for dump file.  Still need to be root here. */
+  unlink(warn_dump_path);
+  if (!testing && !fifo && !filter) {
+    rmdir(warn_dump_dir);
+    result = mkdir(warn_dump_dir, 0755);
+    if (result < 0)
+      Die("could not create %s: %s\n",
+          warn_dump_dir, strerror(errno));
+  }
+
+  if (0) {
+    /* TODO(semenzato): put this back in once we decide it's safe
+     * to make /var/spool/crash rwxrwxrwx root, or use a different
+     * owner and setuid for the crash reporter as well.
+     */
+
+    /* Get low privilege uid, gid. */
+    user = getpwnam("chronos");
+    if (user == NULL)
+      Die("getpwnam failed\n");
+
+    /* Change dump directory ownership. */
+    if (chown(warn_dump_dir, user->pw_uid, user->pw_gid) < 0)
+      Die("chown: %s\n", strerror(errno));
+
+    /* Drop privileges. */
+    if (setuid(user->pw_uid) < 0) {
+      Die("setuid: %s\n", strerror(errno));
+    }
+  }
+
+  /* Go! */
+  return yylex();
+}
+
+/* Flex should really know not to generate these functions.
+ */
+void UnusedFunctionWarningSuppressor(void) {
+  yyunput(0, 0);
+}
diff --git a/base/test_utils.h b/crash_reporter/warn_collector_test.c
similarity index 66%
copy from base/test_utils.h
copy to crash_reporter/warn_collector_test.c
index 132d3a7..7ebe0a8 100644
--- a/base/test_utils.h
+++ b/crash_reporter/warn_collector_test.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2013 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.
@@ -14,19 +14,12 @@
  * limitations under the License.
  */
 
-#ifndef TEST_UTILS_H
-#define TEST_UTILS_H
+/*
+ * Test driver for the warn_collector daemon.
+ */
+#include <stdlib.h>
 
-class TemporaryFile {
- public:
-  TemporaryFile();
-  ~TemporaryFile();
-
-  int fd;
-  char filename[1024];
-
- private:
-  void init(const char* tmp_dir);
-};
-
-#endif // TEST_UTILS_H
+int main(int ac, char **av) {
+  int status = system("exec \"${SRC}\"/warn_collector_test.sh");
+  return status < 0 ? EXIT_FAILURE : WEXITSTATUS(status);
+}
diff --git a/crash_reporter/warn_collector_test.sh b/crash_reporter/warn_collector_test.sh
new file mode 100755
index 0000000..a5af16c
--- /dev/null
+++ b/crash_reporter/warn_collector_test.sh
@@ -0,0 +1,90 @@
+#! /bin/bash
+
+# Copyright (C) 2013 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.
+
+# Test for warn_collector.  Run the warn collector in the background, emulate
+# the kernel by appending lines to the log file "messages", and observe the log
+# of the (fake) crash reporter each time is run by the warn collector daemon.
+
+set -e
+
+fail() {
+  printf '[ FAIL ] %b\n' "$*"
+  exit 1
+}
+
+if [[ -z ${SYSROOT} ]]; then
+  fail "SYSROOT must be set for this test to work"
+fi
+: ${OUT:=${PWD}}
+cd "${OUT}"
+PATH=${OUT}:${PATH}
+TESTLOG="${OUT}/warn-test-log"
+
+echo "Testing: $(which warn_collector)"
+
+cleanup() {
+  # Kill daemon (if started) on exit
+  kill %
+}
+
+check_log() {
+  local n_expected=$1
+  if [[ ! -f ${TESTLOG} ]]; then
+    fail "${TESTLOG} was not created"
+  fi
+  if [[ $(wc -l < "${TESTLOG}") -ne ${n_expected} ]]; then
+    fail "expected ${n_expected} lines in ${TESTLOG}, found this instead:
+$(<"${TESTLOG}")"
+  fi
+  if egrep -qv '^[0-9a-f]{8}' "${TESTLOG}"; then
+    fail "found bad lines in ${TESTLOG}:
+$(<"${TESTLOG}")"
+  fi
+}
+
+rm -f "${TESTLOG}"
+cp "${SRC}/warn_collector_test_reporter.sh" .
+cp "${SRC}/TEST_WARNING" .
+cp TEST_WARNING messages
+
+# Start the collector daemon.  With the --test option, the daemon reads input
+# from ./messages, writes the warning into ./warning, and invokes
+# ./warn_collector_test_reporter.sh to report the warning.
+warn_collector --test &
+trap cleanup EXIT
+
+# After a while, check that the first warning has been collected.
+sleep 1
+check_log 1
+
+# Add the same warning to messages, verify that it is NOT collected
+cat TEST_WARNING >> messages
+sleep 1
+check_log 1
+
+# Add a slightly different warning to messages, check that it is collected.
+sed s/intel_dp.c/intel_xx.c/ < TEST_WARNING >> messages
+sleep 1
+check_log 2
+
+# Emulate log rotation, add a warning, and check.
+mv messages messages.1
+sed s/intel_dp.c/intel_xy.c/ < TEST_WARNING > messages
+sleep 2
+check_log 3
+
+# Success!
+exit 0
diff --git a/crash_reporter/warn_collector_test_reporter.sh b/crash_reporter/warn_collector_test_reporter.sh
new file mode 100755
index 0000000..b6096ed
--- /dev/null
+++ b/crash_reporter/warn_collector_test_reporter.sh
@@ -0,0 +1,27 @@
+#! /bin/sh
+
+# Copyright (C) 2013 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.
+
+# Replacement for the crash reporter, for testing.  Log the first line of the
+# "warning" file, which by convention contains the warning hash, and remove the
+# file.
+
+set -e
+
+exec 1>> warn-test-log
+exec 2>> warn-test-log
+
+head -1 warning
+rm warning
diff --git a/debuggerd/Android.mk b/debuggerd/Android.mk
index 6cfb541..f7a5f82 100644
--- a/debuggerd/Android.mk
+++ b/debuggerd/Android.mk
@@ -27,6 +27,9 @@
 
 LOCAL_CPPFLAGS := $(common_cppflags)
 
+LOCAL_INIT_RC_32 := debuggerd.rc
+LOCAL_INIT_RC_64 := debuggerd64.rc
+
 ifeq ($(TARGET_IS_64_BIT),true)
 LOCAL_CPPFLAGS += -DTARGET_IS_64_BIT
 endif
@@ -77,12 +80,12 @@
 
 debuggerd_test_src_files := \
     utility.cpp \
-    test/dump_maps_test.cpp \
     test/dump_memory_test.cpp \
     test/elf_fake.cpp \
     test/log_fake.cpp \
     test/property_fake.cpp \
     test/ptrace_fake.cpp \
+    test/tombstone_test.cpp \
     test/selinux_fake.cpp \
 
 debuggerd_shared_libraries := \
diff --git a/debuggerd/arm/machine.cpp b/debuggerd/arm/machine.cpp
index b7d6997..78c2306 100644
--- a/debuggerd/arm/machine.cpp
+++ b/debuggerd/arm/machine.cpp
@@ -15,12 +15,15 @@
  * limitations under the License.
  */
 
+#define LOG_TAG "DEBUG"
+
 #include <errno.h>
 #include <stdint.h>
 #include <string.h>
 #include <sys/ptrace.h>
 
 #include <backtrace/Backtrace.h>
+#include <log/log.h>
 
 #include "machine.h"
 #include "utility.h"
@@ -28,7 +31,7 @@
 void dump_memory_and_code(log_t* log, Backtrace* backtrace) {
   pt_regs regs;
   if (ptrace(PTRACE_GETREGS, backtrace->Tid(), 0, &regs)) {
-    _LOG(log, logtype::ERROR, "cannot get registers: %s\n", strerror(errno));
+    ALOGE("cannot get registers: %s\n", strerror(errno));
     return;
   }
 
@@ -48,7 +51,7 @@
 void dump_registers(log_t* log, pid_t tid) {
   pt_regs r;
   if (ptrace(PTRACE_GETREGS, tid, 0, &r)) {
-    _LOG(log, logtype::ERROR, "cannot get registers: %s\n", strerror(errno));
+    ALOGE("cannot get registers: %s\n", strerror(errno));
     return;
   }
 
@@ -68,7 +71,7 @@
 
   user_vfp vfp_regs;
   if (ptrace(PTRACE_GETVFPREGS, tid, 0, &vfp_regs)) {
-    _LOG(log, logtype::ERROR, "cannot get FP registers: %s\n", strerror(errno));
+    ALOGE("cannot get FP registers: %s\n", strerror(errno));
     return;
   }
 
diff --git a/debuggerd/arm64/machine.cpp b/debuggerd/arm64/machine.cpp
index 2e097da..e7bf79a 100644
--- a/debuggerd/arm64/machine.cpp
+++ b/debuggerd/arm64/machine.cpp
@@ -15,6 +15,8 @@
  * limitations under the License.
  */
 
+#define LOG_TAG "DEBUG"
+
 #include <elf.h>
 #include <errno.h>
 #include <stdint.h>
@@ -23,6 +25,7 @@
 #include <sys/uio.h>
 
 #include <backtrace/Backtrace.h>
+#include <log/log.h>
 
 #include "machine.h"
 #include "utility.h"
@@ -34,8 +37,7 @@
   io.iov_len = sizeof(regs);
 
   if (ptrace(PTRACE_GETREGSET, backtrace->Tid(), reinterpret_cast<void*>(NT_PRSTATUS), &io) == -1) {
-    _LOG(log, logtype::ERROR, "%s: ptrace failed to get registers: %s",
-         __func__, strerror(errno));
+    ALOGE("ptrace failed to get registers: %s", strerror(errno));
     return;
   }
 
@@ -57,7 +59,7 @@
   io.iov_len = sizeof(r);
 
   if (ptrace(PTRACE_GETREGSET, tid, (void*) NT_PRSTATUS, (void*) &io) == -1) {
-    _LOG(log, logtype::ERROR, "ptrace error: %s\n", strerror(errno));
+    ALOGE("ptrace error: %s\n", strerror(errno));
     return;
   }
 
@@ -81,7 +83,7 @@
   io.iov_len = sizeof(f);
 
   if (ptrace(PTRACE_GETREGSET, tid, (void*) NT_PRFPREG, (void*) &io) == -1) {
-    _LOG(log, logtype::ERROR, "ptrace error: %s\n", strerror(errno));
+    ALOGE("ptrace error: %s\n", strerror(errno));
     return;
   }
 
diff --git a/debuggerd/backtrace.cpp b/debuggerd/backtrace.cpp
index b8084c5..b46f8f4 100644
--- a/debuggerd/backtrace.cpp
+++ b/debuggerd/backtrace.cpp
@@ -105,7 +105,7 @@
   }
 
   if (!attached && ptrace(PTRACE_DETACH, tid, 0, 0) != 0) {
-    _LOG(log, logtype::ERROR, "ptrace detach from %d failed: %s\n", tid, strerror(errno));
+    ALOGE("ptrace detach from %d failed: %s\n", tid, strerror(errno));
     *detach_failed = true;
   }
 }
diff --git a/debuggerd/crasher.c b/debuggerd/crasher.c
index af86fe9..b5064b4 100644
--- a/debuggerd/crasher.c
+++ b/debuggerd/crasher.c
@@ -157,12 +157,6 @@
     } else if (!strcmp(arg, "SIGFPE")) {
         raise(SIGFPE);
         return EXIT_SUCCESS;
-    } else if (!strcmp(arg, "SIGPIPE")) {
-        int pipe_fds[2];
-        pipe(pipe_fds);
-        close(pipe_fds[0]);
-        write(pipe_fds[1], "oops", 4);
-        return EXIT_SUCCESS;
     } else if (!strcmp(arg, "SIGTRAP")) {
         raise(SIGTRAP);
         return EXIT_SUCCESS;
@@ -189,7 +183,6 @@
     fprintf(stderr, "  LOG_ALWAYS_FATAL      call LOG_ALWAYS_FATAL\n");
     fprintf(stderr, "  LOG_ALWAYS_FATAL_IF   call LOG_ALWAYS_FATAL\n");
     fprintf(stderr, "  SIGFPE                cause a SIGFPE\n");
-    fprintf(stderr, "  SIGPIPE               cause a SIGPIPE\n");
     fprintf(stderr, "  SIGSEGV               cause a SIGSEGV at address 0x0 (synonym: crash)\n");
     fprintf(stderr, "  SIGSEGV-non-null      cause a SIGSEGV at a non-zero address\n");
     fprintf(stderr, "  SIGSEGV-unmapped      mmap/munmap a region of memory and then attempt to access it\n");
diff --git a/debuggerd/debuggerd.cpp b/debuggerd/debuggerd.cpp
index b84a4e5..599995c 100644
--- a/debuggerd/debuggerd.cpp
+++ b/debuggerd/debuggerd.cpp
@@ -63,32 +63,18 @@
   int32_t original_si_code;
 };
 
-static void wait_for_user_action(const debugger_request_t &request) {
-  // Find out the name of the process that crashed.
-  char path[64];
-  snprintf(path, sizeof(path), "/proc/%d/exe", request.pid);
-
-  char exe[PATH_MAX];
-  int count;
-  if ((count = readlink(path, exe, sizeof(exe) - 1)) == -1) {
-    ALOGE("readlink('%s') failed: %s", path, strerror(errno));
-    strlcpy(exe, "unknown", sizeof(exe));
-  } else {
-    exe[count] = '\0';
-  }
-
+static void wait_for_user_action(const debugger_request_t& request) {
   // Explain how to attach the debugger.
-  ALOGI("********************************************************\n"
+  ALOGI("***********************************************************\n"
         "* Process %d has been suspended while crashing.\n"
-        "* To attach gdbserver for a gdb connection on port 5039\n"
-        "* and start gdbclient:\n"
+        "* To attach gdbserver and start gdb, run this on the host:\n"
         "*\n"
-        "*     gdbclient %s :5039 %d\n"
+        "*     gdbclient %d\n"
         "*\n"
         "* Wait for gdb to start, then press the VOLUME DOWN key\n"
         "* to let the process continue crashing.\n"
-        "********************************************************",
-        request.pid, exe, request.tid);
+        "***********************************************************",
+        request.pid, request.tid);
 
   // Wait for VOLUME DOWN.
   if (init_getevent() == 0) {
@@ -134,8 +120,6 @@
   return fields == 7 ? 0 : -1;
 }
 
-static int selinux_enabled;
-
 /*
  * Corresponds with debugger_action_t enum type in
  * include/cutils/debugger.h.
@@ -153,9 +137,6 @@
   const char *perm;
   bool allowed = false;
 
-  if (selinux_enabled <= 0)
-    return true;
-
   if (action <= 0 || action >= (sizeof(debuggerd_perms)/sizeof(debuggerd_perms[0]))) {
     ALOGE("SELinux:  No permission defined for debugger action %d", action);
     return false;
@@ -255,10 +236,7 @@
 
 static bool should_attach_gdb(debugger_request_t* request) {
   if (request->action == DEBUGGER_ACTION_CRASH) {
-    char value[PROPERTY_VALUE_MAX];
-    property_get("debug.db.uid", value, "-1");
-    int debug_uid = atoi(value);
-    return debug_uid >= 0 && request->uid <= (uid_t)debug_uid;
+    return property_get_bool("debug.debuggerd.wait_for_gdb", false);
   }
   return false;
 }
@@ -430,7 +408,6 @@
             case SIGBUS:
             case SIGFPE:
             case SIGILL:
-            case SIGPIPE:
             case SIGSEGV:
 #ifdef SIGSTKFLT
             case SIGSTKFLT:
@@ -589,7 +566,6 @@
 int main(int argc, char** argv) {
   union selinux_callback cb;
   if (argc == 1) {
-    selinux_enabled = is_selinux_enabled();
     cb.func_log = selinux_log_callback;
     selinux_set_callback(SELINUX_CB_LOG, cb);
     return do_server();
diff --git a/debuggerd/debuggerd.rc b/debuggerd/debuggerd.rc
new file mode 100644
index 0000000..4338ae9
--- /dev/null
+++ b/debuggerd/debuggerd.rc
@@ -0,0 +1,2 @@
+service debuggerd /system/bin/debuggerd
+    class main
diff --git a/debuggerd/debuggerd64.rc b/debuggerd/debuggerd64.rc
new file mode 100644
index 0000000..341a329
--- /dev/null
+++ b/debuggerd/debuggerd64.rc
@@ -0,0 +1,2 @@
+service debuggerd64 /system/bin/debuggerd64
+    class main
diff --git a/debuggerd/mips/machine.cpp b/debuggerd/mips/machine.cpp
index f7b8a86..cbf272a 100644
--- a/debuggerd/mips/machine.cpp
+++ b/debuggerd/mips/machine.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#define LOG_TAG "DEBUG"
+
 #include <errno.h>
 #include <inttypes.h>
 #include <stdint.h>
@@ -21,6 +23,7 @@
 #include <sys/ptrace.h>
 
 #include <backtrace/Backtrace.h>
+#include <log/log.h>
 
 #include "machine.h"
 #include "utility.h"
@@ -32,7 +35,7 @@
 void dump_memory_and_code(log_t* log, Backtrace* backtrace) {
   pt_regs r;
   if (ptrace(PTRACE_GETREGS, backtrace->Tid(), 0, &r)) {
-    _LOG(log, logtype::ERROR, "cannot get registers: %s\n", strerror(errno));
+    ALOGE("cannot get registers: %s\n", strerror(errno));
     return;
   }
 
@@ -61,7 +64,7 @@
 void dump_registers(log_t* log, pid_t tid) {
   pt_regs r;
   if(ptrace(PTRACE_GETREGS, tid, 0, &r)) {
-    _LOG(log, logtype::ERROR, "cannot get registers: %s\n", strerror(errno));
+    ALOGE("cannot get registers: %s\n", strerror(errno));
     return;
   }
 
diff --git a/debuggerd/mips64/machine.cpp b/debuggerd/mips64/machine.cpp
index 293dcf6..0a8d532 100644
--- a/debuggerd/mips64/machine.cpp
+++ b/debuggerd/mips64/machine.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#define LOG_TAG "DEBUG"
+
 #include <errno.h>
 #include <inttypes.h>
 #include <stdint.h>
@@ -21,6 +23,7 @@
 #include <sys/ptrace.h>
 
 #include <backtrace/Backtrace.h>
+#include <log/log.h>
 
 #include "machine.h"
 #include "utility.h"
@@ -32,7 +35,7 @@
 void dump_memory_and_code(log_t* log, Backtrace* backtrace) {
   pt_regs r;
   if (ptrace(PTRACE_GETREGS, backtrace->Tid(), 0, &r)) {
-    _LOG(log, logtype::ERROR, "cannot get registers: %s\n", strerror(errno));
+    ALOGE("cannot get registers: %s\n", strerror(errno));
     return;
   }
 
@@ -61,7 +64,7 @@
 void dump_registers(log_t* log, pid_t tid) {
   pt_regs r;
   if(ptrace(PTRACE_GETREGS, tid, 0, &r)) {
-    _LOG(log, logtype::ERROR, "cannot get registers: %s\n", strerror(errno));
+    ALOGE("cannot get registers: %s\n", strerror(errno));
     return;
   }
 
diff --git a/debuggerd/test/BacktraceMock.h b/debuggerd/test/BacktraceMock.h
index 5c252ab..f75534e 100644
--- a/debuggerd/test/BacktraceMock.h
+++ b/debuggerd/test/BacktraceMock.h
@@ -60,12 +60,14 @@
     }
     size_t bytes_available = buffer_.size() - offset;
 
-    if (bytes_partial_read_ > 0) {
+    if (do_partial_read_) {
       // Do a partial read.
       if (bytes > bytes_partial_read_) {
         bytes = bytes_partial_read_;
       }
       bytes_partial_read_ -= bytes;
+      // Only support a single partial read.
+      do_partial_read_ = false;
     } else if (bytes > bytes_available) {
       bytes = bytes_available;
     }
@@ -82,6 +84,7 @@
     buffer_.resize(bytes);
     memcpy(buffer_.data(), buffer, bytes);
     bytes_partial_read_ = 0;
+    do_partial_read_ = false;
     last_read_addr_ = 0;
   }
 
@@ -90,12 +93,14 @@
       abort();
     }
     bytes_partial_read_ = bytes;
+    do_partial_read_ = true;
   }
 
  private:
   std::vector<uint8_t> buffer_;
   size_t bytes_partial_read_ = 0;
   uintptr_t last_read_addr_ = 0;
+  bool do_partial_read_ = false;
 };
 
 #endif //  _DEBUGGERD_TEST_BACKTRACE_MOCK_H
diff --git a/debuggerd/test/dump_memory_test.cpp b/debuggerd/test/dump_memory_test.cpp
index fcb0108..75e7028 100644
--- a/debuggerd/test/dump_memory_test.cpp
+++ b/debuggerd/test/dump_memory_test.cpp
@@ -288,9 +288,9 @@
   ASSERT_STREQ(g_expected_partial_dump, tombstone_contents.c_str());
 
 #if defined(__LP64__)
-  ASSERT_STREQ("DEBUG Bytes read 102, is not a multiple of 8\n", getFakeLogPrint().c_str());
+  ASSERT_STREQ("6 DEBUG Bytes read 102, is not a multiple of 8\n", getFakeLogPrint().c_str());
 #else
-  ASSERT_STREQ("DEBUG Bytes read 102, is not a multiple of 4\n", getFakeLogPrint().c_str());
+  ASSERT_STREQ("6 DEBUG Bytes read 102, is not a multiple of 4\n", getFakeLogPrint().c_str());
 #endif
 
   // Verify that the log buf is empty, and no error messages.
@@ -313,12 +313,12 @@
   ASSERT_STREQ(g_expected_partial_dump, tombstone_contents.c_str());
 
 #if defined(__LP64__)
-  ASSERT_STREQ("DEBUG Bytes read 45, is not a multiple of 8\n"
-               "DEBUG Bytes after second read 106, is not a multiple of 8\n",
+  ASSERT_STREQ("6 DEBUG Bytes read 45, is not a multiple of 8\n"
+               "6 DEBUG Bytes after second read 106, is not a multiple of 8\n",
                getFakeLogPrint().c_str());
 #else
-  ASSERT_STREQ("DEBUG Bytes read 45, is not a multiple of 4\n"
-               "DEBUG Bytes after second read 106, is not a multiple of 4\n",
+  ASSERT_STREQ("6 DEBUG Bytes read 45, is not a multiple of 4\n"
+               "6 DEBUG Bytes after second read 106, is not a multiple of 4\n",
                getFakeLogPrint().c_str());
 #endif
 
@@ -502,3 +502,239 @@
   ASSERT_STREQ("", getFakeLogBuf().c_str());
   ASSERT_STREQ("", getFakeLogPrint().c_str());
 }
+
+TEST_F(DumpMemoryTest, first_read_empty) {
+  uint8_t buffer[256];
+  for (size_t i = 0; i < sizeof(buffer); i++) {
+    buffer[i] = i;
+  }
+  backtrace_mock_->SetReadData(buffer, sizeof(buffer));
+  backtrace_mock_->SetPartialReadAmount(0);
+
+  size_t page_size = sysconf(_SC_PAGE_SIZE);
+  uintptr_t addr = 0x10000020 + page_size - 120;
+  dump_memory(&log_, backtrace_mock_.get(), addr, "memory near %.2s:", "r4");
+
+  std::string tombstone_contents;
+  ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+  ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+  const char* expected_dump = \
+"\nmemory near r4:\n"
+#if defined(__LP64__)
+"    0000000010000f88 ---------------- ----------------  ................\n"
+"    0000000010000f98 ---------------- ----------------  ................\n"
+"    0000000010000fa8 ---------------- ----------------  ................\n"
+"    0000000010000fb8 ---------------- ----------------  ................\n"
+"    0000000010000fc8 ---------------- ----------------  ................\n"
+"    0000000010000fd8 ---------------- ----------------  ................\n"
+"    0000000010000fe8 ---------------- ----------------  ................\n"
+"    0000000010000ff8 ---------------- 7f7e7d7c7b7a7978  ........xyz{|}~.\n"
+"    0000000010001008 8786858483828180 8f8e8d8c8b8a8988  ................\n"
+"    0000000010001018 9796959493929190 9f9e9d9c9b9a9998  ................\n"
+"    0000000010001028 a7a6a5a4a3a2a1a0 afaeadacabaaa9a8  ................\n"
+"    0000000010001038 b7b6b5b4b3b2b1b0 bfbebdbcbbbab9b8  ................\n"
+"    0000000010001048 c7c6c5c4c3c2c1c0 cfcecdcccbcac9c8  ................\n"
+"    0000000010001058 d7d6d5d4d3d2d1d0 dfdedddcdbdad9d8  ................\n"
+"    0000000010001068 e7e6e5e4e3e2e1e0 efeeedecebeae9e8  ................\n"
+"    0000000010001078 f7f6f5f4f3f2f1f0 fffefdfcfbfaf9f8  ................\n";
+#else
+"    10000f88 -------- -------- -------- --------  ................\n"
+"    10000f98 -------- -------- -------- --------  ................\n"
+"    10000fa8 -------- -------- -------- --------  ................\n"
+"    10000fb8 -------- -------- -------- --------  ................\n"
+"    10000fc8 -------- -------- -------- --------  ................\n"
+"    10000fd8 -------- -------- -------- --------  ................\n"
+"    10000fe8 -------- -------- -------- --------  ................\n"
+"    10000ff8 -------- -------- 7b7a7978 7f7e7d7c  ........xyz{|}~.\n"
+"    10001008 83828180 87868584 8b8a8988 8f8e8d8c  ................\n"
+"    10001018 93929190 97969594 9b9a9998 9f9e9d9c  ................\n"
+"    10001028 a3a2a1a0 a7a6a5a4 abaaa9a8 afaeadac  ................\n"
+"    10001038 b3b2b1b0 b7b6b5b4 bbbab9b8 bfbebdbc  ................\n"
+"    10001048 c3c2c1c0 c7c6c5c4 cbcac9c8 cfcecdcc  ................\n"
+"    10001058 d3d2d1d0 d7d6d5d4 dbdad9d8 dfdedddc  ................\n"
+"    10001068 e3e2e1e0 e7e6e5e4 ebeae9e8 efeeedec  ................\n"
+"    10001078 f3f2f1f0 f7f6f5f4 fbfaf9f8 fffefdfc  ................\n";
+#endif
+  ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
+
+  // Verify that the log buf is empty, and no error messages.
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(DumpMemoryTest, first_read_empty_second_read_stops) {
+  uint8_t buffer[224];
+  for (size_t i = 0; i < sizeof(buffer); i++) {
+    buffer[i] = i;
+  }
+  backtrace_mock_->SetReadData(buffer, sizeof(buffer));
+  backtrace_mock_->SetPartialReadAmount(0);
+
+  size_t page_size = sysconf(_SC_PAGE_SIZE);
+  uintptr_t addr = 0x10000020 + page_size - 192;
+  dump_memory(&log_, backtrace_mock_.get(), addr, "memory near %.2s:", "r4");
+
+  std::string tombstone_contents;
+  ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+  ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+  const char* expected_dump = \
+"\nmemory near r4:\n"
+#if defined(__LP64__)
+"    0000000010000f40 ---------------- ----------------  ................\n"
+"    0000000010000f50 ---------------- ----------------  ................\n"
+"    0000000010000f60 ---------------- ----------------  ................\n"
+"    0000000010000f70 ---------------- ----------------  ................\n"
+"    0000000010000f80 ---------------- ----------------  ................\n"
+"    0000000010000f90 ---------------- ----------------  ................\n"
+"    0000000010000fa0 ---------------- ----------------  ................\n"
+"    0000000010000fb0 ---------------- ----------------  ................\n"
+"    0000000010000fc0 ---------------- ----------------  ................\n"
+"    0000000010000fd0 ---------------- ----------------  ................\n"
+"    0000000010000fe0 ---------------- ----------------  ................\n"
+"    0000000010000ff0 ---------------- ----------------  ................\n"
+"    0000000010001000 c7c6c5c4c3c2c1c0 cfcecdcccbcac9c8  ................\n"
+"    0000000010001010 d7d6d5d4d3d2d1d0 dfdedddcdbdad9d8  ................\n"
+"    0000000010001020 ---------------- ----------------  ................\n"
+"    0000000010001030 ---------------- ----------------  ................\n";
+#else
+"    10000f40 -------- -------- -------- --------  ................\n"
+"    10000f50 -------- -------- -------- --------  ................\n"
+"    10000f60 -------- -------- -------- --------  ................\n"
+"    10000f70 -------- -------- -------- --------  ................\n"
+"    10000f80 -------- -------- -------- --------  ................\n"
+"    10000f90 -------- -------- -------- --------  ................\n"
+"    10000fa0 -------- -------- -------- --------  ................\n"
+"    10000fb0 -------- -------- -------- --------  ................\n"
+"    10000fc0 -------- -------- -------- --------  ................\n"
+"    10000fd0 -------- -------- -------- --------  ................\n"
+"    10000fe0 -------- -------- -------- --------  ................\n"
+"    10000ff0 -------- -------- -------- --------  ................\n"
+"    10001000 c3c2c1c0 c7c6c5c4 cbcac9c8 cfcecdcc  ................\n"
+"    10001010 d3d2d1d0 d7d6d5d4 dbdad9d8 dfdedddc  ................\n"
+"    10001020 -------- -------- -------- --------  ................\n"
+"    10001030 -------- -------- -------- --------  ................\n";
+#endif
+  ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
+
+  // Verify that the log buf is empty, and no error messages.
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(DumpMemoryTest, first_read_empty_next_page_out_of_range) {
+  uint8_t buffer[256];
+  for (size_t i = 0; i < sizeof(buffer); i++) {
+    buffer[i] = i;
+  }
+  backtrace_mock_->SetReadData(buffer, sizeof(buffer));
+  backtrace_mock_->SetPartialReadAmount(0);
+
+  uintptr_t addr = 0x10000020;
+  dump_memory(&log_, backtrace_mock_.get(), addr, "memory near %.2s:", "r4");
+
+  std::string tombstone_contents;
+  ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+  ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+  const char* expected_dump = \
+"\nmemory near r4:\n"
+#if defined(__LP64__)
+"    0000000010000000 ---------------- ----------------  ................\n"
+"    0000000010000010 ---------------- ----------------  ................\n"
+"    0000000010000020 ---------------- ----------------  ................\n"
+"    0000000010000030 ---------------- ----------------  ................\n"
+"    0000000010000040 ---------------- ----------------  ................\n"
+"    0000000010000050 ---------------- ----------------  ................\n"
+"    0000000010000060 ---------------- ----------------  ................\n"
+"    0000000010000070 ---------------- ----------------  ................\n"
+"    0000000010000080 ---------------- ----------------  ................\n"
+"    0000000010000090 ---------------- ----------------  ................\n"
+"    00000000100000a0 ---------------- ----------------  ................\n"
+"    00000000100000b0 ---------------- ----------------  ................\n"
+"    00000000100000c0 ---------------- ----------------  ................\n"
+"    00000000100000d0 ---------------- ----------------  ................\n"
+"    00000000100000e0 ---------------- ----------------  ................\n"
+"    00000000100000f0 ---------------- ----------------  ................\n";
+#else
+"    10000000 -------- -------- -------- --------  ................\n"
+"    10000010 -------- -------- -------- --------  ................\n"
+"    10000020 -------- -------- -------- --------  ................\n"
+"    10000030 -------- -------- -------- --------  ................\n"
+"    10000040 -------- -------- -------- --------  ................\n"
+"    10000050 -------- -------- -------- --------  ................\n"
+"    10000060 -------- -------- -------- --------  ................\n"
+"    10000070 -------- -------- -------- --------  ................\n"
+"    10000080 -------- -------- -------- --------  ................\n"
+"    10000090 -------- -------- -------- --------  ................\n"
+"    100000a0 -------- -------- -------- --------  ................\n"
+"    100000b0 -------- -------- -------- --------  ................\n"
+"    100000c0 -------- -------- -------- --------  ................\n"
+"    100000d0 -------- -------- -------- --------  ................\n"
+"    100000e0 -------- -------- -------- --------  ................\n"
+"    100000f0 -------- -------- -------- --------  ................\n";
+#endif
+  ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
+
+  // Verify that the log buf is empty, and no error messages.
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(DumpMemoryTest, first_read_empty_next_page_out_of_range_fence_post) {
+  uint8_t buffer[256];
+  for (size_t i = 0; i < sizeof(buffer); i++) {
+    buffer[i] = i;
+  }
+  backtrace_mock_->SetReadData(buffer, sizeof(buffer));
+  backtrace_mock_->SetPartialReadAmount(0);
+
+  size_t page_size = sysconf(_SC_PAGE_SIZE);
+  uintptr_t addr = 0x10000020 + page_size - 256;
+
+  dump_memory(&log_, backtrace_mock_.get(), addr, "memory near %.2s:", "r4");
+
+  std::string tombstone_contents;
+  ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+  ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+  const char* expected_dump = \
+"\nmemory near r4:\n"
+#if defined(__LP64__)
+"    0000000010000f00 ---------------- ----------------  ................\n"
+"    0000000010000f10 ---------------- ----------------  ................\n"
+"    0000000010000f20 ---------------- ----------------  ................\n"
+"    0000000010000f30 ---------------- ----------------  ................\n"
+"    0000000010000f40 ---------------- ----------------  ................\n"
+"    0000000010000f50 ---------------- ----------------  ................\n"
+"    0000000010000f60 ---------------- ----------------  ................\n"
+"    0000000010000f70 ---------------- ----------------  ................\n"
+"    0000000010000f80 ---------------- ----------------  ................\n"
+"    0000000010000f90 ---------------- ----------------  ................\n"
+"    0000000010000fa0 ---------------- ----------------  ................\n"
+"    0000000010000fb0 ---------------- ----------------  ................\n"
+"    0000000010000fc0 ---------------- ----------------  ................\n"
+"    0000000010000fd0 ---------------- ----------------  ................\n"
+"    0000000010000fe0 ---------------- ----------------  ................\n"
+"    0000000010000ff0 ---------------- ----------------  ................\n";
+#else
+"    10000f00 -------- -------- -------- --------  ................\n"
+"    10000f10 -------- -------- -------- --------  ................\n"
+"    10000f20 -------- -------- -------- --------  ................\n"
+"    10000f30 -------- -------- -------- --------  ................\n"
+"    10000f40 -------- -------- -------- --------  ................\n"
+"    10000f50 -------- -------- -------- --------  ................\n"
+"    10000f60 -------- -------- -------- --------  ................\n"
+"    10000f70 -------- -------- -------- --------  ................\n"
+"    10000f80 -------- -------- -------- --------  ................\n"
+"    10000f90 -------- -------- -------- --------  ................\n"
+"    10000fa0 -------- -------- -------- --------  ................\n"
+"    10000fb0 -------- -------- -------- --------  ................\n"
+"    10000fc0 -------- -------- -------- --------  ................\n"
+"    10000fd0 -------- -------- -------- --------  ................\n"
+"    10000fe0 -------- -------- -------- --------  ................\n"
+"    10000ff0 -------- -------- -------- --------  ................\n";
+#endif
+  ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
+
+  // Verify that the log buf is empty, and no error messages.
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
diff --git a/debuggerd/test/log_fake.cpp b/debuggerd/test/log_fake.cpp
index 26523ad..d584a5e 100644
--- a/debuggerd/test/log_fake.cpp
+++ b/debuggerd/test/log_fake.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <errno.h>
 #include <stdarg.h>
 
 #include <string>
@@ -44,14 +45,16 @@
   return g_fake_log_print;
 }
 
-extern "C" int __android_log_buf_write(int, int, const char* tag, const char* msg) {
+extern "C" int __android_log_buf_write(int bufId, int prio, const char* tag, const char* msg) {
+  g_fake_log_buf += std::to_string(bufId) + ' ' + std::to_string(prio) + ' ';
   g_fake_log_buf += tag;
   g_fake_log_buf += ' ';
   g_fake_log_buf += msg;
   return 1;
 }
 
-extern "C" int __android_log_print(int, const char* tag, const char* fmt, ...) {
+extern "C" int __android_log_print(int prio, const char* tag, const char* fmt, ...) {
+  g_fake_log_print += std::to_string(prio) + ' ';
   g_fake_log_print += tag;
   g_fake_log_print += ' ';
 
@@ -70,6 +73,7 @@
 }
 
 extern "C" struct logger_list* android_logger_list_open(log_id_t, int, unsigned int, pid_t) {
+  errno = EACCES;
   return nullptr;
 }
 
diff --git a/debuggerd/test/dump_maps_test.cpp b/debuggerd/test/tombstone_test.cpp
similarity index 90%
rename from debuggerd/test/dump_maps_test.cpp
rename to debuggerd/test/tombstone_test.cpp
index 230f4f5..d945d27 100644
--- a/debuggerd/test/dump_maps_test.cpp
+++ b/debuggerd/test/tombstone_test.cpp
@@ -45,7 +45,7 @@
 void dump_backtrace_to_log(Backtrace*, log_t*, char const*) {
 }
 
-class DumpMapsTest : public ::testing::Test {
+class TombstoneTest : public ::testing::Test {
  protected:
   virtual void SetUp() {
     map_mock_.reset(new BacktraceMapMock());
@@ -76,7 +76,7 @@
     resetLogs();
     elf_set_fake_build_id("");
     siginfo_t si;
-    si.si_signo = SIGPIPE;
+    si.si_signo = SIGABRT;
     ptrace_set_fake_getsiginfo(si);
   }
 
@@ -92,7 +92,7 @@
   log_t log_;
 };
 
-TEST_F(DumpMapsTest, single_map) {
+TEST_F(TombstoneTest, single_map) {
   backtrace_map_t map;
 #if defined(__LP64__)
   map.start = 0x123456789abcd000UL;
@@ -122,7 +122,7 @@
   ASSERT_STREQ("", getFakeLogPrint().c_str());
 }
 
-TEST_F(DumpMapsTest, single_map_elf_build_id) {
+TEST_F(TombstoneTest, single_map_elf_build_id) {
   backtrace_map_t map;
 #if defined(__LP64__)
   map.start = 0x123456789abcd000UL;
@@ -157,7 +157,7 @@
 
 // Even though build id is present, it should not be printed in either of
 // these cases.
-TEST_F(DumpMapsTest, single_map_no_build_id) {
+TEST_F(TombstoneTest, single_map_no_build_id) {
   backtrace_map_t map;
 #if defined(__LP64__)
   map.start = 0x123456789abcd000UL;
@@ -194,7 +194,7 @@
   ASSERT_STREQ("", getFakeLogPrint().c_str());
 }
 
-TEST_F(DumpMapsTest, multiple_maps) {
+TEST_F(TombstoneTest, multiple_maps) {
   backtrace_map_t map;
 
   map.start = 0xa234000;
@@ -256,7 +256,7 @@
   ASSERT_STREQ("", getFakeLogPrint().c_str());
 }
 
-TEST_F(DumpMapsTest, multiple_maps_fault_address_before) {
+TEST_F(TombstoneTest, multiple_maps_fault_address_before) {
   backtrace_map_t map;
 
   map.start = 0xa434000;
@@ -310,7 +310,7 @@
   ASSERT_STREQ("", getFakeLogPrint().c_str());
 }
 
-TEST_F(DumpMapsTest, multiple_maps_fault_address_between) {
+TEST_F(TombstoneTest, multiple_maps_fault_address_between) {
   backtrace_map_t map;
 
   map.start = 0xa434000;
@@ -364,7 +364,7 @@
   ASSERT_STREQ("", getFakeLogPrint().c_str());
 }
 
-TEST_F(DumpMapsTest, multiple_maps_fault_address_in_map) {
+TEST_F(TombstoneTest, multiple_maps_fault_address_in_map) {
   backtrace_map_t map;
 
   map.start = 0xa434000;
@@ -416,7 +416,7 @@
   ASSERT_STREQ("", getFakeLogPrint().c_str());
 }
 
-TEST_F(DumpMapsTest, multiple_maps_fault_address_after) {
+TEST_F(TombstoneTest, multiple_maps_fault_address_after) {
   backtrace_map_t map;
 
   map.start = 0xa434000;
@@ -474,7 +474,7 @@
   ASSERT_STREQ("", getFakeLogPrint().c_str());
 }
 
-TEST_F(DumpMapsTest, multiple_maps_getsiginfo_fail) {
+TEST_F(TombstoneTest, multiple_maps_getsiginfo_fail) {
   backtrace_map_t map;
 
   map.start = 0xa434000;
@@ -493,7 +493,6 @@
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
   ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
   const char* expected_dump = \
-"Cannot get siginfo for 100: Bad address\n"
 "\nmemory map:\n"
 #if defined(__LP64__)
 "    00000000'0a434000-00000000'0a434fff -w-      1000      1000  (load base 0xd000)\n";
@@ -503,12 +502,11 @@
   ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
 
   // Verify that the log buf is empty, and no error messages.
-  ASSERT_STREQ("DEBUG Cannot get siginfo for 100: Bad address\n",
-               getFakeLogBuf().c_str());
-  ASSERT_STREQ("", getFakeLogPrint().c_str());
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ("6 DEBUG Cannot get siginfo for 100: Bad address\n\n", getFakeLogPrint().c_str());
 }
 
-TEST_F(DumpMapsTest, multiple_maps_check_signal_has_si_addr) {
+TEST_F(TombstoneTest, multiple_maps_check_signal_has_si_addr) {
   backtrace_map_t map;
 
   map.start = 0xa434000;
@@ -569,3 +567,33 @@
     ASSERT_STREQ("", getFakeLogPrint().c_str());
   }
 }
+
+TEST_F(TombstoneTest, dump_signal_info_error) {
+  siginfo_t si;
+  si.si_signo = 0;
+  ptrace_set_fake_getsiginfo(si);
+
+  dump_signal_info(&log_, 123, SIGSEGV, 10);
+
+  std::string tombstone_contents;
+  ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+  ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+  ASSERT_STREQ("", tombstone_contents.c_str());
+
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ("6 DEBUG cannot get siginfo: Bad address\n\n", getFakeLogPrint().c_str());
+}
+
+TEST_F(TombstoneTest, dump_log_file_error) {
+  log_.should_retrieve_logcat = true;
+  dump_log_file(&log_, 123, "/fake/filename", 10);
+
+  std::string tombstone_contents;
+  ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+  ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+  ASSERT_STREQ("", tombstone_contents.c_str());
+
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ("6 DEBUG Unable to open /fake/filename: Permission denied\n\n",
+               getFakeLogPrint().c_str());
+}
diff --git a/debuggerd/tombstone.cpp b/debuggerd/tombstone.cpp
index b0ad274..e283923 100644
--- a/debuggerd/tombstone.cpp
+++ b/debuggerd/tombstone.cpp
@@ -81,7 +81,6 @@
     case SIGBUS: return "SIGBUS";
     case SIGFPE: return "SIGFPE";
     case SIGILL: return "SIGILL";
-    case SIGPIPE: return "SIGPIPE";
     case SIGSEGV: return "SIGSEGV";
 #if defined(SIGSTKFLT)
     case SIGSTKFLT: return "SIGSTKFLT";
@@ -180,7 +179,7 @@
   siginfo_t si;
   memset(&si, 0, sizeof(si));
   if (ptrace(PTRACE_GETSIGINFO, tid, 0, &si) == -1) {
-    _LOG(log, logtype::HEADER, "cannot get siginfo: %s\n", strerror(errno));
+    ALOGE("cannot get siginfo: %s\n", strerror(errno));
     return;
   }
 
@@ -338,7 +337,7 @@
     print_fault_address_marker = signal_has_si_addr(si.si_signo);
     addr = reinterpret_cast<uintptr_t>(si.si_addr);
   } else {
-    _LOG(log, logtype::ERROR, "Cannot get siginfo for %d: %s\n", tid, strerror(errno));
+    ALOGE("Cannot get siginfo for %d: %s\n", tid, strerror(errno));
   }
 
   _LOG(log, logtype::MAPS, "\n");
@@ -448,7 +447,7 @@
 
     // Skip this thread if cannot ptrace it
     if (ptrace(PTRACE_ATTACH, new_tid, 0, 0) < 0) {
-      _LOG(log, logtype::ERROR, "ptrace attach to %d failed: %s\n", new_tid, strerror(errno));
+      ALOGE("ptrace attach to %d failed: %s\n", new_tid, strerror(errno));
       continue;
     }
 
@@ -471,7 +470,7 @@
     log->current_tid = log->crashed_tid;
 
     if (ptrace(PTRACE_DETACH, new_tid, 0, 0) != 0) {
-      _LOG(log, logtype::ERROR, "ptrace detach from %d failed: %s\n", new_tid, strerror(errno));
+      ALOGE("ptrace detach from %d failed: %s\n", new_tid, strerror(errno));
       detach_failed = true;
     }
   }
@@ -517,13 +516,11 @@
         // non-blocking EOF; we're done
         break;
       } else {
-        _LOG(log, logtype::ERROR, "Error while reading log: %s\n",
-          strerror(-actual));
+        ALOGE("Error while reading log: %s\n", strerror(-actual));
         break;
       }
     } else if (actual == 0) {
-      _LOG(log, logtype::ERROR, "Got zero bytes while reading log: %s\n",
-        strerror(errno));
+      ALOGE("Got zero bytes while reading log: %s\n", strerror(errno));
       break;
     }
 
@@ -789,11 +786,11 @@
   log.crashed_tid = tid;
 
   if ((mkdir(TOMBSTONE_DIR, 0755) == -1) && (errno != EEXIST)) {
-    _LOG(&log, logtype::ERROR, "failed to create %s: %s\n", TOMBSTONE_DIR, strerror(errno));
+    ALOGE("failed to create %s: %s\n", TOMBSTONE_DIR, strerror(errno));
   }
 
   if (chown(TOMBSTONE_DIR, AID_SYSTEM, AID_SYSTEM) == -1) {
-    _LOG(&log, logtype::ERROR, "failed to change ownership of %s: %s\n", TOMBSTONE_DIR, strerror(errno));
+    ALOGE("failed to change ownership of %s: %s\n", TOMBSTONE_DIR, strerror(errno));
   }
 
   int fd = -1;
@@ -801,11 +798,11 @@
   if (selinux_android_restorecon(TOMBSTONE_DIR, 0) == 0) {
     path = find_and_open_tombstone(&fd);
   } else {
-    _LOG(&log, logtype::ERROR, "Failed to restore security context, not writing tombstone.\n");
+    ALOGE("Failed to restore security context, not writing tombstone.\n");
   }
 
   if (fd < 0) {
-    _LOG(&log, logtype::ERROR, "Skipping tombstone write, nothing to do.\n");
+    ALOGE("Skipping tombstone write, nothing to do.\n");
     *detach_failed = false;
     return NULL;
   }
diff --git a/debuggerd/utility.cpp b/debuggerd/utility.cpp
index 9f340a8..f5d6ec1 100644
--- a/debuggerd/utility.cpp
+++ b/debuggerd/utility.cpp
@@ -35,8 +35,7 @@
 
 // Whitelist output desired in the logcat output.
 bool is_allowed_in_logcat(enum logtype ltype) {
-  if ((ltype == ERROR)
-   || (ltype == HEADER)
+  if ((ltype == HEADER)
    || (ltype == REGISTERS)
    || (ltype == BACKTRACE)) {
     return true;
@@ -157,13 +156,28 @@
     bytes &= ~(sizeof(uintptr_t) - 1);
   }
 
-  if (bytes < MEMORY_BYTES_TO_DUMP && bytes > 0) {
-    // Try to do one more read. This could happen if a read crosses a map, but
-    // the maps do not have any break between them. Only requires one extra
-    // read because a map has to contain at least one page, and the total
-    // number of bytes to dump is smaller than a page.
-    size_t bytes2 = backtrace->Read(addr + bytes, reinterpret_cast<uint8_t*>(data) + bytes,
-                                    sizeof(data) - bytes);
+  uintptr_t start = 0;
+  bool skip_2nd_read = false;
+  if (bytes == 0) {
+    // In this case, we might want to try another read at the beginning of
+    // the next page only if it's within the amount of memory we would have
+    // read.
+    size_t page_size = sysconf(_SC_PAGE_SIZE);
+    start = ((addr + (page_size - 1)) & ~(page_size - 1)) - addr;
+    if (start == 0 || start >= MEMORY_BYTES_TO_DUMP) {
+      skip_2nd_read = true;
+    }
+  }
+
+  if (bytes < MEMORY_BYTES_TO_DUMP && !skip_2nd_read) {
+    // Try to do one more read. This could happen if a read crosses a map,
+    // but the maps do not have any break between them. Or it could happen
+    // if reading from an unreadable map, but the read would cross back
+    // into a readable map. Only requires one extra read because a map has
+    // to contain at least one page, and the total number of bytes to dump
+    // is smaller than a page.
+    size_t bytes2 = backtrace->Read(addr + start + bytes, reinterpret_cast<uint8_t*>(data) + bytes,
+                                    sizeof(data) - bytes - start);
     bytes += bytes2;
     if (bytes2 > 0 && bytes % sizeof(uintptr_t) != 0) {
       // This should never happen, but we'll try and continue any way.
@@ -179,15 +193,16 @@
   // On 32-bit machines, there are still 16 bytes per line but addresses and
   // words are of course presented differently.
   uintptr_t* data_ptr = data;
+  size_t current = 0;
+  size_t total_bytes = start + bytes;
   for (size_t line = 0; line < MEMORY_BYTES_TO_DUMP / MEMORY_BYTES_PER_LINE; line++) {
     std::string logline;
     android::base::StringAppendF(&logline, "    %" PRIPTR, addr);
 
     addr += MEMORY_BYTES_PER_LINE;
     std::string ascii;
-    for (size_t i = 0; i < MEMORY_BYTES_PER_LINE / sizeof(uintptr_t); i++, data_ptr++) {
-      if (bytes >= sizeof(uintptr_t)) {
-        bytes -= sizeof(uintptr_t);
+    for (size_t i = 0; i < MEMORY_BYTES_PER_LINE / sizeof(uintptr_t); i++) {
+      if (current >= start && current + sizeof(uintptr_t) <= total_bytes) {
         android::base::StringAppendF(&logline, " %" PRIPTR, *data_ptr);
 
         // Fill out the ascii string from the data.
@@ -199,10 +214,12 @@
             ascii += '.';
           }
         }
+        data_ptr++;
       } else {
         logline += ' ' + std::string(sizeof(uintptr_t) * 2, '-');
         ascii += std::string(sizeof(uintptr_t), '.');
       }
+      current += sizeof(uintptr_t);
     }
     _LOG(log, logtype::MEMORY, "%s  %s\n", logline.c_str(), ascii.c_str());
   }
diff --git a/debuggerd/utility.h b/debuggerd/utility.h
index 263374d..8bef192 100644
--- a/debuggerd/utility.h
+++ b/debuggerd/utility.h
@@ -59,7 +59,6 @@
 
 // List of types of logs to simplify the logging decision in _LOG
 enum logtype {
-  ERROR,
   HEADER,
   THREAD,
   REGISTERS,
diff --git a/debuggerd/x86/machine.cpp b/debuggerd/x86/machine.cpp
index c5f9259..af10817 100644
--- a/debuggerd/x86/machine.cpp
+++ b/debuggerd/x86/machine.cpp
@@ -14,12 +14,15 @@
  * limitations under the License.
  */
 
+#define LOG_TAG "DEBUG"
+
 #include <errno.h>
 #include <stdint.h>
 #include <string.h>
 #include <sys/ptrace.h>
 
 #include <backtrace/Backtrace.h>
+#include <log/log.h>
 
 #include "machine.h"
 #include "utility.h"
@@ -27,7 +30,7 @@
 void dump_memory_and_code(log_t* log, Backtrace* backtrace) {
   struct pt_regs r;
   if (ptrace(PTRACE_GETREGS, backtrace->Tid(), 0, &r) == -1) {
-    _LOG(log, logtype::ERROR, "cannot get registers: %s\n", strerror(errno));
+    ALOGE("cannot get registers: %s\n", strerror(errno));
     return;
   }
 
@@ -44,7 +47,7 @@
 void dump_registers(log_t* log, pid_t tid) {
   struct pt_regs r;
   if (ptrace(PTRACE_GETREGS, tid, 0, &r) == -1) {
-    _LOG(log, logtype::ERROR, "cannot get registers: %s\n", strerror(errno));
+    ALOGE("cannot get registers: %s\n", strerror(errno));
     return;
   }
 
diff --git a/debuggerd/x86_64/machine.cpp b/debuggerd/x86_64/machine.cpp
index 4f09a5d..fc86bc2 100644
--- a/debuggerd/x86_64/machine.cpp
+++ b/debuggerd/x86_64/machine.cpp
@@ -14,6 +14,8 @@
 ** limitations under the License.
 */
 
+#define LOG_TAG "DEBUG"
+
 #include <errno.h>
 #include <stdint.h>
 #include <sys/ptrace.h>
@@ -21,6 +23,7 @@
 #include <sys/user.h>
 
 #include <backtrace/Backtrace.h>
+#include <log/log.h>
 
 #include "machine.h"
 #include "utility.h"
@@ -28,7 +31,7 @@
 void dump_memory_and_code(log_t* log, Backtrace* backtrace) {
   struct user_regs_struct r;
   if (ptrace(PTRACE_GETREGS, backtrace->Tid(), 0, &r) == -1) {
-    _LOG(log, logtype::ERROR, "cannot get registers: %s\n", strerror(errno));
+    ALOGE("cannot get registers: %s\n", strerror(errno));
     return;
   }
 
@@ -45,7 +48,7 @@
 void dump_registers(log_t* log, pid_t tid) {
   struct user_regs_struct r;
   if (ptrace(PTRACE_GETREGS, tid, 0, &r) == -1) {
-    _LOG(log, logtype::ERROR, "cannot get registers: %s\n", strerror(errno));
+    ALOGE("cannot get registers: %s\n", strerror(errno));
     return;
   }
 
diff --git a/fastboot/Android.mk b/fastboot/Android.mk
index 66a470a..3f201ec 100644
--- a/fastboot/Android.mk
+++ b/fastboot/Android.mk
@@ -21,41 +21,28 @@
 LOCAL_C_INCLUDES := $(LOCAL_PATH)/../mkbootimg \
   $(LOCAL_PATH)/../../extras/ext4_utils \
   $(LOCAL_PATH)/../../extras/f2fs_utils
-LOCAL_SRC_FILES := protocol.c engine.c bootimg_utils.cpp fastboot.cpp util.c fs.c
+LOCAL_SRC_FILES := protocol.cpp engine.cpp bootimg_utils.cpp fastboot.cpp util.cpp fs.cpp
 LOCAL_MODULE := fastboot
 LOCAL_MODULE_TAGS := debug
+LOCAL_MODULE_HOST_OS := darwin linux windows
 LOCAL_CONLYFLAGS += -std=gnu99
 LOCAL_CFLAGS += -Wall -Wextra -Werror -Wunreachable-code
 
 LOCAL_CFLAGS += -DFASTBOOT_REVISION='"$(fastboot_version)"'
 
-ifeq ($(HOST_OS),linux)
-  LOCAL_SRC_FILES += usb_linux.c util_linux.c
-endif
+LOCAL_SRC_FILES_linux := usb_linux.cpp util_linux.cpp
 
-ifeq ($(HOST_OS),darwin)
-  LOCAL_SRC_FILES += usb_osx.c util_osx.c
-  LOCAL_LDLIBS += -lpthread -framework CoreFoundation -framework IOKit -framework Carbon
-  LOCAL_CFLAGS += -Wno-unused-parameter
-endif
+LOCAL_SRC_FILES_darwin := usb_osx.cpp util_osx.cpp
+LOCAL_LDLIBS_darwin := -lpthread -framework CoreFoundation -framework IOKit -framework Carbon
+LOCAL_CFLAGS_darwin := -Wno-unused-parameter
 
-ifeq ($(HOST_OS),windows)
-  LOCAL_SRC_FILES += usb_windows.c util_windows.c
-  EXTRA_STATIC_LIBS := AdbWinApi
-  ifneq ($(strip $(USE_CYGWIN)),)
-    # Pure cygwin case
-    LOCAL_LDLIBS += -lpthread
-  endif
-  ifneq ($(strip $(USE_MINGW)),)
-    # MinGW under Linux case
-    LOCAL_LDLIBS += -lws2_32
-    USE_SYSDEPS_WIN32 := 1
-  endif
-  LOCAL_C_INCLUDES += development/host/windows/usb/api
-endif
+LOCAL_SRC_FILES_windows := usb_windows.cpp util_windows.cpp
+LOCAL_STATIC_LIBRARIES_windows := AdbWinApi
+LOCAL_REQUIRED_MODULES_windows := AdbWinApi
+LOCAL_LDLIBS_windows := -lws2_32
+LOCAL_C_INCLUDES_windows := development/host/windows/usb/api
 
 LOCAL_STATIC_LIBRARIES := \
-    $(EXTRA_STATIC_LIBS) \
     libziparchive-host \
     libext4_utils_host \
     libsparse_host \
@@ -64,24 +51,18 @@
     libz \
     libbase
 
-ifneq ($(HOST_OS),windows)
-LOCAL_STATIC_LIBRARIES += libselinux
-endif # HOST_OS != windows
+LOCAL_STATIC_LIBRARIES_darwin := libselinux
+LOCAL_STATIC_LIBRARIES_linux := libselinux
 
-ifeq ($(HOST_OS),linux)
 # libf2fs_dlutils_host will dlopen("libf2fs_fmt_host_dyn")
-LOCAL_CFLAGS += -DUSE_F2FS
-LOCAL_LDFLAGS += -ldl -rdynamic -Wl,-rpath,.
-LOCAL_REQUIRED_MODULES := libf2fs_fmt_host_dyn
+LOCAL_CFLAGS_linux := -DUSE_F2FS
+LOCAL_LDFLAGS_linux := -ldl -rdynamic -Wl,-rpath,.
+LOCAL_REQUIRED_MODULES_linux := libf2fs_fmt_host_dyn
 # The following libf2fs_* are from system/extras/f2fs_utils,
 # and do not use code in external/f2fs-tools.
-LOCAL_STATIC_LIBRARIES += libf2fs_utils_host libf2fs_ioutils_host libf2fs_dlutils_host
-endif
+LOCAL_STATIC_LIBRARIES_linux += libf2fs_utils_host libf2fs_ioutils_host libf2fs_dlutils_host
 
-# libc++ not available on windows yet
-ifneq ($(HOST_OS),windows)
-    LOCAL_CXX_STL := libc++_static
-endif
+LOCAL_CXX_STL := libc++_static
 
 # Don't add anything here, we don't want additional shared dependencies
 # on the host fastboot tool, and shared libraries that link against libc++
@@ -97,15 +78,10 @@
 $(call dist-for-goals,dist_files sdk,$(my_dist_files))
 my_dist_files :=
 
-
 ifeq ($(HOST_OS),linux)
 include $(CLEAR_VARS)
-LOCAL_SRC_FILES := usbtest.c usb_linux.c util.c
+LOCAL_SRC_FILES := usbtest.cpp usb_linux.cpp util.cpp
 LOCAL_MODULE := usbtest
 LOCAL_CFLAGS := -Werror
 include $(BUILD_HOST_EXECUTABLE)
 endif
-
-ifeq ($(HOST_OS),windows)
-$(LOCAL_INSTALLED_MODULE): $(HOST_OUT_EXECUTABLES)/AdbWinApi.dll
-endif
diff --git a/fastboot/bootimg_utils.cpp b/fastboot/bootimg_utils.cpp
index d8905a6..c1028ef 100644
--- a/fastboot/bootimg_utils.cpp
+++ b/fastboot/bootimg_utils.cpp
@@ -32,32 +32,27 @@
 #include <stdlib.h>
 #include <string.h>
 
-void bootimg_set_cmdline(boot_img_hdr *h, const char *cmdline)
+void bootimg_set_cmdline(boot_img_hdr* h, const char* cmdline)
 {
     strcpy((char*) h->cmdline, cmdline);
 }
 
-boot_img_hdr *mkbootimg(void *kernel, unsigned kernel_size, unsigned kernel_offset,
-                        void *ramdisk, unsigned ramdisk_size, unsigned ramdisk_offset,
-                        void *second, unsigned second_size, unsigned second_offset,
-                        unsigned page_size, unsigned base, unsigned tags_offset,
-                        unsigned *bootimg_size)
+boot_img_hdr* mkbootimg(void* kernel, int64_t kernel_size, off_t kernel_offset,
+                        void* ramdisk, int64_t ramdisk_size, off_t ramdisk_offset,
+                        void* second, int64_t second_size, off_t second_offset,
+                        size_t page_size, size_t base, off_t tags_offset,
+                        int64_t* bootimg_size)
 {
-    unsigned kernel_actual;
-    unsigned ramdisk_actual;
-    unsigned second_actual;
-    unsigned page_mask;
+    size_t page_mask = page_size - 1;
 
-    page_mask = page_size - 1;
-
-    kernel_actual = (kernel_size + page_mask) & (~page_mask);
-    ramdisk_actual = (ramdisk_size + page_mask) & (~page_mask);
-    second_actual = (second_size + page_mask) & (~page_mask);
+    int64_t kernel_actual = (kernel_size + page_mask) & (~page_mask);
+    int64_t ramdisk_actual = (ramdisk_size + page_mask) & (~page_mask);
+    int64_t second_actual = (second_size + page_mask) & (~page_mask);
 
     *bootimg_size = page_size + kernel_actual + ramdisk_actual + second_actual;
 
     boot_img_hdr* hdr = reinterpret_cast<boot_img_hdr*>(calloc(*bootimg_size, 1));
-    if (hdr == 0) {
+    if (hdr == nullptr) {
         return hdr;
     }
 
@@ -74,12 +69,9 @@
 
     hdr->page_size =    page_size;
 
+    memcpy(hdr->magic + page_size, kernel, kernel_size);
+    memcpy(hdr->magic + page_size + kernel_actual, ramdisk, ramdisk_size);
+    memcpy(hdr->magic + page_size + kernel_actual + ramdisk_actual, second, second_size);
 
-    memcpy(hdr->magic + page_size,
-           kernel, kernel_size);
-    memcpy(hdr->magic + page_size + kernel_actual,
-           ramdisk, ramdisk_size);
-    memcpy(hdr->magic + page_size + kernel_actual + ramdisk_actual,
-           second, second_size);
     return hdr;
 }
diff --git a/fastboot/bootimg_utils.h b/fastboot/bootimg_utils.h
index b1a86cd..fcc8662 100644
--- a/fastboot/bootimg_utils.h
+++ b/fastboot/bootimg_utils.h
@@ -30,20 +30,14 @@
 #define _FASTBOOT_BOOTIMG_UTILS_H_
 
 #include <bootimg.h>
+#include <inttypes.h>
+#include <sys/types.h>
 
-#if defined(__cplusplus)
-extern "C" {
-#endif
-
-void bootimg_set_cmdline(boot_img_hdr *h, const char *cmdline);
-boot_img_hdr *mkbootimg(void *kernel, unsigned kernel_size, unsigned kernel_offset,
-                        void *ramdisk, unsigned ramdisk_size, unsigned ramdisk_offset,
-                        void *second, unsigned second_size, unsigned second_offset,
-                        unsigned page_size, unsigned base, unsigned tags_offset,
-                        unsigned *bootimg_size);
-
-#if defined(__cplusplus)
-}
-#endif
+void bootimg_set_cmdline(boot_img_hdr* h, const char* cmdline);
+boot_img_hdr* mkbootimg(void* kernel, int64_t kernel_size, off_t kernel_offset,
+                        void* ramdisk, int64_t ramdisk_size, off_t ramdisk_offset,
+                        void* second, int64_t second_size, off_t second_offset,
+                        size_t page_size, size_t base, off_t tags_offset,
+                        int64_t* bootimg_size);
 
 #endif
diff --git a/fastboot/engine.c b/fastboot/engine.cpp
similarity index 87%
rename from fastboot/engine.c
rename to fastboot/engine.cpp
index 2f90e41..a0e990a 100644
--- a/fastboot/engine.c
+++ b/fastboot/engine.cpp
@@ -31,7 +31,6 @@
 
 #include <errno.h>
 #include <stdarg.h>
-#include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -39,16 +38,6 @@
 #include <sys/types.h>
 #include <unistd.h>
 
-#ifdef USE_MINGW
-#include <fcntl.h>
-#else
-#include <sys/mman.h>
-#endif
-
-#ifndef __unused
-#define __unused __attribute__((__unused__))
-#endif
-
 #define ARRAY_SIZE(x)           (sizeof(x)/sizeof(x[0]))
 
 #define OP_DOWNLOAD   1
@@ -62,18 +51,20 @@
 
 #define CMD_SIZE 64
 
-struct Action
-{
+struct Action {
     unsigned op;
-    Action *next;
+    Action* next;
 
     char cmd[CMD_SIZE];
-    const char *prod;
-    void *data;
-    unsigned size;
+    const char* prod;
+    void* data;
+
+    // The protocol only supports 32-bit sizes, so you'll have to break
+    // anything larger into chunks.
+    uint32_t size;
 
     const char *msg;
-    int (*func)(Action *a, int status, char *resp);
+    int (*func)(Action* a, int status, const char* resp);
 
     double start;
 };
@@ -121,8 +112,7 @@
     return !!fs_get_generator(fs_type);
 }
 
-static int cb_default(Action *a, int status, char *resp)
-{
+static int cb_default(Action* a, int status, const char* resp) {
     if (status) {
         fprintf(stderr,"FAILED (%s)\n", resp);
     } else {
@@ -135,12 +125,11 @@
 
 static Action *queue_action(unsigned op, const char *fmt, ...)
 {
-    Action *a;
     va_list ap;
     size_t cmdsize;
 
-    a = calloc(1, sizeof(Action));
-    if (a == 0) die("out of memory");
+    Action* a = reinterpret_cast<Action*>(calloc(1, sizeof(Action)));
+    if (a == nullptr) die("out of memory");
 
     va_start(ap, fmt);
     cmdsize = vsnprintf(a->cmd, sizeof(a->cmd), fmt, ap);
@@ -198,8 +187,7 @@
     a->msg = mkmsg("writing '%s'", ptn);
 }
 
-static int match(char *str, const char **value, unsigned count)
-{
+static int match(const char* str, const char** value, unsigned count) {
     unsigned n;
 
     for (n = 0; n < count; n++) {
@@ -222,9 +210,9 @@
 
 
 
-static int cb_check(Action *a, int status, char *resp, int invert)
+static int cb_check(Action* a, int status, const char* resp, int invert)
 {
-    const char **value = a->data;
+    const char** value = reinterpret_cast<const char**>(a->data);
     unsigned count = a->size;
     unsigned n;
     int yes;
@@ -265,18 +253,16 @@
     return -1;
 }
 
-static int cb_require(Action *a, int status, char *resp)
-{
+static int cb_require(Action*a, int status, const char* resp) {
     return cb_check(a, status, resp, 0);
 }
 
-static int cb_reject(Action *a, int status, char *resp)
-{
+static int cb_reject(Action* a, int status, const char* resp) {
     return cb_check(a, status, resp, 1);
 }
 
 void fb_queue_require(const char *prod, const char *var,
-		int invert, unsigned nvalues, const char **value)
+                      bool invert, size_t nvalues, const char **value)
 {
     Action *a;
     a = queue_action(OP_QUERY, "getvar:%s", var);
@@ -285,11 +271,10 @@
     a->size = nvalues;
     a->msg = mkmsg("checking %s", var);
     a->func = invert ? cb_reject : cb_require;
-    if (a->data == 0) die("out of memory");
+    if (a->data == nullptr) die("out of memory");
 }
 
-static int cb_display(Action *a, int status, char *resp)
-{
+static int cb_display(Action* a, int status, const char* resp) {
     if (status) {
         fprintf(stderr, "%s FAILED (%s)\n", a->cmd, resp);
         return status;
@@ -303,17 +288,16 @@
     Action *a;
     a = queue_action(OP_QUERY, "getvar:%s", var);
     a->data = strdup(prettyname);
-    if (a->data == 0) die("out of memory");
+    if (a->data == nullptr) die("out of memory");
     a->func = cb_display;
 }
 
-static int cb_save(Action *a, int status, char *resp)
-{
+static int cb_save(Action* a, int status, const char* resp) {
     if (status) {
         fprintf(stderr, "%s FAILED (%s)\n", a->cmd, resp);
         return status;
     }
-    strncpy(a->data, resp, a->size);
+    strncpy(reinterpret_cast<char*>(a->data), resp, a->size);
     return 0;
 }
 
@@ -326,8 +310,7 @@
     a->func = cb_save;
 }
 
-static int cb_do_nothing(Action *a __unused, int status __unused, char *resp __unused)
-{
+static int cb_do_nothing(Action*, int , const char*) {
     fprintf(stderr,"\n");
     return 0;
 }
@@ -398,7 +381,7 @@
         } else if (a->op == OP_NOTICE) {
             fprintf(stderr,"%s\n",(char*)a->data);
         } else if (a->op == OP_DOWNLOAD_SPARSE) {
-            status = fb_download_data_sparse(usb, a->data);
+            status = fb_download_data_sparse(usb, reinterpret_cast<sparse_file*>(a->data));
             status = a->func(a, status, status ? fb_get_error() : "");
             if (status) break;
         } else if (a->op == OP_WAIT_FOR_DISCONNECT) {
@@ -414,5 +397,5 @@
 
 int fb_queue_is_empty(void)
 {
-    return (action_list == NULL);
+    return (action_list == nullptr);
 }
diff --git a/fastboot/engineering_key.p12 b/fastboot/engineering_key.p12
deleted file mode 100644
index d8183b0..0000000
--- a/fastboot/engineering_key.p12
+++ /dev/null
Binary files differ
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index de4c0ea..0901310 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -34,7 +34,6 @@
 #include <getopt.h>
 #include <inttypes.h>
 #include <limits.h>
-#include <stdbool.h>
 #include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -67,12 +66,12 @@
 static int64_t sparse_limit = -1;
 static int64_t target_sparse_limit = -1;
 
-unsigned page_size = 2048;
-unsigned base_addr      = 0x10000000;
-unsigned kernel_offset  = 0x00008000;
-unsigned ramdisk_offset = 0x01000000;
-unsigned second_offset  = 0x00f00000;
-unsigned tags_offset    = 0x00000100;
+static unsigned page_size = 2048;
+static unsigned base_addr      = 0x10000000;
+static unsigned kernel_offset  = 0x00008000;
+static unsigned ramdisk_offset = 0x01000000;
+static unsigned second_offset  = 0x00f00000;
+static unsigned tags_offset    = 0x00000100;
 
 enum fb_buffer_type {
     FB_BUFFER,
@@ -81,8 +80,8 @@
 
 struct fastboot_buffer {
     enum fb_buffer_type type;
-    void *data;
-    unsigned int sz;
+    void* data;
+    int64_t sz;
 };
 
 static struct {
@@ -97,8 +96,7 @@
     {"vendor.img", "vendor.sig", "vendor", true},
 };
 
-char *find_item(const char *item, const char *product)
-{
+static char* find_item(const char* item, const char* product) {
     char *dir;
     const char *fn;
     char path[PATH_MAX + 128];
@@ -139,36 +137,26 @@
     return strdup(path);
 }
 
-static int64_t file_size(int fd)
-{
-    struct stat st;
-    int ret;
-
-    ret = fstat(fd, &st);
-
-    return ret ? -1 : st.st_size;
+static int64_t get_file_size(int fd) {
+    struct stat sb;
+    return fstat(fd, &sb) == -1 ? -1 : sb.st_size;
 }
 
-static void *load_fd(int fd, unsigned *_sz)
-{
-    char *data;
-    int sz;
+static void* load_fd(int fd, int64_t* sz) {
     int errno_tmp;
+    char* data = nullptr;
 
-    data = 0;
-
-    sz = file_size(fd);
-    if (sz < 0) {
+    *sz = get_file_size(fd);
+    if (*sz < 0) {
         goto oops;
     }
 
-    data = (char*) malloc(sz);
-    if(data == 0) goto oops;
+    data = (char*) malloc(*sz);
+    if (data == nullptr) goto oops;
 
-    if(read(fd, data, sz) != sz) goto oops;
+    if(read(fd, data, *sz) != *sz) goto oops;
     close(fd);
 
-    if(_sz) *_sz = sz;
     return data;
 
 oops:
@@ -179,35 +167,22 @@
     return 0;
 }
 
-static void *load_file(const char *fn, unsigned *_sz)
-{
-    int fd;
-
-    fd = open(fn, O_RDONLY | O_BINARY);
-    if(fd < 0) return 0;
-
-    return load_fd(fd, _sz);
+static void* load_file(const char* fn, int64_t* sz) {
+    int fd = open(fn, O_RDONLY | O_BINARY);
+    if (fd == -1) return nullptr;
+    return load_fd(fd, sz);
 }
 
-int match_fastboot_with_serial(usb_ifc_info *info, const char *local_serial)
-{
-    if(!(vendor_id && (info->dev_vendor == vendor_id)) &&
-       (info->dev_vendor != 0x18d1) &&  // Google
-       (info->dev_vendor != 0x8087) &&  // Intel
-       (info->dev_vendor != 0x0451) &&
-       (info->dev_vendor != 0x0502) &&
-       (info->dev_vendor != 0x0fce) &&  // Sony Ericsson
-       (info->dev_vendor != 0x05c6) &&  // Qualcomm
-       (info->dev_vendor != 0x22b8) &&  // Motorola
-       (info->dev_vendor != 0x0955) &&  // Nvidia
-       (info->dev_vendor != 0x413c) &&  // DELL
-       (info->dev_vendor != 0x2314) &&  // INQ Mobile
-       (info->dev_vendor != 0x0b05) &&  // Asus
-       (info->dev_vendor != 0x0bb4))    // HTC
-            return -1;
-    if(info->ifc_class != 0xff) return -1;
-    if(info->ifc_subclass != 0x42) return -1;
-    if(info->ifc_protocol != 0x03) return -1;
+static int match_fastboot_with_serial(usb_ifc_info* info, const char* local_serial) {
+    // Require a matching vendor id if the user specified one with -i.
+    if (vendor_id != 0 && info->dev_vendor != vendor_id) {
+        return -1;
+    }
+
+    if (info->ifc_class != 0xff || info->ifc_subclass != 0x42 || info->ifc_protocol != 0x03) {
+        return -1;
+    }
+
     // require matching serial number or device path if requested
     // at the command line with the -s option.
     if (local_serial && (strcmp(local_serial, info->serial_number) != 0 &&
@@ -215,14 +190,12 @@
     return 0;
 }
 
-int match_fastboot(usb_ifc_info *info)
-{
+static int match_fastboot(usb_ifc_info* info) {
     return match_fastboot_with_serial(info, serial);
 }
 
-int list_devices_callback(usb_ifc_info *info)
-{
-    if (match_fastboot_with_serial(info, NULL) == 0) {
+static int list_devices_callback(usb_ifc_info* info) {
+    if (match_fastboot_with_serial(info, nullptr) == 0) {
         const char* serial = info->serial_number;
         if (!info->writable) {
             serial = "no permissions"; // like "adb devices"
@@ -243,8 +216,7 @@
     return -1;
 }
 
-usb_handle *open_device(void)
-{
+static usb_handle* open_device() {
     static usb_handle *usb = 0;
     int announce = 1;
 
@@ -255,103 +227,99 @@
         if(usb) return usb;
         if(announce) {
             announce = 0;
-            fprintf(stderr,"< waiting for device >\n");
+            fprintf(stderr, "< waiting for %s >\n", serial ? serial : "any device");
         }
         usleep(1000);
     }
 }
 
-void list_devices(void) {
+static void list_devices() {
     // We don't actually open a USB device here,
     // just getting our callback called so we can
     // list all the connected devices.
     usb_open(list_devices_callback);
 }
 
-void usage(void)
-{
+static void usage() {
     fprintf(stderr,
 /*           1234567890123456789012345678901234567890123456789012345678901234567890123456 */
             "usage: fastboot [ <option> ] <command>\n"
             "\n"
             "commands:\n"
-            "  update <filename>                        reflash device from update.zip\n"
-            "  flashall                                 flash boot, system, vendor and if found,\n"
-            "                                           recovery\n"
-            "  flash <partition> [ <filename> ]         write a file to a flash partition\n"
-            "  flashing lock                            locks the device. Prevents flashing\n"
-            "                                           partitions\n"
-            "  flashing unlock                          unlocks the device. Allows user to\n"
-            "                                           flash any partition except the ones\n"
-            "                                           that are related to bootloader\n"
-            "  flashing lock_critical                   Prevents flashing bootloader related\n"
-            "                                           partitions\n"
-            "  flashing unlock_critical                 Enables flashing bootloader related\n"
-            "                                           partitions\n"
+            "  update <filename>                        Reflash device from update.zip.\n"
+            "  flashall                                 Flash boot, system, vendor, and --\n"
+            "                                           if found -- recovery.\n"
+            "  flash <partition> [ <filename> ]         Write a file to a flash partition.\n"
+            "  flashing lock                            Locks the device. Prevents flashing.\n"
+            "  flashing unlock                          Unlocks the device. Allows flashing\n"
+            "                                           any partition except\n"
+            "                                           bootloader-related partitions.\n"
+            "  flashing lock_critical                   Prevents flashing bootloader-related\n"
+            "                                           partitions.\n"
+            "  flashing unlock_critical                 Enables flashing bootloader-related\n"
+            "                                           partitions.\n"
             "  flashing get_unlock_ability              Queries bootloader to see if the\n"
-            "                                           device is unlocked\n"
+            "                                           device is unlocked.\n"
             "  flashing get_unlock_bootloader_nonce     Queries the bootloader to get the\n"
-            "                                           unlock nonce\n"
-            "  flashing unlock_bootloader <request>     Issue unlock bootloader using request\n"
+            "                                           unlock nonce.\n"
+            "  flashing unlock_bootloader <request>     Issue unlock bootloader using request.\n"
             "  flashing lock_bootloader                 Locks the bootloader to prevent\n"
-            "                                           bootloader version rollback\n"
-            "  erase <partition>                        erase a flash partition\n"
-            "  format[:[<fs type>][:[<size>]] <partition> format a flash partition.\n"
-            "                                           Can override the fs type and/or\n"
-            "                                           size the bootloader reports.\n"
-            "  getvar <variable>                        display a bootloader variable\n"
-            "  boot <kernel> [ <ramdisk> ]              download and boot kernel\n"
-            "  flash:raw boot <kernel> [ <ramdisk> ]    create bootimage and flash it\n"
-            "  devices                                  list all connected devices\n"
-            "  continue                                 continue with autoboot\n"
-            "  reboot [bootloader]                      reboot device, optionally into bootloader\n"
-            "  reboot-bootloader                        reboot device into bootloader\n"
-            "  help                                     show this help message\n"
+            "                                           bootloader version rollback.\n"
+            "  erase <partition>                        Erase a flash partition.\n"
+            "  format[:[<fs type>][:[<size>]] <partition>\n"
+            "                                           Format a flash partition. Can\n"
+            "                                           override the fs type and/or size\n"
+            "                                           the bootloader reports.\n"
+            "  getvar <variable>                        Display a bootloader variable.\n"
+            "  boot <kernel> [ <ramdisk> [ <second> ] ] Download and boot kernel.\n"
+            "  flash:raw boot <kernel> [ <ramdisk> [ <second> ] ]\n"
+            "                                           Create bootimage and flash it.\n"
+            "  devices [-l]                             List all connected devices [with\n"
+            "                                           device paths].\n"
+            "  continue                                 Continue with autoboot.\n"
+            "  reboot [bootloader]                      Reboot device [into bootloader].\n"
+            "  reboot-bootloader                        Reboot device into bootloader.\n"
+            "  help                                     Show this help message.\n"
             "\n"
             "options:\n"
-            "  -w                                       erase userdata and cache (and format\n"
-            "                                           if supported by partition type)\n"
-            "  -u                                       do not first erase partition before\n"
-            "                                           formatting\n"
-            "  -s <specific device>                     specify device serial number\n"
-            "                                           or path to device port\n"
-            "  -l                                       with \"devices\", lists device paths\n"
-            "  -p <product>                             specify product name\n"
-            "  -c <cmdline>                             override kernel commandline\n"
-            "  -i <vendor id>                           specify a custom USB vendor id\n"
-            "  -b <base_addr>                           specify a custom kernel base address.\n"
-            "                                           default: 0x10000000\n"
-            "  -n <page size>                           specify the nand page size.\n"
-            "                                           default: 2048\n"
-            "  -S <size>[K|M|G]                         automatically sparse files greater\n"
-            "                                           than size.  0 to disable\n"
+            "  -w                                       Erase userdata and cache (and format\n"
+            "                                           if supported by partition type).\n"
+            "  -u                                       Do not erase partition before\n"
+            "                                           formatting.\n"
+            "  -s <specific device>                     Specify device serial number\n"
+            "                                           or path to device port.\n"
+            "  -p <product>                             Specify product name.\n"
+            "  -c <cmdline>                             Override kernel commandline.\n"
+            "  -i <vendor id>                           Specify a custom USB vendor id.\n"
+            "  -b <base_addr>                           Specify a custom kernel base\n"
+            "                                           address (default: 0x10000000).\n"
+            "  -n <page size>                           Specify the nand page size\n"
+            "                                           (default: 2048).\n"
+            "  -S <size>[K|M|G]                         Automatically sparse files greater\n"
+            "                                           than 'size'. 0 to disable.\n"
         );
 }
 
-void *load_bootable_image(const char *kernel, const char *ramdisk,
-                          unsigned *sz, const char *cmdline)
-{
-    void *kdata = 0, *rdata = 0;
-    unsigned ksize = 0, rsize = 0;
-    void *bdata;
-    unsigned bsize;
-
-    if(kernel == 0) {
+static void* load_bootable_image(const char* kernel, const char* ramdisk,
+                                 const char* secondstage, int64_t* sz,
+                                 const char* cmdline) {
+    if (kernel == nullptr) {
         fprintf(stderr, "no image specified\n");
         return 0;
     }
 
-    kdata = load_file(kernel, &ksize);
-    if(kdata == 0) {
+    int64_t ksize;
+    void* kdata = load_file(kernel, &ksize);
+    if (kdata == nullptr) {
         fprintf(stderr, "cannot load '%s': %s\n", kernel, strerror(errno));
         return 0;
     }
 
-        /* is this actually a boot image? */
+    // Is this actually a boot image?
     if(!memcmp(kdata, BOOT_MAGIC, BOOT_MAGIC_SIZE)) {
-        if(cmdline) bootimg_set_cmdline((boot_img_hdr*) kdata, cmdline);
+        if (cmdline) bootimg_set_cmdline((boot_img_hdr*) kdata, cmdline);
 
-        if(ramdisk) {
+        if (ramdisk) {
             fprintf(stderr, "cannot boot a boot.img *and* ramdisk\n");
             return 0;
         }
@@ -360,33 +328,46 @@
         return kdata;
     }
 
-    if(ramdisk) {
+    void* rdata = nullptr;
+    int64_t rsize = 0;
+    if (ramdisk) {
         rdata = load_file(ramdisk, &rsize);
-        if(rdata == 0) {
+        if (rdata == nullptr) {
             fprintf(stderr,"cannot load '%s': %s\n", ramdisk, strerror(errno));
             return  0;
         }
     }
 
+    void* sdata = nullptr;
+    int64_t ssize = 0;
+    if (secondstage) {
+        sdata = load_file(secondstage, &ssize);
+        if (sdata == nullptr) {
+            fprintf(stderr,"cannot load '%s': %s\n", secondstage, strerror(errno));
+            return  0;
+        }
+    }
+
     fprintf(stderr,"creating boot image...\n");
-    bdata = mkbootimg(kdata, ksize, kernel_offset,
+    int64_t bsize = 0;
+    void* bdata = mkbootimg(kdata, ksize, kernel_offset,
                       rdata, rsize, ramdisk_offset,
-                      0, 0, second_offset,
+                      sdata, ssize, second_offset,
                       page_size, base_addr, tags_offset, &bsize);
-    if(bdata == 0) {
+    if (bdata == nullptr) {
         fprintf(stderr,"failed to create boot.img\n");
         return 0;
     }
-    if(cmdline) bootimg_set_cmdline((boot_img_hdr*) bdata, cmdline);
-    fprintf(stderr,"creating boot image - %d bytes\n", bsize);
+    if (cmdline) bootimg_set_cmdline((boot_img_hdr*) bdata, cmdline);
+    fprintf(stderr, "creating boot image - %" PRId64 " bytes\n", bsize);
     *sz = bsize;
 
     return bdata;
 }
 
-static void* unzip_file(ZipArchiveHandle zip, const char* entry_name, unsigned* sz)
+static void* unzip_file(ZipArchiveHandle zip, const char* entry_name, int64_t* sz)
 {
-    ZipEntryName zip_entry_name(entry_name);
+    ZipString zip_entry_name(entry_name);
     ZipEntry zip_entry;
     if (FindEntry(zip, zip_entry_name, &zip_entry) != 0) {
         fprintf(stderr, "archive does not contain '%s'\n", entry_name);
@@ -396,8 +377,8 @@
     *sz = zip_entry.uncompressed_length;
 
     uint8_t* data = reinterpret_cast<uint8_t*>(malloc(zip_entry.uncompressed_length));
-    if (data == NULL) {
-        fprintf(stderr, "failed to allocate %u bytes for '%s'\n", *sz, entry_name);
+    if (data == nullptr) {
+        fprintf(stderr, "failed to allocate %" PRId64 " bytes for '%s'\n", *sz, entry_name);
         return 0;
     }
 
@@ -442,13 +423,13 @@
 
 static int unzip_to_file(ZipArchiveHandle zip, char* entry_name) {
     FILE* fp = tmpfile();
-    if (fp == NULL) {
+    if (fp == nullptr) {
         fprintf(stderr, "failed to create temporary file for '%s': %s\n",
                 entry_name, strerror(errno));
         return -1;
     }
 
-    ZipEntryName zip_entry_name(entry_name);
+    ZipString zip_entry_name(entry_name);
     ZipEntry zip_entry;
     if (FindEntry(zip, zip_entry_name, &zip_entry) != 0) {
         fprintf(stderr, "archive does not contain '%s'\n", entry_name);
@@ -482,7 +463,7 @@
 static int setup_requirement_line(char *name)
 {
     char *val[MAX_OPTIONS];
-    char *prod = NULL;
+    char *prod = nullptr;
     unsigned n, count;
     char *x;
     int invert = 0;
@@ -543,13 +524,10 @@
     return 0;
 }
 
-static void setup_requirements(char *data, unsigned sz)
-{
-    char *s;
-
-    s = data;
+static void setup_requirements(char* data, int64_t sz) {
+    char* s = data;
     while (sz-- > 0) {
-        if(*s == '\n') {
+        if (*s == '\n') {
             *s++ = 0;
             if (setup_requirement_line(data)) {
                 die("out of memory");
@@ -561,8 +539,7 @@
     }
 }
 
-void queue_info_dump(void)
-{
+static void queue_info_dump() {
     fb_queue_notice("--------------------------------------------");
     fb_queue_display("version-bootloader", "Bootloader Version...");
     fb_queue_display("version-baseband",   "Baseband Version.....");
@@ -577,7 +554,7 @@
         die("cannot sparse read file\n");
     }
 
-    int files = sparse_file_resparse(s, max_size, NULL, 0);
+    int files = sparse_file_resparse(s, max_size, nullptr, 0);
     if (files < 0) {
         die("Failed to resparse\n");
     }
@@ -602,7 +579,7 @@
     int status = fb_getvar(usb, response, "max-download-size");
 
     if (!status) {
-        limit = strtoul(response, NULL, 0);
+        limit = strtoul(response, nullptr, 0);
         if (limit > 0) {
             fprintf(stderr, "target reported max download size of %" PRId64 " bytes\n",
                     limit);
@@ -647,35 +624,27 @@
     /* The function fb_format_supported() currently returns the value
      * we want, so just call it.
      */
-     return fb_format_supported(usb, part, NULL);
+     return fb_format_supported(usb, part, nullptr);
 }
 
-static int load_buf_fd(usb_handle *usb, int fd,
-        struct fastboot_buffer *buf)
-{
-    int64_t sz64;
-    void *data;
-    int64_t limit;
-
-
-    sz64 = file_size(fd);
-    if (sz64 < 0) {
+static int load_buf_fd(usb_handle* usb, int fd, struct fastboot_buffer* buf) {
+    int64_t sz = get_file_size(fd);
+    if (sz == -1) {
         return -1;
     }
 
-    lseek(fd, 0, SEEK_SET);
-    limit = get_sparse_limit(usb, sz64);
+    lseek64(fd, 0, SEEK_SET);
+    int64_t limit = get_sparse_limit(usb, sz);
     if (limit) {
-        struct sparse_file **s = load_sparse_files(fd, limit);
-        if (s == NULL) {
+        sparse_file** s = load_sparse_files(fd, limit);
+        if (s == nullptr) {
             return -1;
         }
         buf->type = FB_BUFFER_SPARSE;
         buf->data = s;
     } else {
-        unsigned int sz;
-        data = load_fd(fd, &sz);
-        if (data == 0) return -1;
+        void* data = load_fd(fd, &sz);
+        if (data == nullptr) return -1;
         buf->type = FB_BUFFER;
         buf->data = data;
         buf->sz = sz;
@@ -705,8 +674,8 @@
         case FB_BUFFER_SPARSE:
             s = reinterpret_cast<sparse_file**>(buf->data);
             while (*s) {
-                int64_t sz64 = sparse_file_len(*s, true, false);
-                fb_queue_flash_sparse(pname, *s++, sz64);
+                int64_t sz = sparse_file_len(*s, true, false);
+                fb_queue_flash_sparse(pname, *s++, sz);
             }
             break;
         case FB_BUFFER:
@@ -717,8 +686,7 @@
     }
 }
 
-void do_flash(usb_handle *usb, const char *pname, const char *fname)
-{
+static void do_flash(usb_handle* usb, const char* pname, const char* fname) {
     struct fastboot_buffer buf;
 
     if (load_buf(usb, fname, &buf)) {
@@ -727,17 +695,15 @@
     flash_buf(pname, &buf);
 }
 
-void do_update_signature(ZipArchiveHandle zip, char *fn)
-{
-    unsigned sz;
+static void do_update_signature(ZipArchiveHandle zip, char* fn) {
+    int64_t sz;
     void* data = unzip_file(zip, fn, &sz);
-    if (data == 0) return;
+    if (data == nullptr) return;
     fb_queue_download("signature", data, sz);
     fb_queue_command("signature", "installing signature");
 }
 
-void do_update(usb_handle *usb, const char *filename, int erase_first)
-{
+static void do_update(usb_handle* usb, const char* filename, bool erase_first) {
     queue_info_dump();
 
     fb_queue_query_save("product", cur_product, sizeof(cur_product));
@@ -749,9 +715,9 @@
         die("failed to open zip file '%s': %s", filename, ErrorCodeString(error));
     }
 
-    unsigned sz;
+    int64_t sz;
     void* data = unzip_file(zip, "android-info.txt", &sz);
-    if (data == 0) {
+    if (data == nullptr) {
         CloseArchive(zip);
         die("update package '%s' has no android-info.txt", filename);
     }
@@ -784,36 +750,33 @@
     CloseArchive(zip);
 }
 
-void do_send_signature(char *fn)
-{
-    void *data;
-    unsigned sz;
-    char *xtn;
-
-    xtn = strrchr(fn, '.');
+static void do_send_signature(char* fn) {
+    char* xtn = strrchr(fn, '.');
     if (!xtn) return;
+
     if (strcmp(xtn, ".img")) return;
 
-    strcpy(xtn,".sig");
-    data = load_file(fn, &sz);
-    strcpy(xtn,".img");
-    if (data == 0) return;
+    strcpy(xtn, ".sig");
+
+    int64_t sz;
+    void* data = load_file(fn, &sz);
+    strcpy(xtn, ".img");
+    if (data == nullptr) return;
     fb_queue_download("signature", data, sz);
     fb_queue_command("signature", "installing signature");
 }
 
-void do_flashall(usb_handle *usb, int erase_first)
-{
+static void do_flashall(usb_handle* usb, int erase_first) {
     queue_info_dump();
 
     fb_queue_query_save("product", cur_product, sizeof(cur_product));
 
     char* fname = find_item("info", product);
-    if (fname == 0) die("cannot find android-info.txt");
+    if (fname == nullptr) die("cannot find android-info.txt");
 
-    unsigned sz;
+    int64_t sz;
     void* data = load_file(fname, &sz);
-    if (data == 0) die("could not load android-info.txt: %s", strerror(errno));
+    if (data == nullptr) die("could not load android-info.txt: %s", strerror(errno));
 
     setup_requirements(reinterpret_cast<char*>(data), sz);
 
@@ -836,11 +799,8 @@
 #define skip(n) do { argc -= (n); argv += (n); } while (0)
 #define require(n) do { if (argc < (n)) {usage(); exit(1);}} while (0)
 
-int do_bypass_unlock_command(int argc, char **argv)
+static int do_bypass_unlock_command(int argc, char **argv)
 {
-    unsigned sz;
-    void *data;
-
     if (argc <= 2) return 0;
     skip(2);
 
@@ -849,15 +809,17 @@
      * and send that to the remote device.
      */
     require(1);
-    data = load_file(*argv, &sz);
-    if (data == 0) die("could not load '%s': %s", *argv, strerror(errno));
+
+    int64_t sz;
+    void* data = load_file(*argv, &sz);
+    if (data == nullptr) die("could not load '%s': %s", *argv, strerror(errno));
     fb_queue_download("unlock_message", data, sz);
     fb_queue_command("flashing unlock_bootloader", "unlocking bootloader");
     skip(1);
     return 0;
 }
 
-int do_oem_command(int argc, char **argv)
+static int do_oem_command(int argc, char **argv)
 {
     char command[256];
     if (argc <= 1) return 0;
@@ -915,16 +877,15 @@
     return num;
 }
 
-void fb_perform_format(usb_handle* usb,
-                       const char *partition, int skip_if_not_supported,
-                       const char *type_override, const char *size_override)
-{
+static void fb_perform_format(usb_handle* usb,
+                              const char *partition, int skip_if_not_supported,
+                              const char *type_override, const char *size_override) {
     char pTypeBuff[FB_RESPONSE_SZ + 1], pSizeBuff[FB_RESPONSE_SZ + 1];
     char *pType = pTypeBuff;
     char *pSize = pSizeBuff;
     unsigned int limit = INT_MAX;
     struct fastboot_buffer buf;
-    const char *errMsg = NULL;
+    const char *errMsg = nullptr;
     const struct fs_generator *gen;
     uint64_t pSz;
     int status;
@@ -974,7 +935,7 @@
         return;
     }
 
-    pSz = strtoll(pSize, (char **)NULL, 16);
+    pSz = strtoll(pSize, (char **)nullptr, 16);
 
     fd = fileno(tmpfile());
     if (fs_generator_generate(gen, fd, pSz)) {
@@ -1007,9 +968,9 @@
     int wants_wipe = 0;
     int wants_reboot = 0;
     int wants_reboot_bootloader = 0;
-    int erase_first = 1;
+    bool erase_first = true;
     void *data;
-    unsigned sz;
+    int64_t sz;
     int status;
     int c;
     int longindex;
@@ -1045,7 +1006,7 @@
             usage();
             return 1;
         case 'i': {
-                char *endptr = NULL;
+                char *endptr = nullptr;
                 unsigned long val;
 
                 val = strtoul(optarg, &endptr, 0);
@@ -1061,7 +1022,7 @@
             long_listing = 1;
             break;
         case 'n':
-            page_size = (unsigned)strtoul(optarg, NULL, 0);
+            page_size = (unsigned)strtoul(optarg, nullptr, 0);
             if (!page_size) die("invalid page size");
             break;
         case 'p':
@@ -1083,7 +1044,7 @@
             }
             break;
         case 'u':
-            erase_first = 0;
+            erase_first = false;
             break;
         case 'w':
             wants_wipe = 1;
@@ -1092,8 +1053,8 @@
             return 1;
         case 0:
             if (strcmp("unbuffered", longopts[longindex].name) == 0) {
-                setvbuf(stdout, NULL, _IONBF, 0);
-                setvbuf(stderr, NULL, _IONBF, 0);
+                setvbuf(stdout, nullptr, _IONBF, 0);
+                setvbuf(stderr, nullptr, _IONBF, 0);
             } else if (strcmp("version", longopts[longindex].name) == 0) {
                 fprintf(stdout, "fastboot version %s\n", FASTBOOT_REVISION);
                 return 0;
@@ -1133,7 +1094,7 @@
         } else if(!strcmp(*argv, "erase")) {
             require(2);
 
-            if (fb_format_supported(usb, argv[1], NULL)) {
+            if (fb_format_supported(usb, argv[1], nullptr)) {
                 fprintf(stderr, "******** Did you mean to fastboot format this partition?\n");
             }
 
@@ -1141,8 +1102,8 @@
             skip(2);
         } else if(!strncmp(*argv, "format", strlen("format"))) {
             char *overrides;
-            char *type_override = NULL;
-            char *size_override = NULL;
+            char *type_override = nullptr;
+            char *size_override = nullptr;
             require(2);
             /*
              * Parsing for: "format[:[type][:[size]]]"
@@ -1163,8 +1124,8 @@
                 }
                 type_override = overrides;
             }
-            if (type_override && !type_override[0]) type_override = NULL;
-            if (size_override && !size_override[0]) size_override = NULL;
+            if (type_override && !type_override[0]) type_override = nullptr;
+            if (size_override && !size_override[0]) size_override = nullptr;
             if (erase_first && needs_erase(usb, argv[1])) {
                 fb_queue_erase(argv[1]);
             }
@@ -1173,7 +1134,7 @@
         } else if(!strcmp(*argv, "signature")) {
             require(2);
             data = load_file(argv[1], &sz);
-            if (data == 0) die("could not load '%s': %s", argv[1], strerror(errno));
+            if (data == nullptr) die("could not load '%s': %s", argv[1], strerror(errno));
             if (sz != 256) die("signature must be 256 bytes");
             fb_queue_download("signature", data, sz);
             fb_queue_command("signature", "installing signature");
@@ -1198,6 +1159,7 @@
         } else if(!strcmp(*argv, "boot")) {
             char *kname = 0;
             char *rname = 0;
+            char *sname = 0;
             skip(1);
             if (argc > 0) {
                 kname = argv[0];
@@ -1207,7 +1169,11 @@
                 rname = argv[0];
                 skip(1);
             }
-            data = load_bootable_image(kname, rname, &sz, cmdline);
+            if (argc > 0) {
+                sname = argv[0];
+                skip(1);
+            }
+            data = load_bootable_image(kname, rname, sname, &sz, cmdline);
             if (data == 0) return 1;
             fb_queue_download("boot.img", data, sz);
             fb_queue_command("boot", "booting");
@@ -1231,14 +1197,18 @@
             char *pname = argv[1];
             char *kname = argv[2];
             char *rname = 0;
+            char *sname = 0;
             require(3);
-            if(argc > 3) {
-                rname = argv[3];
-                skip(4);
-            } else {
-                skip(3);
+            skip(3);
+            if (argc > 0) {
+                rname = argv[0];
+                skip(1);
             }
-            data = load_bootable_image(kname, rname, &sz, cmdline);
+            if (argc > 0) {
+                sname = argv[0];
+                skip(1);
+            }
+            data = load_bootable_image(kname, rname, sname, &sz, cmdline);
             if (data == 0) die("cannot load bootable image");
             fb_queue_flash(pname, data, sz);
         } else if(!strcmp(*argv, "flashall")) {
@@ -1280,9 +1250,9 @@
 
     if (wants_wipe) {
         fb_queue_erase("userdata");
-        fb_perform_format(usb, "userdata", 1, NULL, NULL);
+        fb_perform_format(usb, "userdata", 1, nullptr, nullptr);
         fb_queue_erase("cache");
-        fb_perform_format(usb, "cache", 1, NULL, NULL);
+        fb_perform_format(usb, "cache", 1, nullptr, nullptr);
     }
     if (wants_reboot) {
         fb_queue_reboot();
diff --git a/fastboot/fastboot.h b/fastboot/fastboot.h
index 481c501..091a70f 100644
--- a/fastboot/fastboot.h
+++ b/fastboot/fastboot.h
@@ -29,18 +29,17 @@
 #ifndef _FASTBOOT_H_
 #define _FASTBOOT_H_
 
-#include "usb.h"
+#include <inttypes.h>
+#include <stdlib.h>
 
-#if defined(__cplusplus)
-extern "C" {
-#endif
+#include "usb.h"
 
 struct sparse_file;
 
 /* protocol.c - fastboot protocol */
 int fb_command(usb_handle *usb, const char *cmd);
 int fb_command_response(usb_handle *usb, const char *cmd, char *response);
-int fb_download_data(usb_handle *usb, const void *data, unsigned size);
+int fb_download_data(usb_handle *usb, const void *data, uint32_t size);
 int fb_download_data_sparse(usb_handle *usb, struct sparse_file *s);
 char *fb_get_error(void);
 
@@ -50,17 +49,17 @@
 /* engine.c - high level command queue engine */
 int fb_getvar(struct usb_handle *usb, char *response, const char *fmt, ...);
 int fb_format_supported(usb_handle *usb, const char *partition, const char *type_override);
-void fb_queue_flash(const char *ptn, void *data, unsigned sz);
-void fb_queue_flash_sparse(const char *ptn, struct sparse_file *s, unsigned sz);
+void fb_queue_flash(const char *ptn, void *data, uint32_t sz);
+void fb_queue_flash_sparse(const char *ptn, struct sparse_file *s, uint32_t sz);
 void fb_queue_erase(const char *ptn);
-void fb_queue_format(const char *ptn, int skip_if_not_supported, unsigned int max_chunk_sz);
-void fb_queue_require(const char *prod, const char *var, int invert,
-        unsigned nvalues, const char **value);
+void fb_queue_format(const char *ptn, int skip_if_not_supported, int32_t max_chunk_sz);
+void fb_queue_require(const char *prod, const char *var, bool invert,
+                      size_t nvalues, const char **value);
 void fb_queue_display(const char *var, const char *prettyname);
-void fb_queue_query_save(const char *var, char *dest, unsigned dest_size);
+void fb_queue_query_save(const char *var, char *dest, uint32_t dest_size);
 void fb_queue_reboot(void);
 void fb_queue_command(const char *cmd, const char *msg);
-void fb_queue_download(const char *name, void *data, unsigned size);
+void fb_queue_download(const char *name, void *data, uint32_t size);
 void fb_queue_notice(const char *notice);
 void fb_queue_wait_for_disconnect(void);
 int fb_execute_queue(usb_handle *usb);
@@ -76,8 +75,4 @@
 /* Current product */
 extern char cur_product[FB_RESPONSE_SZ + 1];
 
-#if defined(__cplusplus)
-}
-#endif
-
 #endif
diff --git a/fastboot/fastboot_protocol.txt b/fastboot/fastboot_protocol.txt
index 37b1959..bb73d8a 100644
--- a/fastboot/fastboot_protocol.txt
+++ b/fastboot/fastboot_protocol.txt
@@ -41,7 +41,7 @@
 
    d. DATA -> the requested command is ready for the data phase.
       A DATA response packet will be 12 bytes long, in the form of
-      DATA00000000 where the 8 digit hexidecimal number represents
+      DATA00000000 where the 8 digit hexadecimal number represents
       the total data size to transfer.
 
 3. Data phase.  Depending on the command, the host or client will 
diff --git a/fastboot/fs.c b/fastboot/fs.cpp
similarity index 69%
rename from fastboot/fs.c
rename to fastboot/fs.cpp
index 8a15e6f..c58a505 100644
--- a/fastboot/fs.c
+++ b/fastboot/fs.cpp
@@ -6,21 +6,12 @@
 #include <errno.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <stdarg.h>
-#include <stdbool.h>
 #include <string.h>
 #include <sys/stat.h>
 #include <sys/types.h>
-#include <sparse/sparse.h>
 #include <unistd.h>
 
-#ifdef USE_MINGW
-#include <fcntl.h>
-#else
-#include <sys/mman.h>
-#endif
-
-
+#include <sparse/sparse.h>
 
 static int generate_ext4_image(int fd, long long partSize)
 {
@@ -38,7 +29,7 @@
 
 static const struct fs_generator {
 
-    char *fs_type;  //must match what fastboot reports for partition type
+    const char* fs_type;  //must match what fastboot reports for partition type
     int (*generate)(int fd, long long partSize); //returns 0 or error value
 
 } generators[] = {
@@ -48,15 +39,13 @@
 #endif
 };
 
-const struct fs_generator* fs_get_generator(const char *fs_type)
-{
-    unsigned i;
-
-    for (i = 0; i < sizeof(generators) / sizeof(*generators); i++)
-        if (!strcmp(generators[i].fs_type, fs_type))
+const struct fs_generator* fs_get_generator(const char* fs_type) {
+    for (size_t i = 0; i < sizeof(generators) / sizeof(*generators); i++) {
+        if (strcmp(generators[i].fs_type, fs_type) == 0) {
             return generators + i;
-
-    return NULL;
+        }
+    }
+    return nullptr;
 }
 
 int fs_generator_generate(const struct fs_generator* gen, int tmpFileNo, long long partSize)
diff --git a/fastboot/fs.h b/fastboot/fs.h
index 307772b..8444081 100644
--- a/fastboot/fs.h
+++ b/fastboot/fs.h
@@ -3,18 +3,10 @@
 
 #include <stdint.h>
 
-#if defined(__cplusplus)
-extern "C" {
-#endif
-
 struct fs_generator;
 
 const struct fs_generator* fs_get_generator(const char *fs_type);
 int fs_generator_generate(const struct fs_generator* gen, int tmpFileNo, long long partSize);
 
-#if defined(__cplusplus)
-}
-#endif
-
 #endif
 
diff --git a/fastboot/p12topem.sh b/fastboot/p12topem.sh
deleted file mode 100755
index f081eb5..0000000
--- a/fastboot/p12topem.sh
+++ /dev/null
@@ -1,9 +0,0 @@
-#!/bin/bash
-
-if [ $# -ne 2 ]
-then
- echo "Usage: $0 alias passphrase"
- exit -1
-fi
-
-openssl pkcs12 -passin pass:"$2" -passout pass:"$2" -in $1.p12 -out $1.pem
diff --git a/fastboot/protocol.c b/fastboot/protocol.cpp
similarity index 68%
rename from fastboot/protocol.c
rename to fastboot/protocol.cpp
index 5b97600..cbd48e8 100644
--- a/fastboot/protocol.c
+++ b/fastboot/protocol.cpp
@@ -26,8 +26,6 @@
  * SUCH DAMAGE.
  */
 
-#define min(a, b) \
-    ({ typeof(a) _a = (a); typeof(b) _b = (b); (_a < _b) ? _a : _b; })
 #define round_down(a, b) \
     ({ typeof(a) _a = (a); typeof(b) _b = (b); _a - (_a % _b); })
 
@@ -36,6 +34,8 @@
 #include <string.h>
 #include <errno.h>
 
+#include <algorithm>
+
 #include <sparse/sparse.h>
 
 #include "fastboot.h"
@@ -47,40 +47,38 @@
     return ERROR;
 }
 
-static int check_response(usb_handle *usb, unsigned int size, char *response)
-{
-    unsigned char status[65];
-    int r;
+static int check_response(usb_handle* usb, uint32_t size, char* response) {
+    char status[65];
 
-    for(;;) {
-        r = usb_read(usb, status, 64);
-        if(r < 0) {
+    while (true) {
+        int r = usb_read(usb, status, 64);
+        if (r < 0) {
             sprintf(ERROR, "status read failed (%s)", strerror(errno));
             usb_close(usb);
             return -1;
         }
         status[r] = 0;
 
-        if(r < 4) {
+        if (r < 4) {
             sprintf(ERROR, "status malformed (%d bytes)", r);
             usb_close(usb);
             return -1;
         }
 
-        if(!memcmp(status, "INFO", 4)) {
+        if (!memcmp(status, "INFO", 4)) {
             fprintf(stderr,"(bootloader) %s\n", status + 4);
             continue;
         }
 
-        if(!memcmp(status, "OKAY", 4)) {
-            if(response) {
+        if (!memcmp(status, "OKAY", 4)) {
+            if (response) {
                 strcpy(response, (char*) status + 4);
             }
             return 0;
         }
 
-        if(!memcmp(status, "FAIL", 4)) {
-            if(r > 4) {
+        if (!memcmp(status, "FAIL", 4)) {
+            if (r > 4) {
                 sprintf(ERROR, "remote: %s", status + 4);
             } else {
                 strcpy(ERROR, "remote failure");
@@ -88,9 +86,9 @@
             return -1;
         }
 
-        if(!memcmp(status, "DATA", 4) && size > 0){
-            unsigned dsize = strtoul((char*) status + 4, 0, 16);
-            if(dsize > size) {
+        if (!memcmp(status, "DATA", 4) && size > 0){
+            uint32_t dsize = strtol(status + 4, 0, 16);
+            if (dsize > size) {
                 strcpy(ERROR, "data size too large");
                 usb_close(usb);
                 return -1;
@@ -106,22 +104,19 @@
     return -1;
 }
 
-static int _command_start(usb_handle *usb, const char *cmd, unsigned size,
-                          char *response)
-{
-    int cmdsize = strlen(cmd);
-
-    if(response) {
-        response[0] = 0;
-    }
-
-    if(cmdsize > 64) {
-        sprintf(ERROR,"command too large");
+static int _command_start(usb_handle* usb, const char* cmd, uint32_t size, char* response) {
+    size_t cmdsize = strlen(cmd);
+    if (cmdsize > 64) {
+        sprintf(ERROR, "command too large");
         return -1;
     }
 
-    if(usb_write(usb, cmd, cmdsize) != cmdsize) {
-        sprintf(ERROR,"command write failed (%s)", strerror(errno));
+    if (response) {
+        response[0] = 0;
+    }
+
+    if (usb_write(usb, cmd, cmdsize) != static_cast<int>(cmdsize)) {
+        sprintf(ERROR, "command write failed (%s)", strerror(errno));
         usb_close(usb);
         return -1;
     }
@@ -129,45 +124,32 @@
     return check_response(usb, size, response);
 }
 
-static int _command_data(usb_handle *usb, const void *data, unsigned size)
-{
-    int r;
-
-    r = usb_write(usb, data, size);
-    if(r < 0) {
+static int _command_data(usb_handle* usb, const void* data, uint32_t size) {
+    int r = usb_write(usb, data, size);
+    if (r < 0) {
         sprintf(ERROR, "data transfer failure (%s)", strerror(errno));
         usb_close(usb);
         return -1;
     }
-    if(r != ((int) size)) {
+    if (r != ((int) size)) {
         sprintf(ERROR, "data transfer failure (short transfer)");
         usb_close(usb);
         return -1;
     }
-
     return r;
 }
 
-static int _command_end(usb_handle *usb)
-{
-    int r;
-    r = check_response(usb, 0, 0);
-    if(r < 0) {
-        return -1;
-    }
-    return 0;
+static int _command_end(usb_handle* usb) {
+    return check_response(usb, 0, 0) < 0 ? -1 : 0;
 }
 
-static int _command_send(usb_handle *usb, const char *cmd,
-                         const void *data, unsigned size,
-                         char *response)
-{
-    int r;
+static int _command_send(usb_handle* usb, const char* cmd, const void* data, uint32_t size,
+                         char* response) {
     if (size == 0) {
         return -1;
     }
 
-    r = _command_start(usb, cmd, size, response);
+    int r = _command_start(usb, cmd, size, response);
     if (r < 0) {
         return -1;
     }
@@ -178,42 +160,29 @@
     }
 
     r = _command_end(usb);
-    if(r < 0) {
+    if (r < 0) {
         return -1;
     }
 
     return size;
 }
 
-static int _command_send_no_data(usb_handle *usb, const char *cmd,
-                                 char *response)
-{
+static int _command_send_no_data(usb_handle* usb, const char* cmd, char* response) {
     return _command_start(usb, cmd, 0, response);
 }
 
-int fb_command(usb_handle *usb, const char *cmd)
-{
+int fb_command(usb_handle* usb, const char* cmd) {
     return _command_send_no_data(usb, cmd, 0);
 }
 
-int fb_command_response(usb_handle *usb, const char *cmd, char *response)
-{
+int fb_command_response(usb_handle* usb, const char* cmd, char* response) {
     return _command_send_no_data(usb, cmd, response);
 }
 
-int fb_download_data(usb_handle *usb, const void *data, unsigned size)
-{
+int fb_download_data(usb_handle* usb, const void* data, uint32_t size) {
     char cmd[64];
-    int r;
-
     sprintf(cmd, "download:%08x", size);
-    r = _command_send(usb, cmd, data, size, 0);
-
-    if(r < 0) {
-        return -1;
-    } else {
-        return 0;
-    }
+    return _command_send(usb, cmd, data, size, 0) < 0 ? -1 : 0;
 }
 
 #define USB_BUF_SIZE 1024
@@ -223,12 +192,12 @@
 static int fb_download_data_sparse_write(void *priv, const void *data, int len)
 {
     int r;
-    usb_handle *usb = priv;
+    usb_handle* usb = reinterpret_cast<usb_handle*>(priv);
     int to_write;
-    const char *ptr = data;
+    const char* ptr = reinterpret_cast<const char*>(data);
 
     if (usb_buf_len) {
-        to_write = min(USB_BUF_SIZE - usb_buf_len, len);
+        to_write = std::min(USB_BUF_SIZE - usb_buf_len, len);
 
         memcpy(usb_buf + usb_buf_len, ptr, to_write);
         usb_buf_len += to_write;
@@ -270,32 +239,25 @@
     return 0;
 }
 
-static int fb_download_data_sparse_flush(usb_handle *usb)
-{
-    int r;
-
+static int fb_download_data_sparse_flush(usb_handle* usb) {
     if (usb_buf_len > 0) {
-        r = _command_data(usb, usb_buf, usb_buf_len);
-        if (r != usb_buf_len) {
+        if (_command_data(usb, usb_buf, usb_buf_len) != usb_buf_len) {
             return -1;
         }
         usb_buf_len = 0;
     }
-
     return 0;
 }
 
-int fb_download_data_sparse(usb_handle *usb, struct sparse_file *s)
-{
-    char cmd[64];
-    int r;
+int fb_download_data_sparse(usb_handle* usb, struct sparse_file* s) {
     int size = sparse_file_len(s, true, false);
     if (size <= 0) {
         return -1;
     }
 
+    char cmd[64];
     sprintf(cmd, "download:%08x", size);
-    r = _command_start(usb, cmd, size, 0);
+    int r = _command_start(usb, cmd, size, 0);
     if (r < 0) {
         return -1;
     }
diff --git a/fastboot/signfile.sh b/fastboot/signfile.sh
deleted file mode 100755
index 3188d2d..0000000
--- a/fastboot/signfile.sh
+++ /dev/null
@@ -1,10 +0,0 @@
-#!/bin/bash
-
-if [ $# -ne 3 ]
-then
- echo "Usage: $0 alias filename passpharse"
- exit -1
-fi
-
-openssl dgst -passin pass:"$3" -binary -sha1 -sign $1.pem $2 > $2.sign
-
diff --git a/fastboot/usb.h b/fastboot/usb.h
index c7b748e..0fda41a 100644
--- a/fastboot/usb.h
+++ b/fastboot/usb.h
@@ -29,16 +29,9 @@
 #ifndef _USB_H_
 #define _USB_H_
 
-#if defined(__cplusplus)
-extern "C" {
-#endif
+struct usb_handle;
 
-typedef struct usb_handle usb_handle;
-
-typedef struct usb_ifc_info usb_ifc_info;
-
-struct usb_ifc_info
-{
+struct usb_ifc_info {
         /* from device descriptor */
     unsigned short dev_vendor;
     unsigned short dev_product;
@@ -68,8 +61,4 @@
 int usb_write(usb_handle *h, const void *_data, int len);
 int usb_wait_for_disconnect(usb_handle *h);
 
-#if defined(__cplusplus)
-}
-#endif
-
 #endif
diff --git a/fastboot/usb_linux.c b/fastboot/usb_linux.cpp
similarity index 93%
rename from fastboot/usb_linux.c
rename to fastboot/usb_linux.cpp
index 022f364..7b87907 100644
--- a/fastboot/usb_linux.c
+++ b/fastboot/usb_linux.cpp
@@ -26,29 +26,22 @@
  * SUCH DAMAGE.
  */
 
+#include <ctype.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <pthread.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <unistd.h>
 #include <string.h>
-
 #include <sys/ioctl.h>
 #include <sys/stat.h>
 #include <sys/types.h>
-#include <dirent.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <pthread.h>
-#include <ctype.h>
+#include <unistd.h>
 
 #include <linux/usbdevice_fs.h>
-#include <linux/usbdevice_fs.h>
 #include <linux/version.h>
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 20)
 #include <linux/usb/ch9.h>
-#else
-#include <linux/usb_ch9.h>
-#endif
-#include <asm/byteorder.h>
 
 #include "fastboot.h"
 #include "usb.h"
@@ -69,9 +62,19 @@
 #define DBG1(x...)
 #endif
 
-/* The max bulk size for linux is 16384 which is defined
- * in drivers/usb/core/devio.c.
- */
+// Kernels before 3.3 have a 16KiB transfer limit. That limit was replaced
+// with a 16MiB global limit in 3.3, but each URB submitted required a
+// contiguous kernel allocation, so you would get ENOMEM if you tried to
+// send something larger than the biggest available contiguous kernel
+// memory region. 256KiB contiguous allocations are generally not reliable
+// on a device kernel that has been running for a while fragmenting its
+// memory, but that shouldn't be a problem for fastboot on the host.
+// In 3.6, the contiguous buffer limit was removed by allocating multiple
+// 16KiB chunks and having the USB driver stitch them back together while
+// transmitting using a scatter-gather list, so 256KiB bulk transfers should
+// be reliable.
+// 256KiB seems to work, but 1MiB bulk transfers lock up my z620 with a 3.13
+// kernel.
 #define MAX_USBFS_BULK_SIZE (16 * 1024)
 
 struct usb_handle
@@ -340,7 +343,7 @@
 
             if(filter_usb_device(de->d_name, desc, n, writable, callback,
                                  &in, &out, &ifc) == 0) {
-                usb = calloc(1, sizeof(usb_handle));
+                usb = reinterpret_cast<usb_handle*>(calloc(1, sizeof(usb_handle)));
                 strcpy(usb->fname, devname);
                 usb->ep_in = in;
                 usb->ep_out = out;
diff --git a/fastboot/usb_osx.c b/fastboot/usb_osx.cpp
similarity index 96%
rename from fastboot/usb_osx.c
rename to fastboot/usb_osx.cpp
index 0b6c515..45ae833 100644
--- a/fastboot/usb_osx.c
+++ b/fastboot/usb_osx.cpp
@@ -26,6 +26,7 @@
  * SUCH DAMAGE.
  */
 
+#include <inttypes.h>
 #include <stdio.h>
 #include <CoreFoundation/CoreFoundation.h>
 #include <IOKit/IOKitLib.h>
@@ -74,7 +75,6 @@
     HRESULT result;
     SInt32 score;
     UInt8 interfaceNumEndpoints;
-    UInt8 endpoint;
     UInt8 configuration;
 
     // Placing the constant KIOUSBFindInterfaceDontCare into the following
@@ -121,7 +121,7 @@
         result = (*plugInInterface)->QueryInterface(
                 plugInInterface,
                 CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID),
-                (LPVOID) &interface);
+                (LPVOID*) &interface);
 
         // No longer need the intermediate plugin
         (*plugInInterface)->Release(plugInInterface);
@@ -181,7 +181,7 @@
 
         // Iterate over the endpoints for this interface and see if there
         // are any that do bulk in/out.
-        for (endpoint = 0; endpoint <= interfaceNumEndpoints; endpoint++) {
+        for (UInt8 endpoint = 1; endpoint <= interfaceNumEndpoints; endpoint++) {
             UInt8   transferType;
             UInt16  maxPacketSize;
             UInt8   interval;
@@ -209,7 +209,7 @@
                     handle->zero_mask = maxPacketSize - 1;
                 }
             } else {
-                ERR("could not get pipe properties\n");
+                ERR("could not get pipe properties for endpoint %u (%08x)\n", endpoint, kr);
             }
 
             if (handle->info.has_bulk_in && handle->info.has_bulk_out) {
@@ -279,7 +279,7 @@
 
     // Now create the device interface.
     result = (*plugin)->QueryInterface(plugin,
-            CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID), (LPVOID) &dev);
+            CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID), (LPVOID*) &dev);
     if ((result != 0) || (dev == NULL)) {
         ERR("Couldn't create a device interface (%08x)\n", (int) result);
         goto error;
@@ -293,6 +293,13 @@
 
     // So, we have a device, finally. Grab its vitals.
 
+
+    kr = (*dev)->USBDeviceOpen(dev);
+    if (kr != 0) {
+        WARN("USBDeviceOpen");
+        goto out;
+    }
+
     kr = (*dev)->GetDeviceVendor(dev, &handle->info.dev_vendor);
     if (kr != 0) {
         ERR("GetDeviceVendor");
@@ -365,12 +372,16 @@
         goto error;
     }
 
+    out:
+
+    (*dev)->USBDeviceClose(dev);
     (*dev)->Release(dev);
     return 0;
 
     error:
 
     if (dev != NULL) {
+        (*dev)->USBDeviceClose(dev);
         (*dev)->Release(dev);
     }
 
@@ -432,7 +443,7 @@
         }
 
         if (h.success) {
-            *handle = calloc(1, sizeof(usb_handle));
+            *handle = reinterpret_cast<usb_handle*>(calloc(1, sizeof(usb_handle)));
             memcpy(*handle, &h, sizeof(usb_handle));
             ret = 0;
             break;
diff --git a/fastboot/usb_windows.c b/fastboot/usb_windows.cpp
similarity index 100%
rename from fastboot/usb_windows.c
rename to fastboot/usb_windows.cpp
diff --git a/fastboot/usbtest.c b/fastboot/usbtest.cpp
similarity index 100%
rename from fastboot/usbtest.c
rename to fastboot/usbtest.cpp
diff --git a/fastboot/util.c b/fastboot/util.cpp
similarity index 100%
rename from fastboot/util.c
rename to fastboot/util.cpp
diff --git a/fastboot/util_linux.c b/fastboot/util_linux.cpp
similarity index 98%
rename from fastboot/util_linux.c
rename to fastboot/util_linux.cpp
index 91c3776..b788199 100644
--- a/fastboot/util_linux.c
+++ b/fastboot/util_linux.cpp
@@ -26,6 +26,8 @@
  * SUCH DAMAGE.
  */
 
+#include "fastboot.h"
+
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
diff --git a/fastboot/util_osx.c b/fastboot/util_osx.cpp
similarity index 98%
rename from fastboot/util_osx.c
rename to fastboot/util_osx.cpp
index e718562..ae0b024 100644
--- a/fastboot/util_osx.c
+++ b/fastboot/util_osx.cpp
@@ -26,6 +26,8 @@
  * SUCH DAMAGE.
  */
 
+#include "fastboot.h"
+
 #import <Carbon/Carbon.h>
 #include <unistd.h>
 
diff --git a/fastboot/util_windows.c b/fastboot/util_windows.cpp
similarity index 98%
rename from fastboot/util_windows.c
rename to fastboot/util_windows.cpp
index 74a5c27..ec52f39 100644
--- a/fastboot/util_windows.c
+++ b/fastboot/util_windows.cpp
@@ -26,6 +26,8 @@
  * SUCH DAMAGE.
  */
 
+#include "fastboot.h"
+
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
diff --git a/fs_mgr/fs_mgr.c b/fs_mgr/fs_mgr.c
index 15d44ef..c47a585 100644
--- a/fs_mgr/fs_mgr.c
+++ b/fs_mgr/fs_mgr.c
@@ -98,7 +98,7 @@
     int status;
     int ret;
     long tmpmnt_flags = MS_NOATIME | MS_NOEXEC | MS_NOSUID;
-    char *tmpmnt_opts = "nomblk_io_submit,errors=remount-ro";
+    char tmpmnt_opts[64] = "errors=remount-ro";
     char *e2fsck_argv[] = {
         E2FSCK_BIN,
         "-y",
@@ -121,6 +121,10 @@
          * fix the filesystem.
          */
         errno = 0;
+        if (!strcmp(fs_type, "ext4")) {
+            // This option is only valid with ext4
+            strlcat(tmpmnt_opts, ",nomblk_io_submit", sizeof(tmpmnt_opts));
+        }
         ret = mount(blk_device, target, fs_type, tmpmnt_flags, tmpmnt_opts);
         INFO("%s(): mount(%s,%s,%s)=%d: %s\n",
              __func__, blk_device, target, fs_type, ret, strerror(errno));
@@ -150,8 +154,8 @@
             INFO("Running %s on %s\n", E2FSCK_BIN, blk_device);
 
             ret = android_fork_execvp_ext(ARRAY_SIZE(e2fsck_argv), e2fsck_argv,
-                                        &status, true, LOG_KLOG | LOG_FILE,
-                                        true, FSCK_LOG_FILE);
+                                          &status, true, LOG_KLOG | LOG_FILE,
+                                          true, FSCK_LOG_FILE, NULL, 0);
 
             if (ret < 0) {
                 /* No need to check for error in fork, we can't really handle it now */
@@ -161,14 +165,14 @@
     } else if (!strcmp(fs_type, "f2fs")) {
             char *f2fs_fsck_argv[] = {
                     F2FS_FSCK_BIN,
-                    "-f",
+                    "-a",
                     blk_device
             };
-        INFO("Running %s -f %s\n", F2FS_FSCK_BIN, blk_device);
+        INFO("Running %s -a %s\n", F2FS_FSCK_BIN, blk_device);
 
         ret = android_fork_execvp_ext(ARRAY_SIZE(f2fs_fsck_argv), f2fs_fsck_argv,
                                       &status, true, LOG_KLOG | LOG_FILE,
-                                      true, FSCK_LOG_FILE);
+                                      true, FSCK_LOG_FILE, NULL, 0);
         if (ret < 0) {
             /* No need to check for error in fork, we can't really handle it now */
             ERROR("Failed trying to run %s\n", F2FS_FSCK_BIN);
@@ -523,6 +527,14 @@
             continue;
         }
 
+        /* Skip mounting the root partition, as it will already have been mounted */
+        if (!strcmp(fstab->recs[i].mount_point, "/")) {
+            if ((fstab->recs[i].fs_mgr_flags & MS_RDONLY) != 0) {
+                fs_mgr_set_blk_ro(fstab->recs[i].blk_device);
+            }
+            continue;
+        }
+
         /* Translate LABEL= file system labels into block devices */
         if (!strcmp(fstab->recs[i].fs_type, "ext2") ||
             !strcmp(fstab->recs[i].fs_type, "ext3") ||
@@ -816,7 +828,8 @@
         /* Initialize the swap area */
         mkswap_argv[1] = fstab->recs[i].blk_device;
         err = android_fork_execvp_ext(ARRAY_SIZE(mkswap_argv), mkswap_argv,
-                                      &status, true, LOG_KLOG, false, NULL);
+                                      &status, true, LOG_KLOG, false, NULL,
+                                      NULL, 0);
         if (err) {
             ERROR("mkswap failed for %s\n", fstab->recs[i].blk_device);
             ret = -1;
diff --git a/fs_mgr/fs_mgr_fstab.c b/fs_mgr/fs_mgr_fstab.c
index f24af1f..a44d034 100644
--- a/fs_mgr/fs_mgr_fstab.c
+++ b/fs_mgr/fs_mgr_fstab.c
@@ -22,6 +22,8 @@
 #include <sys/mount.h>
 #include <unistd.h>
 
+#include <cutils/properties.h>
+
 #include "fs_mgr_priv.h"
 
 struct fs_mgr_flag_values {
@@ -74,6 +76,7 @@
     { "noemulatedsd", MF_NOEMULATEDSD },
     { "notrim",       MF_NOTRIM },
     { "formattable", MF_FORMATTABLE },
+    { "slotselect",  MF_SLOTSELECT },
     { "defaults",    0 },
     { 0,             0 },
 };
@@ -329,6 +332,23 @@
         fstab->recs[cnt].partnum = flag_vals.partnum;
         fstab->recs[cnt].swap_prio = flag_vals.swap_prio;
         fstab->recs[cnt].zram_size = flag_vals.zram_size;
+
+        /* If an A/B partition, modify block device to be the real block device */
+        if (fstab->recs[cnt].fs_mgr_flags & MF_SLOTSELECT) {
+            char propbuf[PROPERTY_VALUE_MAX];
+            char *tmp;
+
+            /* use the kernel parameter if set */
+            property_get("ro.boot.slot_suffix", propbuf, "");
+
+            if (asprintf(&tmp, "%s%s", fstab->recs[cnt].blk_device, propbuf) > 0) {
+                free(fstab->recs[cnt].blk_device);
+                fstab->recs[cnt].blk_device = tmp;
+            } else {
+                ERROR("Error updating block device name\n");
+                goto err;
+            }
+        }
         cnt++;
     }
     fclose(fstab_file);
@@ -480,3 +500,8 @@
 {
     return fstab->fs_mgr_flags & (MF_FORMATTABLE);
 }
+
+int fs_mgr_is_slotselect(struct fstab_rec *fstab)
+{
+    return fstab->fs_mgr_flags & MF_SLOTSELECT;
+}
diff --git a/fs_mgr/fs_mgr_priv.h b/fs_mgr/fs_mgr_priv.h
index 682fd11..a87092e 100644
--- a/fs_mgr/fs_mgr_priv.h
+++ b/fs_mgr/fs_mgr_priv.h
@@ -79,6 +79,7 @@
 #define MF_NOTRIM       0x1000
 #define MF_FILEENCRYPTION 0x2000
 #define MF_FORMATTABLE  0x4000
+#define MF_SLOTSELECT   0x8000
 
 #define DM_BUF_SIZE 4096
 
diff --git a/fs_mgr/fs_mgr_verity.c b/fs_mgr/fs_mgr_verity.c
index 60f5398..a4a99c3 100644
--- a/fs_mgr/fs_mgr_verity.c
+++ b/fs_mgr/fs_mgr_verity.c
@@ -963,10 +963,6 @@
     struct dm_ioctl *io = (struct dm_ioctl *) buffer;
     char *mount_point = basename(fstab->mount_point);
 
-    // set the dm_ioctl flags
-    io->flags |= 1;
-    io->target_count = 1;
-
     // get verity filesystem size
     if (get_fs_size(fstab->fs_type, fstab->blk_device, &device_size) < 0) {
         return retval;
diff --git a/gatekeeperd/Android.mk b/gatekeeperd/Android.mk
index 55b2d5e..3f78955 100644
--- a/gatekeeperd/Android.mk
+++ b/gatekeeperd/Android.mk
@@ -36,6 +36,7 @@
 	libkeystore_binder
 LOCAL_STATIC_LIBRARIES := libscrypt_static
 LOCAL_C_INCLUDES := external/scrypt/lib/crypto
+LOCAL_INIT_RC := gatekeeperd.rc
 include $(BUILD_EXECUTABLE)
 
 include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/gatekeeperd/gatekeeperd.rc b/gatekeeperd/gatekeeperd.rc
new file mode 100644
index 0000000..3f1b92d
--- /dev/null
+++ b/gatekeeperd/gatekeeperd.rc
@@ -0,0 +1,3 @@
+service gatekeeperd /system/bin/gatekeeperd /data/misc/gatekeeper
+    class late_start
+    user system
diff --git a/gpttool/Android.mk b/gpttool/Android.mk
deleted file mode 100644
index 64ad945..0000000
--- a/gpttool/Android.mk
+++ /dev/null
@@ -1,14 +0,0 @@
-ifeq ($(HOST_OS),linux)
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := gpttool.c
-LOCAL_STATIC_LIBRARIES := libz
-LOCAL_CFLAGS := -Werror
-
-LOCAL_MODULE := gpttool
-
-include $(BUILD_HOST_EXECUTABLE)
-
-endif
diff --git a/gpttool/gpttool.c b/gpttool/gpttool.c
deleted file mode 100644
index 398362f..0000000
--- a/gpttool/gpttool.c
+++ /dev/null
@@ -1,373 +0,0 @@
-/*
-** Copyright 2011, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/ioctl.h>
-#include <sys/stat.h>
-#include <unistd.h>
-
-#include <zlib.h>
-
-#include <linux/fs.h>
-
-typedef unsigned char u8;
-typedef unsigned short u16;
-typedef unsigned int u32;
-typedef unsigned long long u64;
-
-const u8 partition_type_uuid[16] = {
-	0xa2, 0xa0, 0xd0, 0xeb, 0xe5, 0xb9, 0x33, 0x44,
-	0x87, 0xc0, 0x68, 0xb6, 0xb7, 0x26, 0x99, 0xc7,
-};
-
-
-#define EFI_VERSION 0x00010000
-#define EFI_MAGIC "EFI PART"
-#define EFI_ENTRIES 128
-#define EFI_NAMELEN 36
-
-struct efi_header {
-	u8 magic[8];
-
-	u32 version;
-	u32 header_sz;
-
-	u32 crc32;
-	u32 reserved;
-
-	u64 header_lba;
-	u64 backup_lba;
-	u64 first_lba;
-	u64 last_lba;
-
-	u8 volume_uuid[16];
-
-	u64 entries_lba;
-
-	u32 entries_count;
-	u32 entries_size;
-	u32 entries_crc32;
-} __attribute__((packed));
-
-struct efi_entry {
-	u8 type_uuid[16];
-	u8 uniq_uuid[16];
-	u64 first_lba;
-	u64 last_lba;
-	u64 attr;
-	u16 name[EFI_NAMELEN];
-};
-
-struct ptable {
-	u8 mbr[512];
-	union {
-		struct efi_header header;
-		u8 block[512];
-	};
-	struct efi_entry entry[EFI_ENTRIES];	
-};
-
-void get_uuid(u8 *uuid)
-{
-	int fd;
-	fd = open("/dev/urandom", O_RDONLY);
-	read(fd, uuid, 16);
-	close(fd);
-}
-
-void init_mbr(u8 *mbr, u32 blocks)
-{
-	mbr[0x1be] = 0x00; // nonbootable
-	mbr[0x1bf] = 0xFF; // bogus CHS
-	mbr[0x1c0] = 0xFF;
-	mbr[0x1c1] = 0xFF;
-
-	mbr[0x1c2] = 0xEE; // GPT partition
-	mbr[0x1c3] = 0xFF; // bogus CHS
-	mbr[0x1c4] = 0xFF;
-	mbr[0x1c5] = 0xFF;
-
-	mbr[0x1c6] = 0x01; // start
-	mbr[0x1c7] = 0x00;
-	mbr[0x1c8] = 0x00;
-	mbr[0x1c9] = 0x00;
-
-	memcpy(mbr + 0x1ca, &blocks, sizeof(u32));
-
-	mbr[0x1fe] = 0x55;
-	mbr[0x1ff] = 0xaa;
-}
-
-int add_ptn(struct ptable *ptbl, u64 first, u64 last, const char *name)
-{
-	struct efi_header *hdr = &ptbl->header;
-	struct efi_entry *entry = ptbl->entry;
-	unsigned n;
-
-	if (first < 34) {
-		fprintf(stderr,"partition '%s' overlaps partition table\n", name);
-		return -1;
-	}
-
-	if (last > hdr->last_lba) {
-		fprintf(stderr,"partition '%s' does not fit on disk\n", name);
-		return -1;
-	}
-	for (n = 0; n < EFI_ENTRIES; n++, entry++) {
-		if (entry->type_uuid[0])
-			continue;
-		memcpy(entry->type_uuid, partition_type_uuid, 16);
-		get_uuid(entry->uniq_uuid);
-		entry->first_lba = first;
-		entry->last_lba = last;
-		for (n = 0; (n < EFI_NAMELEN) && *name; n++)
-			entry->name[n] = *name++;
-		return 0;
-	}
-	fprintf(stderr,"out of partition table entries\n");
-	return -1;
-}
-
-int usage(void)
-{
-	fprintf(stderr,
-		"usage: gpttool write <disk> [ <partition> ]*\n"
-		"       gpttool read <disk>\n"
-		"       gpttool test [ <partition> ]*\n"
-		"\n"
-		"partition:  [<name>]:<size>[kmg] | @<file-of-partitions>\n"
-		);
-	return 0;
-}
-
-void show(struct ptable *ptbl)
-{
-	struct efi_entry *entry = ptbl->entry;
-	unsigned n, m;
-	char name[EFI_NAMELEN + 1];
-
-	fprintf(stderr,"ptn  start block   end block     name\n");
-	fprintf(stderr,"---- ------------- ------------- --------------------\n");
-
-	for (n = 0; n < EFI_ENTRIES; n++, entry++) {
-		if (entry->type_uuid[0] == 0)
-			break;
-		for (m = 0; m < EFI_NAMELEN; m++) {
-			name[m] = entry->name[m] & 127;
-		}
-		name[m] = 0;
-		fprintf(stderr,"#%03d %13lld %13lld %s\n",
-			n + 1, entry->first_lba, entry->last_lba, name);
-	}
-}
-
-u64 find_next_lba(struct ptable *ptbl)
-{
-	struct efi_entry *entry = ptbl->entry;
-	unsigned n;
-	u64 a = 0;
-	for (n = 0; n < EFI_ENTRIES; n++, entry++) {
-		if ((entry->last_lba + 1) > a)
-			a = entry->last_lba + 1;
-	}
-	return a;
-}
-
-u64 next_lba = 0;
-
-u64 parse_size(char *sz)
-{
-	int l = strlen(sz);
-	u64 n = strtoull(sz, 0, 10);
-	if (l) {
-		switch(sz[l-1]){
-		case 'k':
-		case 'K':
-			n *= 1024;
-			break;
-		case 'm':
-		case 'M':
-			n *= (1024 * 1024);
-			break;
-		case 'g':
-		case 'G':
-			n *= (1024 * 1024 * 1024);
-			break;
-		}
-	}
-	return n;
-}
-
-int parse_ptn(struct ptable *ptbl, char *x)
-{
-	char *y = strchr(x, ':');
-	u64 sz;
-
-	if (!y) {
-		fprintf(stderr,"invalid partition entry: %s\n", x);
-		return -1;
-	}
-	*y++ = 0;
-
-	if (*y == 0) {
-		sz = ptbl->header.last_lba - next_lba;
-	} else {
-		sz = parse_size(y);
-		if (sz & 511) {
-			fprintf(stderr,"partition size must be multiple of 512\n");
-			return -1;
-		}
-		sz /= 512;
-	}
-
-	if (sz == 0) {
-		fprintf(stderr,"zero size partitions not allowed\n");
-		return -1;
-	}
-
-	if (x[0] && add_ptn(ptbl, next_lba, next_lba + sz - 1, x))
-		return -1;
-
-	next_lba = next_lba + sz;
-	return 0;
-}
-
-int main(int argc, char **argv)
-{
-	struct ptable ptbl;
-	struct efi_header *hdr = &ptbl.header;
-	u32 n;
-	u64 sz;
-	int fd;
-	const char *device;
-	int real_disk = 0;
-
-	if (argc < 2)
-		return usage();
-
-	if (!strcmp(argv[1], "write")) {
-		if (argc < 3)
-			return usage();
-		device = argv[2];
-		argc -= 2;
-		argv += 2;
-		real_disk = 1;
-	} else if (!strcmp(argv[1], "test")) {
-		argc -= 1;
-		argv += 1;
-		real_disk = 0;
-		sz = 2097152 * 16;
-		fprintf(stderr,"< simulating 16GB disk >\n\n");
-	} else {
-		return usage();
-	}
-
-	if (real_disk) {
-		if (!strcmp(device, "/dev/sda") || 
-		    !strcmp(device, "/dev/sdb")) {
-			fprintf(stderr,"error: refusing to partition sda or sdb\n");
-			return -1;
-		}
-		
-		fd = open(device, O_RDWR);
-		if (fd < 0) {
-			fprintf(stderr,"error: cannot open '%s'\n", device);
-			return -1;
-		}
-		if (ioctl(fd, BLKGETSIZE64, &sz)) {
-			fprintf(stderr,"error: cannot query block device size\n");
-			return -1;
-		}
-		sz /= 512;
-		fprintf(stderr,"blocks %lld\n", sz);
-	}
-
-	memset(&ptbl, 0, sizeof(ptbl));
-
-	init_mbr(ptbl.mbr, sz - 1);
-
-	memcpy(hdr->magic, EFI_MAGIC, sizeof(hdr->magic));
-	hdr->version = EFI_VERSION;
-	hdr->header_sz = sizeof(struct efi_header);
-	hdr->header_lba = 1;
-	hdr->backup_lba = sz - 1;
-	hdr->first_lba = 34;
-	hdr->last_lba = sz - 1;
-	get_uuid(hdr->volume_uuid);
-	hdr->entries_lba = 2;
-	hdr->entries_count = 128;
-	hdr->entries_size = sizeof(struct efi_entry);
-
-	while (argc > 1) {
-		if (argv[1][0] == '@') {
-			char line[256], *p;
-			FILE *f;
-			f = fopen(argv[1] + 1, "r");
-			if (!f) {
-				fprintf(stderr,"cannot read partitions from '%s\n", argv[1]);
-				return -1;
-			}
-			while (fgets(line, sizeof(line), f)) {
-				p = line + strlen(line);
-				while (p > line) {
-					p--;
-					if (*p > ' ')
-						break;
-					*p = 0;
-				}
-				p = line;
-				while (*p && (*p <= ' '))
-					p++;
-				if (*p == '#')
-					continue;
-				if (*p == 0)
-					continue;
-				if (parse_ptn(&ptbl, p))
-					return -1;
-			}
-			fclose(f);
-		} else {	
-			if (parse_ptn(&ptbl, argv[1]))
-				return -1;
-		}
-		argc--;
-		argv++;
-	}
-
-	n = crc32(0, Z_NULL, 0);
-	n = crc32(n, (void*) ptbl.entry, sizeof(ptbl.entry));
-	hdr->entries_crc32 = n;
-
-	n = crc32(0, Z_NULL, 0);
-	n = crc32(n, (void*) &ptbl.header, sizeof(ptbl.header));
-	hdr->crc32 = n;
-
-	show(&ptbl);
-
-	if (real_disk) {
-  		write(fd, &ptbl, sizeof(ptbl));
-		fsync(fd);
-
-		if (ioctl(fd, BLKRRPART, 0)) {
-			fprintf(stderr,"could not re-read partition table\n");
-		}
-		close(fd);
-	}
-	return 0;
-}
diff --git a/healthd/BatteryMonitor.cpp b/healthd/BatteryMonitor.cpp
index 1cad427..cb77a8e 100644
--- a/healthd/BatteryMonitor.cpp
+++ b/healthd/BatteryMonitor.cpp
@@ -195,6 +195,15 @@
         getIntField(mHealthdConfig->batteryCapacityPath);
     props.batteryVoltage = getIntField(mHealthdConfig->batteryVoltagePath) / 1000;
 
+    if (!mHealthdConfig->batteryCurrentNowPath.isEmpty())
+        props.batteryCurrent = getIntField(mHealthdConfig->batteryCurrentNowPath) / 1000;
+
+    if (!mHealthdConfig->batteryFullChargePath.isEmpty())
+        props.batteryFullCharge = getIntField(mHealthdConfig->batteryFullChargePath);
+
+    if (!mHealthdConfig->batteryCycleCountPath.isEmpty())
+        props.batteryCycleCount = getIntField(mHealthdConfig->batteryCycleCountPath);
+
     props.batteryTemperature = mBatteryFixedTemperature ?
         mBatteryFixedTemperature :
         getIntField(mHealthdConfig->batteryTemperaturePath);
@@ -255,7 +264,7 @@
 
     if (logthis) {
         char dmesgline[256];
-
+        size_t len;
         if (props.batteryPresent) {
             snprintf(dmesgline, sizeof(dmesgline),
                  "battery l=%d v=%d t=%s%d.%d h=%d st=%d",
@@ -265,19 +274,27 @@
                  abs(props.batteryTemperature % 10), props.batteryHealth,
                  props.batteryStatus);
 
+            len = strlen(dmesgline);
             if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
-                int c = getIntField(mHealthdConfig->batteryCurrentNowPath);
-                char b[20];
+                len += snprintf(dmesgline + len, sizeof(dmesgline) - len,
+                                " c=%d", props.batteryCurrent);
+            }
 
-                snprintf(b, sizeof(b), " c=%d", c / 1000);
-                strlcat(dmesgline, b, sizeof(dmesgline));
+            if (!mHealthdConfig->batteryFullChargePath.isEmpty()) {
+                len += snprintf(dmesgline + len, sizeof(dmesgline) - len,
+                                " fc=%d", props.batteryFullCharge);
+            }
+
+            if (!mHealthdConfig->batteryCycleCountPath.isEmpty()) {
+                len += snprintf(dmesgline + len, sizeof(dmesgline) - len,
+                                " cc=%d", props.batteryCycleCount);
             }
         } else {
             snprintf(dmesgline, sizeof(dmesgline),
                  "battery none");
         }
 
-        size_t len = strlen(dmesgline);
+        len = strlen(dmesgline);
         snprintf(dmesgline + len, sizeof(dmesgline) - len, " chg=%s%s%s",
                  props.chargerAcOnline ? "a" : "",
                  props.chargerUsbOnline ? "u" : "",
@@ -404,6 +421,21 @@
         snprintf(vs, sizeof(vs), "charge counter: %d\n", v);
         write(fd, vs, strlen(vs));
     }
+
+    if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
+        snprintf(vs, sizeof(vs), "current now: %d\n", props.batteryCurrent);
+        write(fd, vs, strlen(vs));
+    }
+
+    if (!mHealthdConfig->batteryCycleCountPath.isEmpty()) {
+        snprintf(vs, sizeof(vs), "cycle count: %d\n", props.batteryCycleCount);
+        write(fd, vs, strlen(vs));
+    }
+
+    if (!mHealthdConfig->batteryFullChargePath.isEmpty()) {
+        snprintf(vs, sizeof(vs), "Full charge: %d\n", props.batteryFullCharge);
+        write(fd, vs, strlen(vs));
+    }
 }
 
 void BatteryMonitor::init(struct healthd_config *hc) {
@@ -486,6 +518,14 @@
                     }
                 }
 
+                if (mHealthdConfig->batteryFullChargePath.isEmpty()) {
+                    path.clear();
+                    path.appendFormat("%s/%s/charge_full",
+                                      POWER_SUPPLY_SYSFS_PATH, name);
+                    if (access(path, R_OK) == 0)
+                        mHealthdConfig->batteryFullChargePath = path;
+                }
+
                 if (mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
                     path.clear();
                     path.appendFormat("%s/%s/current_now",
@@ -494,6 +534,14 @@
                         mHealthdConfig->batteryCurrentNowPath = path;
                 }
 
+                if (mHealthdConfig->batteryCycleCountPath.isEmpty()) {
+                    path.clear();
+                    path.appendFormat("%s/%s/cycle_count",
+                                      POWER_SUPPLY_SYSFS_PATH, name);
+                    if (access(path, R_OK) == 0)
+                        mHealthdConfig->batteryCycleCountPath = path;
+                }
+
                 if (mHealthdConfig->batteryCurrentAvgPath.isEmpty()) {
                     path.clear();
                     path.appendFormat("%s/%s/current_avg",
@@ -563,6 +611,12 @@
             KLOG_WARNING(LOG_TAG, "BatteryTemperaturePath not found\n");
         if (mHealthdConfig->batteryTechnologyPath.isEmpty())
             KLOG_WARNING(LOG_TAG, "BatteryTechnologyPath not found\n");
+	if (mHealthdConfig->batteryCurrentNowPath.isEmpty())
+            KLOG_WARNING(LOG_TAG, "BatteryCurrentNowPath not found\n");
+        if (mHealthdConfig->batteryFullChargePath.isEmpty())
+            KLOG_WARNING(LOG_TAG, "BatteryFullChargePath not found\n");
+        if (mHealthdConfig->batteryCycleCountPath.isEmpty())
+            KLOG_WARNING(LOG_TAG, "BatteryCycleCountPath not found\n");
     }
 
     if (property_get("ro.boot.fake_battery", pval, NULL) > 0
diff --git a/healthd/healthd.cpp b/healthd/healthd.cpp
index b0002cc..85888c3 100644
--- a/healthd/healthd.cpp
+++ b/healthd/healthd.cpp
@@ -52,6 +52,8 @@
     .batteryCurrentNowPath = String8(String8::kEmptyString),
     .batteryCurrentAvgPath = String8(String8::kEmptyString),
     .batteryChargeCounterPath = String8(String8::kEmptyString),
+    .batteryFullChargePath = String8(String8::kEmptyString),
+    .batteryCycleCountPath = String8(String8::kEmptyString),
     .energyCounter = NULL,
     .boot_min_cap = 0,
     .screen_on = NULL,
diff --git a/healthd/healthd.h b/healthd/healthd.h
index 84b6d76..34ea55f 100644
--- a/healthd/healthd.h
+++ b/healthd/healthd.h
@@ -65,6 +65,8 @@
     android::String8 batteryCurrentNowPath;
     android::String8 batteryCurrentAvgPath;
     android::String8 batteryChargeCounterPath;
+    android::String8 batteryFullChargePath;
+    android::String8 batteryCycleCountPath;
 
     int (*energyCounter)(int64_t *);
     int boot_min_cap;
diff --git a/include/cutils/android_reboot.h b/include/cutils/android_reboot.h
index 85e1b7e..a3861a0 100644
--- a/include/cutils/android_reboot.h
+++ b/include/cutils/android_reboot.h
@@ -17,6 +17,8 @@
 #ifndef __CUTILS_ANDROID_REBOOT_H__
 #define __CUTILS_ANDROID_REBOOT_H__
 
+#include <mntent.h>
+
 __BEGIN_DECLS
 
 /* Commands */
@@ -28,6 +30,9 @@
 #define ANDROID_RB_PROPERTY "sys.powerctl"
 
 int android_reboot(int cmd, int flags, const char *arg);
+int android_reboot_with_callback(
+    int cmd, int flags, const char *arg,
+    void (*cb_on_remount)(const struct mntent*));
 
 __END_DECLS
 
diff --git a/include/cutils/sockets.h b/include/cutils/sockets.h
index f8076ca..2d3c743 100644
--- a/include/cutils/sockets.h
+++ b/include/cutils/sockets.h
@@ -23,7 +23,7 @@
 #include <string.h>
 #include <stdbool.h>
 
-#ifdef HAVE_WINSOCK
+#if defined(_WIN32)
 #include <winsock2.h>
 typedef int  socklen_t;
 #else
@@ -77,7 +77,7 @@
 extern int socket_loopback_client(int port, int type);
 extern int socket_network_client(const char *host, int port, int type);
 extern int socket_network_client_timeout(const char *host, int port, int type,
-                                         int timeout);
+                                         int timeout, int* getaddrinfo_error);
 extern int socket_loopback_server(int port, int type);
 extern int socket_local_server(const char *name, int namespaceId, int type);
 extern int socket_local_server_bind(int s, const char *name, int namespaceId);
diff --git a/include/cutils/trace.h b/include/cutils/trace.h
index e4ed179..087a0c4 100644
--- a/include/cutils/trace.h
+++ b/include/cutils/trace.h
@@ -67,10 +67,12 @@
 #define ATRACE_TAG_RS               (1<<15)
 #define ATRACE_TAG_BIONIC           (1<<16)
 #define ATRACE_TAG_POWER            (1<<17)
-#define ATRACE_TAG_LAST             ATRACE_TAG_POWER
+#define ATRACE_TAG_PACKAGE_MANAGER  (1<<18)
+#define ATRACE_TAG_SYSTEM_SERVER    (1<<19)
+#define ATRACE_TAG_LAST             ATRACE_TAG_SYSTEM_SERVER
 
 // Reserved for initialization.
-#define ATRACE_TAG_NOT_READY        (1LL<<63)
+#define ATRACE_TAG_NOT_READY        (1ULL<<63)
 
 #define ATRACE_TAG_VALID_MASK ((ATRACE_TAG_LAST - 1) | ATRACE_TAG_LAST)
 
diff --git a/include/log/log.h b/include/log/log.h
index 0b17574..1cdf7bc 100644
--- a/include/log/log.h
+++ b/include/log/log.h
@@ -563,6 +563,12 @@
 #define android_btWriteLog(tag, type, payload, len) \
     __android_log_btwrite(tag, type, payload, len)
 
+#define android_errorWriteLog(tag, subTag) \
+    __android_log_error_write(tag, subTag, -1, NULL, 0)
+
+#define android_errorWriteWithInfoLog(tag, subTag, uid, data, dataLen) \
+    __android_log_error_write(tag, subTag, uid, data, dataLen)
+
 /*
  *    IF_ALOG uses android_testLog, but IF_ALOG can be overridden.
  *    android_testLog will remain constant in its purpose as a wrapper
@@ -612,6 +618,9 @@
  */
 int __android_log_is_loggable(int prio, const char *tag, int def);
 
+int __android_log_error_write(int tag, const char *subTag, int32_t uid, const char *data,
+                              uint32_t dataLen);
+
 /*
  * Send a simple string to the log.
  */
diff --git a/include/private/android_filesystem_config.h b/include/private/android_filesystem_config.h
index 2ed27dc..a843171 100644
--- a/include/private/android_filesystem_config.h
+++ b/include/private/android_filesystem_config.h
@@ -26,7 +26,7 @@
 #include <sys/types.h>
 #include <stdint.h>
 
-#ifdef HAVE_ANDROID_OS
+#if defined(__ANDROID__)
 #include <linux/capability.h>
 #else
 #include "android_filesystem_capability.h"
@@ -77,6 +77,7 @@
 #define AID_SDCARD_ALL    1035  /* access all users external storage */
 #define AID_LOGD          1036  /* log daemon */
 #define AID_SHARED_RELRO  1037  /* creator of shared GNU RELRO files */
+#define AID_DBUS          1038  /* dbus-daemon IPC broker process */
 
 #define AID_SHELL         2000  /* adb and debug shell user */
 #define AID_CACHE         2001  /* cache access */
@@ -98,6 +99,10 @@
 #define AID_NET_BW_ACCT   3007  /* change bandwidth statistics accounting */
 #define AID_NET_BT_STACK  3008  /* bluetooth: access config files */
 
+/* The range 5000-5999 is also reserved for OEM, and must never be used here. */
+#define AID_OEM_RESERVED_2_START 5000
+#define AID_OEM_RESERVED_2_END   5999
+
 #define AID_EVERYBODY     9997  /* shared between all apps in the same profile */
 #define AID_MISC          9998  /* access to misc storage */
 #define AID_NOBODY        9999
@@ -168,6 +173,7 @@
     { "sdcard_all",    AID_SDCARD_ALL, },
     { "logd",          AID_LOGD, },
     { "shared_relro",  AID_SHARED_RELRO, },
+    { "dbus",          AID_DBUS, },
 
     { "shell",         AID_SHELL, },
     { "cache",         AID_CACHE, },
diff --git a/include/utils/AndroidThreads.h b/include/utils/AndroidThreads.h
index aad1e82..4c2dd49 100644
--- a/include/utils/AndroidThreads.h
+++ b/include/utils/AndroidThreads.h
@@ -73,7 +73,7 @@
 // ------------------------------------------------------------------
 // Extra functions working with raw pids.
 
-#ifdef HAVE_ANDROID_OS
+#if defined(__ANDROID__)
 // Change the priority AND scheduling group of a particular thread.  The priority
 // should be one of the ANDROID_PRIORITY constants.  Returns INVALID_OPERATION
 // if the priority set failed, else another value if just the group set failed;
diff --git a/include/utils/ByteOrder.h b/include/utils/ByteOrder.h
index baa3a83..44ea13d 100644
--- a/include/utils/ByteOrder.h
+++ b/include/utils/ByteOrder.h
@@ -21,7 +21,7 @@
 
 #include <stdint.h>
 #include <sys/types.h>
-#ifdef HAVE_WINSOCK
+#if defined(_WIN32)
 #include <winsock2.h>
 #else
 #include <netinet/in.h>
diff --git a/include/utils/Compat.h b/include/utils/Compat.h
index 7d96310..c5f9d6a 100644
--- a/include/utils/Compat.h
+++ b/include/utils/Compat.h
@@ -75,4 +75,10 @@
     _rc; })
 #endif
 
+#if defined(_WIN32)
+#define OS_PATH_SEPARATOR '\\'
+#else
+#define OS_PATH_SEPARATOR '/'
+#endif
+
 #endif /* __LIB_UTILS_COMPAT_H */
diff --git a/include/utils/Errors.h b/include/utils/Errors.h
index 46173db..08ddcd2 100644
--- a/include/utils/Errors.h
+++ b/include/utils/Errors.h
@@ -23,7 +23,7 @@
 namespace android {
 
 // use this type to return error codes
-#ifdef HAVE_MS_C_RUNTIME
+#ifdef _WIN32
 typedef int         status_t;
 #else
 typedef int32_t     status_t;
@@ -58,8 +58,7 @@
     ALREADY_EXISTS      = -EEXIST,
     DEAD_OBJECT         = -EPIPE,
     FAILED_TRANSACTION  = (UNKNOWN_ERROR + 2),
-    JPARKS_BROKE_IT     = -EPIPE,
-#if !defined(HAVE_MS_C_RUNTIME)
+#if !defined(_WIN32)
     BAD_INDEX           = -EOVERFLOW,
     NOT_ENOUGH_DATA     = -ENODATA,
     WOULD_BLOCK         = -EWOULDBLOCK, 
diff --git a/include/utils/FileMap.h b/include/utils/FileMap.h
index f70fc92..afd7bfd 100644
--- a/include/utils/FileMap.h
+++ b/include/utils/FileMap.h
@@ -26,7 +26,7 @@
 
 #if defined(__MINGW32__)
 // Ensure that we always pull in winsock2.h before windows.h
-#ifdef HAVE_WINSOCK
+#if defined(_WIN32)
 #include <winsock2.h>
 #endif
 #include <windows.h>
diff --git a/include/utils/JenkinsHash.h b/include/utils/JenkinsHash.h
index 7da5dbd..027c10c 100644
--- a/include/utils/JenkinsHash.h
+++ b/include/utils/JenkinsHash.h
@@ -29,6 +29,9 @@
 /* The Jenkins hash of a sequence of 32 bit words A, B, C is:
  * Whiten(Mix(Mix(Mix(0, A), B), C)) */
 
+#ifdef __clang__
+__attribute__((no_sanitize("integer")))
+#endif
 inline uint32_t JenkinsHashMix(uint32_t hash, uint32_t data) {
     hash += data;
     hash += (hash << 10);
diff --git a/include/utils/Mutex.h b/include/utils/Mutex.h
index 757519b..f027c79 100644
--- a/include/utils/Mutex.h
+++ b/include/utils/Mutex.h
@@ -59,7 +59,7 @@
     // lock if possible; returns 0 on success, error otherwise
     status_t    tryLock();
 
-#if HAVE_ANDROID_OS
+#if defined(__ANDROID__)
     // lock the mutex, but don't wait longer than timeoutMilliseconds.
     // Returns 0 on success, TIMED_OUT for failure due to timeout expiration.
     //
@@ -128,7 +128,7 @@
 inline status_t Mutex::tryLock() {
     return -pthread_mutex_trylock(&mMutex);
 }
-#if HAVE_ANDROID_OS
+#if defined(__ANDROID__)
 inline status_t Mutex::timedLock(nsecs_t timeoutNs) {
     const struct timespec ts = {
         /* .tv_sec = */ static_cast<time_t>(timeoutNs / 1000000000),
diff --git a/include/utils/Thread.h b/include/utils/Thread.h
index 28839fd..1532b7e 100644
--- a/include/utils/Thread.h
+++ b/include/utils/Thread.h
@@ -70,7 +70,7 @@
     // Indicates whether this thread is running or not.
             bool        isRunning() const;
 
-#ifdef HAVE_ANDROID_OS
+#if defined(__ANDROID__)
     // Return the thread's kernel ID, same as the thread itself calling gettid(),
     // or -1 if the thread is not running.
             pid_t       getTid() const;
@@ -101,7 +101,7 @@
     volatile bool           mExitPending;
     volatile bool           mRunning;
             sp<Thread>      mHoldSelf;
-#ifdef HAVE_ANDROID_OS
+#if defined(__ANDROID__)
     // legacy for debugging, not used by getTid() as it is set by the child thread
     // and so is not initialized until the child reaches that point
             pid_t           mTid;
diff --git a/include/utils/Trace.h b/include/utils/Trace.h
index 6ee343d..6ba68f6 100644
--- a/include/utils/Trace.h
+++ b/include/utils/Trace.h
@@ -17,7 +17,7 @@
 #ifndef ANDROID_TRACE_H
 #define ANDROID_TRACE_H
 
-#ifdef HAVE_ANDROID_OS
+#if defined(__ANDROID__)
 
 #include <fcntl.h>
 #include <stdint.h>
@@ -59,11 +59,11 @@
 
 }; // namespace android
 
-#else // HAVE_ANDROID_OS
+#else // !__ANDROID__
 
 #define ATRACE_NAME(...)
 #define ATRACE_CALL()
 
-#endif // HAVE_ANDROID_OS
+#endif // __ANDROID__
 
 #endif // ANDROID_TRACE_H
diff --git a/include/utils/TypeHelpers.h b/include/utils/TypeHelpers.h
index 13c9081..61d618e 100644
--- a/include/utils/TypeHelpers.h
+++ b/include/utils/TypeHelpers.h
@@ -131,7 +131,8 @@
 template<typename TYPE> inline
 void construct_type(TYPE* p, size_t n) {
     if (!traits<TYPE>::has_trivial_ctor) {
-        while (n--) {
+        while (n > 0) {
+            n--;
             new(p++) TYPE;
         }
     }
@@ -140,7 +141,8 @@
 template<typename TYPE> inline
 void destroy_type(TYPE* p, size_t n) {
     if (!traits<TYPE>::has_trivial_dtor) {
-        while (n--) {
+        while (n > 0) {
+            n--;
             p->~TYPE();
             p++;
         }
@@ -150,7 +152,8 @@
 template<typename TYPE> inline
 void copy_type(TYPE* d, const TYPE* s, size_t n) {
     if (!traits<TYPE>::has_trivial_copy) {
-        while (n--) {
+        while (n > 0) {
+            n--;
             new(d) TYPE(*s);
             d++, s++;
         }
@@ -162,12 +165,14 @@
 template<typename TYPE> inline
 void splat_type(TYPE* where, const TYPE* what, size_t n) {
     if (!traits<TYPE>::has_trivial_copy) {
-        while (n--) {
+        while (n > 0) {
+            n--;
             new(where) TYPE(*what);
             where++;
         }
     } else {
-        while (n--) {
+        while (n > 0) {
+            n--;
             *where++ = *what;
         }
     }
@@ -182,7 +187,8 @@
     } else {
         d += n;
         s += n;
-        while (n--) {
+        while (n > 0) {
+            n--;
             --d, --s;
             if (!traits<TYPE>::has_trivial_copy) {
                 new(d) TYPE(*s);
@@ -203,7 +209,8 @@
     {
         memmove(d,s,n*sizeof(TYPE));
     } else {
-        while (n--) {
+        while (n > 0) {
+            n--;
             if (!traits<TYPE>::has_trivial_copy) {
                 new(d) TYPE(*s);
             } else {
diff --git a/include/ziparchive/zip_archive.h b/include/ziparchive/zip_archive.h
index 3b00683..3591a6b 100644
--- a/include/ziparchive/zip_archive.h
+++ b/include/ziparchive/zip_archive.h
@@ -22,6 +22,7 @@
 
 #include <stdint.h>
 #include <string.h>
+#include <sys/cdefs.h>
 #include <sys/types.h>
 #include <utils/Compat.h>
 
@@ -33,17 +34,33 @@
   kCompressDeflated   = 8,        // standard deflate
 };
 
-struct ZipEntryName {
+struct ZipString {
   const uint8_t* name;
   uint16_t name_length;
 
-  ZipEntryName() {}
+  ZipString() {}
 
   /*
    * entry_name has to be an c-style string with only ASCII characters.
    */
-  explicit ZipEntryName(const char* entry_name)
+  explicit ZipString(const char* entry_name)
       : name(reinterpret_cast<const uint8_t*>(entry_name)), name_length(strlen(entry_name)) {}
+
+  bool operator==(const ZipString& rhs) const {
+    return name && (name_length == rhs.name_length) &&
+        (memcmp(name, rhs.name, name_length) == 0);
+  }
+
+  bool StartsWith(const ZipString& prefix) const {
+    return name && (name_length >= prefix.name_length) &&
+        (memcmp(name, prefix.name, prefix.name_length) == 0);
+  }
+
+  bool EndsWith(const ZipString& suffix) const {
+    return name && (name_length >= suffix.name_length) &&
+        (memcmp(name + name_length - suffix.name_length, suffix.name,
+                suffix.name_length) == 0);
+  }
 };
 
 /*
@@ -136,7 +153,7 @@
  * and length, a call to VerifyCrcAndLengths must be made after entry data
  * has been processed.
  */
-int32_t FindEntry(const ZipArchiveHandle handle, const ZipEntryName& entryName,
+int32_t FindEntry(const ZipArchiveHandle handle, const ZipString& entryName,
                   ZipEntry* data);
 
 /*
@@ -147,15 +164,14 @@
  * calls to Next. All calls to StartIteration must be matched by a call to
  * EndIteration to free any allocated memory.
  *
- * This method also accepts an optional prefix to restrict iteration to
- * entry names that start with |optional_prefix|.
+ * This method also accepts optional prefix and suffix to restrict iteration to
+ * entry names that start with |optional_prefix| or end with |optional_suffix|.
  *
  * Returns 0 on success and negative values on failure.
  */
 int32_t StartIteration(ZipArchiveHandle handle, void** cookie_ptr,
-                       const ZipEntryName* optional_prefix,
-                       // TODO: Remove the default parameter.
-                       const ZipEntryName* optional_suffix = NULL);
+                       const ZipString* optional_prefix,
+                       const ZipString* optional_suffix);
 
 /*
  * Advance to the next element in the zipfile in iteration order.
@@ -163,7 +179,7 @@
  * Returns 0 on success, -1 if there are no more elements in this
  * archive and lower negative values on failure.
  */
-int32_t Next(void* cookie, ZipEntry* data, ZipEntryName *name);
+int32_t Next(void* cookie, ZipEntry* data, ZipString* name);
 
 /*
  * End iteration over all entries of a zip file and frees the memory allocated
diff --git a/init/Android.mk b/init/Android.mk
index de065dc..c1cdc1a 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -5,9 +5,9 @@
 # --
 
 ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT)))
-init_options += -DALLOW_LOCAL_PROP_OVERRIDE=1 -DALLOW_DISABLE_SELINUX=1
+init_options += -DALLOW_LOCAL_PROP_OVERRIDE=1 -DALLOW_PERMISSIVE_SELINUX=1
 else
-init_options += -DALLOW_LOCAL_PROP_OVERRIDE=0 -DALLOW_DISABLE_SELINUX=0
+init_options += -DALLOW_LOCAL_PROP_OVERRIDE=0 -DALLOW_PERMISSIVE_SELINUX=0
 endif
 
 init_options += -DLOG_UEVENTS=0
@@ -18,21 +18,44 @@
     -Wno-unused-parameter \
     -Werror \
 
-init_clang := true
-
 # --
 
+# If building on Linux, then build unit test for the host.
+ifeq ($(HOST_OS),linux)
+include $(CLEAR_VARS)
+LOCAL_CPPFLAGS := $(init_cflags)
+LOCAL_SRC_FILES:= \
+    parser/tokenizer.cpp \
+
+LOCAL_MODULE := libinit_parser
+LOCAL_CLANG := true
+include $(BUILD_HOST_STATIC_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := init_parser_tests
+LOCAL_SRC_FILES := \
+    parser/tokenizer_test.cpp \
+
+LOCAL_STATIC_LIBRARIES := libinit_parser
+LOCAL_CLANG := true
+include $(BUILD_HOST_NATIVE_TEST)
+endif
+
 include $(CLEAR_VARS)
 LOCAL_CPPFLAGS := $(init_cflags)
 LOCAL_SRC_FILES:= \
+    action.cpp \
+    import_parser.cpp \
     init_parser.cpp \
     log.cpp \
     parser.cpp \
+    service.cpp \
     util.cpp \
 
 LOCAL_STATIC_LIBRARIES := libbase
 LOCAL_MODULE := libinit
-LOCAL_CLANG := $(init_clang)
+LOCAL_SANITIZE := integer
+LOCAL_CLANG := true
 include $(BUILD_STATIC_LIBRARY)
 
 include $(CLEAR_VARS)
@@ -81,7 +104,8 @@
     ln -sf ../init $(TARGET_ROOT_OUT)/sbin/ueventd; \
     ln -sf ../init $(TARGET_ROOT_OUT)/sbin/watchdogd
 
-LOCAL_CLANG := $(init_clang)
+LOCAL_SANITIZE := integer
+LOCAL_CLANG := true
 include $(BUILD_EXECUTABLE)
 
 
@@ -98,5 +122,6 @@
     libbase \
 
 LOCAL_STATIC_LIBRARIES := libinit
-LOCAL_CLANG := $(init_clang)
+LOCAL_SANITIZE := integer
+LOCAL_CLANG := true
 include $(BUILD_NATIVE_TEST)
diff --git a/init/action.cpp b/init/action.cpp
new file mode 100644
index 0000000..c6cbc2e
--- /dev/null
+++ b/init/action.cpp
@@ -0,0 +1,428 @@
+/*
+ * Copyright (C) 2015 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 "action.h"
+
+#include <errno.h>
+
+#include <base/strings.h>
+#include <base/stringprintf.h>
+
+#include "builtins.h"
+#include "error.h"
+#include "init_parser.h"
+#include "log.h"
+#include "property_service.h"
+#include "util.h"
+
+using android::base::Join;
+using android::base::StringPrintf;
+
+Command::Command(BuiltinFunction f, const std::vector<std::string>& args,
+                 const std::string& filename, int line)
+    : func_(f), args_(args), filename_(filename), line_(line) {
+}
+
+int Command::InvokeFunc() const {
+    std::vector<std::string> expanded_args;
+    expanded_args.resize(args_.size());
+    expanded_args[0] = args_[0];
+    for (std::size_t i = 1; i < args_.size(); ++i) {
+        if (!expand_props(args_[i], &expanded_args[i])) {
+            ERROR("%s: cannot expand '%s'\n", args_[0].c_str(), args_[i].c_str());
+            return -EINVAL;
+        }
+    }
+
+    return func_(expanded_args);
+}
+
+std::string Command::BuildCommandString() const {
+    return Join(args_, ' ');
+}
+
+std::string Command::BuildSourceString() const {
+    if (!filename_.empty()) {
+        return StringPrintf(" (%s:%d)", filename_.c_str(), line_);
+    } else {
+        return std::string();
+    }
+}
+
+Action::Action(bool oneshot) : oneshot_(oneshot) {
+}
+
+const KeywordMap<BuiltinFunction>* Action::function_map_ = nullptr;
+
+bool Action::AddCommand(const std::vector<std::string>& args,
+                        const std::string& filename, int line, std::string* err) {
+    if (!function_map_) {
+        *err = "no function map available";
+        return false;
+    }
+
+    if (args.empty()) {
+        *err = "command needed, but not provided";
+        return false;
+    }
+
+    auto function = function_map_->FindFunction(args[0], args.size() - 1, err);
+    if (!function) {
+        return false;
+    }
+
+    AddCommand(function, args, filename, line);
+    return true;
+}
+
+void Action::AddCommand(BuiltinFunction f,
+                        const std::vector<std::string>& args,
+                        const std::string& filename, int line) {
+    commands_.emplace_back(f, args, filename, line);
+}
+
+void Action::CombineAction(const Action& action) {
+    for (const auto& c : action.commands_) {
+        commands_.emplace_back(c);
+    }
+}
+
+std::size_t Action::NumCommands() const {
+    return commands_.size();
+}
+
+void Action::ExecuteOneCommand(std::size_t command) const {
+    ExecuteCommand(commands_[command]);
+}
+
+void Action::ExecuteAllCommands() const {
+    for (const auto& c : commands_) {
+        ExecuteCommand(c);
+    }
+}
+
+void Action::ExecuteCommand(const Command& command) const {
+    Timer t;
+    int result = command.InvokeFunc();
+
+    if (klog_get_level() >= KLOG_INFO_LEVEL) {
+        std::string trigger_name = BuildTriggersString();
+        std::string cmd_str = command.BuildCommandString();
+        std::string source = command.BuildSourceString();
+
+        INFO("Command '%s' action=%s%s returned %d took %.2fs\n",
+             cmd_str.c_str(), trigger_name.c_str(), source.c_str(),
+             result, t.duration());
+    }
+}
+
+bool Action::ParsePropertyTrigger(const std::string& trigger, std::string* err) {
+    const static std::string prop_str("property:");
+    std::string prop_name(trigger.substr(prop_str.length()));
+    size_t equal_pos = prop_name.find('=');
+    if (equal_pos == std::string::npos) {
+        *err = "property trigger found without matching '='";
+        return false;
+    }
+
+    std::string prop_value(prop_name.substr(equal_pos + 1));
+    prop_name.erase(equal_pos);
+
+    auto res = property_triggers_.emplace(prop_name, prop_value);
+    if (res.second == false) {
+        *err = "multiple property triggers found for same property";
+        return false;
+    }
+    return true;
+}
+
+bool Action::InitTriggers(const std::vector<std::string>& args, std::string* err) {
+    const static std::string prop_str("property:");
+    for (std::size_t i = 0; i < args.size(); ++i) {
+        if (i % 2) {
+            if (args[i] != "&&") {
+                *err = "&& is the only symbol allowed to concatenate actions";
+                return false;
+            } else {
+                continue;
+            }
+        }
+
+        if (!args[i].compare(0, prop_str.length(), prop_str)) {
+            if (!ParsePropertyTrigger(args[i], err)) {
+                return false;
+            }
+        } else {
+            if (!event_trigger_.empty()) {
+                *err = "multiple event triggers are not allowed";
+                return false;
+            }
+
+            event_trigger_ = args[i];
+        }
+    }
+
+    return true;
+}
+
+bool Action::InitSingleTrigger(const std::string& trigger) {
+    std::vector<std::string> name_vector{trigger};
+    std::string err;
+    return InitTriggers(name_vector, &err);
+}
+
+// This function checks that all property triggers are satisfied, that is
+// for each (name, value) in property_triggers_, check that the current
+// value of the property 'name' == value.
+//
+// It takes an optional (name, value) pair, which if provided must
+// be present in property_triggers_; it skips the check of the current
+// property value for this pair.
+bool Action::CheckPropertyTriggers(const std::string& name,
+                                   const std::string& value) const {
+    if (property_triggers_.empty()) {
+        return true;
+    }
+
+    bool found = name.empty();
+    for (const auto& t : property_triggers_) {
+        const auto& trigger_name = t.first;
+        const auto& trigger_value = t.second;
+        if (trigger_name == name) {
+            if (trigger_value != "*" && trigger_value != value) {
+                return false;
+            } else {
+                found = true;
+            }
+        } else {
+            std::string prop_val = property_get(trigger_name.c_str());
+            if (prop_val.empty() || (trigger_value != "*" &&
+                                     trigger_value != prop_val)) {
+                return false;
+            }
+        }
+    }
+    return found;
+}
+
+bool Action::CheckEventTrigger(const std::string& trigger) const {
+    return !event_trigger_.empty() &&
+        trigger == event_trigger_ &&
+        CheckPropertyTriggers();
+}
+
+bool Action::CheckPropertyTrigger(const std::string& name,
+                                  const std::string& value) const {
+    return event_trigger_.empty() && CheckPropertyTriggers(name, value);
+}
+
+bool Action::TriggersEqual(const Action& other) const {
+    return property_triggers_ == other.property_triggers_ &&
+        event_trigger_ == other.event_trigger_;
+}
+
+std::string Action::BuildTriggersString() const {
+    std::string result;
+
+    for (const auto& t : property_triggers_) {
+        result += t.first;
+        result += '=';
+        result += t.second;
+        result += ' ';
+    }
+    if (!event_trigger_.empty()) {
+        result += event_trigger_;
+        result += ' ';
+    }
+    result.pop_back();
+    return result;
+}
+
+void Action::DumpState() const {
+    std::string trigger_name = BuildTriggersString();
+    INFO("on %s\n", trigger_name.c_str());
+
+    for (const auto& c : commands_) {
+        std::string cmd_str = c.BuildCommandString();
+        INFO(" %s\n", cmd_str.c_str());
+    }
+    INFO("\n");
+}
+
+class EventTrigger : public Trigger {
+public:
+    EventTrigger(const std::string& trigger) : trigger_(trigger) {
+    }
+    bool CheckTriggers(const Action& action) const override {
+        return action.CheckEventTrigger(trigger_);
+    }
+private:
+    const std::string trigger_;
+};
+
+class PropertyTrigger : public Trigger {
+public:
+    PropertyTrigger(const std::string& name, const std::string& value)
+        : name_(name), value_(value) {
+    }
+    bool CheckTriggers(const Action& action) const override {
+        return action.CheckPropertyTrigger(name_, value_);
+    }
+private:
+    const std::string name_;
+    const std::string value_;
+};
+
+class BuiltinTrigger : public Trigger {
+public:
+    BuiltinTrigger(Action* action) : action_(action) {
+    }
+    bool CheckTriggers(const Action& action) const override {
+        return action_ == &action;
+    }
+private:
+    const Action* action_;
+};
+
+ActionManager::ActionManager() : current_command_(0) {
+}
+
+ActionManager& ActionManager::GetInstance() {
+    static ActionManager instance;
+    return instance;
+}
+
+void ActionManager::AddAction(std::unique_ptr<Action> action) {
+    auto old_action_it =
+        std::find_if(actions_.begin(), actions_.end(),
+                     [&action] (std::unique_ptr<Action>& a) {
+                         return action->TriggersEqual(*a);
+                     });
+
+    if (old_action_it != actions_.end()) {
+        (*old_action_it)->CombineAction(*action);
+    } else {
+        actions_.emplace_back(std::move(action));
+    }
+}
+
+void ActionManager::QueueEventTrigger(const std::string& trigger) {
+    trigger_queue_.push(std::make_unique<EventTrigger>(trigger));
+}
+
+void ActionManager::QueuePropertyTrigger(const std::string& name,
+                                         const std::string& value) {
+    trigger_queue_.push(std::make_unique<PropertyTrigger>(name, value));
+}
+
+void ActionManager::QueueAllPropertyTriggers() {
+    QueuePropertyTrigger("", "");
+}
+
+void ActionManager::QueueBuiltinAction(BuiltinFunction func,
+                                       const std::string& name) {
+    auto action = std::make_unique<Action>(true);
+    std::vector<std::string> name_vector{name};
+
+    if (!action->InitSingleTrigger(name)) {
+        return;
+    }
+
+    action->AddCommand(func, name_vector);
+
+    trigger_queue_.push(std::make_unique<BuiltinTrigger>(action.get()));
+    actions_.emplace_back(std::move(action));
+}
+
+void ActionManager::ExecuteOneCommand() {
+    // Loop through the trigger queue until we have an action to execute
+    while (current_executing_actions_.empty() && !trigger_queue_.empty()) {
+        for (const auto& action : actions_) {
+            if (trigger_queue_.front()->CheckTriggers(*action)) {
+                current_executing_actions_.emplace(action.get());
+            }
+        }
+        trigger_queue_.pop();
+    }
+
+    if (current_executing_actions_.empty()) {
+        return;
+    }
+
+    auto action = current_executing_actions_.front();
+
+    if (current_command_ == 0) {
+        std::string trigger_name = action->BuildTriggersString();
+        INFO("processing action (%s)\n", trigger_name.c_str());
+    }
+
+    action->ExecuteOneCommand(current_command_);
+
+    // If this was the last command in the current action, then remove
+    // the action from the executing list.
+    // If this action was oneshot, then also remove it from actions_.
+    ++current_command_;
+    if (current_command_ == action->NumCommands()) {
+        current_executing_actions_.pop();
+        current_command_ = 0;
+        if (action->oneshot()) {
+            auto eraser = [&action] (std::unique_ptr<Action>& a) {
+                return a.get() == action;
+            };
+            actions_.erase(std::remove_if(actions_.begin(), actions_.end(), eraser));
+        }
+    }
+}
+
+bool ActionManager::HasMoreCommands() const {
+    return !current_executing_actions_.empty() || !trigger_queue_.empty();
+}
+
+void ActionManager::DumpState() const {
+    for (const auto& a : actions_) {
+        a->DumpState();
+    }
+    INFO("\n");
+}
+
+bool ActionParser::ParseSection(const std::vector<std::string>& args,
+                                std::string* err) {
+    std::vector<std::string> triggers(args.begin() + 1, args.end());
+    if (triggers.size() < 1) {
+        *err = "actions must have a trigger";
+        return false;
+    }
+
+    auto action = std::make_unique<Action>(false);
+    if (!action->InitTriggers(triggers, err)) {
+        return false;
+    }
+
+    action_ = std::move(action);
+    return true;
+}
+
+bool ActionParser::ParseLineSection(const std::vector<std::string>& args,
+                                    const std::string& filename, int line,
+                                    std::string* err) const {
+    return action_ ? action_->AddCommand(args, filename, line, err) : false;
+}
+
+void ActionParser::EndSection() {
+    if (action_ && action_->NumCommands() > 0) {
+        ActionManager::GetInstance().AddAction(std::move(action_));
+    }
+}
diff --git a/init/action.h b/init/action.h
new file mode 100644
index 0000000..6dee2d0
--- /dev/null
+++ b/init/action.h
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2015 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 _INIT_ACTION_H
+#define _INIT_ACTION_H
+
+#include <map>
+#include <queue>
+#include <string>
+#include <vector>
+
+#include "builtins.h"
+#include "init_parser.h"
+#include "keyword_map.h"
+
+class Command {
+public:
+    Command(BuiltinFunction f, const std::vector<std::string>& args,
+            const std::string& filename, int line);
+
+    int InvokeFunc() const;
+    std::string BuildCommandString() const;
+    std::string BuildSourceString() const;
+
+private:
+    BuiltinFunction func_;
+    std::vector<std::string> args_;
+    std::string filename_;
+    int line_;
+};
+
+class Action {
+public:
+    Action(bool oneshot = false);
+
+    bool AddCommand(const std::vector<std::string>& args,
+                    const std::string& filename, int line, std::string* err);
+    void AddCommand(BuiltinFunction f,
+                    const std::vector<std::string>& args,
+                    const std::string& filename = "", int line = 0);
+    void CombineAction(const Action& action);
+    bool InitTriggers(const std::vector<std::string>& args, std::string* err);
+    bool InitSingleTrigger(const std::string& trigger);
+    std::size_t NumCommands() const;
+    void ExecuteOneCommand(std::size_t command) const;
+    void ExecuteAllCommands() const;
+    bool CheckEventTrigger(const std::string& trigger) const;
+    bool CheckPropertyTrigger(const std::string& name,
+                              const std::string& value) const;
+    bool TriggersEqual(const Action& other) const;
+    std::string BuildTriggersString() const;
+    void DumpState() const;
+
+    bool oneshot() const { return oneshot_; }
+    static void set_function_map(const KeywordMap<BuiltinFunction>* function_map) {
+        function_map_ = function_map;
+    }
+
+
+private:
+    void ExecuteCommand(const Command& command) const;
+    bool CheckPropertyTriggers(const std::string& name = "",
+                               const std::string& value = "") const;
+    bool ParsePropertyTrigger(const std::string& trigger, std::string* err);
+
+    std::map<std::string, std::string> property_triggers_;
+    std::string event_trigger_;
+    std::vector<Command> commands_;
+    bool oneshot_;
+    static const KeywordMap<BuiltinFunction>* function_map_;
+};
+
+class Trigger {
+public:
+    virtual ~Trigger() { }
+    virtual bool CheckTriggers(const Action& action) const = 0;
+};
+
+class ActionManager {
+public:
+    static ActionManager& GetInstance();
+
+    void AddAction(std::unique_ptr<Action> action);
+    void QueueEventTrigger(const std::string& trigger);
+    void QueuePropertyTrigger(const std::string& name, const std::string& value);
+    void QueueAllPropertyTriggers();
+    void QueueBuiltinAction(BuiltinFunction func, const std::string& name);
+    void ExecuteOneCommand();
+    bool HasMoreCommands() const;
+    void DumpState() const;
+
+private:
+    ActionManager();
+
+    ActionManager(ActionManager const&) = delete;
+    void operator=(ActionManager const&) = delete;
+
+    std::vector<std::unique_ptr<Action>> actions_;
+    std::queue<std::unique_ptr<Trigger>> trigger_queue_;
+    std::queue<const Action*> current_executing_actions_;
+    std::size_t current_command_;
+};
+
+class ActionParser : public SectionParser {
+public:
+    ActionParser() : action_(nullptr) {
+    }
+    bool ParseSection(const std::vector<std::string>& args,
+                      std::string* err) override;
+    bool ParseLineSection(const std::vector<std::string>& args,
+                          const std::string& filename, int line,
+                          std::string* err) const override;
+    void EndSection() override;
+    void EndFile(const std::string&) override {
+    }
+private:
+    std::unique_ptr<Action> action_;
+};
+
+#endif
diff --git a/init/bootchart.cpp b/init/bootchart.cpp
index 95687cb..a768762 100644
--- a/init/bootchart.cpp
+++ b/init/bootchart.cpp
@@ -15,7 +15,6 @@
  */
 
 #include "bootchart.h"
-#include "keywords.h"
 #include "log.h"
 #include "property_service.h"
 
@@ -32,6 +31,7 @@
 
 #include <memory>
 #include <string>
+#include <vector>
 
 #include <base/file.h>
 
@@ -77,8 +77,8 @@
         return;
     }
 
-    char fingerprint[PROP_VALUE_MAX];
-    if (property_get("ro.build.fingerprint", fingerprint) == -1) {
+    std::string fingerprint = property_get("ro.build.fingerprint");
+    if (fingerprint.empty()) {
         return;
     }
 
@@ -92,7 +92,7 @@
     fprintf(out, "version = Android init 0.8 " __TIME__  "\n");
     fprintf(out, "title = Boot chart for Android (%s)\n", date);
     fprintf(out, "system.uname = %s %s %s %s\n", uts.sysname, uts.release, uts.version, uts.machine);
-    fprintf(out, "system.release = %s\n", fingerprint);
+    fprintf(out, "system.release = %s\n", fingerprint.c_str());
     // TODO: use /proc/cpuinfo "model name" line for x86, "Processor" line for arm.
     fprintf(out, "system.cpu = %s\n", uts.machine);
     fprintf(out, "system.kernel.options = %s\n", kernel_cmdline.c_str());
@@ -164,10 +164,11 @@
         // timeout. this is useful when using -wipe-data since the /data
         // partition is fresh.
         std::string cmdline;
+        const char* s;
         android::base::ReadFileToString("/proc/cmdline", &cmdline);
 #define KERNEL_OPTION  "androidboot.bootchart="
-        if (strstr(cmdline.c_str(), KERNEL_OPTION) != NULL) {
-            timeout = atoi(cmdline.c_str() + sizeof(KERNEL_OPTION) - 1);
+        if ((s = strstr(cmdline.c_str(), KERNEL_OPTION)) != NULL) {
+            timeout = atoi(s + sizeof(KERNEL_OPTION) - 1);
         }
     }
     if (timeout == 0)
@@ -202,7 +203,7 @@
     return count;
 }
 
-int do_bootchart_init(int nargs, char** args) {
+int do_bootchart_init(const std::vector<std::string>& args) {
     g_remaining_samples = bootchart_init();
     if (g_remaining_samples < 0) {
         ERROR("Bootcharting init failure: %s\n", strerror(errno));
diff --git a/init/bootchart.h b/init/bootchart.h
index cf61d83..47eda7a 100644
--- a/init/bootchart.h
+++ b/init/bootchart.h
@@ -17,6 +17,10 @@
 #ifndef _BOOTCHART_H
 #define _BOOTCHART_H
 
+#include <string>
+#include <vector>
+
+int do_bootchart_init(const std::vector<std::string>& args);
 void bootchart_sample(int* timeout);
 
 #endif /* _BOOTCHART_H */
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 8eb5b5b..36ecbb8 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -14,9 +14,13 @@
  * limitations under the License.
  */
 
+#include "builtins.h"
+
 #include <errno.h>
 #include <fcntl.h>
+#include <mntent.h>
 #include <net/if.h>
+#include <signal.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -38,33 +42,29 @@
 #include <base/stringprintf.h>
 #include <cutils/partition_utils.h>
 #include <cutils/android_reboot.h>
+#include <logwrap/logwrap.h>
 #include <private/android_filesystem_config.h>
 
-#include "init.h"
-#include "keywords.h"
-#include "property_service.h"
+#include "action.h"
+#include "bootchart.h"
 #include "devices.h"
+#include "init.h"
 #include "init_parser.h"
-#include "util.h"
 #include "log.h"
+#include "property_service.h"
+#include "service.h"
+#include "util.h"
 
 #define chmod DO_NOT_USE_CHMOD_USE_FCHMODAT_SYMLINK_NOFOLLOW
-
-int add_environment(const char *name, const char *value);
+#define UNMOUNT_CHECK_MS 5000
+#define UNMOUNT_CHECK_TIMES 10
 
 // System call provided by bionic but not in any header file.
 extern "C" int init_module(void *, unsigned long, const char *);
 
-static int insmod(const char *filename, char *options)
-{
-    char filename_val[PROP_VALUE_MAX];
-    if (expand_props(filename_val, filename, sizeof(filename_val)) == -1) {
-        ERROR("insmod: cannot expand '%s'\n", filename);
-        return -EINVAL;
-    }
-
+static int insmod(const char *filename, const char *options) {
     std::string module;
-    if (!read_file(filename_val, &module)) {
+    if (!read_file(filename, &module)) {
         return -1;
     }
 
@@ -72,8 +72,7 @@
     return init_module(&module[0], module.size(), options);
 }
 
-static int __ifupdown(const char *interface, int up)
-{
+static int __ifupdown(const char *interface, int up) {
     struct ifreq ifr;
     int s, ret;
 
@@ -100,154 +99,179 @@
     return ret;
 }
 
-static void service_start_if_not_disabled(struct service *svc)
-{
-    if (!(svc->flags & SVC_DISABLED)) {
-        service_start(svc, NULL);
-    } else {
-        svc->flags |= SVC_DISABLED_START;
+static void unmount_and_fsck(const struct mntent *entry) {
+    if (strcmp(entry->mnt_type, "f2fs") && strcmp(entry->mnt_type, "ext4"))
+        return;
+
+    /* First, lazily unmount the directory. This unmount request finishes when
+     * all processes that open a file or directory in |entry->mnt_dir| exit.
+     */
+    TEMP_FAILURE_RETRY(umount2(entry->mnt_dir, MNT_DETACH));
+
+    /* Next, kill all processes except init, kthreadd, and kthreadd's
+     * children to finish the lazy unmount. Killing all processes here is okay
+     * because this callback function is only called right before reboot().
+     * It might be cleaner to selectively kill processes that actually use
+     * |entry->mnt_dir| rather than killing all, probably by reusing a function
+     * like killProcessesWithOpenFiles() in vold/, but the selinux policy does
+     * not allow init to scan /proc/<pid> files which the utility function
+     * heavily relies on. The policy does not allow the process to execute
+     * killall/pkill binaries either. Note that some processes might
+     * automatically restart after kill(), but that is not really a problem
+     * because |entry->mnt_dir| is no longer visible to such new processes.
+     */
+    ServiceManager::GetInstance().ForEachService([] (Service* s) { s->Stop(); });
+    TEMP_FAILURE_RETRY(kill(-1, SIGKILL));
+
+    int count = 0;
+    while (count++ < UNMOUNT_CHECK_TIMES) {
+        int fd = TEMP_FAILURE_RETRY(open(entry->mnt_fsname, O_RDONLY | O_EXCL));
+        if (fd >= 0) {
+            /* |entry->mnt_dir| has sucessfully been unmounted. */
+            close(fd);
+            break;
+        } else if (errno == EBUSY) {
+            /* Some processes using |entry->mnt_dir| are still alive. Wait for a
+             * while then retry.
+             */
+            TEMP_FAILURE_RETRY(
+                usleep(UNMOUNT_CHECK_MS * 1000 / UNMOUNT_CHECK_TIMES));
+            continue;
+        } else {
+            /* Cannot open the device. Give up. */
+            return;
+        }
+    }
+
+    int st;
+    if (!strcmp(entry->mnt_type, "f2fs")) {
+        const char *f2fs_argv[] = {
+            "/system/bin/fsck.f2fs", "-f", entry->mnt_fsname,
+        };
+        android_fork_execvp_ext(ARRAY_SIZE(f2fs_argv), (char **)f2fs_argv,
+                                &st, true, LOG_KLOG, true, NULL, NULL, 0);
+    } else if (!strcmp(entry->mnt_type, "ext4")) {
+        const char *ext4_argv[] = {
+            "/system/bin/e2fsck", "-f", "-y", entry->mnt_fsname,
+        };
+        android_fork_execvp_ext(ARRAY_SIZE(ext4_argv), (char **)ext4_argv,
+                                &st, true, LOG_KLOG, true, NULL, NULL, 0);
     }
 }
 
-int do_class_start(int nargs, char **args)
-{
+static int do_class_start(const std::vector<std::string>& args) {
         /* Starting a class does not start services
          * which are explicitly disabled.  They must
          * be started individually.
          */
-    service_for_each_class(args[1], service_start_if_not_disabled);
+    ServiceManager::GetInstance().
+        ForEachServiceInClass(args[1], [] (Service* s) { s->StartIfNotDisabled(); });
     return 0;
 }
 
-int do_class_stop(int nargs, char **args)
-{
-    service_for_each_class(args[1], service_stop);
+static int do_class_stop(const std::vector<std::string>& args) {
+    ServiceManager::GetInstance().
+        ForEachServiceInClass(args[1], [] (Service* s) { s->Stop(); });
     return 0;
 }
 
-int do_class_reset(int nargs, char **args)
-{
-    service_for_each_class(args[1], service_reset);
+static int do_class_reset(const std::vector<std::string>& args) {
+    ServiceManager::GetInstance().
+        ForEachServiceInClass(args[1], [] (Service* s) { s->Reset(); });
     return 0;
 }
 
-int do_domainname(int nargs, char **args)
-{
-    return write_file("/proc/sys/kernel/domainname", args[1]);
+static int do_domainname(const std::vector<std::string>& args) {
+    return write_file("/proc/sys/kernel/domainname", args[1].c_str());
 }
 
-int do_enable(int nargs, char **args)
-{
-    struct service *svc;
-    svc = service_find_by_name(args[1]);
-    if (svc) {
-        svc->flags &= ~(SVC_DISABLED | SVC_RC_DISABLED);
-        if (svc->flags & SVC_DISABLED_START) {
-            service_start(svc, NULL);
-        }
-    } else {
+static int do_enable(const std::vector<std::string>& args) {
+    Service* svc = ServiceManager::GetInstance().FindServiceByName(args[1]);
+    if (!svc) {
         return -1;
     }
-    return 0;
+    return svc->Enable();
 }
 
-int do_exec(int nargs, char** args) {
-    service* svc = make_exec_oneshot_service(nargs, args);
-    if (svc == NULL) {
+static int do_exec(const std::vector<std::string>& args) {
+    Service* svc = ServiceManager::GetInstance().MakeExecOneshotService(args);
+    if (!svc) {
         return -1;
     }
-    service_start(svc, NULL);
+    if (!svc->Start()) {
+        return -1;
+    }
+    waiting_for_exec = true;
     return 0;
 }
 
-int do_export(int nargs, char **args)
-{
-    return add_environment(args[1], args[2]);
+static int do_export(const std::vector<std::string>& args) {
+    return add_environment(args[1].c_str(), args[2].c_str());
 }
 
-int do_hostname(int nargs, char **args)
-{
-    return write_file("/proc/sys/kernel/hostname", args[1]);
+static int do_hostname(const std::vector<std::string>& args) {
+    return write_file("/proc/sys/kernel/hostname", args[1].c_str());
 }
 
-int do_ifup(int nargs, char **args)
-{
-    return __ifupdown(args[1], 1);
+static int do_ifup(const std::vector<std::string>& args) {
+    return __ifupdown(args[1].c_str(), 1);
 }
 
+static int do_insmod(const std::vector<std::string>& args) {
+    std::string options;
 
-static int do_insmod_inner(int nargs, char **args, int opt_len)
-{
-    char options[opt_len + 1];
-    int i;
-
-    options[0] = '\0';
-    if (nargs > 2) {
-        strcpy(options, args[2]);
-        for (i = 3; i < nargs; ++i) {
-            strcat(options, " ");
-            strcat(options, args[i]);
+    if (args.size() > 2) {
+        options += args[2];
+        for (std::size_t i = 3; i < args.size(); ++i) {
+            options += ' ';
+            options += args[i];
         }
     }
 
-    return insmod(args[1], options);
+    return insmod(args[1].c_str(), options.c_str());
 }
 
-int do_insmod(int nargs, char **args)
-{
-    int i;
-    int size = 0;
-
-    if (nargs > 2) {
-        for (i = 2; i < nargs; ++i)
-            size += strlen(args[i]) + 1;
-    }
-
-    return do_insmod_inner(nargs, args, size);
-}
-
-int do_mkdir(int nargs, char **args)
-{
+static int do_mkdir(const std::vector<std::string>& args) {
     mode_t mode = 0755;
     int ret;
 
     /* mkdir <path> [mode] [owner] [group] */
 
-    if (nargs >= 3) {
-        mode = strtoul(args[2], 0, 8);
+    if (args.size() >= 3) {
+        mode = std::stoul(args[2], 0, 8);
     }
 
-    ret = make_dir(args[1], mode);
+    ret = make_dir(args[1].c_str(), mode);
     /* chmod in case the directory already exists */
     if (ret == -1 && errno == EEXIST) {
-        ret = fchmodat(AT_FDCWD, args[1], mode, AT_SYMLINK_NOFOLLOW);
+        ret = fchmodat(AT_FDCWD, args[1].c_str(), mode, AT_SYMLINK_NOFOLLOW);
     }
     if (ret == -1) {
         return -errno;
     }
 
-    if (nargs >= 4) {
-        uid_t uid = decode_uid(args[3]);
+    if (args.size() >= 4) {
+        uid_t uid = decode_uid(args[3].c_str());
         gid_t gid = -1;
 
-        if (nargs == 5) {
-            gid = decode_uid(args[4]);
+        if (args.size() == 5) {
+            gid = decode_uid(args[4].c_str());
         }
 
-        if (lchown(args[1], uid, gid) == -1) {
+        if (lchown(args[1].c_str(), uid, gid) == -1) {
             return -errno;
         }
 
         /* chown may have cleared S_ISUID and S_ISGID, chmod again */
         if (mode & (S_ISUID | S_ISGID)) {
-            ret = fchmodat(AT_FDCWD, args[1], mode, AT_SYMLINK_NOFOLLOW);
+            ret = fchmodat(AT_FDCWD, args[1].c_str(), mode, AT_SYMLINK_NOFOLLOW);
             if (ret == -1) {
                 return -errno;
             }
         }
     }
 
-    return e4crypt_set_directory_policy(args[1]);
+    return e4crypt_set_directory_policy(args[1].c_str());
 }
 
 static struct {
@@ -275,35 +299,35 @@
 #define DATA_MNT_POINT "/data"
 
 /* mount <type> <device> <path> <flags ...> <options> */
-int do_mount(int nargs, char **args)
-{
+static int do_mount(const std::vector<std::string>& args) {
     char tmp[64];
-    char *source, *target, *system;
-    char *options = NULL;
+    const char *source, *target, *system;
+    const char *options = NULL;
     unsigned flags = 0;
+    std::size_t na = 0;
     int n, i;
     int wait = 0;
 
-    for (n = 4; n < nargs; n++) {
+    for (na = 4; na < args.size(); na++) {
         for (i = 0; mount_flags[i].name; i++) {
-            if (!strcmp(args[n], mount_flags[i].name)) {
+            if (!args[na].compare(mount_flags[i].name)) {
                 flags |= mount_flags[i].flag;
                 break;
             }
         }
 
         if (!mount_flags[i].name) {
-            if (!strcmp(args[n], "wait"))
+            if (!args[na].compare("wait"))
                 wait = 1;
             /* if our last argument isn't a flag, wolf it up as an option string */
-            else if (n + 1 == nargs)
-                options = args[n];
+            else if (na + 1 == args.size())
+                options = args[na].c_str();
         }
     }
 
-    system = args[1];
-    source = args[2];
-    target = args[3];
+    system = args[1].c_str();
+    source = args[2].c_str();
+    target = args[3].c_str();
 
     if (!strncmp(source, "mtd@", 4)) {
         n = mtd_name_to_number(source + 4);
@@ -375,8 +399,7 @@
 
 }
 
-static int wipe_data_via_recovery()
-{
+static int wipe_data_via_recovery() {
     mkdir("/cache/recovery", 0700);
     int fd = open("/cache/recovery/command", O_RDWR|O_CREAT|O_TRUNC|O_CLOEXEC, 0600);
     if (fd >= 0) {
@@ -391,22 +414,31 @@
     while (1) { pause(); }  // never reached
 }
 
+static void import_late() {
+    static const std::vector<std::string> init_directories = {
+        "/system/etc/init",
+        "/vendor/etc/init",
+        "/odm/etc/init"
+    };
+
+    Parser& parser = Parser::GetInstance();
+    for (const auto& dir : init_directories) {
+        parser.ParseConfig(dir.c_str());
+    }
+}
+
 /*
  * This function might request a reboot, in which case it will
  * not return.
  */
-int do_mount_all(int nargs, char **args)
-{
+static int do_mount_all(const std::vector<std::string>& args) {
     pid_t pid;
     int ret = -1;
     int child_ret = -1;
     int status;
     struct fstab *fstab;
 
-    if (nargs != 2) {
-        return -1;
-    }
-
+    const char* fstabfile = args[1].c_str();
     /*
      * Call fs_mgr_mount_all() to mount all filesystems.  We fork(2) and
      * do the call in the child to provide protection to the main init
@@ -430,7 +462,7 @@
     } else if (pid == 0) {
         /* child, call fs_mgr_mount_all() */
         klog_set_level(6);  /* So we can see what fs_mgr_mount_all() does */
-        fstab = fs_mgr_read_fstab(args[1]);
+        fstab = fs_mgr_read_fstab(fstabfile);
         child_ret = fs_mgr_mount_all(fstab);
         fs_mgr_free_fstab(fstab);
         if (child_ret == -1) {
@@ -442,6 +474,8 @@
         return -1;
     }
 
+    import_late();
+
     if (ret == FS_MGR_MNTALL_DEV_NEEDS_ENCRYPTION) {
         property_set("vold.decrypt", "trigger_encryption");
     } else if (ret == FS_MGR_MNTALL_DEV_MIGHT_BE_ENCRYPTED) {
@@ -453,7 +487,7 @@
         /* If fs_mgr determined this is an unencrypted device, then trigger
          * that action.
          */
-        action_for_each_trigger("nonencrypted", action_add_queue_tail);
+        ActionManager::GetInstance().QueueEventTrigger("nonencrypted");
     } else if (ret == FS_MGR_MNTALL_DEV_NEEDS_RECOVERY) {
         /* Setup a wipe via recovery, and reboot into recovery */
         ERROR("fs_mgr_mount_all suggested recovery, so wiping data via recovery.\n");
@@ -468,7 +502,7 @@
 
         // Although encrypted, we have device key, so we do not need to
         // do anything different from the nonencrypted case.
-        action_for_each_trigger("nonencrypted", action_add_queue_tail);
+        ActionManager::GetInstance().QueueEventTrigger("nonencrypted");
     } else if (ret == FS_MGR_MNTALL_DEV_NON_DEFAULT_FILE_ENCRYPTED) {
         if (e4crypt_install_keyring()) {
             return -1;
@@ -484,87 +518,70 @@
     return ret;
 }
 
-int do_swapon_all(int nargs, char **args)
-{
+static int do_swapon_all(const std::vector<std::string>& args) {
     struct fstab *fstab;
     int ret;
 
-    fstab = fs_mgr_read_fstab(args[1]);
+    fstab = fs_mgr_read_fstab(args[1].c_str());
     ret = fs_mgr_swapon_all(fstab);
     fs_mgr_free_fstab(fstab);
 
     return ret;
 }
 
-int do_setprop(int nargs, char **args)
-{
-    const char *name = args[1];
-    const char *value = args[2];
-    char prop_val[PROP_VALUE_MAX];
-    int ret;
-
-    ret = expand_props(prop_val, value, sizeof(prop_val));
-    if (ret) {
-        ERROR("cannot expand '%s' while assigning to '%s'\n", value, name);
-        return -EINVAL;
-    }
-    property_set(name, prop_val);
+static int do_setprop(const std::vector<std::string>& args) {
+    const char* name = args[1].c_str();
+    const char* value = args[2].c_str();
+    property_set(name, value);
     return 0;
 }
 
-int do_setrlimit(int nargs, char **args)
-{
+static int do_setrlimit(const std::vector<std::string>& args) {
     struct rlimit limit;
     int resource;
-    resource = atoi(args[1]);
-    limit.rlim_cur = atoi(args[2]);
-    limit.rlim_max = atoi(args[3]);
+    resource = std::stoi(args[1]);
+    limit.rlim_cur = std::stoi(args[2]);
+    limit.rlim_max = std::stoi(args[3]);
     return setrlimit(resource, &limit);
 }
 
-int do_start(int nargs, char **args)
-{
-    struct service *svc;
-    svc = service_find_by_name(args[1]);
-    if (svc) {
-        service_start(svc, NULL);
+static int do_start(const std::vector<std::string>& args) {
+    Service* svc = ServiceManager::GetInstance().FindServiceByName(args[1]);
+    if (!svc) {
+        ERROR("do_start: Service %s not found\n", args[1].c_str());
+        return -1;
     }
+    if (!svc->Start())
+        return -1;
     return 0;
 }
 
-int do_stop(int nargs, char **args)
-{
-    struct service *svc;
-    svc = service_find_by_name(args[1]);
-    if (svc) {
-        service_stop(svc);
+static int do_stop(const std::vector<std::string>& args) {
+    Service* svc = ServiceManager::GetInstance().FindServiceByName(args[1]);
+    if (!svc) {
+        ERROR("do_stop: Service %s not found\n", args[1].c_str());
+        return -1;
     }
+    svc->Stop();
     return 0;
 }
 
-int do_restart(int nargs, char **args)
-{
-    struct service *svc;
-    svc = service_find_by_name(args[1]);
-    if (svc) {
-        service_restart(svc);
+static int do_restart(const std::vector<std::string>& args) {
+    Service* svc = ServiceManager::GetInstance().FindServiceByName(args[1]);
+    if (!svc) {
+        ERROR("do_restart: Service %s not found\n", args[1].c_str());
+        return -1;
     }
+    svc->Restart();
     return 0;
 }
 
-int do_powerctl(int nargs, char **args)
-{
-    char command[PROP_VALUE_MAX];
-    int res;
+static int do_powerctl(const std::vector<std::string>& args) {
+    const char* command = args[1].c_str();
     int len = 0;
-    int cmd = 0;
-    const char *reboot_target;
-
-    res = expand_props(command, args[1], sizeof(command));
-    if (res) {
-        ERROR("powerctl: cannot expand '%s'\n", args[1]);
-        return -EINVAL;
-    }
+    unsigned int cmd = 0;
+    const char *reboot_target = "";
+    void (*callback_on_ro_remount)(const struct mntent*) = NULL;
 
     if (strncmp(command, "shutdown", 8) == 0) {
         cmd = ANDROID_RB_POWEROFF;
@@ -578,85 +595,76 @@
     }
 
     if (command[len] == ',') {
-        reboot_target = &command[len + 1];
-    } else if (command[len] == '\0') {
-        reboot_target = "";
-    } else {
+        if (cmd == ANDROID_RB_POWEROFF &&
+            !strcmp(&command[len + 1], "userrequested")) {
+            // The shutdown reason is PowerManager.SHUTDOWN_USER_REQUESTED.
+            // Run fsck once the file system is remounted in read-only mode.
+            callback_on_ro_remount = unmount_and_fsck;
+        } else if (cmd == ANDROID_RB_RESTART2) {
+            reboot_target = &command[len + 1];
+        }
+    } else if (command[len] != '\0') {
         ERROR("powerctl: unrecognized reboot target '%s'\n", &command[len]);
         return -EINVAL;
     }
 
-    return android_reboot(cmd, 0, reboot_target);
+    return android_reboot_with_callback(cmd, 0, reboot_target,
+                                        callback_on_ro_remount);
 }
 
-int do_trigger(int nargs, char **args)
-{
-    action_for_each_trigger(args[1], action_add_queue_tail);
+static int do_trigger(const std::vector<std::string>& args) {
+    ActionManager::GetInstance().QueueEventTrigger(args[1]);
     return 0;
 }
 
-int do_symlink(int nargs, char **args)
-{
-    return symlink(args[1], args[2]);
+static int do_symlink(const std::vector<std::string>& args) {
+    return symlink(args[1].c_str(), args[2].c_str());
 }
 
-int do_rm(int nargs, char **args)
-{
-    return unlink(args[1]);
+static int do_rm(const std::vector<std::string>& args) {
+    return unlink(args[1].c_str());
 }
 
-int do_rmdir(int nargs, char **args)
-{
-    return rmdir(args[1]);
+static int do_rmdir(const std::vector<std::string>& args) {
+    return rmdir(args[1].c_str());
 }
 
-int do_sysclktz(int nargs, char **args)
-{
+static int do_sysclktz(const std::vector<std::string>& args) {
     struct timezone tz;
 
-    if (nargs != 2)
-        return -1;
-
     memset(&tz, 0, sizeof(tz));
-    tz.tz_minuteswest = atoi(args[1]);
+    tz.tz_minuteswest = std::stoi(args[1]);
     if (settimeofday(NULL, &tz))
         return -1;
     return 0;
 }
 
-int do_verity_load_state(int nargs, char **args) {
+static int do_verity_load_state(const std::vector<std::string>& args) {
     int mode = -1;
     int rc = fs_mgr_load_verity_state(&mode);
     if (rc == 0 && mode == VERITY_MODE_LOGGING) {
-        action_for_each_trigger("verity-logging", action_add_queue_tail);
+        ActionManager::GetInstance().QueueEventTrigger("verity-logging");
     }
     return rc;
 }
 
-static void verity_update_property(fstab_rec *fstab, const char *mount_point, int mode, int status) {
+static void verity_update_property(fstab_rec *fstab, const char *mount_point,
+                                   int mode, int status) {
     property_set(android::base::StringPrintf("partition.%s.verified", mount_point).c_str(),
                  android::base::StringPrintf("%d", mode).c_str());
 }
 
-int do_verity_update_state(int nargs, char** args) {
+static int do_verity_update_state(const std::vector<std::string>& args) {
     return fs_mgr_update_verity_state(verity_update_property);
 }
 
-int do_write(int nargs, char **args)
-{
-    const char *path = args[1];
-    const char *value = args[2];
-
-    char expanded_value[256];
-    if (expand_props(expanded_value, value, sizeof(expanded_value))) {
-        ERROR("cannot expand '%s' while writing to '%s'\n", value, path);
-        return -EINVAL;
-    }
-    return write_file(path, expanded_value);
+static int do_write(const std::vector<std::string>& args) {
+    const char* path = args[1].c_str();
+    const char* value = args[2].c_str();
+    return write_file(path, value);
 }
 
-int do_copy(int nargs, char **args)
-{
+static int do_copy(const std::vector<std::string>& args) {
     char *buffer = NULL;
     int rc = 0;
     int fd1 = -1, fd2 = -1;
@@ -664,16 +672,13 @@
     int brtw, brtr;
     char *p;
 
-    if (nargs != 3)
+    if (stat(args[1].c_str(), &info) < 0)
         return -1;
 
-    if (stat(args[1], &info) < 0)
-        return -1;
-
-    if ((fd1 = open(args[1], O_RDONLY|O_CLOEXEC)) < 0)
+    if ((fd1 = open(args[1].c_str(), O_RDONLY|O_CLOEXEC)) < 0)
         goto out_err;
 
-    if ((fd2 = open(args[2], O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC, 0660)) < 0)
+    if ((fd2 = open(args[2].c_str(), O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC, 0660)) < 0)
         goto out_err;
 
     if (!(buffer = (char*) malloc(info.st_size)))
@@ -717,13 +722,14 @@
     return rc;
 }
 
-int do_chown(int nargs, char **args) {
+static int do_chown(const std::vector<std::string>& args) {
     /* GID is optional. */
-    if (nargs == 3) {
-        if (lchown(args[2], decode_uid(args[1]), -1) == -1)
+    if (args.size() == 3) {
+        if (lchown(args[2].c_str(), decode_uid(args[1].c_str()), -1) == -1)
             return -errno;
-    } else if (nargs == 4) {
-        if (lchown(args[3], decode_uid(args[1]), decode_uid(args[2])) == -1)
+    } else if (args.size() == 4) {
+        if (lchown(args[3].c_str(), decode_uid(args[1].c_str()),
+                   decode_uid(args[2].c_str())) == -1)
             return -errno;
     } else {
         return -1;
@@ -744,49 +750,36 @@
     return mode;
 }
 
-int do_chmod(int nargs, char **args) {
-    mode_t mode = get_mode(args[1]);
-    if (fchmodat(AT_FDCWD, args[2], mode, AT_SYMLINK_NOFOLLOW) < 0) {
+static int do_chmod(const std::vector<std::string>& args) {
+    mode_t mode = get_mode(args[1].c_str());
+    if (fchmodat(AT_FDCWD, args[2].c_str(), mode, AT_SYMLINK_NOFOLLOW) < 0) {
         return -errno;
     }
     return 0;
 }
 
-int do_restorecon(int nargs, char **args) {
-    int i;
+static int do_restorecon(const std::vector<std::string>& args) {
     int ret = 0;
 
-    for (i = 1; i < nargs; i++) {
-        if (restorecon(args[i]) < 0)
+    for (auto it = std::next(args.begin()); it != args.end(); ++it) {
+        if (restorecon(it->c_str()) < 0)
             ret = -errno;
     }
     return ret;
 }
 
-int do_restorecon_recursive(int nargs, char **args) {
-    int i;
+static int do_restorecon_recursive(const std::vector<std::string>& args) {
     int ret = 0;
 
-    for (i = 1; i < nargs; i++) {
-        if (restorecon_recursive(args[i]) < 0)
+    for (auto it = std::next(args.begin()); it != args.end(); ++it) {
+        if (restorecon_recursive(it->c_str()) < 0)
             ret = -errno;
     }
     return ret;
 }
 
-int do_loglevel(int nargs, char **args) {
-    int log_level;
-    char log_level_str[PROP_VALUE_MAX] = "";
-    if (nargs != 2) {
-        ERROR("loglevel: missing argument\n");
-        return -EINVAL;
-    }
-
-    if (expand_props(log_level_str, args[1], sizeof(log_level_str))) {
-        ERROR("loglevel: cannot expand '%s'\n", args[1]);
-        return -EINVAL;
-    }
-    log_level = atoi(log_level_str);
+static int do_loglevel(const std::vector<std::string>& args) {
+    int log_level = std::stoi(args[1]);
     if (log_level < KLOG_ERROR_LEVEL || log_level > KLOG_DEBUG_LEVEL) {
         ERROR("loglevel: invalid log level'%d'\n", log_level);
         return -EINVAL;
@@ -795,28 +788,21 @@
     return 0;
 }
 
-int do_load_persist_props(int nargs, char **args) {
-    if (nargs == 1) {
-        load_persist_props();
-        return 0;
-    }
-    return -1;
+static int do_load_persist_props(const std::vector<std::string>& args) {
+    load_persist_props();
+    return 0;
 }
 
-int do_load_system_props(int nargs, char **args) {
-    if (nargs == 1) {
-        load_system_props();
-        return 0;
-    }
-    return -1;
+static int do_load_system_props(const std::vector<std::string>& args) {
+    load_system_props();
+    return 0;
 }
 
-int do_wait(int nargs, char **args)
-{
-    if (nargs == 2) {
-        return wait_for_file(args[1], COMMAND_RETRY_TIMEOUT);
-    } else if (nargs == 3) {
-        return wait_for_file(args[1], atoi(args[2]));
+static int do_wait(const std::vector<std::string>& args) {
+    if (args.size() == 2) {
+        return wait_for_file(args[1].c_str(), COMMAND_RETRY_TIMEOUT);
+    } else if (args.size() == 3) {
+        return wait_for_file(args[1].c_str(), std::stoi(args[2]));
     } else
         return -1;
 }
@@ -824,8 +810,7 @@
 /*
  * Callback to make a directory from the ext4 code
  */
-static int do_installkeys_ensure_dir_exists(const char* dir)
-{
+static int do_installkeys_ensure_dir_exists(const char* dir) {
     if (make_dir(dir, 0700) && errno != EEXIST) {
         return -1;
     }
@@ -834,30 +819,68 @@
 }
 
 static bool is_file_crypto() {
-    char prop_value[PROP_VALUE_MAX] = {0};
-    property_get("ro.crypto.type", prop_value);
-    return strcmp(prop_value, "file") == 0;
+    std::string value = property_get("ro.crypto.type");
+    return value == "file";
 }
 
-int do_installkey(int nargs, char **args)
-{
-    if (nargs != 2) {
-        return -1;
-    }
+static int do_installkey(const std::vector<std::string>& args) {
     if (!is_file_crypto()) {
         return 0;
     }
-    return e4crypt_create_device_key(args[1],
+    return e4crypt_create_device_key(args[1].c_str(),
                                      do_installkeys_ensure_dir_exists);
 }
 
-int do_setusercryptopolicies(int nargs, char **args)
-{
-    if (nargs != 2) {
-        return -1;
-    }
+static int do_setusercryptopolicies(const std::vector<std::string>& args) {
     if (!is_file_crypto()) {
         return 0;
     }
-    return e4crypt_set_user_crypto_policies(args[1]);
+    return e4crypt_set_user_crypto_policies(args[1].c_str());
+}
+
+BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const {
+    constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
+    static const Map builtin_functions = {
+        {"bootchart_init",          {0,     0,    do_bootchart_init}},
+        {"chmod",                   {2,     2,    do_chmod}},
+        {"chown",                   {2,     3,    do_chown}},
+        {"class_reset",             {1,     1,    do_class_reset}},
+        {"class_start",             {1,     1,    do_class_start}},
+        {"class_stop",              {1,     1,    do_class_stop}},
+        {"copy",                    {2,     2,    do_copy}},
+        {"domainname",              {1,     1,    do_domainname}},
+        {"enable",                  {1,     1,    do_enable}},
+        {"exec",                    {1,     kMax, do_exec}},
+        {"export",                  {2,     2,    do_export}},
+        {"hostname",                {1,     1,    do_hostname}},
+        {"ifup",                    {1,     1,    do_ifup}},
+        {"insmod",                  {1,     kMax, do_insmod}},
+        {"installkey",              {1,     1,    do_installkey}},
+        {"load_persist_props",      {0,     0,    do_load_persist_props}},
+        {"load_system_props",       {0,     0,    do_load_system_props}},
+        {"loglevel",                {1,     1,    do_loglevel}},
+        {"mkdir",                   {1,     4,    do_mkdir}},
+        {"mount_all",               {1,     1,    do_mount_all}},
+        {"mount",                   {3,     kMax, do_mount}},
+        {"powerctl",                {1,     1,    do_powerctl}},
+        {"restart",                 {1,     1,    do_restart}},
+        {"restorecon",              {1,     kMax, do_restorecon}},
+        {"restorecon_recursive",    {1,     kMax, do_restorecon_recursive}},
+        {"rm",                      {1,     1,    do_rm}},
+        {"rmdir",                   {1,     1,    do_rmdir}},
+        {"setprop",                 {2,     2,    do_setprop}},
+        {"setrlimit",               {3,     3,    do_setrlimit}},
+        {"setusercryptopolicies",   {1,     1,    do_setusercryptopolicies}},
+        {"start",                   {1,     1,    do_start}},
+        {"stop",                    {1,     1,    do_stop}},
+        {"swapon_all",              {1,     1,    do_swapon_all}},
+        {"symlink",                 {2,     2,    do_symlink}},
+        {"sysclktz",                {1,     1,    do_sysclktz}},
+        {"trigger",                 {1,     1,    do_trigger}},
+        {"verity_load_state",       {0,     0,    do_verity_load_state}},
+        {"verity_update_state",     {0,     0,    do_verity_update_state}},
+        {"wait",                    {1,     2,    do_wait}},
+        {"write",                   {2,     2,    do_write}},
+    };
+    return builtin_functions;
 }
diff --git a/base/test_utils.h b/init/builtins.h
similarity index 63%
copy from base/test_utils.h
copy to init/builtins.h
index 132d3a7..53f4a71 100644
--- a/base/test_utils.h
+++ b/init/builtins.h
@@ -14,19 +14,22 @@
  * limitations under the License.
  */
 
-#ifndef TEST_UTILS_H
-#define TEST_UTILS_H
+#ifndef _INIT_BUILTINS_H
+#define _INIT_BUILTINS_H
 
-class TemporaryFile {
- public:
-  TemporaryFile();
-  ~TemporaryFile();
+#include <map>
+#include <string>
+#include <vector>
 
-  int fd;
-  char filename[1024];
+#include "keyword_map.h"
 
- private:
-  void init(const char* tmp_dir);
+using BuiltinFunction = int (*) (const std::vector<std::string>& args);
+class BuiltinFunctionMap : public KeywordMap<BuiltinFunction> {
+public:
+    BuiltinFunctionMap() {
+    }
+private:
+    Map& map() const override;
 };
 
-#endif // TEST_UTILS_H
+#endif
diff --git a/init/compare-bootcharts.py b/init/compare-bootcharts.py
new file mode 100755
index 0000000..2057b55
--- /dev/null
+++ b/init/compare-bootcharts.py
@@ -0,0 +1,146 @@
+#!/usr/bin/env python
+
+# Copyright (C) 2015 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.
+
+"""Compare two bootcharts and list start/end timestamps on key processes.
+
+This script extracts two bootchart.tgz files and compares the timestamps
+in proc_ps.log for selected processes. The proc_ps.log file consists of
+repetitive blocks of the following format:
+
+timestamp1 (jiffies)
+dumps of /proc/<pid>/stat
+
+timestamp2
+dumps of /proc/<pid>/stat
+
+The timestamps are 200ms apart, and the creation time of selected processes
+are listed. The termination time of the boot animation process is also listed
+as a coarse indication about when the boot process is complete as perceived by
+the user.
+"""
+
+import sys
+import tarfile
+
+# The bootchart timestamps are 200ms apart, but the USER_HZ value is not
+# reported in the bootchart, so we use the first two timestamps to calculate
+# the wall clock time of a jiffy.
+jiffy_to_wallclock = {
+   '1st_timestamp': -1,
+   '2nd_timestamp': -1,
+   'jiffy_to_wallclock': -1
+}
+
+def analyze_process_maps(process_map1, process_map2, jiffy_record):
+    # List interesting processes here
+    processes_of_interest = [
+        '/init',
+        '/system/bin/surfaceflinger',
+        '/system/bin/bootanimation',
+        'zygote64',
+        'zygote',
+        'system_server'
+    ]
+
+    jw = jiffy_record['jiffy_to_wallclock']
+    print "process: baseline experiment (delta)"
+    print " - Unit is ms (a jiffy is %d ms on the system)" % jw
+    print "------------------------------------"
+    for p in processes_of_interest:
+        # e.g., 32-bit system doesn't have zygote64
+        if p in process_map1 and p in process_map2:
+            print "%s: %d %d (%+d)" % (
+                p, process_map1[p]['start_time'] * jw,
+                process_map2[p]['start_time'] * jw,
+                (process_map2[p]['start_time'] -
+                 process_map1[p]['start_time']) * jw)
+
+    # Print the last tick for the bootanimation process
+    print "bootanimation ends at: %d %d (%+d)" % (
+        process_map1['/system/bin/bootanimation']['last_tick'] * jw,
+        process_map2['/system/bin/bootanimation']['last_tick'] * jw,
+        (process_map2['/system/bin/bootanimation']['last_tick'] -
+            process_map1['/system/bin/bootanimation']['last_tick']) * jw)
+
+def parse_proc_file(pathname, process_map, jiffy_record=None):
+    # Uncompress bootchart.tgz
+    with tarfile.open(pathname + '/bootchart.tgz', 'r:*') as tf:
+        try:
+            # Read proc_ps.log
+            f = tf.extractfile('proc_ps.log')
+
+            # Break proc_ps into chunks based on timestamps
+            blocks = f.read().split('\n\n')
+            for b in blocks:
+                lines = b.split('\n')
+                if not lines[0]:
+                    break
+
+                # 200ms apart in jiffies
+                timestamp = int(lines[0]);
+
+                # Figure out the wall clock time of a jiffy
+                if jiffy_record is not None:
+                    if jiffy_record['1st_timestamp'] == -1:
+                        jiffy_record['1st_timestamp'] = timestamp
+                    elif jiffy_record['jiffy_to_wallclock'] == -1:
+                        # Not really needed but for debugging purposes
+                        jiffy_record['2nd_timestamp'] = timestamp
+                        value = 200 / (timestamp -
+                                       jiffy_record['1st_timestamp'])
+                        # Fix the rounding error
+                        # e.g., 201 jiffies in 200ms when USER_HZ is 1000
+                        if value == 0:
+                            value = 1
+                        # e.g., 21 jiffies in 200ms when USER_HZ is 100
+                        elif value == 9:
+                            value = 10
+                        jiffy_record['jiffy_to_wallclock'] = value
+
+                # Populate the process_map table
+                for line in lines[1:]:
+                    segs = line.split(' ')
+
+                    #  0: pid
+                    #  1: process name
+                    # 17: priority
+                    # 18: nice
+                    # 21: creation time
+
+                    proc_name = segs[1].strip('()')
+                    if proc_name in process_map:
+                        process = process_map[proc_name]
+                    else:
+                        process = {'start_time': int(segs[21])}
+                        process_map[proc_name] = process
+
+                    process['last_tick'] = timestamp
+        finally:
+            f.close()
+
+def main():
+    if len(sys.argv) != 3:
+        print "Usage: %s base_bootchart_dir exp_bootchart_dir" % sys.argv[0]
+        sys.exit(1)
+
+    process_map1 = {}
+    process_map2 = {}
+    parse_proc_file(sys.argv[1], process_map1, jiffy_to_wallclock)
+    parse_proc_file(sys.argv[2], process_map2)
+    analyze_process_maps(process_map1, process_map2, jiffy_to_wallclock)
+
+if __name__ == "__main__":
+    main()
diff --git a/init/devices.cpp b/init/devices.cpp
index 4944cec..d556e30 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -241,10 +241,8 @@
 
     mode = get_device_perm(path, links, &uid, &gid) | (block ? S_IFBLK : S_IFCHR);
 
-    if (sehandle) {
-        selabel_lookup_best_match(sehandle, &secontext, path, links, mode);
-        setfscreatecon(secontext);
-    }
+    selabel_lookup_best_match(sehandle, &secontext, path, links, mode);
+    setfscreatecon(secontext);
 
     dev = makedev(major, minor);
     /* Temporarily change egid to avoid race condition setting the gid of the
@@ -907,7 +905,7 @@
         struct uevent uevent;
         parse_event(msg, &uevent);
 
-        if (sehandle && selinux_status_updated() > 0) {
+        if (selinux_status_updated() > 0) {
             struct selabel_handle *sehandle2;
             sehandle2 = selinux_android_file_context_handle();
             if (sehandle2) {
@@ -974,11 +972,8 @@
 }
 
 void device_init() {
-    sehandle = NULL;
-    if (is_selinux_enabled() > 0) {
-        sehandle = selinux_android_file_context_handle();
-        selinux_status_open(true);
-    }
+    sehandle = selinux_android_file_context_handle();
+    selinux_status_open(true);
 
     /* is 256K enough? udev uses 16MB! */
     device_fd = uevent_open_socket(256*1024, true);
diff --git a/init/import_parser.cpp b/init/import_parser.cpp
new file mode 100644
index 0000000..e2a0f83
--- /dev/null
+++ b/init/import_parser.cpp
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2015 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 "import_parser.h"
+
+#include "errno.h"
+
+#include <string>
+#include <vector>
+
+#include "log.h"
+#include "util.h"
+
+bool ImportParser::ParseSection(const std::vector<std::string>& args,
+                                std::string* err) {
+    if (args.size() != 2) {
+        *err = "single argument needed for import\n";
+        return false;
+    }
+
+    std::string conf_file;
+    bool ret = expand_props(args[1], &conf_file);
+    if (!ret) {
+        *err = "error while expanding import";
+        return false;
+    }
+
+    INFO("Added '%s' to import list\n", conf_file.c_str());
+    imports_.emplace_back(std::move(conf_file));
+    return true;
+}
+
+void ImportParser::EndFile(const std::string& filename) {
+    auto current_imports = std::move(imports_);
+    imports_.clear();
+    for (const auto& s : current_imports) {
+        if (!Parser::GetInstance().ParseConfig(s)) {
+            ERROR("could not import file '%s' from '%s': %s\n",
+                  s.c_str(), filename.c_str(), strerror(errno));
+        }
+    }
+}
diff --git a/init/import_parser.h b/init/import_parser.h
new file mode 100644
index 0000000..0e91025
--- /dev/null
+++ b/init/import_parser.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2015 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 _INIT_IMPORT_PARSER_H
+#define _INIT_IMPORT_PARSER_H
+
+#include "init_parser.h"
+
+#include <string>
+#include <vector>
+
+class ImportParser : public SectionParser {
+public:
+    ImportParser()  {
+    }
+    bool ParseSection(const std::vector<std::string>& args,
+                      std::string* err) override;
+    bool ParseLineSection(const std::vector<std::string>& args,
+                          const std::string& filename, int line,
+                          std::string* err) const override {
+        return true;
+    }
+    void EndSection() override {
+    }
+    void EndFile(const std::string& filename) override;
+private:
+    std::vector<std::string> imports_;
+};
+
+#endif
diff --git a/init/init.cpp b/init/init.cpp
index 93fe944..ee1351d 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -32,7 +32,6 @@
 #include <sys/types.h>
 #include <sys/un.h>
 #include <sys/wait.h>
-#include <termios.h>
 #include <unistd.h>
 
 #include <mtd/mtd-user.h>
@@ -43,6 +42,7 @@
 
 #include <base/file.h>
 #include <base/stringprintf.h>
+#include <base/strings.h>
 #include <cutils/android_reboot.h>
 #include <cutils/fs.h>
 #include <cutils/iosched_policy.h>
@@ -52,16 +52,19 @@
 
 #include <memory>
 
+#include "action.h"
+#include "bootchart.h"
 #include "devices.h"
+#include "import_parser.h"
 #include "init.h"
+#include "init_parser.h"
+#include "keychords.h"
 #include "log.h"
 #include "property_service.h"
-#include "bootchart.h"
+#include "service.h"
 #include "signal_handler.h"
-#include "keychords.h"
-#include "init_parser.h"
-#include "util.h"
 #include "ueventd.h"
+#include "util.h"
 #include "watchdogd.h"
 
 struct selabel_handle *sehandle;
@@ -71,14 +74,11 @@
 
 static char qemu[32];
 
-static struct action *cur_action = NULL;
-static struct command *cur_command = NULL;
-
-static int have_console;
-static char console_name[PROP_VALUE_MAX] = "/dev/console";
+int have_console;
+std::string console_name = "/dev/console";
 static time_t process_needs_restart;
 
-static const char *ENV[32];
+const char *ENV[32];
 
 bool waiting_for_exec = false;
 
@@ -93,27 +93,6 @@
     }
 }
 
-void service::NotifyStateChange(const char* new_state) {
-    if (!properties_initialized()) {
-        // If properties aren't available yet, we can't set them.
-        return;
-    }
-
-    if ((flags & SVC_EXEC) != 0) {
-        // 'exec' commands don't have properties tracking their state.
-        return;
-    }
-
-    char prop_name[PROP_NAME_MAX];
-    if (snprintf(prop_name, sizeof(prop_name), "init.svc.%s", name) >= PROP_NAME_MAX) {
-        // If the property name would be too long, we can't set it.
-        ERROR("Property name \"init.svc.%s\" too long; not setting to %s\n", name, new_state);
-        return;
-    }
-
-    property_set(prop_name, new_state);
-}
-
 /* add_environment - add "key=value" to the current environment */
 int add_environment(const char *key, const char *val)
 {
@@ -146,486 +125,80 @@
     return -1;
 }
 
-void zap_stdio(void)
-{
-    int fd;
-    fd = open("/dev/null", O_RDWR);
-    dup2(fd, 0);
-    dup2(fd, 1);
-    dup2(fd, 2);
-    close(fd);
-}
-
-static void open_console()
-{
-    int fd;
-    if ((fd = open(console_name, O_RDWR)) < 0) {
-        fd = open("/dev/null", O_RDWR);
-    }
-    ioctl(fd, TIOCSCTTY, 0);
-    dup2(fd, 0);
-    dup2(fd, 1);
-    dup2(fd, 2);
-    close(fd);
-}
-
-static void publish_socket(const char *name, int fd)
-{
-    char key[64] = ANDROID_SOCKET_ENV_PREFIX;
-    char val[64];
-
-    strlcpy(key + sizeof(ANDROID_SOCKET_ENV_PREFIX) - 1,
-            name,
-            sizeof(key) - sizeof(ANDROID_SOCKET_ENV_PREFIX));
-    snprintf(val, sizeof(val), "%d", fd);
-    add_environment(key, val);
-
-    /* make sure we don't close-on-exec */
-    fcntl(fd, F_SETFD, 0);
-}
-
-void service_start(struct service *svc, const char *dynamic_args)
-{
-    // Starting a service removes it from the disabled or reset state and
-    // immediately takes it out of the restarting state if it was in there.
-    svc->flags &= (~(SVC_DISABLED|SVC_RESTARTING|SVC_RESET|SVC_RESTART|SVC_DISABLED_START));
-    svc->time_started = 0;
-
-    // Running processes require no additional work --- if they're in the
-    // process of exiting, we've ensured that they will immediately restart
-    // on exit, unless they are ONESHOT.
-    if (svc->flags & SVC_RUNNING) {
-        return;
-    }
-
-    bool needs_console = (svc->flags & SVC_CONSOLE);
-    if (needs_console && !have_console) {
-        ERROR("service '%s' requires console\n", svc->name);
-        svc->flags |= SVC_DISABLED;
-        return;
-    }
-
-    struct stat s;
-    if (stat(svc->args[0], &s) != 0) {
-        ERROR("cannot find '%s', disabling '%s'\n", svc->args[0], svc->name);
-        svc->flags |= SVC_DISABLED;
-        return;
-    }
-
-    if ((!(svc->flags & SVC_ONESHOT)) && dynamic_args) {
-        ERROR("service '%s' must be one-shot to use dynamic args, disabling\n",
-               svc->args[0]);
-        svc->flags |= SVC_DISABLED;
-        return;
-    }
-
-    char* scon = NULL;
-    if (is_selinux_enabled() > 0) {
-        if (svc->seclabel) {
-            scon = strdup(svc->seclabel);
-            if (!scon) {
-                ERROR("Out of memory while starting '%s'\n", svc->name);
-                return;
-            }
-        } else {
-            char *mycon = NULL, *fcon = NULL;
-
-            INFO("computing context for service '%s'\n", svc->args[0]);
-            int rc = getcon(&mycon);
-            if (rc < 0) {
-                ERROR("could not get context while starting '%s'\n", svc->name);
-                return;
-            }
-
-            rc = getfilecon(svc->args[0], &fcon);
-            if (rc < 0) {
-                ERROR("could not get context while starting '%s'\n", svc->name);
-                freecon(mycon);
-                return;
-            }
-
-            rc = security_compute_create(mycon, fcon, string_to_security_class("process"), &scon);
-            if (rc == 0 && !strcmp(scon, mycon)) {
-                ERROR("Warning!  Service %s needs a SELinux domain defined; please fix!\n", svc->name);
-            }
-            freecon(mycon);
-            freecon(fcon);
-            if (rc < 0) {
-                ERROR("could not get context while starting '%s'\n", svc->name);
-                return;
-            }
-        }
-    }
-
-    NOTICE("Starting service '%s'...\n", svc->name);
-
-    pid_t pid = fork();
-    if (pid == 0) {
-        struct socketinfo *si;
-        struct svcenvinfo *ei;
-        char tmp[32];
-        int fd, sz;
-
-        umask(077);
-        if (properties_initialized()) {
-            get_property_workspace(&fd, &sz);
-            snprintf(tmp, sizeof(tmp), "%d,%d", dup(fd), sz);
-            add_environment("ANDROID_PROPERTY_WORKSPACE", tmp);
-        }
-
-        for (ei = svc->envvars; ei; ei = ei->next)
-            add_environment(ei->name, ei->value);
-
-        for (si = svc->sockets; si; si = si->next) {
-            int socket_type = (
-                    !strcmp(si->type, "stream") ? SOCK_STREAM :
-                        (!strcmp(si->type, "dgram") ? SOCK_DGRAM : SOCK_SEQPACKET));
-            int s = create_socket(si->name, socket_type,
-                                  si->perm, si->uid, si->gid, si->socketcon ?: scon);
-            if (s >= 0) {
-                publish_socket(si->name, s);
-            }
-        }
-
-        freecon(scon);
-        scon = NULL;
-
-        if (svc->writepid_files_) {
-            std::string pid_str = android::base::StringPrintf("%d", pid);
-            for (auto& file : *svc->writepid_files_) {
-                if (!android::base::WriteStringToFile(pid_str, file)) {
-                    ERROR("couldn't write %s to %s: %s\n",
-                          pid_str.c_str(), file.c_str(), strerror(errno));
-                }
-            }
-        }
-
-        if (svc->ioprio_class != IoSchedClass_NONE) {
-            if (android_set_ioprio(getpid(), svc->ioprio_class, svc->ioprio_pri)) {
-                ERROR("Failed to set pid %d ioprio = %d,%d: %s\n",
-                      getpid(), svc->ioprio_class, svc->ioprio_pri, strerror(errno));
-            }
-        }
-
-        if (needs_console) {
-            setsid();
-            open_console();
-        } else {
-            zap_stdio();
-        }
-
-        if (false) {
-            for (size_t n = 0; svc->args[n]; n++) {
-                INFO("args[%zu] = '%s'\n", n, svc->args[n]);
-            }
-            for (size_t n = 0; ENV[n]; n++) {
-                INFO("env[%zu] = '%s'\n", n, ENV[n]);
-            }
-        }
-
-        setpgid(0, getpid());
-
-        // As requested, set our gid, supplemental gids, and uid.
-        if (svc->gid) {
-            if (setgid(svc->gid) != 0) {
-                ERROR("setgid failed: %s\n", strerror(errno));
-                _exit(127);
-            }
-        }
-        if (svc->nr_supp_gids) {
-            if (setgroups(svc->nr_supp_gids, svc->supp_gids) != 0) {
-                ERROR("setgroups failed: %s\n", strerror(errno));
-                _exit(127);
-            }
-        }
-        if (svc->uid) {
-            if (setuid(svc->uid) != 0) {
-                ERROR("setuid failed: %s\n", strerror(errno));
-                _exit(127);
-            }
-        }
-        if (svc->seclabel) {
-            if (is_selinux_enabled() > 0 && setexeccon(svc->seclabel) < 0) {
-                ERROR("cannot setexeccon('%s'): %s\n", svc->seclabel, strerror(errno));
-                _exit(127);
-            }
-        }
-
-        if (!dynamic_args) {
-            if (execve(svc->args[0], (char**) svc->args, (char**) ENV) < 0) {
-                ERROR("cannot execve('%s'): %s\n", svc->args[0], strerror(errno));
-            }
-        } else {
-            char *arg_ptrs[INIT_PARSER_MAXARGS+1];
-            int arg_idx = svc->nargs;
-            char *tmp = strdup(dynamic_args);
-            char *next = tmp;
-            char *bword;
-
-            /* Copy the static arguments */
-            memcpy(arg_ptrs, svc->args, (svc->nargs * sizeof(char *)));
-
-            while((bword = strsep(&next, " "))) {
-                arg_ptrs[arg_idx++] = bword;
-                if (arg_idx == INIT_PARSER_MAXARGS)
-                    break;
-            }
-            arg_ptrs[arg_idx] = NULL;
-            execve(svc->args[0], (char**) arg_ptrs, (char**) ENV);
-        }
-        _exit(127);
-    }
-
-    freecon(scon);
-
-    if (pid < 0) {
-        ERROR("failed to start '%s'\n", svc->name);
-        svc->pid = 0;
-        return;
-    }
-
-    svc->time_started = gettime();
-    svc->pid = pid;
-    svc->flags |= SVC_RUNNING;
-
-    if ((svc->flags & SVC_EXEC) != 0) {
-        INFO("SVC_EXEC pid %d (uid %d gid %d+%zu context %s) started; waiting...\n",
-             svc->pid, svc->uid, svc->gid, svc->nr_supp_gids,
-             svc->seclabel ? : "default");
-        waiting_for_exec = true;
-    }
-
-    svc->NotifyStateChange("running");
-}
-
-/* The how field should be either SVC_DISABLED, SVC_RESET, or SVC_RESTART */
-static void service_stop_or_reset(struct service *svc, int how)
-{
-    /* The service is still SVC_RUNNING until its process exits, but if it has
-     * already exited it shoudn't attempt a restart yet. */
-    svc->flags &= ~(SVC_RESTARTING | SVC_DISABLED_START);
-
-    if ((how != SVC_DISABLED) && (how != SVC_RESET) && (how != SVC_RESTART)) {
-        /* Hrm, an illegal flag.  Default to SVC_DISABLED */
-        how = SVC_DISABLED;
-    }
-        /* if the service has not yet started, prevent
-         * it from auto-starting with its class
-         */
-    if (how == SVC_RESET) {
-        svc->flags |= (svc->flags & SVC_RC_DISABLED) ? SVC_DISABLED : SVC_RESET;
-    } else {
-        svc->flags |= how;
-    }
-
-    if (svc->pid) {
-        NOTICE("Service '%s' is being killed...\n", svc->name);
-        kill(-svc->pid, SIGKILL);
-        svc->NotifyStateChange("stopping");
-    } else {
-        svc->NotifyStateChange("stopped");
-    }
-}
-
-void service_reset(struct service *svc)
-{
-    service_stop_or_reset(svc, SVC_RESET);
-}
-
-void service_stop(struct service *svc)
-{
-    service_stop_or_reset(svc, SVC_DISABLED);
-}
-
-void service_restart(struct service *svc)
-{
-    if (svc->flags & SVC_RUNNING) {
-        /* Stop, wait, then start the service. */
-        service_stop_or_reset(svc, SVC_RESTART);
-    } else if (!(svc->flags & SVC_RESTARTING)) {
-        /* Just start the service since it's not running. */
-        service_start(svc, NULL);
-    } /* else: Service is restarting anyways. */
-}
-
 void property_changed(const char *name, const char *value)
 {
     if (property_triggers_enabled)
-        queue_property_triggers(name, value);
-}
-
-static void restart_service_if_needed(struct service *svc)
-{
-    time_t next_start_time = svc->time_started + 5;
-
-    if (next_start_time <= gettime()) {
-        svc->flags &= (~SVC_RESTARTING);
-        service_start(svc, NULL);
-        return;
-    }
-
-    if ((next_start_time < process_needs_restart) ||
-        (process_needs_restart == 0)) {
-        process_needs_restart = next_start_time;
-    }
+        ActionManager::GetInstance().QueuePropertyTrigger(name, value);
 }
 
 static void restart_processes()
 {
     process_needs_restart = 0;
-    service_for_each_flags(SVC_RESTARTING,
-                           restart_service_if_needed);
+    ServiceManager::GetInstance().
+        ForEachServiceWithFlags(SVC_RESTARTING, [] (Service* s) {
+                s->RestartIfNeeded(process_needs_restart);
+            });
 }
 
-static void msg_start(const char *name)
+static void msg_start(const std::string& name)
 {
-    struct service *svc = NULL;
-    char *tmp = NULL;
-    char *args = NULL;
+    Service* svc = nullptr;
+    std::vector<std::string> vargs;
 
-    if (!strchr(name, ':'))
-        svc = service_find_by_name(name);
-    else {
-        tmp = strdup(name);
-        if (tmp) {
-            args = strchr(tmp, ':');
-            *args = '\0';
-            args++;
+    size_t colon_pos = name.find(':');
+    if (colon_pos == std::string::npos) {
+        svc = ServiceManager::GetInstance().FindServiceByName(name);
+    } else {
+        std::string service_name(name.substr(0, colon_pos));
+        std::string args(name.substr(colon_pos + 1));
+        vargs = android::base::Split(args, " ");
 
-            svc = service_find_by_name(tmp);
-        }
+        svc = ServiceManager::GetInstance().FindServiceByName(service_name);
     }
 
     if (svc) {
-        service_start(svc, args);
+        svc->Start(vargs);
     } else {
-        ERROR("no such service '%s'\n", name);
+        ERROR("no such service '%s'\n", name.c_str());
     }
-    if (tmp)
-        free(tmp);
 }
 
-static void msg_stop(const char *name)
+static void msg_stop(const std::string& name)
 {
-    struct service *svc = service_find_by_name(name);
+    Service* svc = ServiceManager::GetInstance().FindServiceByName(name);
 
     if (svc) {
-        service_stop(svc);
+        svc->Stop();
     } else {
-        ERROR("no such service '%s'\n", name);
+        ERROR("no such service '%s'\n", name.c_str());
     }
 }
 
-static void msg_restart(const char *name)
+static void msg_restart(const std::string& name)
 {
-    struct service *svc = service_find_by_name(name);
+    Service* svc = ServiceManager::GetInstance().FindServiceByName(name);
 
     if (svc) {
-        service_restart(svc);
+        svc->Restart();
     } else {
-        ERROR("no such service '%s'\n", name);
+        ERROR("no such service '%s'\n", name.c_str());
     }
 }
 
-void handle_control_message(const char *msg, const char *arg)
+void handle_control_message(const std::string& msg, const std::string& arg)
 {
-    if (!strcmp(msg,"start")) {
+    if (msg == "start") {
         msg_start(arg);
-    } else if (!strcmp(msg,"stop")) {
+    } else if (msg == "stop") {
         msg_stop(arg);
-    } else if (!strcmp(msg,"restart")) {
+    } else if (msg == "restart") {
         msg_restart(arg);
     } else {
-        ERROR("unknown control msg '%s'\n", msg);
+        ERROR("unknown control msg '%s'\n", msg.c_str());
     }
 }
 
-static struct command *get_first_command(struct action *act)
-{
-    struct listnode *node;
-    node = list_head(&act->commands);
-    if (!node || list_empty(&act->commands))
-        return NULL;
-
-    return node_to_item(node, struct command, clist);
-}
-
-static struct command *get_next_command(struct action *act, struct command *cmd)
-{
-    struct listnode *node;
-    node = cmd->clist.next;
-    if (!node)
-        return NULL;
-    if (node == &act->commands)
-        return NULL;
-
-    return node_to_item(node, struct command, clist);
-}
-
-static int is_last_command(struct action *act, struct command *cmd)
-{
-    return (list_tail(&act->commands) == &cmd->clist);
-}
-
-
-void build_triggers_string(char *name_str, int length, struct action *cur_action) {
-    struct listnode *node;
-    struct trigger *cur_trigger;
-
-    list_for_each(node, &cur_action->triggers) {
-        cur_trigger = node_to_item(node, struct trigger, nlist);
-        if (node != cur_action->triggers.next) {
-            strlcat(name_str, " " , length);
-        }
-        strlcat(name_str, cur_trigger->name , length);
-    }
-}
-
-void execute_one_command() {
-    Timer t;
-
-    char cmd_str[256] = "";
-    char name_str[256] = "";
-
-    if (!cur_action || !cur_command || is_last_command(cur_action, cur_command)) {
-        cur_action = action_remove_queue_head();
-        cur_command = NULL;
-        if (!cur_action) {
-            return;
-        }
-
-        build_triggers_string(name_str, sizeof(name_str), cur_action);
-
-        INFO("processing action %p (%s)\n", cur_action, name_str);
-        cur_command = get_first_command(cur_action);
-    } else {
-        cur_command = get_next_command(cur_action, cur_command);
-    }
-
-    if (!cur_command) {
-        return;
-    }
-
-    int result = cur_command->func(cur_command->nargs, cur_command->args);
-    if (klog_get_level() >= KLOG_INFO_LEVEL) {
-        for (int i = 0; i < cur_command->nargs; i++) {
-            strlcat(cmd_str, cur_command->args[i], sizeof(cmd_str));
-            if (i < cur_command->nargs - 1) {
-                strlcat(cmd_str, " ", sizeof(cmd_str));
-            }
-        }
-        char source[256];
-        if (cur_command->filename) {
-            snprintf(source, sizeof(source), " (%s:%d)", cur_command->filename, cur_command->line);
-        } else {
-            *source = '\0';
-        }
-        INFO("Command '%s' action=%s%s returned %d took %.2fs\n",
-             cmd_str, cur_action ? name_str : "", source, result, t.duration());
-    }
-}
-
-static int wait_for_coldboot_done_action(int nargs, char **args) {
+static int wait_for_coldboot_done_action(const std::vector<std::string>& args) {
     Timer t;
 
     NOTICE("Waiting for %s...\n", COLDBOOT_DONE);
@@ -655,7 +228,7 @@
  * time. We do not reboot or halt on failures, as this is a best-effort
  * attempt.
  */
-static int mix_hwrng_into_linux_rng_action(int nargs, char **args)
+static int mix_hwrng_into_linux_rng_action(const std::vector<std::string>& args)
 {
     int result = -1;
     int hwrandom_fd = -1;
@@ -717,20 +290,20 @@
     return result;
 }
 
-static int keychord_init_action(int nargs, char **args)
+static int keychord_init_action(const std::vector<std::string>& args)
 {
     keychord_init();
     return 0;
 }
 
-static int console_init_action(int nargs, char **args)
+static int console_init_action(const std::vector<std::string>& args)
 {
-    char console[PROP_VALUE_MAX];
-    if (property_get("ro.boot.console", console) > 0) {
-        snprintf(console_name, sizeof(console_name), "/dev/%s", console);
+    std::string console = property_get("ro.boot.console");
+    if (!console.empty()) {
+        console_name = "/dev/" + console;
     }
 
-    int fd = open(console_name, O_RDWR | O_CLOEXEC);
+    int fd = open(console_name.c_str(), O_RDWR | O_CLOEXEC);
     if (fd >= 0)
         have_console = 1;
     close(fd);
@@ -760,36 +333,20 @@
     return 0;
 }
 
-static void import_kernel_nv(char *name, bool for_emulator)
-{
-    char *value = strchr(name, '=');
-    int name_len = strlen(name);
-
-    if (value == 0) return;
-    *value++ = 0;
-    if (name_len == 0) return;
+static void import_kernel_nv(const std::string& key, const std::string& value, bool for_emulator) {
+    if (key.empty()) return;
 
     if (for_emulator) {
-        /* in the emulator, export any kernel option with the
-         * ro.kernel. prefix */
-        char buff[PROP_NAME_MAX];
-        int len = snprintf( buff, sizeof(buff), "ro.kernel.%s", name );
-
-        if (len < (int)sizeof(buff))
-            property_set( buff, value );
+        // In the emulator, export any kernel option with the "ro.kernel." prefix.
+        property_set(android::base::StringPrintf("ro.kernel.%s", key.c_str()).c_str(), value.c_str());
         return;
     }
 
-    if (!strcmp(name,"qemu")) {
-        strlcpy(qemu, value, sizeof(qemu));
-    } else if (!strncmp(name, "androidboot.", 12) && name_len > 12) {
-        const char *boot_prop_name = name + 12;
-        char prop[PROP_NAME_MAX];
-        int cnt;
-
-        cnt = snprintf(prop, sizeof(prop), "ro.boot.%s", boot_prop_name);
-        if (cnt < PROP_NAME_MAX)
-            property_set(prop, value);
+    if (key == "qemu") {
+        strlcpy(qemu, value.c_str(), sizeof(qemu));
+    } else if (android::base::StartsWith(key, "androidboot.")) {
+        property_set(android::base::StringPrintf("ro.boot.%s", key.c_str() + 12).c_str(),
+                     value.c_str());
     }
 }
 
@@ -807,14 +364,12 @@
         { "ro.boot.revision",   "ro.revision",   "0", },
     };
     for (size_t i = 0; i < ARRAY_SIZE(prop_map); i++) {
-        char value[PROP_VALUE_MAX];
-        int rc = property_get(prop_map[i].src_prop, value);
-        property_set(prop_map[i].dst_prop, (rc > 0) ? value : prop_map[i].default_value);
+        std::string value = property_get(prop_map[i].src_prop);
+        property_set(prop_map[i].dst_prop, (!value.empty()) ? value.c_str() : prop_map[i].default_value);
     }
 }
 
-static void process_kernel_dt(void)
-{
+static void process_kernel_dt() {
     static const char android_dir[] = "/proc/device-tree/firmware/android";
 
     std::string file_name = android::base::StringPrintf("%s/compatible", android_dir);
@@ -827,13 +382,13 @@
     }
 
     std::unique_ptr<DIR, int(*)(DIR*)>dir(opendir(android_dir), closedir);
-    if (!dir)
-        return;
+    if (!dir) return;
 
     struct dirent *dp;
     while ((dp = readdir(dir.get())) != NULL) {
-        if (dp->d_type != DT_REG || !strcmp(dp->d_name, "compatible"))
+        if (dp->d_type != DT_REG || !strcmp(dp->d_name, "compatible")) {
             continue;
+        }
 
         file_name = android::base::StringPrintf("%s/%s", android_dir, dp->d_name);
 
@@ -845,23 +400,20 @@
     }
 }
 
-static void process_kernel_cmdline(void)
-{
-    /* don't expose the raw commandline to nonpriv processes */
+static void process_kernel_cmdline() {
+    // Don't expose the raw commandline to unprivileged processes.
     chmod("/proc/cmdline", 0440);
 
-    /* first pass does the common stuff, and finds if we are in qemu.
-     * second pass is only necessary for qemu to export all kernel params
-     * as props.
-     */
+    // The first pass does the common stuff, and finds if we are in qemu.
+    // The second pass is only necessary for qemu to export all kernel params
+    // as properties.
     import_kernel_cmdline(false, import_kernel_nv);
-    if (qemu[0])
-        import_kernel_cmdline(true, import_kernel_nv);
+    if (qemu[0]) import_kernel_cmdline(true, import_kernel_nv);
 }
 
-static int queue_property_triggers_action(int nargs, char **args)
+static int queue_property_triggers_action(const std::vector<std::string>& args)
 {
-    queue_all_property_triggers();
+    ActionManager::GetInstance().QueueAllPropertyTriggers();
     /* enable property triggers */
     property_triggers_enabled = 1;
     return 0;
@@ -874,46 +426,23 @@
     sehandle_prop = selinux_android_prop_context_handle();
 }
 
-enum selinux_enforcing_status { SELINUX_DISABLED, SELINUX_PERMISSIVE, SELINUX_ENFORCING };
+enum selinux_enforcing_status { SELINUX_PERMISSIVE, SELINUX_ENFORCING };
 
 static selinux_enforcing_status selinux_status_from_cmdline() {
     selinux_enforcing_status status = SELINUX_ENFORCING;
 
-    std::function<void(char*,bool)> fn = [&](char* name, bool in_qemu) {
-        char *value = strchr(name, '=');
-        if (value == nullptr) { return; }
-        *value++ = '\0';
-        if (strcmp(name, "androidboot.selinux") == 0) {
-            if (strcmp(value, "disabled") == 0) {
-                status = SELINUX_DISABLED;
-            } else if (strcmp(value, "permissive") == 0) {
-                status = SELINUX_PERMISSIVE;
-            }
+    import_kernel_cmdline(false, [&](const std::string& key, const std::string& value, bool in_qemu) {
+        if (key == "androidboot.selinux" && value == "permissive") {
+            status = SELINUX_PERMISSIVE;
         }
-    };
-    import_kernel_cmdline(false, fn);
+    });
 
     return status;
 }
 
-
-static bool selinux_is_disabled(void)
-{
-    if (ALLOW_DISABLE_SELINUX) {
-        if (access("/sys/fs/selinux", F_OK) != 0) {
-            // SELinux is not compiled into the kernel, or has been disabled
-            // via the kernel command line "selinux=0".
-            return true;
-        }
-        return selinux_status_from_cmdline() == SELINUX_DISABLED;
-    }
-
-    return false;
-}
-
 static bool selinux_is_enforcing(void)
 {
-    if (ALLOW_DISABLE_SELINUX) {
+    if (ALLOW_PERMISSIVE_SELINUX) {
         return selinux_status_from_cmdline() == SELINUX_ENFORCING;
     }
     return true;
@@ -921,10 +450,6 @@
 
 int selinux_reload_policy(void)
 {
-    if (selinux_is_disabled()) {
-        return -1;
-    }
-
     INFO("SELinux: Attempting to reload policy files\n");
 
     if (selinux_android_reload_policy() == -1) {
@@ -961,10 +486,6 @@
     cb.func_audit = audit_callback;
     selinux_set_callback(SELINUX_CB_AUDIT, cb);
 
-    if (selinux_is_disabled()) {
-        return;
-    }
-
     if (in_kernel_domain) {
         INFO("Loading SELinux policy...\n");
         if (selinux_android_load_policy() < 0) {
@@ -972,8 +493,15 @@
             security_failure();
         }
 
+        bool kernel_enforcing = (security_getenforce() == 1);
         bool is_enforcing = selinux_is_enforcing();
-        security_setenforce(is_enforcing);
+        if (kernel_enforcing != is_enforcing) {
+            if (security_setenforce(is_enforcing)) {
+                ERROR("security_setenforce(%s) failed: %s\n",
+                      is_enforcing ? "true" : "false", strerror(errno));
+                security_failure();
+            }
+        }
 
         if (write_file("/sys/fs/selinux/checkreqprot", "0") == -1) {
             security_failure();
@@ -1021,7 +549,7 @@
     klog_init();
     klog_set_level(KLOG_NOTICE_LEVEL);
 
-    NOTICE("init%s started!\n", is_first_stage ? "" : " second stage");
+    NOTICE("init %s started!\n", is_first_stage ? "first stage" : "second stage");
 
     if (!is_first_stage) {
         // Indicate that booting is in progress to background fw loaders, etc.
@@ -1034,7 +562,7 @@
         process_kernel_dt();
         process_kernel_cmdline();
 
-        // Propogate the kernel variables to internal variables
+        // Propagate the kernel variables to internal variables
         // used by init as well as the current required properties.
         export_kernel_boot_props();
     }
@@ -1060,7 +588,7 @@
     // These directories were necessarily created before initial policy load
     // and therefore need their security context restored to the proper value.
     // This must happen before /dev is populated by ueventd.
-    INFO("Running restorecon...\n");
+    NOTICE("Running restorecon...\n");
     restorecon("/dev");
     restorecon("/dev/socket");
     restorecon("/dev/__properties__");
@@ -1077,38 +605,47 @@
     property_load_boot_defaults();
     start_property_service();
 
-    init_parse_config_file("/init.rc");
+    const BuiltinFunctionMap function_map;
+    Action::set_function_map(&function_map);
 
-    action_for_each_trigger("early-init", action_add_queue_tail);
+    Parser& parser = Parser::GetInstance();
+    parser.AddSectionParser("service",std::make_unique<ServiceParser>());
+    parser.AddSectionParser("on", std::make_unique<ActionParser>());
+    parser.AddSectionParser("import", std::make_unique<ImportParser>());
+    parser.ParseConfig("/init.rc");
+
+    ActionManager& am = ActionManager::GetInstance();
+
+    am.QueueEventTrigger("early-init");
 
     // Queue an action that waits for coldboot done so we know ueventd has set up all of /dev...
-    queue_builtin_action(wait_for_coldboot_done_action, "wait_for_coldboot_done");
+    am.QueueBuiltinAction(wait_for_coldboot_done_action, "wait_for_coldboot_done");
     // ... so that we can start queuing up actions that require stuff from /dev.
-    queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
-    queue_builtin_action(keychord_init_action, "keychord_init");
-    queue_builtin_action(console_init_action, "console_init");
+    am.QueueBuiltinAction(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
+    am.QueueBuiltinAction(keychord_init_action, "keychord_init");
+    am.QueueBuiltinAction(console_init_action, "console_init");
 
     // Trigger all the boot actions to get us started.
-    action_for_each_trigger("init", action_add_queue_tail);
+    am.QueueEventTrigger("init");
 
     // Repeat mix_hwrng_into_linux_rng in case /dev/hw_random or /dev/random
     // wasn't ready immediately after wait_for_coldboot_done
-    queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
+    am.QueueBuiltinAction(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
 
     // Don't mount filesystems or start core system services in charger mode.
-    char bootmode[PROP_VALUE_MAX];
-    if (property_get("ro.bootmode", bootmode) > 0 && strcmp(bootmode, "charger") == 0) {
-        action_for_each_trigger("charger", action_add_queue_tail);
+    std::string bootmode = property_get("ro.bootmode");
+    if (bootmode == "charger") {
+        am.QueueEventTrigger("charger");
     } else {
-        action_for_each_trigger("late-init", action_add_queue_tail);
+        am.QueueEventTrigger("late-init");
     }
 
     // Run all property triggers based on current state of the properties.
-    queue_builtin_action(queue_property_triggers_action, "queue_property_triggers");
+    am.QueueBuiltinAction(queue_property_triggers_action, "queue_property_triggers");
 
     while (true) {
         if (!waiting_for_exec) {
-            execute_one_command();
+            am.ExecuteOneCommand();
             restart_processes();
         }
 
@@ -1119,7 +656,7 @@
                 timeout = 0;
         }
 
-        if (!action_queue_empty() || cur_action) {
+        if (am.HasMoreCommands()) {
             timeout = 0;
         }
 
diff --git a/init/init.h b/init/init.h
index c166969..345d442 100644
--- a/init/init.h
+++ b/init/init.h
@@ -17,149 +17,28 @@
 #ifndef _INIT_INIT_H
 #define _INIT_INIT_H
 
-#include <sys/types.h>
-
 #include <string>
-#include <vector>
 
-#include <cutils/list.h>
-#include <cutils/iosched_policy.h>
-
-struct command
-{
-        /* list of commands in an action */
-    struct listnode clist;
-
-    int (*func)(int nargs, char **args);
-
-    int line;
-    const char *filename;
-
-    int nargs;
-    char *args[1];
-};
-
-struct trigger {
-    struct listnode nlist;
-    const char *name;
-};
-
-struct action {
-        /* node in list of all actions */
-    struct listnode alist;
-        /* node in the queue of pending actions */
-    struct listnode qlist;
-        /* node in list of actions for a trigger */
-    struct listnode tlist;
-
-    unsigned hash;
-
-        /* list of actions which triggers the commands*/
-    struct listnode triggers;
-    struct listnode commands;
-    struct command *current;
-};
-
-struct socketinfo {
-    struct socketinfo *next;
-    const char *name;
-    const char *type;
-    uid_t uid;
-    gid_t gid;
-    int perm;
-    const char *socketcon;
-};
-
-struct svcenvinfo {
-    struct svcenvinfo *next;
-    const char *name;
-    const char *value;
-};
-
-#define SVC_DISABLED       0x001  // do not autostart with class
-#define SVC_ONESHOT        0x002  // do not restart on exit
-#define SVC_RUNNING        0x004  // currently active
-#define SVC_RESTARTING     0x008  // waiting to restart
-#define SVC_CONSOLE        0x010  // requires console
-#define SVC_CRITICAL       0x020  // will reboot into recovery if keeps crashing
-#define SVC_RESET          0x040  // Use when stopping a process, but not disabling so it can be restarted with its class.
-#define SVC_RC_DISABLED    0x080  // Remember if the disabled flag was set in the rc script.
-#define SVC_RESTART        0x100  // Use to safely restart (stop, wait, start) a service.
-#define SVC_DISABLED_START 0x200  // A start was requested but it was disabled at the time.
-#define SVC_EXEC           0x400  // This synthetic service corresponds to an 'exec'.
-
-#define NR_SVC_SUPP_GIDS 12    /* twelve supplementary groups */
+class Action;
+class Service;
 
 #define COMMAND_RETRY_TIMEOUT 5
 
-struct service {
-    void NotifyStateChange(const char* new_state);
-
-        /* list of all services */
-    struct listnode slist;
-
-    char *name;
-    const char *classname;
-
-    unsigned flags;
-    pid_t pid;
-    time_t time_started;    /* time of last start */
-    time_t time_crashed;    /* first crash within inspection window */
-    int nr_crashed;         /* number of times crashed within window */
-
-    uid_t uid;
-    gid_t gid;
-    gid_t supp_gids[NR_SVC_SUPP_GIDS];
-    size_t nr_supp_gids;
-
-    const char* seclabel;
-
-    struct socketinfo *sockets;
-    struct svcenvinfo *envvars;
-
-    struct action onrestart;  /* Actions to execute on restart. */
-
-    std::vector<std::string>* writepid_files_;
-
-    /* keycodes for triggering this service via /dev/keychord */
-    int *keycodes;
-    int nkeycodes;
-    int keychord_id;
-
-    IoSchedClass ioprio_class;
-    int ioprio_pri;
-
-    int nargs;
-    /* "MUST BE AT THE END OF THE STRUCT" */
-    char *args[1];
-}; /*     ^-------'args' MUST be at the end of this struct! */
-
+extern const char *ENV[32];
 extern bool waiting_for_exec;
+extern int have_console;
+extern std::string console_name;
 extern struct selabel_handle *sehandle;
 extern struct selabel_handle *sehandle_prop;
 
-void build_triggers_string(char *name_str, int length, struct action *cur_action);
+void handle_control_message(const std::string& msg, const std::string& arg);
 
-void handle_control_message(const char *msg, const char *arg);
-
-struct service *service_find_by_name(const char *name);
-struct service *service_find_by_pid(pid_t pid);
-struct service *service_find_by_keychord(int keychord_id);
-void service_for_each(void (*func)(struct service *svc));
-void service_for_each_class(const char *classname,
-                            void (*func)(struct service *svc));
-void service_for_each_flags(unsigned matchflags,
-                            void (*func)(struct service *svc));
-void service_stop(struct service *svc);
-void service_reset(struct service *svc);
-void service_restart(struct service *svc);
-void service_start(struct service *svc, const char *dynamic_args);
 void property_changed(const char *name, const char *value);
 
 int selinux_reload_policy(void);
 
-void zap_stdio(void);
-
 void register_epoll_handler(int fd, void (*fn)());
 
-#endif	/* _INIT_INIT_H */
+int add_environment(const char* key, const char* val);
+
+#endif  /* _INIT_INIT_H */
diff --git a/init/init_parser.cpp b/init/init_parser.cpp
index 62e8b10..02b3985 100644
--- a/init/init_parser.cpp
+++ b/init/init_parser.cpp
@@ -14,1001 +14,132 @@
  * limitations under the License.
  */
 
-#include <ctype.h>
+#include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
-#include <inttypes.h>
-#include <stdarg.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
 
-#include "init.h"
-#include "parser.h"
+#include "action.h"
 #include "init_parser.h"
 #include "log.h"
-#include "property_service.h"
+#include "parser.h"
+#include "service.h"
 #include "util.h"
 
-#include <cutils/iosched_policy.h>
-#include <cutils/list.h>
+#include <base/stringprintf.h>
 
-static list_declare(service_list);
-static list_declare(action_list);
-static list_declare(action_queue);
-
-struct import {
-    struct listnode list;
-    const char *filename;
-};
-
-static void *parse_service(struct parse_state *state, int nargs, char **args);
-static void parse_line_service(struct parse_state *state, int nargs, char **args);
-
-static void *parse_action(struct parse_state *state, int nargs, char **args);
-static void parse_line_action(struct parse_state *state, int nargs, char **args);
-
-#define SECTION 0x01
-#define COMMAND 0x02
-#define OPTION  0x04
-
-#include "keywords.h"
-
-#define KEYWORD(symbol, flags, nargs, func) \
-    [ K_##symbol ] = { #symbol, func, nargs + 1, flags, },
-
-static struct {
-    const char *name;
-    int (*func)(int nargs, char **args);
-    unsigned char nargs;
-    unsigned char flags;
-} keyword_info[KEYWORD_COUNT] = {
-    [ K_UNKNOWN ] = { "unknown", 0, 0, 0 },
-#include "keywords.h"
-};
-#undef KEYWORD
-
-#define kw_is(kw, type) (keyword_info[kw].flags & (type))
-#define kw_name(kw) (keyword_info[kw].name)
-#define kw_func(kw) (keyword_info[kw].func)
-#define kw_nargs(kw) (keyword_info[kw].nargs)
-
-void dump_parser_state() {
-    if (false) {
-        struct listnode* node;
-        list_for_each(node, &service_list) {
-            service* svc = node_to_item(node, struct service, slist);
-            INFO("service %s\n", svc->name);
-            INFO("  class '%s'\n", svc->classname);
-            INFO("  exec");
-            for (int n = 0; n < svc->nargs; n++) {
-                INFO(" '%s'", svc->args[n]);
-            }
-            INFO("\n");
-            for (socketinfo* si = svc->sockets; si; si = si->next) {
-                INFO("  socket %s %s 0%o\n", si->name, si->type, si->perm);
-            }
-        }
-
-        list_for_each(node, &action_list) {
-            action* act = node_to_item(node, struct action, alist);
-            INFO("on ");
-            char name_str[256] = "";
-            build_triggers_string(name_str, sizeof(name_str), act);
-            INFO("%s", name_str);
-            INFO("\n");
-
-            struct listnode* node2;
-            list_for_each(node2, &act->commands) {
-                command* cmd = node_to_item(node2, struct command, clist);
-                INFO("  %p", cmd->func);
-                for (int n = 0; n < cmd->nargs; n++) {
-                    INFO(" %s", cmd->args[n]);
-                }
-                INFO("\n");
-            }
-            INFO("\n");
-        }
-    }
+Parser::Parser() {
 }
 
-static int lookup_keyword(const char *s)
-{
-    switch (*s++) {
-    case 'b':
-        if (!strcmp(s, "ootchart_init")) return K_bootchart_init;
-        break;
-    case 'c':
-        if (!strcmp(s, "opy")) return K_copy;
-        if (!strcmp(s, "lass")) return K_class;
-        if (!strcmp(s, "lass_start")) return K_class_start;
-        if (!strcmp(s, "lass_stop")) return K_class_stop;
-        if (!strcmp(s, "lass_reset")) return K_class_reset;
-        if (!strcmp(s, "onsole")) return K_console;
-        if (!strcmp(s, "hown")) return K_chown;
-        if (!strcmp(s, "hmod")) return K_chmod;
-        if (!strcmp(s, "ritical")) return K_critical;
-        break;
-    case 'd':
-        if (!strcmp(s, "isabled")) return K_disabled;
-        if (!strcmp(s, "omainname")) return K_domainname;
-        break;
-    case 'e':
-        if (!strcmp(s, "nable")) return K_enable;
-        if (!strcmp(s, "xec")) return K_exec;
-        if (!strcmp(s, "xport")) return K_export;
-        break;
-    case 'g':
-        if (!strcmp(s, "roup")) return K_group;
-        break;
-    case 'h':
-        if (!strcmp(s, "ostname")) return K_hostname;
-        break;
-    case 'i':
-        if (!strcmp(s, "oprio")) return K_ioprio;
-        if (!strcmp(s, "fup")) return K_ifup;
-        if (!strcmp(s, "nsmod")) return K_insmod;
-        if (!strcmp(s, "mport")) return K_import;
-        if (!strcmp(s, "nstallkey")) return K_installkey;
-        break;
-    case 'k':
-        if (!strcmp(s, "eycodes")) return K_keycodes;
-        break;
-    case 'l':
-        if (!strcmp(s, "oglevel")) return K_loglevel;
-        if (!strcmp(s, "oad_persist_props")) return K_load_persist_props;
-        if (!strcmp(s, "oad_system_props")) return K_load_system_props;
-        break;
-    case 'm':
-        if (!strcmp(s, "kdir")) return K_mkdir;
-        if (!strcmp(s, "ount_all")) return K_mount_all;
-        if (!strcmp(s, "ount")) return K_mount;
-        break;
-    case 'o':
-        if (!strcmp(s, "n")) return K_on;
-        if (!strcmp(s, "neshot")) return K_oneshot;
-        if (!strcmp(s, "nrestart")) return K_onrestart;
-        break;
-    case 'p':
-        if (!strcmp(s, "owerctl")) return K_powerctl;
-        break;
-    case 'r':
-        if (!strcmp(s, "estart")) return K_restart;
-        if (!strcmp(s, "estorecon")) return K_restorecon;
-        if (!strcmp(s, "estorecon_recursive")) return K_restorecon_recursive;
-        if (!strcmp(s, "mdir")) return K_rmdir;
-        if (!strcmp(s, "m")) return K_rm;
-        break;
-    case 's':
-        if (!strcmp(s, "eclabel")) return K_seclabel;
-        if (!strcmp(s, "ervice")) return K_service;
-        if (!strcmp(s, "etenv")) return K_setenv;
-        if (!strcmp(s, "etprop")) return K_setprop;
-        if (!strcmp(s, "etrlimit")) return K_setrlimit;
-        if (!strcmp(s, "etusercryptopolicies")) return K_setusercryptopolicies;
-        if (!strcmp(s, "ocket")) return K_socket;
-        if (!strcmp(s, "tart")) return K_start;
-        if (!strcmp(s, "top")) return K_stop;
-        if (!strcmp(s, "wapon_all")) return K_swapon_all;
-        if (!strcmp(s, "ymlink")) return K_symlink;
-        if (!strcmp(s, "ysclktz")) return K_sysclktz;
-        break;
-    case 't':
-        if (!strcmp(s, "rigger")) return K_trigger;
-        break;
-    case 'u':
-        if (!strcmp(s, "ser")) return K_user;
-        break;
-    case 'v':
-        if (!strcmp(s, "erity_load_state")) return K_verity_load_state;
-        if (!strcmp(s, "erity_update_state")) return K_verity_update_state;
-        break;
-    case 'w':
-        if (!strcmp(s, "rite")) return K_write;
-        if (!strcmp(s, "ritepid")) return K_writepid;
-        if (!strcmp(s, "ait")) return K_wait;
-        break;
-    }
-    return K_UNKNOWN;
+Parser& Parser::GetInstance() {
+    static Parser instance;
+    return instance;
 }
 
-static void parse_line_no_op(struct parse_state*, int, char**) {
+void Parser::AddSectionParser(const std::string& name,
+                              std::unique_ptr<SectionParser> parser) {
+    section_parsers_[name] = std::move(parser);
 }
 
-static int push_chars(char **dst, int *len, const char *chars, int cnt)
-{
-    if (cnt > *len)
-        return -1;
-
-    memcpy(*dst, chars, cnt);
-    *dst += cnt;
-    *len -= cnt;
-
-    return 0;
-}
-
-int expand_props(char *dst, const char *src, int dst_size)
-{
-    char *dst_ptr = dst;
-    const char *src_ptr = src;
-    int ret = 0;
-    int left = dst_size - 1;
-
-    if (!src || !dst || dst_size == 0)
-        return -1;
-
-    /* - variables can either be $x.y or ${x.y}, in case they are only part
-     *   of the string.
-     * - will accept $$ as a literal $.
-     * - no nested property expansion, i.e. ${foo.${bar}} is not supported,
-     *   bad things will happen
-     */
-    while (*src_ptr && left > 0) {
-        char *c;
-        char prop[PROP_NAME_MAX + 1];
-        char prop_val[PROP_VALUE_MAX];
-        int prop_len = 0;
-        int prop_val_len;
-
-        c = strchr(src_ptr, '$');
-        if (!c) {
-            while (left-- > 0 && *src_ptr)
-                *(dst_ptr++) = *(src_ptr++);
-            break;
-        }
-
-        memset(prop, 0, sizeof(prop));
-
-        ret = push_chars(&dst_ptr, &left, src_ptr, c - src_ptr);
-        if (ret < 0)
-            goto err_nospace;
-        c++;
-
-        if (*c == '$') {
-            *(dst_ptr++) = *(c++);
-            src_ptr = c;
-            left--;
-            continue;
-        } else if (*c == '\0') {
-            break;
-        }
-
-        if (*c == '{') {
-            c++;
-            while (*c && *c != '}' && prop_len < PROP_NAME_MAX)
-                prop[prop_len++] = *(c++);
-            if (*c != '}') {
-                /* failed to find closing brace, abort. */
-                if (prop_len == PROP_NAME_MAX)
-                    ERROR("prop name too long during expansion of '%s'\n",
-                          src);
-                else if (*c == '\0')
-                    ERROR("unexpected end of string in '%s', looking for }\n",
-                          src);
-                goto err;
-            }
-            prop[prop_len] = '\0';
-            c++;
-        } else if (*c) {
-            while (*c && prop_len < PROP_NAME_MAX)
-                prop[prop_len++] = *(c++);
-            if (prop_len == PROP_NAME_MAX && *c != '\0') {
-                ERROR("prop name too long in '%s'\n", src);
-                goto err;
-            }
-            prop[prop_len] = '\0';
-            ERROR("using deprecated syntax for specifying property '%s', use ${name} instead\n",
-                  prop);
-        }
-
-        if (prop_len == 0) {
-            ERROR("invalid zero-length prop name in '%s'\n", src);
-            goto err;
-        }
-
-        prop_val_len = property_get(prop, prop_val);
-        if (!prop_val_len) {
-            ERROR("property '%s' doesn't exist while expanding '%s'\n",
-                  prop, src);
-            goto err;
-        }
-
-        ret = push_chars(&dst_ptr, &left, prop_val, prop_val_len);
-        if (ret < 0)
-            goto err_nospace;
-        src_ptr = c;
-        continue;
-    }
-
-    *dst_ptr = '\0';
-    return 0;
-
-err_nospace:
-    ERROR("destination buffer overflow while expanding '%s'\n", src);
-err:
-    return -1;
-}
-
-static void parse_import(struct parse_state *state, int nargs, char **args)
-{
-    struct listnode *import_list = (listnode*) state->priv;
-    char conf_file[PATH_MAX];
-    int ret;
-
-    if (nargs != 2) {
-        ERROR("single argument needed for import\n");
-        return;
-    }
-
-    ret = expand_props(conf_file, args[1], sizeof(conf_file));
-    if (ret) {
-        ERROR("error while handling import on line '%d' in '%s'\n",
-              state->line, state->filename);
-        return;
-    }
-
-    struct import* import = (struct import*) calloc(1, sizeof(struct import));
-    import->filename = strdup(conf_file);
-    list_add_tail(import_list, &import->list);
-    INFO("Added '%s' to import list\n", import->filename);
-}
-
-static void parse_new_section(struct parse_state *state, int kw,
-                       int nargs, char **args)
-{
-    printf("[ %s %s ]\n", args[0],
-           nargs > 1 ? args[1] : "");
-    switch(kw) {
-    case K_service:
-        state->context = parse_service(state, nargs, args);
-        if (state->context) {
-            state->parse_line = parse_line_service;
-            return;
-        }
-        break;
-    case K_on:
-        state->context = parse_action(state, nargs, args);
-        if (state->context) {
-            state->parse_line = parse_line_action;
-            return;
-        }
-        break;
-    case K_import:
-        parse_import(state, nargs, args);
-        break;
-    }
-    state->parse_line = parse_line_no_op;
-}
-
-static void parse_config(const char *fn, const std::string& data)
-{
-    struct listnode import_list;
-    struct listnode *node;
-    char *args[INIT_PARSER_MAXARGS];
-
-    int nargs = 0;
+void Parser::ParseData(const std::string& filename, const std::string& data) {
+    //TODO: Use a parser with const input and remove this copy
+    std::vector<char> data_copy(data.begin(), data.end());
+    data_copy.push_back('\0');
 
     parse_state state;
-    state.filename = fn;
+    state.filename = filename.c_str();
     state.line = 0;
-    state.ptr = strdup(data.c_str());  // TODO: fix this code!
+    state.ptr = &data_copy[0];
     state.nexttoken = 0;
-    state.parse_line = parse_line_no_op;
 
-    list_init(&import_list);
-    state.priv = &import_list;
+    SectionParser* section_parser = nullptr;
+    std::vector<std::string> args;
 
     for (;;) {
         switch (next_token(&state)) {
         case T_EOF:
-            state.parse_line(&state, 0, 0);
-            goto parser_done;
+            if (section_parser) {
+                section_parser->EndSection();
+            }
+            return;
         case T_NEWLINE:
             state.line++;
-            if (nargs) {
-                int kw = lookup_keyword(args[0]);
-                if (kw_is(kw, SECTION)) {
-                    state.parse_line(&state, 0, 0);
-                    parse_new_section(&state, kw, nargs, args);
-                } else {
-                    state.parse_line(&state, nargs, args);
-                }
-                nargs = 0;
+            if (args.empty()) {
+                break;
             }
+            if (section_parsers_.count(args[0])) {
+                if (section_parser) {
+                    section_parser->EndSection();
+                }
+                section_parser = section_parsers_[args[0]].get();
+                std::string ret_err;
+                if (!section_parser->ParseSection(args, &ret_err)) {
+                    parse_error(&state, "%s\n", ret_err.c_str());
+                    section_parser = nullptr;
+                }
+            } else if (section_parser) {
+                std::string ret_err;
+                if (!section_parser->ParseLineSection(args, state.filename,
+                                                      state.line, &ret_err)) {
+                    parse_error(&state, "%s\n", ret_err.c_str());
+                }
+            }
+            args.clear();
             break;
         case T_TEXT:
-            if (nargs < INIT_PARSER_MAXARGS) {
-                args[nargs++] = state.text;
-            }
+            args.emplace_back(state.text);
             break;
         }
     }
-
-parser_done:
-    list_for_each(node, &import_list) {
-         struct import *import = node_to_item(node, struct import, list);
-         int ret;
-
-         ret = init_parse_config_file(import->filename);
-         if (ret)
-             ERROR("could not import file '%s' from '%s'\n",
-                   import->filename, fn);
-    }
 }
 
-int init_parse_config_file(const char* path) {
-    INFO("Parsing %s...\n", path);
+bool Parser::ParseConfigFile(const std::string& path) {
+    INFO("Parsing file %s...\n", path.c_str());
     Timer t;
     std::string data;
-    if (!read_file(path, &data)) {
-        return -1;
+    if (!read_file(path.c_str(), &data)) {
+        return false;
     }
 
     data.push_back('\n'); // TODO: fix parse_config.
-    parse_config(path, data);
-    dump_parser_state();
+    ParseData(path, data);
+    for (const auto& sp : section_parsers_) {
+        sp.second->EndFile(path);
+    }
+    DumpState();
 
-    NOTICE("(Parsing %s took %.2fs.)\n", path, t.duration());
-    return 0;
+    NOTICE("(Parsing %s took %.2fs.)\n", path.c_str(), t.duration());
+    return true;
 }
 
-static int valid_name(const char *name)
-{
-    if (strlen(name) > 16) {
-        return 0;
+bool Parser::ParseConfigDir(const std::string& path) {
+    INFO("Parsing directory %s...\n", path.c_str());
+    std::unique_ptr<DIR, int(*)(DIR*)> config_dir(opendir(path.c_str()), closedir);
+    if (!config_dir) {
+        ERROR("Could not import directory '%s'\n", path.c_str());
+        return false;
     }
-    while (*name) {
-        if (!isalnum(*name) && (*name != '_') && (*name != '-')) {
-            return 0;
-        }
-        name++;
-    }
-    return 1;
-}
-
-struct service *service_find_by_name(const char *name)
-{
-    struct listnode *node;
-    struct service *svc;
-    list_for_each(node, &service_list) {
-        svc = node_to_item(node, struct service, slist);
-        if (!strcmp(svc->name, name)) {
-            return svc;
-        }
-    }
-    return 0;
-}
-
-struct service *service_find_by_pid(pid_t pid)
-{
-    struct listnode *node;
-    struct service *svc;
-    list_for_each(node, &service_list) {
-        svc = node_to_item(node, struct service, slist);
-        if (svc->pid == pid) {
-            return svc;
-        }
-    }
-    return 0;
-}
-
-struct service *service_find_by_keychord(int keychord_id)
-{
-    struct listnode *node;
-    struct service *svc;
-    list_for_each(node, &service_list) {
-        svc = node_to_item(node, struct service, slist);
-        if (svc->keychord_id == keychord_id) {
-            return svc;
-        }
-    }
-    return 0;
-}
-
-void service_for_each(void (*func)(struct service *svc))
-{
-    struct listnode *node;
-    struct service *svc;
-    list_for_each(node, &service_list) {
-        svc = node_to_item(node, struct service, slist);
-        func(svc);
-    }
-}
-
-void service_for_each_class(const char *classname,
-                            void (*func)(struct service *svc))
-{
-    struct listnode *node;
-    struct service *svc;
-    list_for_each(node, &service_list) {
-        svc = node_to_item(node, struct service, slist);
-        if (!strcmp(svc->classname, classname)) {
-            func(svc);
-        }
-    }
-}
-
-void service_for_each_flags(unsigned matchflags,
-                            void (*func)(struct service *svc))
-{
-    struct listnode *node;
-    struct service *svc;
-    list_for_each(node, &service_list) {
-        svc = node_to_item(node, struct service, slist);
-        if (svc->flags & matchflags) {
-            func(svc);
-        }
-    }
-}
-
-void action_for_each_trigger(const char *trigger,
-                             void (*func)(struct action *act))
-{
-    struct listnode *node, *node2;
-    struct action *act;
-    struct trigger *cur_trigger;
-
-    list_for_each(node, &action_list) {
-        act = node_to_item(node, struct action, alist);
-        list_for_each(node2, &act->triggers) {
-            cur_trigger = node_to_item(node2, struct trigger, nlist);
-            if (!strcmp(cur_trigger->name, trigger)) {
-                func(act);
+    dirent* current_file;
+    while ((current_file = readdir(config_dir.get()))) {
+        std::string current_path =
+            android::base::StringPrintf("%s/%s", path.c_str(), current_file->d_name);
+        // Ignore directories and only process regular files.
+        if (current_file->d_type == DT_REG) {
+            if (!ParseConfigFile(current_path)) {
+                ERROR("could not import file '%s'\n", current_path.c_str());
             }
         }
     }
+    return true;
 }
 
-
-void queue_property_triggers(const char *name, const char *value)
-{
-    struct listnode *node, *node2;
-    struct action *act;
-    struct trigger *cur_trigger;
-    bool match;
-    int name_length;
-
-    list_for_each(node, &action_list) {
-        act = node_to_item(node, struct action, alist);
-            match = !name;
-        list_for_each(node2, &act->triggers) {
-            cur_trigger = node_to_item(node2, struct trigger, nlist);
-            if (!strncmp(cur_trigger->name, "property:", strlen("property:"))) {
-                const char *test = cur_trigger->name + strlen("property:");
-                if (!match) {
-                    name_length = strlen(name);
-                    if (!strncmp(name, test, name_length) &&
-                        test[name_length] == '=' &&
-                        (!strcmp(test + name_length + 1, value) ||
-                        !strcmp(test + name_length + 1, "*"))) {
-                        match = true;
-                        continue;
-                    }
-                } else {
-                     const char* equals = strchr(test, '=');
-                     if (equals) {
-                         char prop_name[PROP_NAME_MAX + 1];
-                         char value[PROP_VALUE_MAX];
-                         int length = equals - test;
-                         if (length <= PROP_NAME_MAX) {
-                             int ret;
-                             memcpy(prop_name, test, length);
-                             prop_name[length] = 0;
-
-                             /* does the property exist, and match the trigger value? */
-                             ret = property_get(prop_name, value);
-                             if (ret > 0 && (!strcmp(equals + 1, value) ||
-                                !strcmp(equals + 1, "*"))) {
-                                 continue;
-                             }
-                         }
-                     }
-                 }
-             }
-             match = false;
-             break;
-        }
-        if (match) {
-            action_add_queue_tail(act);
-        }
+bool Parser::ParseConfig(const std::string& path) {
+    if (is_dir(path.c_str())) {
+        return ParseConfigDir(path);
     }
+    return ParseConfigFile(path);
 }
 
-void queue_all_property_triggers()
-{
-    queue_property_triggers(NULL, NULL);
-}
-
-void queue_builtin_action(int (*func)(int nargs, char **args), const char *name)
-{
-    action* act = (action*) calloc(1, sizeof(*act));
-    trigger* cur_trigger = (trigger*) calloc(1, sizeof(*cur_trigger));
-    cur_trigger->name = name;
-    list_init(&act->triggers);
-    list_add_tail(&act->triggers, &cur_trigger->nlist);
-    list_init(&act->commands);
-    list_init(&act->qlist);
-
-    command* cmd = (command*) calloc(1, sizeof(*cmd));
-    cmd->func = func;
-    cmd->args[0] = const_cast<char*>(name);
-    cmd->nargs = 1;
-    list_add_tail(&act->commands, &cmd->clist);
-
-    list_add_tail(&action_list, &act->alist);
-    action_add_queue_tail(act);
-}
-
-void action_add_queue_tail(struct action *act)
-{
-    if (list_empty(&act->qlist)) {
-        list_add_tail(&action_queue, &act->qlist);
-    }
-}
-
-struct action *action_remove_queue_head(void)
-{
-    if (list_empty(&action_queue)) {
-        return 0;
-    } else {
-        struct listnode *node = list_head(&action_queue);
-        struct action *act = node_to_item(node, struct action, qlist);
-        list_remove(node);
-        list_init(node);
-        return act;
-    }
-}
-
-int action_queue_empty()
-{
-    return list_empty(&action_queue);
-}
-
-service* make_exec_oneshot_service(int nargs, char** args) {
-    // Parse the arguments: exec [SECLABEL [UID [GID]*] --] COMMAND ARGS...
-    // SECLABEL can be a - to denote default
-    int command_arg = 1;
-    for (int i = 1; i < nargs; ++i) {
-        if (strcmp(args[i], "--") == 0) {
-            command_arg = i + 1;
-            break;
-        }
-    }
-    if (command_arg > 4 + NR_SVC_SUPP_GIDS) {
-        ERROR("exec called with too many supplementary group ids\n");
-        return NULL;
-    }
-
-    int argc = nargs - command_arg;
-    char** argv = (args + command_arg);
-    if (argc < 1) {
-        ERROR("exec called without command\n");
-        return NULL;
-    }
-
-    service* svc = (service*) calloc(1, sizeof(*svc) + sizeof(char*) * argc);
-    if (svc == NULL) {
-        ERROR("Couldn't allocate service for exec of '%s': %s", argv[0], strerror(errno));
-        return NULL;
-    }
-
-    if ((command_arg > 2) && strcmp(args[1], "-")) {
-        svc->seclabel = args[1];
-    }
-    if (command_arg > 3) {
-        svc->uid = decode_uid(args[2]);
-    }
-    if (command_arg > 4) {
-        svc->gid = decode_uid(args[3]);
-        svc->nr_supp_gids = command_arg - 1 /* -- */ - 4 /* exec SECLABEL UID GID */;
-        for (size_t i = 0; i < svc->nr_supp_gids; ++i) {
-            svc->supp_gids[i] = decode_uid(args[4 + i]);
-        }
-    }
-
-    static int exec_count; // Every service needs a unique name.
-    char* name = NULL;
-    asprintf(&name, "exec %d (%s)", exec_count++, argv[0]);
-    if (name == NULL) {
-        ERROR("Couldn't allocate name for exec service '%s'\n", argv[0]);
-        free(svc);
-        return NULL;
-    }
-    svc->name = name;
-    svc->classname = "default";
-    svc->flags = SVC_EXEC | SVC_ONESHOT;
-    svc->nargs = argc;
-    memcpy(svc->args, argv, sizeof(char*) * svc->nargs);
-    svc->args[argc] = NULL;
-    list_add_tail(&service_list, &svc->slist);
-    return svc;
-}
-
-static void *parse_service(struct parse_state *state, int nargs, char **args)
-{
-    if (nargs < 3) {
-        parse_error(state, "services must have a name and a program\n");
-        return 0;
-    }
-    if (!valid_name(args[1])) {
-        parse_error(state, "invalid service name '%s'\n", args[1]);
-        return 0;
-    }
-
-    service* svc = (service*) service_find_by_name(args[1]);
-    if (svc) {
-        parse_error(state, "ignored duplicate definition of service '%s'\n", args[1]);
-        return 0;
-    }
-
-    nargs -= 2;
-    svc = (service*) calloc(1, sizeof(*svc) + sizeof(char*) * nargs);
-    if (!svc) {
-        parse_error(state, "out of memory\n");
-        return 0;
-    }
-    svc->name = strdup(args[1]);
-    svc->classname = "default";
-    memcpy(svc->args, args + 2, sizeof(char*) * nargs);
-    trigger* cur_trigger = (trigger*) calloc(1, sizeof(*cur_trigger));
-    svc->args[nargs] = 0;
-    svc->nargs = nargs;
-    list_init(&svc->onrestart.triggers);
-    cur_trigger->name = "onrestart";
-    list_add_tail(&svc->onrestart.triggers, &cur_trigger->nlist);
-    list_init(&svc->onrestart.commands);
-    list_add_tail(&service_list, &svc->slist);
-    return svc;
-}
-
-static void parse_line_service(struct parse_state *state, int nargs, char **args)
-{
-    struct service *svc = (service*) state->context;
-    struct command *cmd;
-    int i, kw, kw_nargs;
-
-    if (nargs == 0) {
-        return;
-    }
-
-    svc->ioprio_class = IoSchedClass_NONE;
-
-    kw = lookup_keyword(args[0]);
-    switch (kw) {
-    case K_class:
-        if (nargs != 2) {
-            parse_error(state, "class option requires a classname\n");
-        } else {
-            svc->classname = args[1];
-        }
-        break;
-    case K_console:
-        svc->flags |= SVC_CONSOLE;
-        break;
-    case K_disabled:
-        svc->flags |= SVC_DISABLED;
-        svc->flags |= SVC_RC_DISABLED;
-        break;
-    case K_ioprio:
-        if (nargs != 3) {
-            parse_error(state, "ioprio optin usage: ioprio <rt|be|idle> <ioprio 0-7>\n");
-        } else {
-            svc->ioprio_pri = strtoul(args[2], 0, 8);
-
-            if (svc->ioprio_pri < 0 || svc->ioprio_pri > 7) {
-                parse_error(state, "priority value must be range 0 - 7\n");
-                break;
-            }
-
-            if (!strcmp(args[1], "rt")) {
-                svc->ioprio_class = IoSchedClass_RT;
-            } else if (!strcmp(args[1], "be")) {
-                svc->ioprio_class = IoSchedClass_BE;
-            } else if (!strcmp(args[1], "idle")) {
-                svc->ioprio_class = IoSchedClass_IDLE;
-            } else {
-                parse_error(state, "ioprio option usage: ioprio <rt|be|idle> <0-7>\n");
-            }
-        }
-        break;
-    case K_group:
-        if (nargs < 2) {
-            parse_error(state, "group option requires a group id\n");
-        } else if (nargs > NR_SVC_SUPP_GIDS + 2) {
-            parse_error(state, "group option accepts at most %d supp. groups\n",
-                        NR_SVC_SUPP_GIDS);
-        } else {
-            int n;
-            svc->gid = decode_uid(args[1]);
-            for (n = 2; n < nargs; n++) {
-                svc->supp_gids[n-2] = decode_uid(args[n]);
-            }
-            svc->nr_supp_gids = n - 2;
-        }
-        break;
-    case K_keycodes:
-        if (nargs < 2) {
-            parse_error(state, "keycodes option requires atleast one keycode\n");
-        } else {
-            svc->keycodes = (int*) malloc((nargs - 1) * sizeof(svc->keycodes[0]));
-            if (!svc->keycodes) {
-                parse_error(state, "could not allocate keycodes\n");
-            } else {
-                svc->nkeycodes = nargs - 1;
-                for (i = 1; i < nargs; i++) {
-                    svc->keycodes[i - 1] = atoi(args[i]);
-                }
-            }
-        }
-        break;
-    case K_oneshot:
-        svc->flags |= SVC_ONESHOT;
-        break;
-    case K_onrestart:
-        nargs--;
-        args++;
-        kw = lookup_keyword(args[0]);
-        if (!kw_is(kw, COMMAND)) {
-            parse_error(state, "invalid command '%s'\n", args[0]);
-            break;
-        }
-        kw_nargs = kw_nargs(kw);
-        if (nargs < kw_nargs) {
-            parse_error(state, "%s requires %d %s\n", args[0], kw_nargs - 1,
-                kw_nargs > 2 ? "arguments" : "argument");
-            break;
-        }
-
-        cmd = (command*) malloc(sizeof(*cmd) + sizeof(char*) * nargs);
-        cmd->func = kw_func(kw);
-        cmd->nargs = nargs;
-        memcpy(cmd->args, args, sizeof(char*) * nargs);
-        list_add_tail(&svc->onrestart.commands, &cmd->clist);
-        break;
-    case K_critical:
-        svc->flags |= SVC_CRITICAL;
-        break;
-    case K_setenv: { /* name value */
-        if (nargs < 3) {
-            parse_error(state, "setenv option requires name and value arguments\n");
-            break;
-        }
-        svcenvinfo* ei = (svcenvinfo*) calloc(1, sizeof(*ei));
-        if (!ei) {
-            parse_error(state, "out of memory\n");
-            break;
-        }
-        ei->name = args[1];
-        ei->value = args[2];
-        ei->next = svc->envvars;
-        svc->envvars = ei;
-        break;
-    }
-    case K_socket: {/* name type perm [ uid gid context ] */
-        if (nargs < 4) {
-            parse_error(state, "socket option requires name, type, perm arguments\n");
-            break;
-        }
-        if (strcmp(args[2],"dgram") && strcmp(args[2],"stream")
-                && strcmp(args[2],"seqpacket")) {
-            parse_error(state, "socket type must be 'dgram', 'stream' or 'seqpacket'\n");
-            break;
-        }
-        socketinfo* si = (socketinfo*) calloc(1, sizeof(*si));
-        if (!si) {
-            parse_error(state, "out of memory\n");
-            break;
-        }
-        si->name = args[1];
-        si->type = args[2];
-        si->perm = strtoul(args[3], 0, 8);
-        if (nargs > 4)
-            si->uid = decode_uid(args[4]);
-        if (nargs > 5)
-            si->gid = decode_uid(args[5]);
-        if (nargs > 6)
-            si->socketcon = args[6];
-        si->next = svc->sockets;
-        svc->sockets = si;
-        break;
-    }
-    case K_user:
-        if (nargs != 2) {
-            parse_error(state, "user option requires a user id\n");
-        } else {
-            svc->uid = decode_uid(args[1]);
-        }
-        break;
-    case K_seclabel:
-        if (nargs != 2) {
-            parse_error(state, "seclabel option requires a label string\n");
-        } else {
-            svc->seclabel = args[1];
-        }
-        break;
-    case K_writepid:
-        if (nargs < 2) {
-            parse_error(state, "writepid option requires at least one filename\n");
-            break;
-        }
-        svc->writepid_files_ = new std::vector<std::string>;
-        for (int i = 1; i < nargs; ++i) {
-            svc->writepid_files_->push_back(args[i]);
-        }
-        break;
-
-    default:
-        parse_error(state, "invalid option '%s'\n", args[0]);
-    }
-}
-
-static void *parse_action(struct parse_state *state, int nargs, char **args)
-{
-    struct trigger *cur_trigger;
-    int i;
-    if (nargs < 2) {
-        parse_error(state, "actions must have a trigger\n");
-        return 0;
-    }
-
-    action* act = (action*) calloc(1, sizeof(*act));
-    list_init(&act->triggers);
-
-    for (i = 1; i < nargs; i++) {
-        if (!(i % 2)) {
-            if (strcmp(args[i], "&&")) {
-                struct listnode *node;
-                struct listnode *node2;
-                parse_error(state, "& is the only symbol allowed to concatenate actions\n");
-                list_for_each_safe(node, node2, &act->triggers) {
-                    struct trigger *trigger = node_to_item(node, struct trigger, nlist);
-                    free(trigger);
-                }
-                free(act);
-                return 0;
-            } else
-                continue;
-        }
-        cur_trigger = (trigger*) calloc(1, sizeof(*cur_trigger));
-        cur_trigger->name = args[i];
-        list_add_tail(&act->triggers, &cur_trigger->nlist);
-    }
-
-    list_init(&act->commands);
-    list_init(&act->qlist);
-    list_add_tail(&action_list, &act->alist);
-        /* XXX add to hash */
-    return act;
-}
-
-static void parse_line_action(struct parse_state* state, int nargs, char **args)
-{
-    struct action *act = (action*) state->context;
-    int kw, n;
-
-    if (nargs == 0) {
-        return;
-    }
-
-    kw = lookup_keyword(args[0]);
-    if (!kw_is(kw, COMMAND)) {
-        parse_error(state, "invalid command '%s'\n", args[0]);
-        return;
-    }
-
-    n = kw_nargs(kw);
-    if (nargs < n) {
-        parse_error(state, "%s requires %d %s\n", args[0], n - 1,
-            n > 2 ? "arguments" : "argument");
-        return;
-    }
-    command* cmd = (command*) malloc(sizeof(*cmd) + sizeof(char*) * nargs);
-    cmd->func = kw_func(kw);
-    cmd->line = state->line;
-    cmd->filename = state->filename;
-    cmd->nargs = nargs;
-    memcpy(cmd->args, args, sizeof(char*) * nargs);
-    list_add_tail(&act->commands, &cmd->clist);
+void Parser::DumpState() const {
+    ServiceManager::GetInstance().DumpState();
+    ActionManager::GetInstance().DumpState();
 }
diff --git a/init/init_parser.h b/init/init_parser.h
index 6348607..5ed30ad 100644
--- a/init/init_parser.h
+++ b/init/init_parser.h
@@ -17,23 +17,39 @@
 #ifndef _INIT_INIT_PARSER_H_
 #define _INIT_INIT_PARSER_H_
 
-#define INIT_PARSER_MAXARGS 64
+#include <map>
+#include <string>
+#include <vector>
 
-struct action;
-struct service;
+class SectionParser {
+public:
+    virtual ~SectionParser() {
+    }
+    virtual bool ParseSection(const std::vector<std::string>& args,
+                              std::string* err) = 0;
+    virtual bool ParseLineSection(const std::vector<std::string>& args,
+                                  const std::string& filename, int line,
+                                  std::string* err) const = 0;
+    virtual void EndSection() = 0;
+    virtual void EndFile(const std::string& filename) = 0;
+};
 
-struct action *action_remove_queue_head(void);
-void action_add_queue_tail(struct action *act);
-void action_for_each_trigger(const char *trigger,
-                             void (*func)(struct action *act));
-int action_queue_empty(void);
-void queue_property_triggers(const char *name, const char *value);
-void queue_all_property_triggers();
-void queue_builtin_action(int (*func)(int nargs, char **args), const char *name);
+class Parser {
+public:
+    static Parser& GetInstance();
+    void DumpState() const;
+    bool ParseConfig(const std::string& path);
+    void AddSectionParser(const std::string& name,
+                          std::unique_ptr<SectionParser> parser);
 
-int init_parse_config_file(const char *fn);
-int expand_props(char *dst, const char *src, int len);
+private:
+    Parser();
 
-service* make_exec_oneshot_service(int argc, char** argv);
+    void ParseData(const std::string& filename, const std::string& data);
+    bool ParseConfigFile(const std::string& path);
+    bool ParseConfigDir(const std::string& path);
+
+    std::map<std::string, std::unique_ptr<SectionParser>> section_parsers_;
+};
 
 #endif
diff --git a/init/init_parser_test.cpp b/init/init_parser_test.cpp
index 170a73a..52aaa37 100644
--- a/init/init_parser_test.cpp
+++ b/init/init_parser_test.cpp
@@ -17,96 +17,97 @@
 #include "init_parser.h"
 
 #include "init.h"
+#include "service.h"
 #include "util.h"
 
 #include <errno.h>
 #include <gtest/gtest.h>
 
-TEST(init_parser, make_exec_oneshot_service_invalid_syntax) {
-    char* argv[10];
-    memset(argv, 0, sizeof(argv));
+#include <string>
+#include <vector>
 
+TEST(init_parser, make_exec_oneshot_service_invalid_syntax) {
+    ServiceManager& sm = ServiceManager::GetInstance();
+    std::vector<std::string> args;
     // Nothing.
-    ASSERT_EQ(nullptr, make_exec_oneshot_service(0, argv));
+    ASSERT_EQ(nullptr, sm.MakeExecOneshotService(args));
 
     // No arguments to 'exec'.
-    argv[0] = const_cast<char*>("exec");
-    ASSERT_EQ(nullptr, make_exec_oneshot_service(1, argv));
+    args.push_back("exec");
+    ASSERT_EQ(nullptr, sm.MakeExecOneshotService(args));
 
     // No command in "exec --".
-    argv[1] = const_cast<char*>("--");
-    ASSERT_EQ(nullptr, make_exec_oneshot_service(2, argv));
+    args.push_back("--");
+    ASSERT_EQ(nullptr, sm.MakeExecOneshotService(args));
 }
 
 TEST(init_parser, make_exec_oneshot_service_too_many_supplementary_gids) {
-    int argc = 0;
-    char* argv[4 + NR_SVC_SUPP_GIDS + 3];
-    argv[argc++] = const_cast<char*>("exec");
-    argv[argc++] = const_cast<char*>("seclabel");
-    argv[argc++] = const_cast<char*>("root"); // uid.
-    argv[argc++] = const_cast<char*>("root"); // gid.
+    ServiceManager& sm = ServiceManager::GetInstance();
+    std::vector<std::string> args;
+    args.push_back("exec");
+    args.push_back("seclabel");
+    args.push_back("root"); // uid.
+    args.push_back("root"); // gid.
     for (int i = 0; i < NR_SVC_SUPP_GIDS; ++i) {
-        argv[argc++] = const_cast<char*>("root"); // Supplementary gid.
+        args.push_back("root"); // Supplementary gid.
     }
-    argv[argc++] = const_cast<char*>("--");
-    argv[argc++] = const_cast<char*>("/system/bin/id");
-    argv[argc] = nullptr;
-    ASSERT_EQ(nullptr, make_exec_oneshot_service(argc, argv));
+    args.push_back("--");
+    args.push_back("/system/bin/id");
+    ASSERT_EQ(nullptr, sm.MakeExecOneshotService(args));
 }
 
-static void Test_make_exec_oneshot_service(bool dash_dash, bool seclabel, bool uid, bool gid, bool supplementary_gids) {
-    int argc = 0;
-    char* argv[10];
-    argv[argc++] = const_cast<char*>("exec");
+static void Test_make_exec_oneshot_service(bool dash_dash, bool seclabel, bool uid,
+                                           bool gid, bool supplementary_gids) {
+    ServiceManager& sm = ServiceManager::GetInstance();
+    std::vector<std::string> args;
+    args.push_back("exec");
     if (seclabel) {
-        argv[argc++] = const_cast<char*>("u:r:su:s0"); // seclabel
+        args.push_back("u:r:su:s0"); // seclabel
         if (uid) {
-            argv[argc++] = const_cast<char*>("log");      // uid
+            args.push_back("log");      // uid
             if (gid) {
-                argv[argc++] = const_cast<char*>("shell");     // gid
+                args.push_back("shell");     // gid
                 if (supplementary_gids) {
-                    argv[argc++] = const_cast<char*>("system");    // supplementary gid 0
-                    argv[argc++] = const_cast<char*>("adb");       // supplementary gid 1
+                    args.push_back("system");    // supplementary gid 0
+                    args.push_back("adb");       // supplementary gid 1
                 }
             }
         }
     }
     if (dash_dash) {
-        argv[argc++] = const_cast<char*>("--");
+        args.push_back("--");
     }
-    argv[argc++] = const_cast<char*>("/system/bin/toybox");
-    argv[argc++] = const_cast<char*>("id");
-    argv[argc] = nullptr;
-    service* svc = make_exec_oneshot_service(argc, argv);
+    args.push_back("/system/bin/toybox");
+    args.push_back("id");
+    Service* svc = sm.MakeExecOneshotService(args);
     ASSERT_NE(nullptr, svc);
 
     if (seclabel) {
-        ASSERT_STREQ("u:r:su:s0", svc->seclabel);
+        ASSERT_EQ("u:r:su:s0", svc->seclabel());
     } else {
-        ASSERT_EQ(nullptr, svc->seclabel);
+        ASSERT_EQ("", svc->seclabel());
     }
     if (uid) {
-        ASSERT_EQ(decode_uid("log"), svc->uid);
+        ASSERT_EQ(decode_uid("log"), svc->uid());
     } else {
-        ASSERT_EQ(0U, svc->uid);
+        ASSERT_EQ(0U, svc->uid());
     }
     if (gid) {
-        ASSERT_EQ(decode_uid("shell"), svc->gid);
+        ASSERT_EQ(decode_uid("shell"), svc->gid());
     } else {
-        ASSERT_EQ(0U, svc->gid);
+        ASSERT_EQ(0U, svc->gid());
     }
     if (supplementary_gids) {
-        ASSERT_EQ(2U, svc->nr_supp_gids);
-        ASSERT_EQ(decode_uid("system"), svc->supp_gids[0]);
-        ASSERT_EQ(decode_uid("adb"), svc->supp_gids[1]);
+        ASSERT_EQ(2U, svc->supp_gids().size());
+        ASSERT_EQ(decode_uid("system"), svc->supp_gids()[0]);
+        ASSERT_EQ(decode_uid("adb"), svc->supp_gids()[1]);
     } else {
-        ASSERT_EQ(0U, svc->nr_supp_gids);
+        ASSERT_EQ(0U, svc->supp_gids().size());
     }
 
-    ASSERT_EQ(2, svc->nargs);
-    ASSERT_EQ("/system/bin/toybox", svc->args[0]);
-    ASSERT_EQ("id", svc->args[1]);
-    ASSERT_EQ(nullptr, svc->args[2]);
+    ASSERT_EQ(static_cast<std::size_t>(2), svc->args().size());
+    ASSERT_EQ("/system/bin/toybox", svc->args()[0]);
+    ASSERT_EQ("id", svc->args()[1]);
 }
 
 TEST(init_parser, make_exec_oneshot_service_with_everything) {
diff --git a/init/keychords.cpp b/init/keychords.cpp
index 10d9573..7a7838d 100644
--- a/init/keychords.cpp
+++ b/init/keychords.cpp
@@ -26,20 +26,21 @@
 #include "init.h"
 #include "log.h"
 #include "property_service.h"
+#include "service.h"
 
 static struct input_keychord *keychords = 0;
 static int keychords_count = 0;
 static int keychords_length = 0;
 static int keychord_fd = -1;
 
-void add_service_keycodes(struct service *svc)
+void add_service_keycodes(Service* svc)
 {
     struct input_keychord *keychord;
-    int i, size;
+    size_t i, size;
 
-    if (svc->keycodes) {
+    if (!svc->keycodes().empty()) {
         /* add a new keychord to the list */
-        size = sizeof(*keychord) + svc->nkeycodes * sizeof(keychord->keycodes[0]);
+        size = sizeof(*keychord) + svc->keycodes().size() * sizeof(keychord->keycodes[0]);
         keychords = (input_keychord*) realloc(keychords, keychords_length + size);
         if (!keychords) {
             ERROR("could not allocate keychords\n");
@@ -51,11 +52,11 @@
         keychord = (struct input_keychord *)((char *)keychords + keychords_length);
         keychord->version = KEYCHORD_VERSION;
         keychord->id = keychords_count + 1;
-        keychord->count = svc->nkeycodes;
-        svc->keychord_id = keychord->id;
+        keychord->count = svc->keycodes().size();
+        svc->set_keychord_id(keychord->id);
 
-        for (i = 0; i < svc->nkeycodes; i++) {
-            keychord->keycodes[i] = svc->keycodes[i];
+        for (i = 0; i < svc->keycodes().size(); i++) {
+            keychord->keycodes[i] = svc->keycodes()[i];
         }
         keychords_count++;
         keychords_length += size;
@@ -63,24 +64,22 @@
 }
 
 static void handle_keychord() {
-    struct service *svc;
-    char adb_enabled[PROP_VALUE_MAX];
     int ret;
     __u16 id;
 
-    // Only handle keychords if adb is enabled.
-    property_get("init.svc.adbd", adb_enabled);
     ret = read(keychord_fd, &id, sizeof(id));
     if (ret != sizeof(id)) {
         ERROR("could not read keychord id\n");
         return;
     }
 
-    if (!strcmp(adb_enabled, "running")) {
-        svc = service_find_by_keychord(id);
+    // Only handle keychords if adb is enabled.
+    std::string adb_enabled = property_get("init.svc.adbd");
+    if (adb_enabled == "running") {
+        Service* svc = ServiceManager::GetInstance().FindServiceByKeychord(id);
         if (svc) {
-            INFO("Starting service %s from keychord\n", svc->name);
-            service_start(svc, NULL);
+            INFO("Starting service %s from keychord\n", svc->name().c_str());
+            svc->Start();
         } else {
             ERROR("service for keychord %d not found\n", id);
         }
@@ -88,7 +87,7 @@
 }
 
 void keychord_init() {
-    service_for_each(add_service_keycodes);
+    ServiceManager::GetInstance().ForEachService(add_service_keycodes);
 
     // Nothing to do if no services require keychords.
     if (!keychords) {
diff --git a/init/keyword_map.h b/init/keyword_map.h
new file mode 100644
index 0000000..dc2357b
--- /dev/null
+++ b/init/keyword_map.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2015 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 _INIT_KEYWORD_MAP_H_
+#define _INIT_KEYWORD_MAP_H_
+
+#include <map>
+#include <string>
+
+#include <base/stringprintf.h>
+
+template <typename Function>
+class KeywordMap {
+public:
+    using FunctionInfo = std::tuple<std::size_t, std::size_t, Function>;
+    using Map = const std::map<std::string, FunctionInfo>;
+
+    virtual ~KeywordMap() {
+    }
+
+    const Function FindFunction(const std::string& keyword,
+                                size_t num_args,
+                                std::string* err) const {
+        using android::base::StringPrintf;
+
+        auto function_info_it = map().find(keyword);
+        if (function_info_it == map().end()) {
+            *err = StringPrintf("invalid keyword '%s'", keyword.c_str());
+            return nullptr;
+        }
+
+        auto function_info = function_info_it->second;
+
+        auto min_args = std::get<0>(function_info);
+        auto max_args = std::get<1>(function_info);
+        if (min_args == max_args && num_args != min_args) {
+            *err = StringPrintf("%s requires %zu argument%s",
+                                keyword.c_str(), min_args,
+                                (min_args > 1 || min_args == 0) ? "s" : "");
+            return nullptr;
+        }
+
+        if (num_args < min_args || num_args > max_args) {
+            if (max_args == std::numeric_limits<decltype(max_args)>::max()) {
+                *err = StringPrintf("%s requires at least %zu argument%s",
+                                    keyword.c_str(), min_args,
+                                    min_args > 1 ? "s" : "");
+            } else {
+                *err = StringPrintf("%s requires between %zu and %zu arguments",
+                                    keyword.c_str(), min_args, max_args);
+            }
+            return nullptr;
+        }
+
+        return std::get<Function>(function_info);
+    }
+
+private:
+//Map of keyword ->
+//(minimum number of arguments, maximum number of arguments, function pointer)
+    virtual Map& map() const = 0;
+};
+
+#endif
diff --git a/init/keywords.h b/init/keywords.h
deleted file mode 100644
index 0910f60..0000000
--- a/init/keywords.h
+++ /dev/null
@@ -1,109 +0,0 @@
-#ifndef KEYWORD
-int do_bootchart_init(int nargs, char **args);
-int do_class_start(int nargs, char **args);
-int do_class_stop(int nargs, char **args);
-int do_class_reset(int nargs, char **args);
-int do_domainname(int nargs, char **args);
-int do_enable(int nargs, char **args);
-int do_exec(int nargs, char **args);
-int do_export(int nargs, char **args);
-int do_hostname(int nargs, char **args);
-int do_ifup(int nargs, char **args);
-int do_insmod(int nargs, char **args);
-int do_installkey(int nargs, char **args);
-int do_mkdir(int nargs, char **args);
-int do_mount_all(int nargs, char **args);
-int do_mount(int nargs, char **args);
-int do_powerctl(int nargs, char **args);
-int do_restart(int nargs, char **args);
-int do_restorecon(int nargs, char **args);
-int do_restorecon_recursive(int nargs, char **args);
-int do_rm(int nargs, char **args);
-int do_rmdir(int nargs, char **args);
-int do_setprop(int nargs, char **args);
-int do_setrlimit(int nargs, char **args);
-int do_setusercryptopolicies(int nargs, char **args);
-int do_start(int nargs, char **args);
-int do_stop(int nargs, char **args);
-int do_swapon_all(int nargs, char **args);
-int do_trigger(int nargs, char **args);
-int do_symlink(int nargs, char **args);
-int do_sysclktz(int nargs, char **args);
-int do_write(int nargs, char **args);
-int do_copy(int nargs, char **args);
-int do_chown(int nargs, char **args);
-int do_chmod(int nargs, char **args);
-int do_loglevel(int nargs, char **args);
-int do_load_persist_props(int nargs, char **args);
-int do_load_system_props(int nargs, char **args);
-int do_verity_load_state(int nargs, char **args);
-int do_verity_update_state(int nargs, char **args);
-int do_wait(int nargs, char **args);
-#define __MAKE_KEYWORD_ENUM__
-#define KEYWORD(symbol, flags, nargs, func) K_##symbol,
-enum {
-    K_UNKNOWN,
-#endif
-    KEYWORD(bootchart_init,        COMMAND, 0, do_bootchart_init)
-    KEYWORD(chmod,       COMMAND, 2, do_chmod)
-    KEYWORD(chown,       COMMAND, 2, do_chown)
-    KEYWORD(class,       OPTION,  0, 0)
-    KEYWORD(class_reset, COMMAND, 1, do_class_reset)
-    KEYWORD(class_start, COMMAND, 1, do_class_start)
-    KEYWORD(class_stop,  COMMAND, 1, do_class_stop)
-    KEYWORD(console,     OPTION,  0, 0)
-    KEYWORD(copy,        COMMAND, 2, do_copy)
-    KEYWORD(critical,    OPTION,  0, 0)
-    KEYWORD(disabled,    OPTION,  0, 0)
-    KEYWORD(domainname,  COMMAND, 1, do_domainname)
-    KEYWORD(enable,      COMMAND, 1, do_enable)
-    KEYWORD(exec,        COMMAND, 1, do_exec)
-    KEYWORD(export,      COMMAND, 2, do_export)
-    KEYWORD(group,       OPTION,  0, 0)
-    KEYWORD(hostname,    COMMAND, 1, do_hostname)
-    KEYWORD(ifup,        COMMAND, 1, do_ifup)
-    KEYWORD(import,      SECTION, 1, 0)
-    KEYWORD(insmod,      COMMAND, 1, do_insmod)
-    KEYWORD(installkey,  COMMAND, 1, do_installkey)
-    KEYWORD(ioprio,      OPTION,  0, 0)
-    KEYWORD(keycodes,    OPTION,  0, 0)
-    KEYWORD(load_system_props,     COMMAND, 0, do_load_system_props)
-    KEYWORD(load_persist_props,    COMMAND, 0, do_load_persist_props)
-    KEYWORD(loglevel,    COMMAND, 1, do_loglevel)
-    KEYWORD(mkdir,       COMMAND, 1, do_mkdir)
-    KEYWORD(mount_all,   COMMAND, 1, do_mount_all)
-    KEYWORD(mount,       COMMAND, 3, do_mount)
-    KEYWORD(oneshot,     OPTION,  0, 0)
-    KEYWORD(onrestart,   OPTION,  0, 0)
-    KEYWORD(on,          SECTION, 0, 0)
-    KEYWORD(powerctl,    COMMAND, 1, do_powerctl)
-    KEYWORD(restart,     COMMAND, 1, do_restart)
-    KEYWORD(restorecon,  COMMAND, 1, do_restorecon)
-    KEYWORD(restorecon_recursive,  COMMAND, 1, do_restorecon_recursive)
-    KEYWORD(rm,          COMMAND, 1, do_rm)
-    KEYWORD(rmdir,       COMMAND, 1, do_rmdir)
-    KEYWORD(seclabel,    OPTION,  0, 0)
-    KEYWORD(service,     SECTION, 0, 0)
-    KEYWORD(setenv,      OPTION,  2, 0)
-    KEYWORD(setprop,     COMMAND, 2, do_setprop)
-    KEYWORD(setrlimit,   COMMAND, 3, do_setrlimit)
-    KEYWORD(setusercryptopolicies,   COMMAND, 1, do_setusercryptopolicies)
-    KEYWORD(socket,      OPTION,  0, 0)
-    KEYWORD(start,       COMMAND, 1, do_start)
-    KEYWORD(stop,        COMMAND, 1, do_stop)
-    KEYWORD(swapon_all,  COMMAND, 1, do_swapon_all)
-    KEYWORD(symlink,     COMMAND, 1, do_symlink)
-    KEYWORD(sysclktz,    COMMAND, 1, do_sysclktz)
-    KEYWORD(trigger,     COMMAND, 1, do_trigger)
-    KEYWORD(user,        OPTION,  0, 0)
-    KEYWORD(verity_load_state,      COMMAND, 0, do_verity_load_state)
-    KEYWORD(verity_update_state,    COMMAND, 0, do_verity_update_state)
-    KEYWORD(wait,        COMMAND, 1, do_wait)
-    KEYWORD(write,       COMMAND, 2, do_write)
-    KEYWORD(writepid,    OPTION,  0, 0)
-#ifdef __MAKE_KEYWORD_ENUM__
-    KEYWORD_COUNT,
-};
-#undef __MAKE_KEYWORD_ENUM__
-#undef KEYWORD
-#endif
diff --git a/init/log.cpp b/init/log.cpp
index d32f2da..eb5ec42 100644
--- a/init/log.cpp
+++ b/init/log.cpp
@@ -14,30 +14,35 @@
  * limitations under the License.
  */
 
+#include "log.h"
+
 #include <stdlib.h>
 #include <string.h>
 #include <sys/uio.h>
 
 #include <selinux/selinux.h>
 
-#include "log.h"
+#include <base/stringprintf.h>
 
 static void init_klog_vwrite(int level, const char* fmt, va_list ap) {
     static const char* tag = basename(getprogname());
 
-    char prefix[64];
-    snprintf(prefix, sizeof(prefix), "<%d>%s: ", level, tag);
+    // The kernel's printk buffer is only 1024 bytes.
+    // TODO: should we automatically break up long lines into multiple lines?
+    // Or we could log but with something like "..." at the end?
+    char buf[1024];
+    size_t prefix_size = snprintf(buf, sizeof(buf), "<%d>%s: ", level, tag);
+    size_t msg_size = vsnprintf(buf + prefix_size, sizeof(buf) - prefix_size, fmt, ap);
+    if (msg_size >= sizeof(buf) - prefix_size) {
+        msg_size = snprintf(buf + prefix_size, sizeof(buf) - prefix_size,
+                            "(%zu-byte message too long for printk)\n", msg_size);
+    }
 
-    char msg[512];
-    vsnprintf(msg, sizeof(msg), fmt, ap);
+    iovec iov[1];
+    iov[0].iov_base = buf;
+    iov[0].iov_len = prefix_size + msg_size;
 
-    iovec iov[2];
-    iov[0].iov_base = prefix;
-    iov[0].iov_len = strlen(prefix);
-    iov[1].iov_base = msg;
-    iov[1].iov_len = strlen(msg);
-
-    klog_writev(level, iov, 2);
+    klog_writev(level, iov, 1);
 }
 
 void init_klog_write(int level, const char* fmt, ...) {
diff --git a/init/log.h b/init/log.h
index b804d1f..c5c30af 100644
--- a/init/log.h
+++ b/init/log.h
@@ -20,8 +20,11 @@
 #include <cutils/klog.h>
 
 #define ERROR(x...)   init_klog_write(KLOG_ERROR_LEVEL, x)
+#define WARNING(x...) init_klog_write(KLOG_WARNING_LEVEL, x)
 #define NOTICE(x...)  init_klog_write(KLOG_NOTICE_LEVEL, x)
 #define INFO(x...)    init_klog_write(KLOG_INFO_LEVEL, x)
+#define DEBUG(x...)   init_klog_write(KLOG_DEBUG_LEVEL, x)
+#define VERBOSE(x...) init_klog_write(KLOG_DEBUG_LEVEL, x)
 
 void init_klog_write(int level, const char* fmt, ...) __printflike(2, 3);
 int selinux_klog_callback(int level, const char* fmt, ...) __printflike(2, 3);
diff --git a/init/parser/tokenizer.cpp b/init/parser/tokenizer.cpp
new file mode 100644
index 0000000..340e0d9
--- /dev/null
+++ b/init/parser/tokenizer.cpp
@@ -0,0 +1,129 @@
+// Copyright (C) 2015 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 "tokenizer.h"
+
+namespace init {
+
+Tokenizer::Tokenizer(const std::string& data)
+    : data_(data), eof_(false), pos_(0), tok_start_(0) {
+  current_.type = TOK_START;
+
+  if (data.size() > 0) {
+    cur_char_ = data[0];
+  } else {
+    eof_ = true;
+    cur_char_ = '\0';
+  }
+}
+
+Tokenizer::~Tokenizer() {}
+
+const Tokenizer::Token& Tokenizer::current() {
+  return current_;
+}
+
+bool Tokenizer::Next() {
+  while (!eof_) {
+    AdvWhiteSpace();
+
+    // Check for comments.
+    if (cur_char_ == '#') {
+      AdvChar();
+      // Skip rest of line
+      while (!eof_ && cur_char_ != '\n') {
+        AdvChar();
+      }
+    }
+
+    if (eof_) {
+      break;
+    }
+
+    if (cur_char_ == '\0') {
+      AdvChar();
+    } else if (cur_char_ == '\n') {
+      current_.type = TOK_NEWLINE;
+      current_.text.clear();
+      AdvChar();
+      return true;
+    } else if (cur_char_ == '\\') {
+      AdvChar();  // skip backslash
+      // This is line continuation so
+      // do not generated TOK_NEWLINE at
+      // the next \n.
+      AdvUntil('\n');
+      AdvChar();  // skip \n
+    } else if (cur_char_ == '\"') {
+      AdvChar();
+      StartText();
+      // Grab everything until the next quote.
+      AdvUntil('\"');
+      EndText();
+      AdvChar();  // skip quote.
+      return true;
+    } else {
+      StartText();
+      AdvText();
+      EndText();
+      return true;
+    }
+  }
+  current_.type = TOK_END;
+  current_.text.clear();
+  return false;
+}
+
+void Tokenizer::AdvChar() {
+  pos_++;
+  if (pos_ < data_.size()) {
+    cur_char_ = data_[pos_];
+  } else {
+    eof_ = true;
+    cur_char_ = '\0';
+  }
+}
+
+void Tokenizer::AdvWhiteSpace() {
+  while (cur_char_ == '\t' || cur_char_ == '\r' || cur_char_ == ' ') {
+    AdvChar();
+  }
+}
+
+void Tokenizer::AdvUntil(char x) {
+  while (!eof_ && cur_char_ != x) {
+    AdvChar();
+  }
+}
+
+void Tokenizer::AdvText() {
+  while (cur_char_ != '\t' && cur_char_ != '\r' && cur_char_ != '\0' &&
+         cur_char_ != ' ' && cur_char_ != '\n' && cur_char_ != '#') {
+    AdvChar();
+  }
+}
+
+void Tokenizer::StartText() {
+  current_.text.clear();
+  tok_start_ = pos_;
+  current_.type = TOK_TEXT;
+}
+
+void Tokenizer::EndText() {
+  if (pos_ != tok_start_) {
+    current_.text.append(data_, tok_start_, pos_ - tok_start_);
+  }
+}
+
+}  // namespace init
\ No newline at end of file
diff --git a/init/parser/tokenizer.h b/init/parser/tokenizer.h
new file mode 100644
index 0000000..8312a08
--- /dev/null
+++ b/init/parser/tokenizer.h
@@ -0,0 +1,74 @@
+// Copyright (C) 2015 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 _INIT_PARSER_TOKENIZER_H
+#define _INIT_PARSER_TOKENIZER_H
+
+#include <string>
+
+namespace init {
+
+// Used to tokenize a std::string.
+// Call Next() to advance through each token until it returns false,
+// indicating there are no more tokens left in the string.
+// The current token can be accessed with current(), which returns
+// a Token.
+// Supported tokens are:
+// TOK_START - Next() has yet to be called
+// TOK_END - At the end of string
+// TOK_NEWLINE - The end of a line denoted by \n.
+// TOK_TEXT - A word.
+// Comments are denoted with '#' and the tokenizer will ignore
+// the rest of the line.
+// Double quotes can be used to insert whitespace into words.
+// A backslash at the end of a line denotes continuation and
+// a TOK_NEWLINE will not be generated for that line.
+class Tokenizer {
+ public:
+  Tokenizer(const std::string& data);
+  ~Tokenizer();
+
+  enum TokenType { TOK_START, TOK_END, TOK_NEWLINE, TOK_TEXT };
+  struct Token {
+    TokenType type;
+    std::string text;
+  };
+
+  // Returns the curret token.
+  const Token& current();
+
+  // Move to the next token, returns false at the end of input.
+  bool Next();
+
+ private:
+  void GetData();
+  void AdvChar();
+  void AdvText();
+  void AdvUntil(char x);
+  void AdvWhiteSpace();
+  void StartText();
+  void EndText();
+
+  const std::string& data_;
+  Token current_;
+
+  bool eof_;
+  size_t pos_;
+  char cur_char_;
+  size_t tok_start_;
+};
+
+}  // namespace init
+
+#endif
diff --git a/init/parser/tokenizer_test.cpp b/init/parser/tokenizer_test.cpp
new file mode 100644
index 0000000..c4a48df
--- /dev/null
+++ b/init/parser/tokenizer_test.cpp
@@ -0,0 +1,230 @@
+// Copyright (C) 2015 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 "tokenizer.h"
+
+#include <errno.h>
+#include <gtest/gtest.h>
+
+#include <string>
+
+namespace init {
+
+#define SETUP_TEST(test_data)  \
+  std::string data(test_data); \
+  Tokenizer tokenizer(data);   \
+  ASSERT_EQ(Tokenizer::TOK_START, tokenizer.current().type)
+
+#define ASSERT_TEXT_TOKEN(test_text)              \
+  ASSERT_TRUE(tokenizer.Next());                  \
+  ASSERT_EQ(test_text, tokenizer.current().text); \
+  ASSERT_EQ(Tokenizer::TOK_TEXT, tokenizer.current().type)
+
+#define ASSERT_NEWLINE_TOKEN()   \
+  ASSERT_TRUE(tokenizer.Next()); \
+  ASSERT_EQ(Tokenizer::TOK_NEWLINE, tokenizer.current().type)
+
+TEST(Tokenizer, Empty) {
+  SETUP_TEST("");
+  ASSERT_FALSE(tokenizer.Next());
+}
+
+TEST(Tokenizer, Simple) {
+  SETUP_TEST("test");
+  ASSERT_TEXT_TOKEN("test");
+  ASSERT_FALSE(tokenizer.Next());
+}
+
+TEST(Tokenizer, LeadingWhiteSpace) {
+  SETUP_TEST(" \t  \r  test");
+  ASSERT_TEXT_TOKEN("test");
+  ASSERT_FALSE(tokenizer.Next());
+}
+
+TEST(Tokenizer, TrailingWhiteSpace) {
+  SETUP_TEST("test \t  \r  ");
+  ASSERT_TEXT_TOKEN("test");
+  ASSERT_FALSE(tokenizer.Next());
+}
+
+TEST(Tokenizer, WhiteSpace) {
+  SETUP_TEST(" \t  \r  test \t  \r  ");
+  ASSERT_TEXT_TOKEN("test");
+
+  ASSERT_FALSE(tokenizer.Next());
+}
+
+TEST(Tokenizer, TwoTokens) {
+  SETUP_TEST("  foo   bar ");
+  ASSERT_TEXT_TOKEN("foo");
+  ASSERT_TEXT_TOKEN("bar");
+
+  ASSERT_FALSE(tokenizer.Next());
+}
+
+TEST(Tokenizer, MultiToken) {
+  SETUP_TEST("one two three four five");
+  ASSERT_TEXT_TOKEN("one");
+  ASSERT_TEXT_TOKEN("two");
+  ASSERT_TEXT_TOKEN("three");
+  ASSERT_TEXT_TOKEN("four");
+  ASSERT_TEXT_TOKEN("five");
+
+  ASSERT_FALSE(tokenizer.Next());
+}
+
+TEST(Tokenizer, NewLine) {
+  SETUP_TEST("\n");
+  ASSERT_NEWLINE_TOKEN();
+
+  ASSERT_FALSE(tokenizer.Next());
+}
+
+TEST(Tokenizer, TextNewLine) {
+  SETUP_TEST("test\n");
+  ASSERT_TEXT_TOKEN("test");
+  ASSERT_NEWLINE_TOKEN();
+
+  ASSERT_FALSE(tokenizer.Next());
+}
+
+TEST(Tokenizer, MultiTextNewLine) {
+  SETUP_TEST("one\ntwo\nthree\n");
+  ASSERT_TEXT_TOKEN("one");
+  ASSERT_NEWLINE_TOKEN();
+  ASSERT_TEXT_TOKEN("two");
+  ASSERT_NEWLINE_TOKEN();
+  ASSERT_TEXT_TOKEN("three");
+  ASSERT_NEWLINE_TOKEN();
+
+  ASSERT_FALSE(tokenizer.Next());
+}
+
+TEST(Tokenizer, MultiTextNewLineNoLineEnding) {
+  SETUP_TEST("one\ntwo\nthree");
+  ASSERT_TEXT_TOKEN("one");
+  ASSERT_NEWLINE_TOKEN();
+  ASSERT_TEXT_TOKEN("two");
+  ASSERT_NEWLINE_TOKEN();
+  ASSERT_TEXT_TOKEN("three");
+
+  ASSERT_FALSE(tokenizer.Next());
+}
+
+TEST(Tokenizer, Comment) {
+  SETUP_TEST("#test");
+  ASSERT_FALSE(tokenizer.Next());
+}
+
+TEST(Tokenizer, CommentWhiteSpace) {
+  SETUP_TEST(" \t  \r  #test \t  \r  ");
+  ASSERT_FALSE(tokenizer.Next());
+}
+
+TEST(Tokenizer, CommentNewLine) {
+  SETUP_TEST(" #test   \n");
+  ASSERT_NEWLINE_TOKEN();
+  ASSERT_FALSE(tokenizer.Next());
+}
+
+TEST(Tokenizer, CommentTwoNewLine) {
+  SETUP_TEST(" #test   \n#test");
+  ASSERT_NEWLINE_TOKEN();
+  ASSERT_FALSE(tokenizer.Next());
+}
+
+TEST(Tokenizer, CommentWithText) {
+  SETUP_TEST("foo bar #test");
+  ASSERT_TEXT_TOKEN("foo");
+  ASSERT_TEXT_TOKEN("bar");
+  ASSERT_FALSE(tokenizer.Next());
+}
+
+TEST(Tokenizer, CommentWithTextNoSpace) {
+  SETUP_TEST("foo bar#test");
+  ASSERT_TEXT_TOKEN("foo");
+  ASSERT_TEXT_TOKEN("bar");
+  ASSERT_FALSE(tokenizer.Next());
+}
+
+TEST(Tokenizer, CommentWithTextLineFeed) {
+  SETUP_TEST("foo bar #test\n");
+  ASSERT_TEXT_TOKEN("foo");
+  ASSERT_TEXT_TOKEN("bar");
+  ASSERT_NEWLINE_TOKEN();
+  ASSERT_FALSE(tokenizer.Next());
+}
+
+TEST(Tokenizer, CommentWithMultiTextLineFeed) {
+  SETUP_TEST("#blah\nfoo bar #test\n#blah");
+  ASSERT_NEWLINE_TOKEN();
+  ASSERT_TEXT_TOKEN("foo");
+  ASSERT_TEXT_TOKEN("bar");
+  ASSERT_NEWLINE_TOKEN();
+  ASSERT_FALSE(tokenizer.Next());
+}
+
+TEST(Tokenizer, SimpleEscaped) {
+  SETUP_TEST("fo\\no bar");
+  ASSERT_TEXT_TOKEN("fo\\no");
+  ASSERT_TEXT_TOKEN("bar");
+  ASSERT_FALSE(tokenizer.Next());
+}
+
+TEST(Tokenizer, EscapedLineContNoLineFeed) {
+  SETUP_TEST("fo\\no bar \\ hello");
+  ASSERT_TEXT_TOKEN("fo\\no");
+  ASSERT_TEXT_TOKEN("bar");
+  ASSERT_FALSE(tokenizer.Next());
+}
+
+TEST(Tokenizer, EscapedLineContLineFeed) {
+  SETUP_TEST("fo\\no bar \\ hello\n");
+  ASSERT_TEXT_TOKEN("fo\\no");
+  ASSERT_TEXT_TOKEN("bar");
+  ASSERT_FALSE(tokenizer.Next());
+}
+
+TEST(Tokenizer, EscapedLineCont) {
+  SETUP_TEST("fo\\no bar \\\ntest");
+  ASSERT_TEXT_TOKEN("fo\\no");
+  ASSERT_TEXT_TOKEN("bar");
+  ASSERT_TEXT_TOKEN("test");
+  ASSERT_FALSE(tokenizer.Next());
+}
+
+TEST(Tokenizer, EscapedLineContWithBadChars) {
+  SETUP_TEST("fo\\no bar \\bad bad bad\ntest");
+  ASSERT_TEXT_TOKEN("fo\\no");
+  ASSERT_TEXT_TOKEN("bar");
+  ASSERT_TEXT_TOKEN("test");
+  ASSERT_FALSE(tokenizer.Next());
+}
+
+TEST(Tokenizer, SimpleQuotes) {
+  SETUP_TEST("foo \"single token\" bar");
+  ASSERT_TEXT_TOKEN("foo");
+  ASSERT_TEXT_TOKEN("single token");
+  ASSERT_TEXT_TOKEN("bar");
+  ASSERT_FALSE(tokenizer.Next());
+}
+
+TEST(Tokenizer, BadQuotes) {
+  SETUP_TEST("foo \"single token");
+  ASSERT_TEXT_TOKEN("foo");
+  ASSERT_TEXT_TOKEN("single token");
+  ASSERT_FALSE(tokenizer.Next());
+}
+
+}  // namespace init
diff --git a/init/perfboot.py b/init/perfboot.py
new file mode 100755
index 0000000..2a17ab6
--- /dev/null
+++ b/init/perfboot.py
@@ -0,0 +1,461 @@
+#!/usr/bin/env python
+# Copyright (C) 2015 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.
+"""Record the event logs during boot and output them to a file.
+
+This script repeats the record of each event log during Android boot specified
+times. By default, interval between measurements is adjusted in such a way that
+CPUs are cooled down sufficiently to avoid boot time slowdown caused by CPU
+thermal throttling. The result is output in a tab-separated value format.
+
+Examples:
+
+Repeat measurements 10 times. Interval between iterations is adjusted based on
+CPU temperature of the device.
+
+$ ./perfboot.py --iterations=10
+
+Repeat measurements 20 times. 60 seconds interval is taken between each
+iteration.
+
+$ ./perfboot.py --iterations=20 --interval=60
+
+Repeat measurements 20 times, show verbose output, output the result to
+data.tsv, and read event tags from eventtags.txt.
+
+$ ./perfboot.py --iterations=30 -v --output=data.tsv --tags=eventtags.txt
+"""
+
+import argparse
+import atexit
+import cStringIO
+import glob
+import inspect
+import logging
+import math
+import os
+import re
+import subprocess
+import sys
+import threading
+import time
+
+sys.path.append(os.path.dirname(os.path.dirname(__file__)))
+import adb
+
+# The default event tags to record.
+_DEFAULT_EVENT_TAGS = [
+    'boot_progress_start',
+    'boot_progress_preload_start',
+    'boot_progress_preload_end',
+    'boot_progress_system_run',
+    'boot_progress_pms_start',
+    'boot_progress_pms_system_scan_start',
+    'boot_progress_pms_data_scan_start',
+    'boot_progress_pms_scan_end',
+    'boot_progress_pms_ready',
+    'boot_progress_ams_ready',
+    'boot_progress_enable_screen',
+    'sf_stop_bootanim',
+    'wm_boot_animation_done',
+]
+
+
+class IntervalAdjuster(object):
+    """A helper class to take suffficient interval between iterations."""
+
+    # CPU temperature values per product used to decide interval
+    _CPU_COOL_DOWN_THRESHOLDS = {
+        'flo': 40,
+        'flounder': 40000,
+        'razor': 40,
+        'volantis': 40000,
+    }
+    # The interval between CPU temperature checks
+    _CPU_COOL_DOWN_WAIT_INTERVAL = 10
+    # The wait time used when the value of _CPU_COOL_DOWN_THRESHOLDS for
+    # the product is not defined.
+    _CPU_COOL_DOWN_WAIT_TIME_DEFAULT = 120
+
+    def __init__(self, interval, device):
+        self._interval = interval
+        self._device = device
+        self._temp_paths = device.shell(
+            ['ls', '/sys/class/thermal/thermal_zone*/temp']).splitlines()
+        self._product = device.get_prop('ro.build.product')
+        self._waited = False
+
+    def wait(self):
+        """Waits certain amount of time for CPUs cool-down."""
+        if self._interval is None:
+            self._wait_cpu_cool_down(self._product, self._temp_paths)
+        else:
+            if self._waited:
+                print 'Waiting for %d seconds' % self._interval
+                time.sleep(self._interval)
+        self._waited = True
+
+    def _get_cpu_temp(self, threshold):
+        max_temp = 0
+        for temp_path in self._temp_paths:
+            temp = int(self._device.shell(['cat', temp_path]).rstrip())
+            max_temp = max(max_temp, temp)
+            if temp >= threshold:
+                return temp
+        return max_temp
+
+    def _wait_cpu_cool_down(self, product, temp_paths):
+        threshold = IntervalAdjuster._CPU_COOL_DOWN_THRESHOLDS.get(
+            self._product)
+        if threshold is None:
+            print 'No CPU temperature threshold is set for ' + self._product
+            print ('Just wait %d seconds' %
+                   IntervalAdjuster._CPU_COOL_DOWN_WAIT_TIME_DEFAULT)
+            time.sleep(IntervalAdjuster._CPU_COOL_DOWN_WAIT_TIME_DEFAULT)
+            return
+        while True:
+            temp = self._get_cpu_temp(threshold)
+            if temp < threshold:
+                logging.info('Current CPU temperature %s' % temp)
+                return
+            print 'Waiting until CPU temperature (%d) falls below %d' % (
+                temp, threshold)
+            time.sleep(IntervalAdjuster._CPU_COOL_DOWN_WAIT_INTERVAL)
+
+
+class WatchdogTimer(object):
+    """A timer that makes is_timedout() return true in |timeout| seconds."""
+    def __init__(self, timeout):
+        self._timedout = False
+
+        def notify_timeout():
+            self._timedout = True
+        self._timer = threading.Timer(timeout, notify_timeout)
+        self._timer.daemon = True
+        self._timer.start()
+
+    def is_timedout(self):
+        return self._timedout
+
+    def cancel(self):
+        self._timer.cancel()
+
+
+def readlines_unbuffered(proc):
+    """Read lines from |proc|'s standard out without buffering."""
+    while True:
+        buf = []
+        c = proc.stdout.read(1)
+        if c == '' and proc.poll() is not None:
+            break
+        while c != '\n':
+            if c == '' and proc.poll() is not None:
+                break
+            buf.append(c)
+            c = proc.stdout.read(1)
+        yield ''.join(buf)
+
+
+def disable_dropbox(device):
+    """Removes the files created by Dropbox and avoids creating the files."""
+    device.root()
+    device.wait()
+    device.shell(['rm', '-rf', '/system/data/dropbox'])
+    original_dropbox_max_files = device.shell(
+        ['settings', 'get', 'global', 'dropbox_max_files']).rstrip()
+    device.shell(['settings', 'put', 'global', 'dropbox_max_files', '0'])
+    return original_dropbox_max_files
+
+
+def restore_dropbox(device, original_dropbox_max_files):
+    """Restores the dropbox_max_files setting."""
+    device.root()
+    device.wait()
+    if original_dropbox_max_files == 'null':
+        device.shell(['settings', 'delete', 'global', 'dropbox_max_files'])
+    else:
+        device.shell(['settings', 'put', 'global', 'dropbox_max_files',
+                      original_dropbox_max_files])
+
+
+def init_perf(device, output, record_list, tags):
+    device.wait()
+    build_type = device.get_prop('ro.build.type')
+    original_dropbox_max_files = None
+    if build_type != 'user':
+        # Workaround for Dropbox issue (http://b/20890386).
+        original_dropbox_max_files = disable_dropbox(device)
+
+    def cleanup():
+        try:
+            if record_list:
+                print_summary(record_list, tags[-1])
+                output_results(output, record_list, tags)
+            if original_dropbox_max_files is not None:
+                restore_dropbox(device, original_dropbox_max_files)
+        except (subprocess.CalledProcessError, RuntimeError):
+            pass
+    atexit.register(cleanup)
+
+
+def check_dm_verity_settings(device):
+    device.wait()
+    for partition in ['system', 'vendor']:
+        verity_mode = device.get_prop('partition.%s.verified' % partition)
+        if verity_mode is None:
+            logging.warning('dm-verity is not enabled for /%s. Did you run '
+                            'adb disable-verity? That may skew the result.',
+                            partition)
+
+
+def read_event_tags(tags_file):
+    """Reads event tags from |tags_file|."""
+    if not tags_file:
+        return _DEFAULT_EVENT_TAGS
+    tags = []
+    with open(tags_file) as f:
+        for line in f:
+            if '#' in line:
+                line = line[:line.find('#')]
+            line = line.strip()
+            if line:
+                tags.append(line)
+    return tags
+
+
+def make_event_tags_re(tags):
+    """Makes a regular expression object that matches event logs of |tags|."""
+    return re.compile(r'(?P<pid>[0-9]+) +[0-9]+ I (?P<tag>%s): (?P<time>\d+)' %
+                      '|'.join(tags))
+
+
+def filter_event_tags(tags, device):
+    """Drop unknown tags not listed in device's event-log-tags file."""
+    device.wait()
+    supported_tags = set()
+    for l in device.shell(['cat', '/system/etc/event-log-tags']).splitlines():
+        tokens = l.split(' ')
+        if len(tokens) >= 2:
+            supported_tags.add(tokens[1])
+    filtered = []
+    for tag in tags:
+        if tag in supported_tags:
+            filtered.append(tag)
+        else:
+            logging.warning('Unknown tag \'%s\'. Ignoring...', tag)
+    return filtered
+
+
+def get_values(record, tag):
+    """Gets values that matches |tag| from |record|."""
+    keys = [key for key in record.keys() if key[0] == tag]
+    return [record[k] for k in sorted(keys)]
+
+
+def get_last_value(record, tag):
+    """Gets the last value that matches |tag| from |record|."""
+    values = get_values(record, tag)
+    if not values:
+        return 0
+    return values[-1]
+
+
+def output_results(filename, record_list, tags):
+    """Outputs |record_list| into |filename| in a TSV format."""
+    # First, count the number of the values of each tag.
+    # This is for dealing with events that occur multiple times.
+    # For instance, boot_progress_preload_start and boot_progress_preload_end
+    # are recorded twice on 64-bit system. One is for 64-bit zygote process
+    # and the other is for 32-bit zygote process.
+    values_counter = {}
+    for record in record_list:
+        for tag in tags:
+            # Some record might lack values for some tags due to unanticipated
+            # problems (e.g. timeout), so take the maximum count among all the
+            # record.
+            values_counter[tag] = max(values_counter.get(tag, 1),
+                                      len(get_values(record, tag)))
+
+    # Then creates labels for the data. If there are multiple values for one
+    # tag, labels for these values are numbered except the first one as
+    # follows:
+    #
+    # event_tag event_tag2 event_tag3
+    #
+    # The corresponding values are sorted in an ascending order of PID.
+    labels = []
+    for tag in tags:
+        for i in range(1, values_counter[tag] + 1):
+            labels.append('%s%s' % (tag, '' if i == 1 else str(i)))
+
+    # Finally write the data into the file.
+    with open(filename, 'w') as f:
+        f.write('\t'.join(labels) + '\n')
+        for record in record_list:
+            line = cStringIO.StringIO()
+            invalid_line = False
+            for i, tag in enumerate(tags):
+                if i != 0:
+                    line.write('\t')
+                values = get_values(record, tag)
+                if len(values) < values_counter[tag]:
+                    invalid_line = True
+                    # Fill invalid record with 0
+                    values += [0] * (values_counter[tag] - len(values))
+                line.write('\t'.join(str(t) for t in values))
+            if invalid_line:
+                logging.error('Invalid record found: ' + line.getvalue())
+            line.write('\n')
+            f.write(line.getvalue())
+    print 'Wrote: ' + filename
+
+
+def median(data):
+    """Calculates the median value from |data|."""
+    data = sorted(data)
+    n = len(data)
+    if n % 2 == 1:
+        return data[n / 2]
+    else:
+        n2 = n / 2
+        return (data[n2 - 1] + data[n2]) / 2.0
+
+
+def mean(data):
+    """Calculates the mean value from |data|."""
+    return float(sum(data)) / len(data)
+
+
+def stddev(data):
+    """Calculates the standard deviation value from |value|."""
+    m = mean(data)
+    return math.sqrt(sum((x - m) ** 2 for x in data) / len(data))
+
+
+def print_summary(record_list, end_tag):
+    """Prints the summary of |record_list|."""
+    # Filter out invalid data.
+    end_times = [get_last_value(record, end_tag) for record in record_list
+                 if get_last_value(record, end_tag) != 0]
+    print 'mean: ', mean(end_times)
+    print 'median:', median(end_times)
+    print 'standard deviation:', stddev(end_times)
+
+
+def do_iteration(device, interval_adjuster, event_tags_re, end_tag):
+    """Measures the boot time once."""
+    device.wait()
+    interval_adjuster.wait()
+    device.reboot()
+    print 'Rebooted the device'
+    record = {}
+    booted = False
+    while not booted:
+        device.wait()
+        # Stop the iteration if it does not finish within 120 seconds.
+        timeout = 120
+        t = WatchdogTimer(timeout)
+        p = subprocess.Popen(
+                ['adb', 'logcat', '-b', 'events', '-v', 'threadtime'],
+                stdout=subprocess.PIPE)
+        for line in readlines_unbuffered(p):
+            if t.is_timedout():
+                print '*** Timed out ***'
+                return record
+            m = event_tags_re.search(line)
+            if not m:
+                continue
+            tag = m.group('tag')
+            event_time = int(m.group('time'))
+            pid = m.group('pid')
+            record[(tag, pid)] = event_time
+            print 'Event log recorded: %s (%s) - %d ms' % (
+                tag, pid, event_time)
+            if tag == end_tag:
+                booted = True
+                t.cancel()
+                break
+    return record
+
+
+def parse_args():
+    """Parses the command line arguments."""
+    parser = argparse.ArgumentParser(
+        description=inspect.getdoc(sys.modules[__name__]),
+        formatter_class=argparse.RawDescriptionHelpFormatter)
+    parser.add_argument('--iterations', type=int, default=5,
+                        help='Number of times to repeat boot measurements.')
+    parser.add_argument('--interval', type=int,
+                        help=('Duration between iterations. If this is not '
+                              'set explicitly, durations are determined '
+                              'adaptively based on CPUs temperature.'))
+    parser.add_argument('-o', '--output', help='File name of output data.')
+    parser.add_argument('-v', '--verbose', action='store_true',
+                        help='Show verbose output.')
+    parser.add_argument('-s', '--serial', default=os.getenv('ANDROID_SERIAL'),
+                        help='Adb device serial number.')
+    parser.add_argument('-t', '--tags', help='Specify the filename from which '
+                        'event tags are read. Every line contains one event '
+                        'tag and the last event tag is used to detect that '
+                        'the device has finished booting unless --end-tag is '
+                        'specified.')
+    parser.add_argument('--end-tag', help='An event tag on which the script '
+                        'stops measuring the boot time.')
+    parser.add_argument('--apk-dir', help='Specify the directory which contains '
+                        'APK files to be installed before measuring boot time.')
+    return parser.parse_args()
+
+
+def install_apks(device, apk_dir):
+    for apk in glob.glob(os.path.join(apk_dir, '*.apk')):
+        print 'Installing: ' + apk
+        device.install(apk, replace=True)
+
+
+def main():
+    args = parse_args()
+    if args.verbose:
+        logging.getLogger().setLevel(logging.INFO)
+
+    device = adb.get_device(args.serial)
+
+    if not args.output:
+        device.wait()
+        args.output = 'perf-%s-%s.tsv' % (
+            device.get_prop('ro.build.flavor'),
+            device.get_prop('ro.build.version.incremental'))
+    check_dm_verity_settings(device)
+
+    if args.apk_dir:
+        install_apks(device, args.apk_dir)
+
+    record_list = []
+    event_tags = filter_event_tags(read_event_tags(args.tags), device)
+    end_tag = args.end_tag or event_tags[-1]
+    if end_tag not in event_tags:
+        sys.exit('%s is not a valid tag.' % end_tag)
+    event_tags = event_tags[0 : event_tags.index(end_tag) + 1]
+    init_perf(device, args.output, record_list, event_tags)
+    interval_adjuster = IntervalAdjuster(args.interval, device)
+    event_tags_re = make_event_tags_re(event_tags)
+
+    for i in range(args.iterations):
+        print 'Run #%d ' % i
+        record = do_iteration(
+            device, interval_adjuster, event_tags_re, end_tag)
+        record_list.append(record)
+
+
+if __name__ == '__main__':
+    main()
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 52f6b98..a37d6f6 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -92,9 +92,6 @@
 
 static int check_mac_perms(const char *name, char *sctx)
 {
-    if (is_selinux_enabled() <= 0)
-        return 1;
-
     char *tctx = NULL;
     int result = 0;
 
@@ -144,9 +141,10 @@
     return check_mac_perms(name, sctx);
 }
 
-int __property_get(const char *name, char *value)
-{
-    return __system_property_get(name, value);
+std::string property_get(const char* name) {
+    char value[PROP_VALUE_MAX] = {0};
+    __system_property_get(name, value);
+    return value;
 }
 
 static void write_persistent_property(const char *name, const char *value)
@@ -501,9 +499,8 @@
 
 static void load_override_properties() {
     if (ALLOW_LOCAL_PROP_OVERRIDE) {
-        char debuggable[PROP_VALUE_MAX];
-        int ret = property_get("ro.debuggable", debuggable);
-        if (ret && (strcmp(debuggable, "1") == 0)) {
+        std::string debuggable = property_get("ro.debuggable");
+        if (debuggable == "1") {
             load_properties_from_file(PROP_PATH_LOCAL_OVERRIDE, NULL);
         }
     }
@@ -521,19 +518,17 @@
 }
 
 void load_recovery_id_prop() {
-    char fstab_filename[PROP_VALUE_MAX + sizeof(FSTAB_PREFIX)];
-    char propbuf[PROP_VALUE_MAX];
-    int ret = property_get("ro.hardware", propbuf);
-    if (!ret) {
+    std::string ro_hardware = property_get("ro.hardware");
+    if (ro_hardware.empty()) {
         ERROR("ro.hardware not set - unable to load recovery id\n");
         return;
     }
-    snprintf(fstab_filename, sizeof(fstab_filename), FSTAB_PREFIX "%s", propbuf);
+    std::string fstab_filename = FSTAB_PREFIX + ro_hardware;
 
-    std::unique_ptr<fstab, void(*)(fstab*)> tab(fs_mgr_read_fstab(fstab_filename),
+    std::unique_ptr<fstab, void(*)(fstab*)> tab(fs_mgr_read_fstab(fstab_filename.c_str()),
             fs_mgr_free_fstab);
     if (!tab) {
-        ERROR("unable to read fstab %s: %s\n", fstab_filename, strerror(errno));
+        ERROR("unable to read fstab %s: %s\n", fstab_filename.c_str(), strerror(errno));
         return;
     }
 
diff --git a/init/property_service.h b/init/property_service.h
index 303f251..f30577b 100644
--- a/init/property_service.h
+++ b/init/property_service.h
@@ -19,6 +19,7 @@
 
 #include <stddef.h>
 #include <sys/system_properties.h>
+#include <string>
 
 extern void property_init(void);
 extern void property_load_boot_defaults(void);
@@ -26,30 +27,9 @@
 extern void load_system_props(void);
 extern void start_property_service(void);
 void get_property_workspace(int *fd, int *sz);
-extern int __property_get(const char *name, char *value);
+std::string property_get(const char* name);
 extern int property_set(const char *name, const char *value);
 extern bool properties_initialized();
 
-#ifndef __clang__
-extern void __property_get_size_error()
-    __attribute__((__error__("property_get called with too small buffer")));
-#else
-extern void __property_get_size_error();
-#endif
-
-static inline
-__attribute__ ((always_inline))
-__attribute__ ((gnu_inline))
-#ifndef __clang__
-__attribute__ ((artificial))
-#endif
-int property_get(const char *name, char *value)
-{
-    size_t value_len = __builtin_object_size(value, 0);
-    if (value_len != PROP_VALUE_MAX)
-        __property_get_size_error();
-
-    return __property_get(name, value);
-}
 
 #endif	/* _INIT_PROPERTY_H */
diff --git a/init/readme.txt b/init/readme.txt
index 9e3394e..bf440c2 100644
--- a/init/readme.txt
+++ b/init/readme.txt
@@ -2,8 +2,8 @@
 Android Init Language
 ---------------------
 
-The Android Init Language consists of four broad classes of statements,
-which are Actions, Commands, Services, and Options.
+The Android Init Language consists of five broad classes of statements,
+which are Actions, Commands, Services, Options, and Imports.
 
 All of these are line-oriented, consisting of tokens separated by
 whitespace.  The c-style backslash escapes may be used to insert
@@ -17,9 +17,37 @@
 or options belong to the section most recently declared.  Commands
 or options before the first section are ignored.
 
-Actions and Services have unique names.  If a second Action or Service
-is declared with the same name as an existing one, it is ignored as
-an error.  (??? should we override instead)
+Actions and Services have unique names.  If a second Action is defined
+with the same name as an existing one, its commands are appended to
+the commands of the existing action.  If a second Service is defined
+with the same name as an existing one, it is ignored and an error
+message is logged.
+
+
+Init .rc Files
+--------------
+The init language is used in plaintext files that take the .rc file
+extension.  These are typically multiple of these in multiple
+locations on the system, described below.
+
+/init.rc is the primary .rc file and is loaded by the init executable
+at the beginning of its execution.  It is responsible for the initial
+set up of the system.  It imports /init.${ro.hardware}.rc which is the
+primary vendor supplied .rc file.
+
+During the mount_all command, the init executable loads all of the
+files contained within the /{system,vendor,odm}/etc/init/ directories.
+These directories are intended for all Actions and Services used after
+file system mounting.
+
+The intention of these directories is as follows
+   1) /system/etc/init/ is for core system items such as
+      SurfaceFlinger and MediaService.
+   2) /vendor/etc/init/ is for SoC vendor items such as actions or
+      daemons needed for core SoC functionality.
+   3) /odm/etc/init/ is for device manufacturer items such as
+      actions or daemons needed for motion sensor or other peripheral
+      functionality.
 
 
 Actions
@@ -37,7 +65,7 @@
 
 Actions take the form of:
 
-on <trigger>
+on <trigger> [&& <trigger>]*
    <command>
    <command>
    <command>
@@ -117,25 +145,36 @@
 
 Triggers
 --------
-Triggers are strings which can be used to match certain kinds
-of events and used to cause an action to occur.
+Triggers are strings which can be used to match certain kinds of
+events and used to cause an action to occur.
 
-boot
-   This is the first trigger that will occur when init starts
-   (after /init.conf is loaded)
+Triggers are subdivided into event triggers and property triggers.
 
-<name>=<value>
-   Triggers of this form occur when the property <name> is set
-   to the specific value <value>.
+Event triggers are strings triggered by the 'trigger' command or by
+the QueueEventTrigger() function within the init executable.  These
+take the form of a simple string such as 'boot' or 'late-init'.
 
-   One can also test multiple properties to execute a group
-   of commands. For example:
+Property triggers are strings triggered when a named property changes
+value to a given new value or when a named property changes value to
+any new value.  These take the form of 'property:<name>=<value>' and
+'property:<name>=*' respectively.  Property triggers are additionally
+evaluated and triggered accordingly during the initial boot phase of
+init.
 
-   on property:test.a=1 && property:test.b=1
-       setprop test.c 1
+An Action can have multiple property triggers but may only have one
+event trigger.
 
-   The above stub sets test.c to 1 only when
-   both test.a=1 and test.b=1
+For example:
+'on boot && property:a=b' defines an action that is only executed when
+the 'boot' event trigger happens and the property a equals b.
+
+'on property:a=b && property:c=d' defines an action that is executed
+at three times,
+   1) During initial boot if property a=b and property c=d
+   2) Any time that property a transitions to value b, while property
+      c already equals d.
+   3) Any time that property c transitions to value d, while property
+      a already equals b.
 
 
 Commands
@@ -197,9 +236,6 @@
 ifup <interface>
    Bring the network interface <interface> online.
 
-import <filename>
-   Parse an init config file, extending the current configuration.
-
 insmod <path>
    Install the module at <path>
 
@@ -301,19 +337,30 @@
    it will be truncated. Properties are expanded within <content>.
 
 
+Imports
+-------
+The import keyword is not a command, but rather its own section and is
+handled immediately after the .rc file that contains it has finished
+being parsed.  It takes the below form:
+
+import <path>
+   Parse an init config file, extending the current configuration.
+   If <path> is a directory, each file in the directory is parsed as
+   a config file. It is not recursive, nested directories will
+   not be parsed.
+
+There are only two times where the init executable imports .rc files,
+   1) When it imports /init.rc during initial boot
+   2) When it imports /{system,vendor,odm}/etc/init/ during mount_all
+
+
 Properties
 ----------
-Init updates some system properties to provide some insight into
-what it's doing:
-
-init.action
-   Equal to the name of the action currently being executed or "" if none
-
-init.command
-   Equal to the command being executed or "" if none.
+Init provides information about the services that it is responsible
+for via the below properties.
 
 init.svc.<name>
-   State of a named service ("stopped", "running", "restarting")
+   State of a named service ("stopped", "stopping", "running", "restarting")
 
 
 Bootcharting
@@ -351,6 +398,52 @@
 actually started init.
 
 
+Comparing two bootcharts
+------------------------
+A handy script named compare-bootcharts.py can be used to compare the
+start/end time of selected processes. The aforementioned grab-bootchart.sh
+will leave a bootchart tarball named bootchart.tgz at /tmp/android-bootchart.
+If two such barballs are preserved on the host machine under different
+directories, the script can list the timestamps differences. For example:
+
+Usage: system/core/init/compare-bootcharts.py base_bootchart_dir
+       exp_bootchart_dir
+
+process: baseline experiment (delta)
+ - Unit is ms (a jiffy is 10 ms on the system)
+------------------------------------
+/init: 50 40 (-10)
+/system/bin/surfaceflinger: 4320 4470 (+150)
+/system/bin/bootanimation: 6980 6990 (+10)
+zygote64: 10410 10640 (+230)
+zygote: 10410 10640 (+230)
+system_server: 15350 15150 (-200)
+bootanimation ends at: 33790 31230 (-2560)
+
+
+Systrace
+--------
+Systrace [1] can be used for obtaining performance analysis reports during boot
+time on userdebug or eng builds.
+Here is an example of trace events of "wm" and "am" categories:
+
+  $ANDROID_BUILD_TOP/external/chromium-trace/systrace.py wm am --boot
+
+This command will cause the device to reboot. After the device is rebooted and
+the boot sequence has finished, the trace report is obtained from the device
+and written as trace.html on the host by hitting Ctrl+C.
+
+LIMITATION
+Recording trace events is started after persistent properties are loaded, so
+the trace events that are emitted before that are not recorded. Several
+services such as vold, surfaceflinger, and servicemanager are affected by this
+limitation since they are started before persistent properties are loaded.
+Zygote initialization and the processes that are forked from the zygote are not
+affected.
+
+[1] http://developer.android.com/tools/help/systrace.html
+
+
 Debugging init
 --------------
 By default, programs executed by init will drop stdout and stderr into
diff --git a/init/service.cpp b/init/service.cpp
new file mode 100644
index 0000000..a3c5ca4
--- /dev/null
+++ b/init/service.cpp
@@ -0,0 +1,822 @@
+/*
+ * Copyright (C) 2015 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 "service.h"
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include <selinux/selinux.h>
+
+#include <base/file.h>
+#include <base/stringprintf.h>
+#include <cutils/android_reboot.h>
+#include <cutils/sockets.h>
+
+#include "action.h"
+#include "init.h"
+#include "init_parser.h"
+#include "log.h"
+#include "property_service.h"
+#include "util.h"
+
+using android::base::StringPrintf;
+using android::base::WriteStringToFile;
+
+#define CRITICAL_CRASH_THRESHOLD    4       // if we crash >4 times ...
+#define CRITICAL_CRASH_WINDOW       (4*60)  // ... in 4 minutes, goto recovery
+
+SocketInfo::SocketInfo() : uid(0), gid(0), perm(0) {
+}
+
+SocketInfo::SocketInfo(const std::string& name, const std::string& type, uid_t uid,
+                       gid_t gid, int perm, const std::string& socketcon)
+    : name(name), type(type), uid(uid), gid(gid), perm(perm), socketcon(socketcon) {
+}
+
+ServiceEnvironmentInfo::ServiceEnvironmentInfo() {
+}
+
+ServiceEnvironmentInfo::ServiceEnvironmentInfo(const std::string& name,
+                                               const std::string& value)
+    : name(name), value(value) {
+}
+
+Service::Service(const std::string& name, const std::string& classname,
+                 const std::vector<std::string>& args)
+    : name_(name), classname_(classname), flags_(0), pid_(0), time_started_(0),
+      time_crashed_(0), nr_crashed_(0), uid_(0), gid_(0), seclabel_(""),
+      ioprio_class_(IoSchedClass_NONE), ioprio_pri_(0), args_(args) {
+    onrestart_.InitSingleTrigger("onrestart");
+}
+
+Service::Service(const std::string& name, const std::string& classname,
+                 unsigned flags, uid_t uid, gid_t gid, const std::vector<gid_t>& supp_gids,
+                 const std::string& seclabel,  const std::vector<std::string>& args)
+    : name_(name), classname_(classname), flags_(flags), pid_(0), time_started_(0),
+      time_crashed_(0), nr_crashed_(0), uid_(uid), gid_(gid), supp_gids_(supp_gids),
+      seclabel_(seclabel), ioprio_class_(IoSchedClass_NONE), ioprio_pri_(0), args_(args) {
+    onrestart_.InitSingleTrigger("onrestart");
+}
+
+void Service::NotifyStateChange(const std::string& new_state) const {
+    if (!properties_initialized()) {
+        // If properties aren't available yet, we can't set them.
+        return;
+    }
+
+    if ((flags_ & SVC_EXEC) != 0) {
+        // 'exec' commands don't have properties tracking their state.
+        return;
+    }
+
+    std::string prop_name = StringPrintf("init.svc.%s", name_.c_str());
+    if (prop_name.length() >= PROP_NAME_MAX) {
+        // If the property name would be too long, we can't set it.
+        ERROR("Property name \"init.svc.%s\" too long; not setting to %s\n",
+              name_.c_str(), new_state.c_str());
+        return;
+    }
+
+    property_set(prop_name.c_str(), new_state.c_str());
+}
+
+bool Service::Reap() {
+    if (!(flags_ & SVC_ONESHOT) || (flags_ & SVC_RESTART)) {
+        NOTICE("Service '%s' (pid %d) killing any children in process group\n",
+               name_.c_str(), pid_);
+        kill(-pid_, SIGKILL);
+    }
+
+    // Remove any sockets we may have created.
+    for (const auto& si : sockets_) {
+        std::string tmp = StringPrintf(ANDROID_SOCKET_DIR "/%s", si.name.c_str());
+        unlink(tmp.c_str());
+    }
+
+    if (flags_ & SVC_EXEC) {
+        INFO("SVC_EXEC pid %d finished...\n", pid_);
+        return true;
+    }
+
+    pid_ = 0;
+    flags_ &= (~SVC_RUNNING);
+
+    // Oneshot processes go into the disabled state on exit,
+    // except when manually restarted.
+    if ((flags_ & SVC_ONESHOT) && !(flags_ & SVC_RESTART)) {
+        flags_ |= SVC_DISABLED;
+    }
+
+    // Disabled and reset processes do not get restarted automatically.
+    if (flags_ & (SVC_DISABLED | SVC_RESET))  {
+        NotifyStateChange("stopped");
+        return false;
+    }
+
+    time_t now = gettime();
+    if ((flags_ & SVC_CRITICAL) && !(flags_ & SVC_RESTART)) {
+        if (time_crashed_ + CRITICAL_CRASH_WINDOW >= now) {
+            if (++nr_crashed_ > CRITICAL_CRASH_THRESHOLD) {
+                ERROR("critical process '%s' exited %d times in %d minutes; "
+                      "rebooting into recovery mode\n", name_.c_str(),
+                      CRITICAL_CRASH_THRESHOLD, CRITICAL_CRASH_WINDOW / 60);
+                android_reboot(ANDROID_RB_RESTART2, 0, "recovery");
+                return false;
+            }
+        } else {
+            time_crashed_ = now;
+            nr_crashed_ = 1;
+        }
+    }
+
+    flags_ &= (~SVC_RESTART);
+    flags_ |= SVC_RESTARTING;
+
+    // Execute all onrestart commands for this service.
+    onrestart_.ExecuteAllCommands();
+
+    NotifyStateChange("restarting");
+    return false;
+}
+
+void Service::DumpState() const {
+    INFO("service %s\n", name_.c_str());
+    INFO("  class '%s'\n", classname_.c_str());
+    INFO("  exec");
+    for (const auto& s : args_) {
+        INFO(" '%s'", s.c_str());
+    }
+    INFO("\n");
+    for (const auto& si : sockets_) {
+        INFO("  socket %s %s 0%o\n", si.name.c_str(), si.type.c_str(), si.perm);
+    }
+}
+
+bool Service::HandleClass(const std::vector<std::string>& args, std::string* err) {
+    classname_ = args[1];
+    return true;
+}
+
+bool Service::HandleConsole(const std::vector<std::string>& args, std::string* err) {
+    flags_ |= SVC_CONSOLE;
+    return true;
+}
+
+bool Service::HandleCritical(const std::vector<std::string>& args, std::string* err) {
+    flags_ |= SVC_CRITICAL;
+    return true;
+}
+
+bool Service::HandleDisabled(const std::vector<std::string>& args, std::string* err) {
+    flags_ |= SVC_DISABLED;
+    flags_ |= SVC_RC_DISABLED;
+    return true;
+}
+
+bool Service::HandleGroup(const std::vector<std::string>& args, std::string* err) {
+    gid_ = decode_uid(args[1].c_str());
+    for (std::size_t n = 2; n < args.size(); n++) {
+        supp_gids_.emplace_back(decode_uid(args[n].c_str()));
+    }
+    return true;
+}
+
+bool Service::HandleIoprio(const std::vector<std::string>& args, std::string* err) {
+    ioprio_pri_ = std::stoul(args[2], 0, 8);
+
+    if (ioprio_pri_ < 0 || ioprio_pri_ > 7) {
+        *err = "priority value must be range 0 - 7";
+        return false;
+    }
+
+    if (args[1] == "rt") {
+        ioprio_class_ = IoSchedClass_RT;
+    } else if (args[1] == "be") {
+        ioprio_class_ = IoSchedClass_BE;
+    } else if (args[1] == "idle") {
+        ioprio_class_ = IoSchedClass_IDLE;
+    } else {
+        *err = "ioprio option usage: ioprio <rt|be|idle> <0-7>";
+        return false;
+    }
+
+    return true;
+}
+
+bool Service::HandleKeycodes(const std::vector<std::string>& args, std::string* err) {
+    for (std::size_t i = 1; i < args.size(); i++) {
+        keycodes_.emplace_back(std::stoi(args[i]));
+    }
+    return true;
+}
+
+bool Service::HandleOneshot(const std::vector<std::string>& args, std::string* err) {
+    flags_ |= SVC_ONESHOT;
+    return true;
+}
+
+bool Service::HandleOnrestart(const std::vector<std::string>& args, std::string* err) {
+    std::vector<std::string> str_args(args.begin() + 1, args.end());
+    onrestart_.AddCommand(str_args, "", 0, err);
+    return true;
+}
+
+bool Service::HandleSeclabel(const std::vector<std::string>& args, std::string* err) {
+    seclabel_ = args[1];
+    return true;
+}
+
+bool Service::HandleSetenv(const std::vector<std::string>& args, std::string* err) {
+    envvars_.emplace_back(args[1], args[2]);
+    return true;
+}
+
+/* name type perm [ uid gid context ] */
+bool Service::HandleSocket(const std::vector<std::string>& args, std::string* err) {
+    if (args[2] != "dgram" && args[2] != "stream" && args[2] != "seqpacket") {
+        *err = "socket type must be 'dgram', 'stream' or 'seqpacket'";
+        return false;
+    }
+
+    int perm = std::stoul(args[3], 0, 8);
+    uid_t uid = args.size() > 4 ? decode_uid(args[4].c_str()) : 0;
+    gid_t gid = args.size() > 5 ? decode_uid(args[5].c_str()) : 0;
+    std::string socketcon = args.size() > 6 ? args[6] : "";
+
+    sockets_.emplace_back(args[1], args[2], uid, gid, perm, socketcon);
+    return true;
+}
+
+bool Service::HandleUser(const std::vector<std::string>& args, std::string* err) {
+    uid_ = decode_uid(args[1].c_str());
+    return true;
+}
+
+bool Service::HandleWritepid(const std::vector<std::string>& args, std::string* err) {
+    writepid_files_.assign(args.begin() + 1, args.end());
+    return true;
+}
+
+class Service::OptionHandlerMap : public KeywordMap<OptionHandler> {
+public:
+    OptionHandlerMap() {
+    }
+private:
+    Map& map() const override;
+};
+
+Service::OptionHandlerMap::Map& Service::OptionHandlerMap::map() const {
+    constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
+    static const Map option_handlers = {
+        {"class",       {1,     1,    &Service::HandleClass}},
+        {"console",     {0,     0,    &Service::HandleConsole}},
+        {"critical",    {0,     0,    &Service::HandleCritical}},
+        {"disabled",    {0,     0,    &Service::HandleDisabled}},
+        {"group",       {1,     NR_SVC_SUPP_GIDS + 1, &Service::HandleGroup}},
+        {"ioprio",      {2,     2,    &Service::HandleIoprio}},
+        {"keycodes",    {1,     kMax, &Service::HandleKeycodes}},
+        {"oneshot",     {0,     0,    &Service::HandleOneshot}},
+        {"onrestart",   {1,     kMax, &Service::HandleOnrestart}},
+        {"seclabel",    {1,     1,    &Service::HandleSeclabel}},
+        {"setenv",      {2,     2,    &Service::HandleSetenv}},
+        {"socket",      {3,     6,    &Service::HandleSocket}},
+        {"user",        {1,     1,    &Service::HandleUser}},
+        {"writepid",    {1,     kMax, &Service::HandleWritepid}},
+    };
+    return option_handlers;
+}
+
+bool Service::HandleLine(const std::vector<std::string>& args, std::string* err) {
+    if (args.empty()) {
+        *err = "option needed, but not provided";
+        return false;
+    }
+
+    static const OptionHandlerMap handler_map;
+    auto handler = handler_map.FindFunction(args[0], args.size() - 1, err);
+
+    if (!handler) {
+        return false;
+    }
+
+    return (this->*handler)(args, err);
+}
+
+bool Service::Start(const std::vector<std::string>& dynamic_args) {
+    // Starting a service removes it from the disabled or reset state and
+    // immediately takes it out of the restarting state if it was in there.
+    flags_ &= (~(SVC_DISABLED|SVC_RESTARTING|SVC_RESET|SVC_RESTART|SVC_DISABLED_START));
+    time_started_ = 0;
+
+    // Running processes require no additional work --- if they're in the
+    // process of exiting, we've ensured that they will immediately restart
+    // on exit, unless they are ONESHOT.
+    if (flags_ & SVC_RUNNING) {
+        return false;
+    }
+
+    bool needs_console = (flags_ & SVC_CONSOLE);
+    if (needs_console && !have_console) {
+        ERROR("service '%s' requires console\n", name_.c_str());
+        flags_ |= SVC_DISABLED;
+        return false;
+    }
+
+    struct stat sb;
+    if (stat(args_[0].c_str(), &sb) == -1) {
+        ERROR("cannot find '%s' (%s), disabling '%s'\n",
+              args_[0].c_str(), strerror(errno), name_.c_str());
+        flags_ |= SVC_DISABLED;
+        return false;
+    }
+
+    if ((!(flags_ & SVC_ONESHOT)) && !dynamic_args.empty()) {
+        ERROR("service '%s' must be one-shot to use dynamic args, disabling\n",
+              args_[0].c_str());
+        flags_ |= SVC_DISABLED;
+        return false;
+    }
+
+    std::string scon;
+    if (!seclabel_.empty()) {
+        scon = seclabel_;
+    } else {
+        char* mycon = nullptr;
+        char* fcon = nullptr;
+
+        INFO("computing context for service '%s'\n", args_[0].c_str());
+        int rc = getcon(&mycon);
+        if (rc < 0) {
+            ERROR("could not get context while starting '%s'\n", name_.c_str());
+            return false;
+        }
+
+        rc = getfilecon(args_[0].c_str(), &fcon);
+        if (rc < 0) {
+            ERROR("could not get context while starting '%s'\n", name_.c_str());
+            free(mycon);
+            return false;
+        }
+
+        char* ret_scon = nullptr;
+        rc = security_compute_create(mycon, fcon, string_to_security_class("process"),
+                                     &ret_scon);
+        if (rc == 0) {
+            scon = ret_scon;
+            free(ret_scon);
+        }
+        if (rc == 0 && scon == mycon) {
+            ERROR("Service %s does not have a SELinux domain defined.\n", name_.c_str());
+            free(mycon);
+            free(fcon);
+            return false;
+        }
+        free(mycon);
+        free(fcon);
+        if (rc < 0) {
+            ERROR("could not get context while starting '%s'\n", name_.c_str());
+            return false;
+        }
+    }
+
+    NOTICE("Starting service '%s'...\n", name_.c_str());
+
+    pid_t pid = fork();
+    if (pid == 0) {
+        int fd, sz;
+
+        umask(077);
+        if (properties_initialized()) {
+            get_property_workspace(&fd, &sz);
+            std::string tmp = StringPrintf("%d,%d", dup(fd), sz);
+            add_environment("ANDROID_PROPERTY_WORKSPACE", tmp.c_str());
+        }
+
+        for (const auto& ei : envvars_) {
+            add_environment(ei.name.c_str(), ei.value.c_str());
+        }
+
+        for (const auto& si : sockets_) {
+            int socket_type = ((si.type == "stream" ? SOCK_STREAM :
+                                (si.type == "dgram" ? SOCK_DGRAM :
+                                 SOCK_SEQPACKET)));
+            const char* socketcon =
+                !si.socketcon.empty() ? si.socketcon.c_str() : scon.c_str();
+
+            int s = create_socket(si.name.c_str(), socket_type, si.perm,
+                                  si.uid, si.gid, socketcon);
+            if (s >= 0) {
+                PublishSocket(si.name, s);
+            }
+        }
+
+        std::string pid_str = StringPrintf("%d", pid);
+        for (const auto& file : writepid_files_) {
+            if (!WriteStringToFile(pid_str, file)) {
+                ERROR("couldn't write %s to %s: %s\n",
+                      pid_str.c_str(), file.c_str(), strerror(errno));
+            }
+        }
+
+        if (ioprio_class_ != IoSchedClass_NONE) {
+            if (android_set_ioprio(getpid(), ioprio_class_, ioprio_pri_)) {
+                ERROR("Failed to set pid %d ioprio = %d,%d: %s\n",
+                      getpid(), ioprio_class_, ioprio_pri_, strerror(errno));
+            }
+        }
+
+        if (needs_console) {
+            setsid();
+            OpenConsole();
+        } else {
+            ZapStdio();
+        }
+
+        setpgid(0, getpid());
+
+        // As requested, set our gid, supplemental gids, and uid.
+        if (gid_) {
+            if (setgid(gid_) != 0) {
+                ERROR("setgid failed: %s\n", strerror(errno));
+                _exit(127);
+            }
+        }
+        if (!supp_gids_.empty()) {
+            if (setgroups(supp_gids_.size(), &supp_gids_[0]) != 0) {
+                ERROR("setgroups failed: %s\n", strerror(errno));
+                _exit(127);
+            }
+        }
+        if (uid_) {
+            if (setuid(uid_) != 0) {
+                ERROR("setuid failed: %s\n", strerror(errno));
+                _exit(127);
+            }
+        }
+        if (!seclabel_.empty()) {
+            if (setexeccon(seclabel_.c_str()) < 0) {
+                ERROR("cannot setexeccon('%s'): %s\n",
+                      seclabel_.c_str(), strerror(errno));
+                _exit(127);
+            }
+        }
+
+        std::vector<char*> strs;
+        for (const auto& s : args_) {
+            strs.push_back(const_cast<char*>(s.c_str()));
+        }
+        for (const auto& s : dynamic_args) {
+            strs.push_back(const_cast<char*>(s.c_str()));
+        }
+        strs.push_back(nullptr);
+        if (execve(args_[0].c_str(), (char**) &strs[0], (char**) ENV) < 0) {
+            ERROR("cannot execve('%s'): %s\n", args_[0].c_str(), strerror(errno));
+        }
+
+        _exit(127);
+    }
+
+    if (pid < 0) {
+        ERROR("failed to start '%s'\n", name_.c_str());
+        pid_ = 0;
+        return false;
+    }
+
+    time_started_ = gettime();
+    pid_ = pid;
+    flags_ |= SVC_RUNNING;
+
+    if ((flags_ & SVC_EXEC) != 0) {
+        INFO("SVC_EXEC pid %d (uid %d gid %d+%zu context %s) started; waiting...\n",
+             pid_, uid_, gid_, supp_gids_.size(),
+             !seclabel_.empty() ? seclabel_.c_str() : "default");
+    }
+
+    NotifyStateChange("running");
+    return true;
+}
+
+bool Service::Start() {
+    const std::vector<std::string> null_dynamic_args;
+    return Start(null_dynamic_args);
+}
+
+bool Service::StartIfNotDisabled() {
+    if (!(flags_ & SVC_DISABLED)) {
+        return Start();
+    } else {
+        flags_ |= SVC_DISABLED_START;
+    }
+    return true;
+}
+
+bool Service::Enable() {
+    flags_ &= ~(SVC_DISABLED | SVC_RC_DISABLED);
+    if (flags_ & SVC_DISABLED_START) {
+        return Start();
+    }
+    return true;
+}
+
+void Service::Reset() {
+    StopOrReset(SVC_RESET);
+}
+
+void Service::Stop() {
+    StopOrReset(SVC_DISABLED);
+}
+
+void Service::Restart() {
+    if (flags_ & SVC_RUNNING) {
+        /* Stop, wait, then start the service. */
+        StopOrReset(SVC_RESTART);
+    } else if (!(flags_ & SVC_RESTARTING)) {
+        /* Just start the service since it's not running. */
+        Start();
+    } /* else: Service is restarting anyways. */
+}
+
+void Service::RestartIfNeeded(time_t& process_needs_restart) {
+    time_t next_start_time = time_started_ + 5;
+
+    if (next_start_time <= gettime()) {
+        flags_ &= (~SVC_RESTARTING);
+        Start();
+        return;
+    }
+
+    if ((next_start_time < process_needs_restart) ||
+        (process_needs_restart == 0)) {
+        process_needs_restart = next_start_time;
+    }
+}
+
+/* The how field should be either SVC_DISABLED, SVC_RESET, or SVC_RESTART */
+void Service::StopOrReset(int how) {
+    /* The service is still SVC_RUNNING until its process exits, but if it has
+     * already exited it shoudn't attempt a restart yet. */
+    flags_ &= ~(SVC_RESTARTING | SVC_DISABLED_START);
+
+    if ((how != SVC_DISABLED) && (how != SVC_RESET) && (how != SVC_RESTART)) {
+        /* Hrm, an illegal flag.  Default to SVC_DISABLED */
+        how = SVC_DISABLED;
+    }
+        /* if the service has not yet started, prevent
+         * it from auto-starting with its class
+         */
+    if (how == SVC_RESET) {
+        flags_ |= (flags_ & SVC_RC_DISABLED) ? SVC_DISABLED : SVC_RESET;
+    } else {
+        flags_ |= how;
+    }
+
+    if (pid_) {
+        NOTICE("Service '%s' is being killed...\n", name_.c_str());
+        kill(-pid_, SIGKILL);
+        NotifyStateChange("stopping");
+    } else {
+        NotifyStateChange("stopped");
+    }
+}
+
+void Service::ZapStdio() const {
+    int fd;
+    fd = open("/dev/null", O_RDWR);
+    dup2(fd, 0);
+    dup2(fd, 1);
+    dup2(fd, 2);
+    close(fd);
+}
+
+void Service::OpenConsole() const {
+    int fd;
+    if ((fd = open(console_name.c_str(), O_RDWR)) < 0) {
+        fd = open("/dev/null", O_RDWR);
+    }
+    ioctl(fd, TIOCSCTTY, 0);
+    dup2(fd, 0);
+    dup2(fd, 1);
+    dup2(fd, 2);
+    close(fd);
+}
+
+void Service::PublishSocket(const std::string& name, int fd) const {
+    std::string key = StringPrintf(ANDROID_SOCKET_ENV_PREFIX "%s", name.c_str());
+    std::string val = StringPrintf("%d", fd);
+    add_environment(key.c_str(), val.c_str());
+
+    /* make sure we don't close-on-exec */
+    fcntl(fd, F_SETFD, 0);
+}
+
+int ServiceManager::exec_count_ = 0;
+
+ServiceManager::ServiceManager() {
+}
+
+ServiceManager& ServiceManager::GetInstance() {
+    static ServiceManager instance;
+    return instance;
+}
+
+void ServiceManager::AddService(std::unique_ptr<Service> service) {
+    Service* old_service = FindServiceByName(service->name());
+    if (old_service) {
+        ERROR("ignored duplicate definition of service '%s'",
+              service->name().c_str());
+        return;
+    }
+    services_.emplace_back(std::move(service));
+}
+
+Service* ServiceManager::MakeExecOneshotService(const std::vector<std::string>& args) {
+    // Parse the arguments: exec [SECLABEL [UID [GID]*] --] COMMAND ARGS...
+    // SECLABEL can be a - to denote default
+    std::size_t command_arg = 1;
+    for (std::size_t i = 1; i < args.size(); ++i) {
+        if (args[i] == "--") {
+            command_arg = i + 1;
+            break;
+        }
+    }
+    if (command_arg > 4 + NR_SVC_SUPP_GIDS) {
+        ERROR("exec called with too many supplementary group ids\n");
+        return nullptr;
+    }
+
+    if (command_arg >= args.size()) {
+        ERROR("exec called without command\n");
+        return nullptr;
+    }
+    std::vector<std::string> str_args(args.begin() + command_arg, args.end());
+
+    exec_count_++;
+    std::string name = StringPrintf("exec %d (%s)", exec_count_, str_args[0].c_str());
+    unsigned flags = SVC_EXEC | SVC_ONESHOT;
+
+    std::string seclabel = "";
+    if (command_arg > 2 && args[1] != "-") {
+        seclabel = args[1];
+    }
+    uid_t uid = 0;
+    if (command_arg > 3) {
+        uid = decode_uid(args[2].c_str());
+    }
+    gid_t gid = 0;
+    std::vector<gid_t> supp_gids;
+    if (command_arg > 4) {
+        gid = decode_uid(args[3].c_str());
+        std::size_t nr_supp_gids = command_arg - 1 /* -- */ - 4 /* exec SECLABEL UID GID */;
+        for (size_t i = 0; i < nr_supp_gids; ++i) {
+            supp_gids.push_back(decode_uid(args[4 + i].c_str()));
+        }
+    }
+
+    std::unique_ptr<Service> svc_p(new Service(name, "default", flags, uid, gid,
+                                               supp_gids, seclabel, str_args));
+    if (!svc_p) {
+        ERROR("Couldn't allocate service for exec of '%s'",
+              str_args[0].c_str());
+        return nullptr;
+    }
+    Service* svc = svc_p.get();
+    services_.push_back(std::move(svc_p));
+
+    return svc;
+}
+
+Service* ServiceManager::FindServiceByName(const std::string& name) const {
+    auto svc = std::find_if(services_.begin(), services_.end(),
+                            [&name] (const std::unique_ptr<Service>& s) {
+                                return name == s->name();
+                            });
+    if (svc != services_.end()) {
+        return svc->get();
+    }
+    return nullptr;
+}
+
+Service* ServiceManager::FindServiceByPid(pid_t pid) const {
+    auto svc = std::find_if(services_.begin(), services_.end(),
+                            [&pid] (const std::unique_ptr<Service>& s) {
+                                return s->pid() == pid;
+                            });
+    if (svc != services_.end()) {
+        return svc->get();
+    }
+    return nullptr;
+}
+
+Service* ServiceManager::FindServiceByKeychord(int keychord_id) const {
+    auto svc = std::find_if(services_.begin(), services_.end(),
+                            [&keychord_id] (const std::unique_ptr<Service>& s) {
+                                return s->keychord_id() == keychord_id;
+                            });
+
+    if (svc != services_.end()) {
+        return svc->get();
+    }
+    return nullptr;
+}
+
+void ServiceManager::ForEachService(void (*func)(Service* svc)) const {
+    for (const auto& s : services_) {
+        func(s.get());
+    }
+}
+
+void ServiceManager::ForEachServiceInClass(const std::string& classname,
+                                           void (*func)(Service* svc)) const {
+    for (const auto& s : services_) {
+        if (classname == s->classname()) {
+            func(s.get());
+        }
+    }
+}
+
+void ServiceManager::ForEachServiceWithFlags(unsigned matchflags,
+                                             void (*func)(Service* svc)) const {
+    for (const auto& s : services_) {
+        if (s->flags() & matchflags) {
+            func(s.get());
+        }
+    }
+}
+
+void ServiceManager::RemoveService(const Service& svc) {
+    auto svc_it = std::find_if(services_.begin(), services_.end(),
+                               [&svc] (const std::unique_ptr<Service>& s) {
+                                   return svc.name() == s->name();
+                               });
+    if (svc_it == services_.end()) {
+        return;
+    }
+
+    services_.erase(svc_it);
+}
+
+void ServiceManager::DumpState() const {
+    for (const auto& s : services_) {
+        s->DumpState();
+    }
+    INFO("\n");
+}
+
+bool ServiceParser::ParseSection(const std::vector<std::string>& args,
+                                 std::string* err) {
+    if (args.size() < 3) {
+        *err = "services must have a name and a program";
+        return false;
+    }
+
+    const std::string& name = args[1];
+    if (!IsValidName(name)) {
+        *err = StringPrintf("invalid service name '%s'", name.c_str());
+        return false;
+    }
+
+    std::vector<std::string> str_args(args.begin() + 2, args.end());
+    service_ = std::make_unique<Service>(name, "default", str_args);
+    return true;
+}
+
+bool ServiceParser::ParseLineSection(const std::vector<std::string>& args,
+                                     const std::string& filename, int line,
+                                     std::string* err) const {
+    return service_ ? service_->HandleLine(args, err) : false;
+}
+
+void ServiceParser::EndSection() {
+    if (service_) {
+        ServiceManager::GetInstance().AddService(std::move(service_));
+    }
+}
+
+bool ServiceParser::IsValidName(const std::string& name) const {
+    if (name.size() > 16) {
+        return false;
+    }
+    for (const auto& c : name) {
+        if (!isalnum(c) && (c != '_') && (c != '-')) {
+            return false;
+        }
+    }
+    return true;
+}
diff --git a/init/service.h b/init/service.h
new file mode 100644
index 0000000..10eb736
--- /dev/null
+++ b/init/service.h
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2015 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 _INIT_SERVICE_H
+#define _INIT_SERVICE_H
+
+#include <sys/types.h>
+
+#include <cutils/iosched_policy.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "action.h"
+#include "init_parser.h"
+#include "keyword_map.h"
+
+#define SVC_DISABLED       0x001  // do not autostart with class
+#define SVC_ONESHOT        0x002  // do not restart on exit
+#define SVC_RUNNING        0x004  // currently active
+#define SVC_RESTARTING     0x008  // waiting to restart
+#define SVC_CONSOLE        0x010  // requires console
+#define SVC_CRITICAL       0x020  // will reboot into recovery if keeps crashing
+#define SVC_RESET          0x040  // Use when stopping a process,
+                                  // but not disabling so it can be restarted with its class.
+#define SVC_RC_DISABLED    0x080  // Remember if the disabled flag was set in the rc script.
+#define SVC_RESTART        0x100  // Use to safely restart (stop, wait, start) a service.
+#define SVC_DISABLED_START 0x200  // A start was requested but it was disabled at the time.
+#define SVC_EXEC           0x400  // This synthetic service corresponds to an 'exec'.
+
+#define NR_SVC_SUPP_GIDS 12    // twelve supplementary groups
+
+class Action;
+class ServiceManager;
+
+struct SocketInfo {
+    SocketInfo();
+    SocketInfo(const std::string& name, const std::string& type, uid_t uid,
+                       gid_t gid, int perm, const std::string& socketcon);
+    std::string name;
+    std::string type;
+    uid_t uid;
+    gid_t gid;
+    int perm;
+    std::string socketcon;
+};
+
+struct ServiceEnvironmentInfo {
+    ServiceEnvironmentInfo();
+    ServiceEnvironmentInfo(const std::string& name, const std::string& value);
+    std::string name;
+    std::string value;
+};
+
+class Service {
+public:
+    Service(const std::string& name, const std::string& classname,
+            const std::vector<std::string>& args);
+
+    Service(const std::string& name, const std::string& classname,
+            unsigned flags, uid_t uid, gid_t gid, const std::vector<gid_t>& supp_gids,
+            const std::string& seclabel,  const std::vector<std::string>& args);
+
+    bool HandleLine(const std::vector<std::string>& args, std::string* err);
+    bool Start(const std::vector<std::string>& dynamic_args);
+    bool Start();
+    bool StartIfNotDisabled();
+    bool Enable();
+    void Reset();
+    void Stop();
+    void Restart();
+    void RestartIfNeeded(time_t& process_needs_restart);
+    bool Reap();
+    void DumpState() const;
+
+    const std::string& name() const { return name_; }
+    const std::string& classname() const { return classname_; }
+    unsigned flags() const { return flags_; }
+    pid_t pid() const { return pid_; }
+    uid_t uid() const { return uid_; }
+    gid_t gid() const { return gid_; }
+    const std::vector<gid_t>& supp_gids() const { return supp_gids_; }
+    const std::string& seclabel() const { return seclabel_; }
+    const std::vector<int>& keycodes() const { return keycodes_; }
+    int keychord_id() const { return keychord_id_; }
+    void set_keychord_id(int keychord_id) { keychord_id_ = keychord_id; }
+    const std::vector<std::string>& args() const { return args_; }
+
+private:
+    using OptionHandler = bool (Service::*) (const std::vector<std::string>& args,
+                                             std::string* err);
+    class OptionHandlerMap;
+
+    void NotifyStateChange(const std::string& new_state) const;
+    void StopOrReset(int how);
+    void ZapStdio() const;
+    void OpenConsole() const;
+    void PublishSocket(const std::string& name, int fd) const;
+
+    bool HandleClass(const std::vector<std::string>& args, std::string* err);
+    bool HandleConsole(const std::vector<std::string>& args, std::string* err);
+    bool HandleCritical(const std::vector<std::string>& args, std::string* err);
+    bool HandleDisabled(const std::vector<std::string>& args, std::string* err);
+    bool HandleGroup(const std::vector<std::string>& args, std::string* err);
+    bool HandleIoprio(const std::vector<std::string>& args, std::string* err);
+    bool HandleKeycodes(const std::vector<std::string>& args, std::string* err);
+    bool HandleOneshot(const std::vector<std::string>& args, std::string* err);
+    bool HandleOnrestart(const std::vector<std::string>& args, std::string* err);
+    bool HandleSeclabel(const std::vector<std::string>& args, std::string* err);
+    bool HandleSetenv(const std::vector<std::string>& args, std::string* err);
+    bool HandleSocket(const std::vector<std::string>& args, std::string* err);
+    bool HandleUser(const std::vector<std::string>& args, std::string* err);
+    bool HandleWritepid(const std::vector<std::string>& args, std::string* err);
+
+    std::string name_;
+    std::string classname_;
+
+    unsigned flags_;
+    pid_t pid_;
+    time_t time_started_;    // time of last start
+    time_t time_crashed_;    // first crash within inspection window
+    int nr_crashed_;         // number of times crashed within window
+
+    uid_t uid_;
+    gid_t gid_;
+    std::vector<gid_t> supp_gids_;
+
+    std::string seclabel_;
+
+    std::vector<SocketInfo> sockets_;
+    std::vector<ServiceEnvironmentInfo> envvars_;
+
+    Action onrestart_;  // Commands to execute on restart.
+
+    std::vector<std::string> writepid_files_;
+
+    // keycodes for triggering this service via /dev/keychord
+    std::vector<int> keycodes_;
+    int keychord_id_;
+
+    IoSchedClass ioprio_class_;
+    int ioprio_pri_;
+
+    std::vector<std::string> args_;
+};
+
+class ServiceManager {
+public:
+    static ServiceManager& GetInstance();
+
+    void AddService(std::unique_ptr<Service> service);
+    Service* MakeExecOneshotService(const std::vector<std::string>& args);
+    Service* FindServiceByName(const std::string& name) const;
+    Service* FindServiceByPid(pid_t pid) const;
+    Service* FindServiceByKeychord(int keychord_id) const;
+    void ForEachService(void (*func)(Service* svc)) const;
+    void ForEachServiceInClass(const std::string& classname,
+                               void (*func)(Service* svc)) const;
+    void ForEachServiceWithFlags(unsigned matchflags,
+                             void (*func)(Service* svc)) const;
+    void RemoveService(const Service& svc);
+    void DumpState() const;
+
+private:
+    ServiceManager();
+
+    static int exec_count_; // Every service needs a unique name.
+    std::vector<std::unique_ptr<Service>> services_;
+};
+
+class ServiceParser : public SectionParser {
+public:
+    ServiceParser() : service_(nullptr) {
+    }
+    bool ParseSection(const std::vector<std::string>& args,
+                      std::string* err) override;
+    bool ParseLineSection(const std::vector<std::string>& args,
+                          const std::string& filename, int line,
+                          std::string* err) const override;
+    void EndSection() override;
+    void EndFile(const std::string&) override {
+    }
+private:
+    bool IsValidName(const std::string& name) const;
+
+    std::unique_ptr<Service> service_;
+};
+
+#endif
diff --git a/init/signal_handler.cpp b/init/signal_handler.cpp
index 39a466d..867abbc 100644
--- a/init/signal_handler.cpp
+++ b/init/signal_handler.cpp
@@ -28,13 +28,12 @@
 #include <cutils/list.h>
 #include <cutils/sockets.h>
 
+#include "action.h"
 #include "init.h"
 #include "log.h"
+#include "service.h"
 #include "util.h"
 
-#define CRITICAL_CRASH_THRESHOLD    4       /* if we crash >4 times ... */
-#define CRITICAL_CRASH_WINDOW       (4*60)  /* ... in 4 minutes, goto recovery */
-
 static int signal_write_fd = -1;
 static int signal_read_fd = -1;
 
@@ -60,11 +59,11 @@
         return false;
     }
 
-    service* svc = service_find_by_pid(pid);
+    Service* svc = ServiceManager::GetInstance().FindServiceByPid(pid);
 
     std::string name;
     if (svc) {
-        name = android::base::StringPrintf("Service '%s' (pid %d)", svc->name, pid);
+        name = android::base::StringPrintf("Service '%s' (pid %d)", svc->name().c_str(), pid);
     } else {
         name = android::base::StringPrintf("Untracked pid %d", pid);
     }
@@ -75,70 +74,11 @@
         return true;
     }
 
-    // TODO: all the code from here down should be a member function on service.
-
-    if (!(svc->flags & SVC_ONESHOT) || (svc->flags & SVC_RESTART)) {
-        NOTICE("Service '%s' (pid %d) killing any children in process group\n", svc->name, pid);
-        kill(-pid, SIGKILL);
-    }
-
-    // Remove any sockets we may have created.
-    for (socketinfo* si = svc->sockets; si; si = si->next) {
-        char tmp[128];
-        snprintf(tmp, sizeof(tmp), ANDROID_SOCKET_DIR"/%s", si->name);
-        unlink(tmp);
-    }
-
-    if (svc->flags & SVC_EXEC) {
-        INFO("SVC_EXEC pid %d finished...\n", svc->pid);
+    if (svc->Reap()) {
         waiting_for_exec = false;
-        list_remove(&svc->slist);
-        free(svc->name);
-        free(svc);
-        return true;
+        ServiceManager::GetInstance().RemoveService(*svc);
     }
 
-    svc->pid = 0;
-    svc->flags &= (~SVC_RUNNING);
-
-    // Oneshot processes go into the disabled state on exit,
-    // except when manually restarted.
-    if ((svc->flags & SVC_ONESHOT) && !(svc->flags & SVC_RESTART)) {
-        svc->flags |= SVC_DISABLED;
-    }
-
-    // Disabled and reset processes do not get restarted automatically.
-    if (svc->flags & (SVC_DISABLED | SVC_RESET))  {
-        svc->NotifyStateChange("stopped");
-        return true;
-    }
-
-    time_t now = gettime();
-    if ((svc->flags & SVC_CRITICAL) && !(svc->flags & SVC_RESTART)) {
-        if (svc->time_crashed + CRITICAL_CRASH_WINDOW >= now) {
-            if (++svc->nr_crashed > CRITICAL_CRASH_THRESHOLD) {
-                ERROR("critical process '%s' exited %d times in %d minutes; "
-                      "rebooting into recovery mode\n", svc->name,
-                      CRITICAL_CRASH_THRESHOLD, CRITICAL_CRASH_WINDOW / 60);
-                android_reboot(ANDROID_RB_RESTART2, 0, "recovery");
-                return true;
-            }
-        } else {
-            svc->time_crashed = now;
-            svc->nr_crashed = 1;
-        }
-    }
-
-    svc->flags &= (~SVC_RESTART);
-    svc->flags |= SVC_RESTARTING;
-
-    // Execute all onrestart commands for this service.
-    struct listnode* node;
-    list_for_each(node, &svc->onrestart.commands) {
-        command* cmd = node_to_item(node, struct command, clist);
-        cmd->func(cmd->nargs, cmd->args);
-    }
-    svc->NotifyStateChange("restarting");
     return true;
 }
 
diff --git a/init/ueventd.cpp b/init/ueventd.cpp
index c63fdaa..75924cb 100644
--- a/init/ueventd.cpp
+++ b/init/ueventd.cpp
@@ -59,11 +59,10 @@
     cb.func_log = selinux_klog_callback;
     selinux_set_callback(SELINUX_CB_LOG, cb);
 
-    char hardware[PROP_VALUE_MAX];
-    property_get("ro.hardware", hardware);
+    std::string hardware = property_get("ro.hardware");
 
     ueventd_parse_config_file("/ueventd.rc");
-    ueventd_parse_config_file(android::base::StringPrintf("/ueventd.%s.rc", hardware).c_str());
+    ueventd_parse_config_file(android::base::StringPrintf("/ueventd.%s.rc", hardware.c_str()).c_str());
 
     device_init();
 
diff --git a/init/ueventd_parser.cpp b/init/ueventd_parser.cpp
index 497c606..09f4638 100644
--- a/init/ueventd_parser.cpp
+++ b/init/ueventd_parser.cpp
@@ -233,7 +233,6 @@
 
     data.push_back('\n'); // TODO: fix parse_config.
     parse_config(fn, data);
-    dump_parser_state();
     return 0;
 }
 
diff --git a/init/util.cpp b/init/util.cpp
index a5392c6..b316d6e 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -33,6 +33,7 @@
 #include <sys/un.h>
 
 #include <base/file.h>
+#include <base/strings.h>
 
 /* for ANDROID_SOCKET_* */
 #include <cutils/sockets.h>
@@ -42,11 +43,12 @@
 
 #include "init.h"
 #include "log.h"
+#include "property_service.h"
 #include "util.h"
 
 /*
  * android_name_to_id - returns the integer uid/gid associated with the given
- * name, or -1U on error.
+ * name, or UINT_MAX on error.
  */
 static unsigned int android_name_to_id(const char *name)
 {
@@ -58,27 +60,35 @@
             return info[n].aid;
     }
 
-    return -1U;
+    return UINT_MAX;
 }
 
-/*
- * decode_uid - decodes and returns the given string, which can be either the
- * numeric or name representation, into the integer uid or gid. Returns -1U on
- * error.
- */
-unsigned int decode_uid(const char *s)
+static unsigned int do_decode_uid(const char *s)
 {
     unsigned int v;
 
     if (!s || *s == '\0')
-        return -1U;
+        return UINT_MAX;
     if (isalpha(s[0]))
         return android_name_to_id(s);
 
     errno = 0;
     v = (unsigned int) strtoul(s, 0, 0);
     if (errno)
-        return -1U;
+        return UINT_MAX;
+    return v;
+}
+
+/*
+ * decode_uid - decodes and returns the given string, which can be either the
+ * numeric or name representation, into the integer uid or gid. Returns
+ * UINT_MAX on error.
+ */
+unsigned int decode_uid(const char *s) {
+    unsigned int v = do_decode_uid(s);
+    if (v == UINT_MAX) {
+        ERROR("decode_uid: Unable to find UID for '%s'. Returning UINT_MAX\n", s);
+    }
     return v;
 }
 
@@ -401,32 +411,16 @@
     }
 }
 
-void import_kernel_cmdline(bool in_qemu, std::function<void(char*,bool)> import_kernel_nv)
-{
-    char cmdline[2048];
-    char *ptr;
-    int fd;
+void import_kernel_cmdline(bool in_qemu,
+                           std::function<void(const std::string&, const std::string&, bool)> fn) {
+    std::string cmdline;
+    android::base::ReadFileToString("/proc/cmdline", &cmdline);
 
-    fd = open("/proc/cmdline", O_RDONLY | O_CLOEXEC);
-    if (fd >= 0) {
-        int n = read(fd, cmdline, sizeof(cmdline) - 1);
-        if (n < 0) n = 0;
-
-        /* get rid of trailing newline, it happens */
-        if (n > 0 && cmdline[n-1] == '\n') n--;
-
-        cmdline[n] = 0;
-        close(fd);
-    } else {
-        cmdline[0] = 0;
-    }
-
-    ptr = cmdline;
-    while (ptr && *ptr) {
-        char *x = strchr(ptr, ' ');
-        if (x != 0) *x++ = 0;
-        import_kernel_nv(ptr, in_qemu);
-        ptr = x;
+    for (const auto& entry : android::base::Split(android::base::Trim(cmdline), " ")) {
+        std::vector<std::string> pieces = android::base::Split(entry, "=");
+        if (pieces.size() == 2) {
+            fn(pieces[0], pieces[1], in_qemu);
+        }
     }
 }
 
@@ -472,3 +466,84 @@
         android::base::StringAppendF(&hex, "%02x", bytes[i]);
     return hex;
 }
+
+/*
+ * Returns true is pathname is a directory
+ */
+bool is_dir(const char* pathname) {
+    struct stat info;
+    if (stat(pathname, &info) == -1) {
+        return false;
+    }
+    return S_ISDIR(info.st_mode);
+}
+
+bool expand_props(const std::string& src, std::string* dst) {
+    const char* src_ptr = src.c_str();
+
+    if (!dst) {
+        return false;
+    }
+
+    /* - variables can either be $x.y or ${x.y}, in case they are only part
+     *   of the string.
+     * - will accept $$ as a literal $.
+     * - no nested property expansion, i.e. ${foo.${bar}} is not supported,
+     *   bad things will happen
+     */
+    while (*src_ptr) {
+        const char* c;
+
+        c = strchr(src_ptr, '$');
+        if (!c) {
+            dst->append(src_ptr);
+            return true;
+        }
+
+        dst->append(src_ptr, c);
+        c++;
+
+        if (*c == '$') {
+            dst->push_back(*(c++));
+            src_ptr = c;
+            continue;
+        } else if (*c == '\0') {
+            return true;
+        }
+
+        std::string prop_name;
+        if (*c == '{') {
+            c++;
+            const char* end = strchr(c, '}');
+            if (!end) {
+                // failed to find closing brace, abort.
+                ERROR("unexpected end of string in '%s', looking for }\n", src.c_str());
+                return false;
+            }
+            prop_name = std::string(c, end);
+            c = end + 1;
+        } else {
+            prop_name = c;
+            ERROR("using deprecated syntax for specifying property '%s', use ${name} instead\n",
+                  c);
+            c += prop_name.size();
+        }
+
+        if (prop_name.empty()) {
+            ERROR("invalid zero-length prop name in '%s'\n", src.c_str());
+            return false;
+        }
+
+        std::string prop_val = property_get(prop_name.c_str());
+        if (prop_val.empty()) {
+            ERROR("property '%s' doesn't exist while expanding '%s'\n",
+                  prop_name.c_str(), src.c_str());
+            return false;
+        }
+
+        dst->append(prop_val);
+        src_ptr = c;
+    }
+
+    return true;
+}
diff --git a/init/util.h b/init/util.h
index 09d64cd..c2efb01 100644
--- a/init/util.h
+++ b/init/util.h
@@ -58,9 +58,12 @@
 void remove_link(const char *oldpath, const char *newpath);
 int wait_for_file(const char *filename, int timeout);
 void open_devnull_stdio(void);
-void import_kernel_cmdline(bool in_qemu, std::function<void(char*,bool)>);
+void import_kernel_cmdline(bool in_qemu,
+                           std::function<void(const std::string&, const std::string&, bool)>);
 int make_dir(const char *path, mode_t mode);
 int restorecon(const char *pathname);
 int restorecon_recursive(const char *pathname);
 std::string bytes_to_hex(const uint8_t *bytes, size_t bytes_len);
+bool is_dir(const char* pathname);
+bool expand_props(const std::string& src, std::string* dst);
 #endif
diff --git a/init/util_test.cpp b/init/util_test.cpp
index 5b3ab50..228954b 100644
--- a/init/util_test.cpp
+++ b/init/util_test.cpp
@@ -38,6 +38,6 @@
 
 TEST(util, decode_uid) {
   EXPECT_EQ(0U, decode_uid("root"));
-  EXPECT_EQ(-1U, decode_uid("toot"));
+  EXPECT_EQ(UINT_MAX, decode_uid("toot"));
   EXPECT_EQ(123U, decode_uid("123"));
 }
diff --git a/init/watchdogd.cpp b/init/watchdogd.cpp
index 881a4df..0d16db9 100644
--- a/init/watchdogd.cpp
+++ b/init/watchdogd.cpp
@@ -38,29 +38,30 @@
     int margin = 10;
     if (argc >= 3) margin = atoi(argv[2]);
 
-    NOTICE("watchdogd started (interval %d, margin %d)!\n", interval, margin);
+    NOTICE("started (interval %d, margin %d)!\n", interval, margin);
 
     int fd = open(DEV_NAME, O_RDWR|O_CLOEXEC);
     if (fd == -1) {
-        ERROR("watchdogd: Failed to open %s: %s\n", DEV_NAME, strerror(errno));
+        ERROR("Failed to open %s: %s\n", DEV_NAME, strerror(errno));
         return 1;
     }
 
     int timeout = interval + margin;
     int ret = ioctl(fd, WDIOC_SETTIMEOUT, &timeout);
     if (ret) {
-        ERROR("watchdogd: Failed to set timeout to %d: %s\n", timeout, strerror(errno));
+        ERROR("Failed to set timeout to %d: %s\n", timeout, strerror(errno));
         ret = ioctl(fd, WDIOC_GETTIMEOUT, &timeout);
         if (ret) {
-            ERROR("watchdogd: Failed to get timeout: %s\n", strerror(errno));
+            ERROR("Failed to get timeout: %s\n", strerror(errno));
         } else {
             if (timeout > margin) {
                 interval = timeout - margin;
             } else {
                 interval = 1;
             }
-            ERROR("watchdogd: Adjusted interval to timeout returned by driver: timeout %d, interval %d, margin %d\n",
-                  timeout, interval, margin);
+            WARNING("Adjusted interval to timeout returned by driver:"
+                    " timeout %d, interval %d, margin %d\n",
+                    timeout, interval, margin);
         }
     }
 
diff --git a/libbacktrace/Android.build.mk b/libbacktrace/Android.build.mk
index 35fed6d..4983b55 100644
--- a/libbacktrace/Android.build.mk
+++ b/libbacktrace/Android.build.mk
@@ -20,7 +20,7 @@
 LOCAL_MODULE_TAGS := $(module_tag)
 LOCAL_MULTILIB := $($(module)_multilib)
 ifeq ($(LOCAL_MULTILIB),both)
-ifneq ($(build_target),$(filter $(build_target),SHARED_LIBRARY STATIC_LIBRRARY))
+ifneq ($(build_target),$(filter $(build_target),SHARED_LIBRARY STATIC_LIBRARY))
   LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
   LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
 endif
diff --git a/libbacktrace/Android.mk b/libbacktrace/Android.mk
index 6a689a6..395d677 100644
--- a/libbacktrace/Android.mk
+++ b/libbacktrace/Android.mk
@@ -51,18 +51,10 @@
 	UnwindMap.cpp \
 	UnwindPtrace.cpp \
 
-libbacktrace_shared_libraries_target := \
-	libcutils \
-
 libbacktrace_shared_libraries := \
 	libbase \
-	libunwind \
-
-libbacktrace_shared_libraries_host := \
 	liblog \
-
-libbacktrace_static_libraries_host := \
-	libcutils \
+	libunwind \
 
 libbacktrace_ldlibs_host := \
 	-lpthread \
@@ -76,6 +68,14 @@
 build_type := host
 libbacktrace_multilib := both
 include $(LOCAL_PATH)/Android.build.mk
+libbacktrace_static_libraries := \
+	libbase \
+	liblog \
+	libunwind \
+
+build_target := STATIC_LIBRARY
+include $(LOCAL_PATH)/Android.build.mk
+libbacktrace_static_libraries :=
 
 #-------------------------------------------------------------------------
 # The libbacktrace_test library needed by backtrace_test.
diff --git a/libbacktrace/BacktraceMap.cpp b/libbacktrace/BacktraceMap.cpp
index b0ada46..ca47f67 100644
--- a/libbacktrace/BacktraceMap.cpp
+++ b/libbacktrace/BacktraceMap.cpp
@@ -135,7 +135,7 @@
 #if defined(__APPLE__)
 // Corkscrew and libunwind don't compile on the mac, so create a generic
 // map object.
-BacktraceMap* BacktraceMap::Create(pid_t pid, bool uncached) {
+BacktraceMap* BacktraceMap::Create(pid_t pid, bool /*uncached*/) {
   BacktraceMap* map = new BacktraceMap(pid);
   if (!map->Build()) {
     delete map;
diff --git a/libbacktrace/backtrace_test.cpp b/libbacktrace/backtrace_test.cpp
index c650755..9ebd639 100644
--- a/libbacktrace/backtrace_test.cpp
+++ b/libbacktrace/backtrace_test.cpp
@@ -983,8 +983,8 @@
           << "Offset at " << i << " length " << j << " wrote too much data";
     }
   }
-  delete data;
-  delete expected;
+  delete[] data;
+  delete[] expected;
 }
 
 TEST(libbacktrace, thread_read) {
diff --git a/libcutils/Android.mk b/libcutils/Android.mk
index 0963076..3ca544f 100644
--- a/libcutils/Android.mk
+++ b/libcutils/Android.mk
@@ -37,19 +37,7 @@
 # some files must not be compiled when building against Mingw
 # they correspond to features not used by our host development tools
 # which are also hard or even impossible to port to native Win32
-WINDOWS_HOST_ONLY :=
-ifeq ($(HOST_OS),windows)
-    ifeq ($(strip $(USE_CYGWIN)),)
-        WINDOWS_HOST_ONLY := 1
-    endif
-endif
-# USE_MINGW is defined when we build against Mingw on Linux
-ifneq ($(strip $(USE_MINGW)),)
-    WINDOWS_HOST_ONLY := 1
-endif
-
-ifneq ($(WINDOWS_HOST_ONLY),1)
-    commonSources += \
+nonWindowsSources := \
         fs.c \
         multiuser.c \
         socket_inaddr_any_server.c \
@@ -60,31 +48,32 @@
         socket_network_client.c \
         sockets.c \
 
-    commonHostSources += \
+nonWindowsHostSources := \
         ashmem-host.c \
         trace-host.c
 
-endif
-
 
 # Shared and static library for host
 # ========================================================
 LOCAL_MODULE := libcutils
-LOCAL_SRC_FILES := $(commonSources) $(commonHostSources) dlmalloc_stubs.c
+LOCAL_SRC_FILES := $(commonSources) dlmalloc_stubs.c
+LOCAL_SRC_FILES_darwin := $(nonWindowsSources) $(nonWindowsHostSources)
+LOCAL_SRC_FILES_linux := $(nonWindowsSources) $(nonWindowsHostSources)
 LOCAL_STATIC_LIBRARIES := liblog
-ifneq ($(HOST_OS),windows)
-LOCAL_CFLAGS += -Werror
-endif
+LOCAL_CFLAGS_darwin := -Werror -Wall -Wextra
+LOCAL_CFLAGS_linux := -Werror -Wall -Wextra
 LOCAL_MULTILIB := both
+LOCAL_MODULE_HOST_OS := darwin linux windows
 include $(BUILD_HOST_STATIC_LIBRARY)
 
 include $(CLEAR_VARS)
 LOCAL_MODULE := libcutils
-LOCAL_SRC_FILES := $(commonSources) $(commonHostSources) dlmalloc_stubs.c
+LOCAL_SRC_FILES := $(commonSources) dlmalloc_stubs.c
+LOCAL_SRC_FILES_darwin := $(nonWindowsSources) $(nonWindowsHostSources)
+LOCAL_SRC_FILES_linux := $(nonWindowsSources) $(nonWindowsHostSources)
 LOCAL_SHARED_LIBRARIES := liblog
-ifneq ($(HOST_OS),windows)
-LOCAL_CFLAGS += -Werror
-endif
+LOCAL_CFLAGS_darwin := -Werror -Wall -Wextra
+LOCAL_CFLAGS_linux := -Werror -Wall -Wextra
 LOCAL_MULTILIB := both
 include $(BUILD_HOST_SHARED_LIBRARY)
 
@@ -96,6 +85,7 @@
 include $(CLEAR_VARS)
 LOCAL_MODULE := libcutils
 LOCAL_SRC_FILES := $(commonSources) \
+        $(nonWindowsSources) \
         android_reboot.c \
         ashmem-dev.c \
         debugger.c \
@@ -106,9 +96,6 @@
         trace-dev.c \
         uevent.c \
 
-# arch-arm/memset32.S does not compile with Clang.
-LOCAL_CLANG_ASFLAGS_arm += -no-integrated-as
-
 LOCAL_SRC_FILES_arm += arch-arm/memset32.S
 LOCAL_SRC_FILES_arm64 += arch-arm64/android_memset.S
 
@@ -128,7 +115,9 @@
 ifneq ($(ENABLE_CPUSETS),)
 LOCAL_CFLAGS += -DUSE_CPUSETS
 endif
-LOCAL_CFLAGS += -Werror -std=gnu90
+LOCAL_CFLAGS += -Werror -Wall -Wextra -std=gnu90
+LOCAL_CLANG := true
+LOCAL_SANITIZE := integer
 include $(BUILD_STATIC_LIBRARY)
 
 include $(CLEAR_VARS)
@@ -140,8 +129,10 @@
 ifneq ($(ENABLE_CPUSETS),)
 LOCAL_CFLAGS += -DUSE_CPUSETS
 endif
-LOCAL_CFLAGS += -Werror
+LOCAL_CFLAGS += -Werror -Wall -Wextra
 LOCAL_C_INCLUDES := $(libcutils_c_includes)
+LOCAL_CLANG := true
+LOCAL_SANITIZE := integer
 include $(BUILD_SHARED_LIBRARY)
 
 include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/libcutils/android_reboot.c b/libcutils/android_reboot.c
index 6ae23c1..af7e189 100644
--- a/libcutils/android_reboot.c
+++ b/libcutils/android_reboot.c
@@ -14,43 +14,108 @@
  * limitations under the License.
  */
 
-#include <unistd.h>
-#include <sys/reboot.h>
-#include <sys/syscall.h>
-#include <sys/types.h>
-#include <sys/stat.h>
+#include <errno.h>
 #include <fcntl.h>
 #include <mntent.h>
+#include <stdbool.h>
 #include <stdio.h>
+#include <stdlib.h>
 #include <string.h>
+#include <sys/cdefs.h>
+#include <sys/mount.h>
+#include <sys/reboot.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <unistd.h>
 
 #include <cutils/android_reboot.h>
+#include <cutils/klog.h>
+#include <cutils/list.h>
 
-#define UNUSED __attribute__((unused))
+#define TAG "android_reboot"
+#define READONLY_CHECK_MS 5000
+#define READONLY_CHECK_TIMES 50
 
-/* Check to see if /proc/mounts contains any writeable filesystems
- * backed by a block device.
- * Return true if none found, else return false.
+typedef struct {
+    struct listnode list;
+    struct mntent entry;
+} mntent_list;
+
+static bool has_mount_option(const char* opts, const char* opt_to_find)
+{
+  bool ret = false;
+  char* copy = NULL;
+  char* opt;
+  char* rem;
+
+  while ((opt = strtok_r(copy ? NULL : (copy = strdup(opts)), ",", &rem))) {
+      if (!strcmp(opt, opt_to_find)) {
+          ret = true;
+          break;
+      }
+  }
+
+  free(copy);
+  return ret;
+}
+
+static bool is_block_device(const char* fsname)
+{
+    return !strncmp(fsname, "/dev/block", 10);
+}
+
+/* Find all read+write block devices in /proc/mounts and add them to
+ * |rw_entries|.
  */
-static int remount_ro_done(void)
+static void find_rw(struct listnode* rw_entries)
 {
     FILE* fp;
     struct mntent* mentry;
-    int found_rw_fs = 0;
 
     if ((fp = setmntent("/proc/mounts", "r")) == NULL) {
-        /* If we can't read /proc/mounts, just give up. */
-        return 1;
+        KLOG_WARNING(TAG, "Failed to open /proc/mounts.\n");
+        return;
     }
     while ((mentry = getmntent(fp)) != NULL) {
-        if (!strncmp(mentry->mnt_fsname, "/dev/block", 10) && strstr(mentry->mnt_opts, "rw,")) {
-            found_rw_fs = 1;
-            break;
+        if (is_block_device(mentry->mnt_fsname) &&
+            has_mount_option(mentry->mnt_opts, "rw")) {
+            mntent_list* item = (mntent_list*)calloc(1, sizeof(mntent_list));
+            item->entry = *mentry;
+            item->entry.mnt_fsname = strdup(mentry->mnt_fsname);
+            item->entry.mnt_dir = strdup(mentry->mnt_dir);
+            item->entry.mnt_type = strdup(mentry->mnt_type);
+            item->entry.mnt_opts = strdup(mentry->mnt_opts);
+            list_add_tail(rw_entries, &item->list);
         }
     }
     endmntent(fp);
+}
 
-    return !found_rw_fs;
+static void free_entries(struct listnode* entries)
+{
+    struct listnode* node;
+    struct listnode* n;
+    list_for_each_safe(node, n, entries) {
+        mntent_list* item = node_to_item(node, mntent_list, list);
+        free(item->entry.mnt_fsname);
+        free(item->entry.mnt_dir);
+        free(item->entry.mnt_type);
+        free(item->entry.mnt_opts);
+        free(item);
+    }
+}
+
+static mntent_list* find_item(struct listnode* rw_entries, const char* fsname_to_find)
+{
+    struct listnode* node;
+    list_for_each(node, rw_entries) {
+        mntent_list* item = node_to_item(node, mntent_list, list);
+        if (!strcmp(item->entry.mnt_fsname, fsname_to_find)) {
+            return item;
+        }
+    }
+    return NULL;
 }
 
 /* Remounting filesystems read-only is difficult when there are files
@@ -64,38 +129,92 @@
  * repeatedly until there are no more writable filesystems mounted on
  * block devices.
  */
-static void remount_ro(void)
+static void remount_ro(void (*cb_on_remount)(const struct mntent*))
 {
-    int fd, cnt = 0;
+    int fd, cnt;
+    FILE* fp;
+    struct mntent* mentry;
+    struct listnode* node;
+
+    list_declare(rw_entries);
+    list_declare(ro_entries);
+
+    sync();
+    find_rw(&rw_entries);
 
     /* Trigger the remount of the filesystems as read-only,
      * which also marks them clean.
      */
-    fd = open("/proc/sysrq-trigger", O_WRONLY);
+    fd = TEMP_FAILURE_RETRY(open("/proc/sysrq-trigger", O_WRONLY));
     if (fd < 0) {
-        return;
+        KLOG_WARNING(TAG, "Failed to open sysrq-trigger.\n");
+        /* TODO: Try to remount each rw parition manually in readonly mode.
+         * This may succeed if no process is using the partition.
+         */
+        goto out;
     }
-    write(fd, "u", 1);
+    if (TEMP_FAILURE_RETRY(write(fd, "u", 1)) != 1) {
+        close(fd);
+        KLOG_WARNING(TAG, "Failed to write to sysrq-trigger.\n");
+        /* TODO: The same. Manually remount the paritions. */
+        goto out;
+    }
     close(fd);
 
-
     /* Now poll /proc/mounts till it's done */
-    while (!remount_ro_done() && (cnt < 50)) {
-        usleep(100000);
+    cnt = 0;
+    while (cnt < READONLY_CHECK_TIMES) {
+        if ((fp = setmntent("/proc/mounts", "r")) == NULL) {
+            /* If we can't read /proc/mounts, just give up. */
+            KLOG_WARNING(TAG, "Failed to open /proc/mounts.\n");
+            goto out;
+        }
+        while ((mentry = getmntent(fp)) != NULL) {
+            if (!is_block_device(mentry->mnt_fsname) ||
+                !has_mount_option(mentry->mnt_opts, "ro")) {
+                continue;
+            }
+            mntent_list* item = find_item(&rw_entries, mentry->mnt_fsname);
+            if (item) {
+                /* |item| has now been ro remounted. */
+                list_remove(&item->list);
+                list_add_tail(&ro_entries, &item->list);
+            }
+        }
+        endmntent(fp);
+        if (list_empty(&rw_entries)) {
+            /* All rw block devices are now readonly. */
+            break;
+        }
+        TEMP_FAILURE_RETRY(
+            usleep(READONLY_CHECK_MS * 1000 / READONLY_CHECK_TIMES));
         cnt++;
     }
 
-    return;
+    list_for_each(node, &rw_entries) {
+        mntent_list* item = node_to_item(node, mntent_list, list);
+        KLOG_WARNING(TAG, "Failed to remount %s in readonly mode.\n",
+                     item->entry.mnt_fsname);
+    }
+
+    if (cb_on_remount) {
+        list_for_each(node, &ro_entries) {
+            mntent_list* item = node_to_item(node, mntent_list, list);
+            cb_on_remount(&item->entry);
+        }
+    }
+
+out:
+    free_entries(&rw_entries);
+    free_entries(&ro_entries);
 }
 
-
-int android_reboot(int cmd, int flags UNUSED, const char *arg)
+int android_reboot_with_callback(
+    int cmd, int flags __unused, const char *arg,
+    void (*cb_on_remount)(const struct mntent*))
 {
     int ret;
-
-    sync();
-    remount_ro();
-
+    remount_ro(cb_on_remount);
     switch (cmd) {
         case ANDROID_RB_RESTART:
             ret = reboot(RB_AUTOBOOT);
@@ -117,3 +236,7 @@
     return ret;
 }
 
+int android_reboot(int cmd, int flags, const char *arg)
+{
+    return android_reboot_with_callback(cmd, flags, arg, NULL);
+}
diff --git a/libcutils/arch-arm/memset32.S b/libcutils/arch-arm/memset32.S
index 6efab9f..1e89636 100644
--- a/libcutils/arch-arm/memset32.S
+++ b/libcutils/arch-arm/memset32.S
@@ -18,6 +18,8 @@
  *
  */
 
+    .syntax unified
+
     .text
     .align
 
@@ -45,7 +47,7 @@
 
         /* align to 32 bits */
         tst         r0, #2
-        strneh      r1, [r0], #2
+        strhne      r1, [r0], #2
         subne       r2, r2, #2
         .fnend
 
@@ -68,27 +70,27 @@
 
         /* 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}
+        stmiacs     r0!, {r1, lr}
+        stmiacs     r0!, {r1, lr}
+        stmiami     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}
+        stmiahs     r0!, {r1,r3,r12,lr}
+        stmiahs     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}
+        stmiacs     r0!, {r1,r3,r12,lr}
+        stmiami     r0!, {r1,lr}
         movs        r2, r2, lsl #2
         strcs       r1, [r0], #4
-        strmih      lr, [r0], #2
+        strhmi      lr, [r0], #2
 
         ldr         lr, [sp], #4
         .cfi_def_cfa_offset 0
diff --git a/libcutils/arch-mips/android_memset.c b/libcutils/arch-mips/android_memset.c
index a6b7496..c0fe3d1 100644
--- a/libcutils/arch-mips/android_memset.c
+++ b/libcutils/arch-mips/android_memset.c
@@ -30,6 +30,9 @@
 
 #include <cutils/memory.h>
 
+#ifdef __clang__
+__attribute__((no_sanitize("integer")))
+#endif
 void android_memset16(uint16_t* dst, uint16_t value, size_t size)
 {
    /* optimized version of
@@ -46,7 +49,7 @@
    }
    /* dst is now 32-bit-aligned */
    /* fill body with 32-bit pairs */
-   uint32_t value32 = (value << 16) | value;
+   uint32_t value32 = (((uint32_t)value) << 16) | ((uint32_t)value);
    android_memset32((uint32_t*) dst, value32, size<<1);
    if (size & 1) {
       dst[size-1] = value;  /* fill unpaired last elem */
@@ -54,6 +57,9 @@
 }
 
 
+#ifdef __clang__
+__attribute__((no_sanitize("integer")))
+#endif
 void android_memset32(uint32_t* dst, uint32_t value, size_t size)
 {
    /* optimized version of
@@ -70,7 +76,7 @@
    }
    /* dst is now 64-bit aligned */
    /* fill body with 64-bit pairs */
-   uint64_t value64 = (((uint64_t)value)<<32) | value;
+   uint64_t value64 = (((uint64_t)value) << 32) | ((uint64_t)value);
    uint64_t* dst64 = (uint64_t*)dst;
 
    while (size >= 12) {
@@ -86,7 +92,8 @@
 
    /* fill remainder with original 32-bit single-elem loop */
    dst = (uint32_t*) dst64;
-   while (size--) {
+   while (size != 0) {
+       size--;
       *dst++ = value;
    }
 
diff --git a/libcutils/fs_config.c b/libcutils/fs_config.c
index 9a1ad19..3a9fb8a 100644
--- a/libcutils/fs_config.c
+++ b/libcutils/fs_config.c
@@ -76,6 +76,7 @@
 
 static const struct fs_path_config android_dirs[] = {
     { 00770, AID_SYSTEM, AID_CACHE,  0, "cache" },
+    { 00500, AID_ROOT,   AID_ROOT,   0, "config" },
     { 00771, AID_SYSTEM, AID_SYSTEM, 0, "data/app" },
     { 00771, AID_SYSTEM, AID_SYSTEM, 0, "data/app-private" },
     { 00771, AID_ROOT,   AID_ROOT,   0, "data/dalvik-cache" },
@@ -88,7 +89,10 @@
     { 00775, AID_MEDIA_RW, AID_MEDIA_RW, 0, "data/media" },
     { 00775, AID_MEDIA_RW, AID_MEDIA_RW, 0, "data/media/Music" },
     { 00771, AID_SYSTEM, AID_SYSTEM, 0, "data" },
+    { 00755, AID_ROOT,   AID_SYSTEM, 0, "mnt" },
+    { 00755, AID_ROOT,   AID_ROOT,   0, "root" },
     { 00750, AID_ROOT,   AID_SHELL,  0, "sbin" },
+    { 00751, AID_ROOT,   AID_SDCARD_R, 0, "storage" },
     { 00755, AID_ROOT,   AID_SHELL,  0, "system/bin" },
     { 00755, AID_ROOT,   AID_SHELL,  0, "system/vendor" },
     { 00755, AID_ROOT,   AID_SHELL,  0, "system/xbin" },
@@ -114,6 +118,7 @@
     { 00550, AID_DHCP,      AID_SHELL,     0, "system/etc/dhcpcd/dhcpcd-run-hooks" },
     { 00555, AID_ROOT,      AID_ROOT,      0, "system/etc/ppp/*" },
     { 00555, AID_ROOT,      AID_ROOT,      0, "system/etc/rc.*" },
+    { 00440, AID_ROOT,      AID_ROOT,      0, "system/etc/recovery.img" },
     { 00444, AID_ROOT,      AID_ROOT,      0, conf_dir + 1 },
     { 00444, AID_ROOT,      AID_ROOT,      0, conf_file + 1 },
     { 00644, AID_SYSTEM,    AID_SYSTEM,    0, "data/app/*" },
@@ -163,8 +168,7 @@
         if (target_out_path[target_out_path_len] == '/') {
             skip_len++;
         }
-        asprintf(&name, "%s%s", target_out_path, (dir ? conf_dir : conf_file) + skip_len);
-        if (name) {
+        if (asprintf(&name, "%s%s", target_out_path, (dir ? conf_dir : conf_file) + skip_len) != -1) {
             fd = TEMP_FAILURE_RETRY(open(name, O_RDONLY | O_BINARY));
             free(name);
         }
diff --git a/libcutils/hashmap.c b/libcutils/hashmap.c
index 65539ea..ede3b98 100644
--- a/libcutils/hashmap.c
+++ b/libcutils/hashmap.c
@@ -77,6 +77,9 @@
 /**
  * Hashes the given key.
  */
+#ifdef __clang__
+__attribute__((no_sanitize("integer")))
+#endif
 static inline int hashKey(Hashmap* map, void* key) {
     int h = map->hash(key);
 
@@ -152,6 +155,10 @@
     free(map);
 }
 
+#ifdef __clang__
+__attribute__((no_sanitize("integer")))
+#endif
+/* FIXME: relies on signed integer overflow, which is undefined behavior */
 int hashmapHash(void* key, size_t keySize) {
     int h = keySize;
     char* data = (char*) key;
diff --git a/libcutils/iosched_policy.c b/libcutils/iosched_policy.c
index 8946d3c..71bc94b 100644
--- a/libcutils/iosched_policy.c
+++ b/libcutils/iosched_policy.c
@@ -23,7 +23,7 @@
 
 #include <cutils/iosched_policy.h>
 
-#ifdef HAVE_ANDROID_OS
+#if defined(__ANDROID__)
 #include <linux/ioprio.h>
 #include <sys/syscall.h>
 #define __android_unused
@@ -32,7 +32,7 @@
 #endif
 
 int android_set_ioprio(int pid __android_unused, IoSchedClass clazz __android_unused, int ioprio __android_unused) {
-#ifdef HAVE_ANDROID_OS
+#if defined(__ANDROID__)
     if (syscall(SYS_ioprio_set, IOPRIO_WHO_PROCESS, pid, ioprio | (clazz << IOPRIO_CLASS_SHIFT))) {
         return -1;
     }
@@ -41,7 +41,7 @@
 }
 
 int android_get_ioprio(int pid __android_unused, IoSchedClass *clazz, int *ioprio) {
-#ifdef HAVE_ANDROID_OS
+#if defined(__ANDROID__)
     int rc;
 
     if ((rc = syscall(SYS_ioprio_get, IOPRIO_WHO_PROCESS, pid)) < 0) {
diff --git a/libcutils/process_name.c b/libcutils/process_name.c
index cc931eb..5d28b6f 100644
--- a/libcutils/process_name.c
+++ b/libcutils/process_name.c
@@ -25,19 +25,19 @@
 #include <unistd.h>
 
 #include <cutils/process_name.h>
-#ifdef HAVE_ANDROID_OS
+#if defined(__ANDROID__)
 #include <cutils/properties.h>
 #endif
 
 #define PROCESS_NAME_DEVICE "/sys/qemu_trace/process_name"
 
 static const char* process_name = "unknown";
-#ifdef HAVE_ANDROID_OS
+#if defined(__ANDROID__)
 static int running_in_emulator = -1;
 #endif
 
 void set_process_name(const char* new_name) {
-#ifdef HAVE_ANDROID_OS
+#if defined(__ANDROID__)
     char  propBuf[PROPERTY_VALUE_MAX];
 #endif
 
@@ -59,7 +59,7 @@
     }
 #endif
 
-#ifdef HAVE_ANDROID_OS
+#if defined(__ANDROID__)
     // If we know we are not running in the emulator, then return.
     if (running_in_emulator == 0) {
         return;
diff --git a/libcutils/record_stream.c b/libcutils/record_stream.c
index 6994904..2bc4226 100644
--- a/libcutils/record_stream.c
+++ b/libcutils/record_stream.c
@@ -22,7 +22,7 @@
 #include <cutils/record_stream.h>
 #include <string.h>
 #include <stdint.h>
-#ifdef HAVE_WINSOCK
+#if defined(_WIN32)
 #include <winsock2.h>   /* for ntohl */
 #else
 #include <netinet/in.h>
diff --git a/libcutils/sched_policy.c b/libcutils/sched_policy.c
index 83222f4..298e3da 100644
--- a/libcutils/sched_policy.c
+++ b/libcutils/sched_policy.c
@@ -37,7 +37,7 @@
    return p == SP_DEFAULT ? SP_SYSTEM_DEFAULT : p;
 }
 
-#if defined(HAVE_ANDROID_OS)
+#if defined(__ANDROID__)
 
 #include <pthread.h>
 #include <sched.h>
@@ -60,9 +60,11 @@
 static int bg_cgroup_fd = -1;
 static int fg_cgroup_fd = -1;
 
+#ifdef USE_CPUSETS
 // File descriptors open to /dev/cpuset/../tasks, setup by initialize, or -1 on error
 static int bg_cpuset_fd = -1;
 static int fg_cpuset_fd = -1;
+#endif
 
 /* Add tid to the scheduling group defined by the policy */
 static int add_tid_to_cgroup(int tid, int fd)
@@ -144,7 +146,7 @@
  */
 static int getSchedulerGroup(int tid, char* buf, size_t bufLen)
 {
-#ifdef HAVE_ANDROID_OS
+#if defined(__ANDROID__)
     char pathBuf[32];
     char lineBuf[256];
     FILE *fp;
diff --git a/libcutils/socket_inaddr_any_server.c b/libcutils/socket_inaddr_any_server.c
index 6c849de..7f0ccb8 100644
--- a/libcutils/socket_inaddr_any_server.c
+++ b/libcutils/socket_inaddr_any_server.c
@@ -20,7 +20,7 @@
 #include <string.h>
 #include <unistd.h>
 
-#ifndef HAVE_WINSOCK
+#if !defined(_WIN32)
 #include <sys/socket.h>
 #include <sys/select.h>
 #include <sys/types.h>
diff --git a/libcutils/socket_local_client.c b/libcutils/socket_local_client.c
index 7b42daa..2526146 100644
--- a/libcutils/socket_local_client.c
+++ b/libcutils/socket_local_client.c
@@ -22,7 +22,7 @@
 
 #include <cutils/sockets.h>
 
-#ifdef HAVE_WINSOCK
+#if defined(_WIN32)
 
 int socket_local_client(const char *name, int namespaceId, int type)
 {
@@ -30,7 +30,7 @@
     return -1;
 }
 
-#else /* !HAVE_WINSOCK */
+#else /* !_WIN32 */
 
 #include <sys/socket.h>
 #include <sys/un.h>
@@ -165,4 +165,4 @@
     return s;
 }
 
-#endif /* !HAVE_WINSOCK */
+#endif /* !_WIN32 */
diff --git a/libcutils/socket_local_server.c b/libcutils/socket_local_server.c
index 60eb86b..c9acdad 100644
--- a/libcutils/socket_local_server.c
+++ b/libcutils/socket_local_server.c
@@ -23,7 +23,7 @@
 #include <errno.h>
 #include <stddef.h>
 
-#ifdef HAVE_WINSOCK
+#if defined(_WIN32)
 
 int socket_local_server(const char *name, int namespaceId, int type)
 {
@@ -31,7 +31,7 @@
     return -1;
 }
 
-#else /* !HAVE_WINSOCK */
+#else /* !_WIN32 */
 
 #include <sys/socket.h>
 #include <sys/un.h>
@@ -123,4 +123,4 @@
     return s;
 }
 
-#endif /* !HAVE_WINSOCK */
+#endif /* !_WIN32 */
diff --git a/libcutils/socket_loopback_client.c b/libcutils/socket_loopback_client.c
index 9aed7b7..e14cffb 100644
--- a/libcutils/socket_loopback_client.c
+++ b/libcutils/socket_loopback_client.c
@@ -20,7 +20,7 @@
 #include <string.h>
 #include <unistd.h>
 
-#ifndef HAVE_WINSOCK
+#if !defined(_WIN32)
 #include <sys/socket.h>
 #include <sys/select.h>
 #include <sys/types.h>
diff --git a/libcutils/socket_loopback_server.c b/libcutils/socket_loopback_server.c
index 71afce7..b600e34 100644
--- a/libcutils/socket_loopback_server.c
+++ b/libcutils/socket_loopback_server.c
@@ -22,7 +22,7 @@
 
 #define LISTEN_BACKLOG 4
 
-#ifndef HAVE_WINSOCK
+#if !defined(_WIN32)
 #include <sys/socket.h>
 #include <sys/select.h>
 #include <sys/types.h>
diff --git a/libcutils/socket_network_client.c b/libcutils/socket_network_client.c
index e0031ba..3300b8f 100644
--- a/libcutils/socket_network_client.c
+++ b/libcutils/socket_network_client.c
@@ -29,77 +29,78 @@
 
 #include <cutils/sockets.h>
 
-/* 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)
-{
-    return socket_network_client_timeout(host, port, type, 0);
+static int toggle_O_NONBLOCK(int s) {
+    int flags = fcntl(s, F_GETFL);
+    if (flags == -1 || fcntl(s, F_SETFL, flags ^ O_NONBLOCK) == -1) {
+        close(s);
+        return -1;
+    }
+    return s;
 }
 
-/* Connect to port on the IP interface. type is SOCK_STREAM or SOCK_DGRAM.
- * timeout in seconds return is a file descriptor or -1 on error
- */
-int socket_network_client_timeout(const char *host, int port, int type, int timeout)
-{
-    struct hostent *hp;
-    struct sockaddr_in addr;
-    int s;
-    int flags = 0, error = 0, ret = 0;
-    fd_set rset, wset;
-    socklen_t len = sizeof(error);
-    struct timeval ts;
+// Connect to the given host and port.
+// 'timeout' is in seconds (0 for no timeout).
+// Returns a file descriptor or -1 on error.
+// On error, check *getaddrinfo_error (for use with gai_strerror) first;
+// if that's 0, use errno instead.
+int socket_network_client_timeout(const char* host, int port, int type, int timeout,
+                                  int* getaddrinfo_error) {
+    struct addrinfo hints;
+    memset(&hints, 0, sizeof(hints));
+    hints.ai_family = AF_UNSPEC;
+    hints.ai_socktype = type;
 
+    char port_str[16];
+    snprintf(port_str, sizeof(port_str), "%d", port);
+
+    struct addrinfo* addrs;
+    *getaddrinfo_error = getaddrinfo(host, port_str, &hints, &addrs);
+    if (*getaddrinfo_error != 0) {
+        return -1;
+    }
+
+    // TODO: try all the addresses if there's more than one?
+    int family = addrs[0].ai_family;
+    int protocol = addrs[0].ai_protocol;
+    socklen_t addr_len = addrs[0].ai_addrlen;
+    struct sockaddr_storage addr;
+    memcpy(&addr, addrs[0].ai_addr, addr_len);
+
+    freeaddrinfo(addrs);
+
+    // The Mac doesn't have SOCK_NONBLOCK.
+    int s = socket(family, type, protocol);
+    if (s == -1 || toggle_O_NONBLOCK(s) == -1) return -1;
+
+    int rc = connect(s, (const struct sockaddr*) &addr, addr_len);
+    if (rc == 0) {
+        return toggle_O_NONBLOCK(s);
+    } else if (rc == -1 && errno != EINPROGRESS) {
+        close(s);
+        return -1;
+    }
+
+    fd_set r_set;
+    FD_ZERO(&r_set);
+    FD_SET(s, &r_set);
+    fd_set w_set = r_set;
+
+    struct timeval ts;
     ts.tv_sec = timeout;
     ts.tv_usec = 0;
-
-    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 ((flags = fcntl(s, F_GETFL, 0)) < 0) {
+    if ((rc = select(s + 1, &r_set, &w_set, NULL, (timeout != 0) ? &ts : NULL)) == -1) {
         close(s);
         return -1;
     }
-
-    if (fcntl(s, F_SETFL, flags | O_NONBLOCK) < 0) {
-        close(s);
-        return -1;
-    }
-
-    if ((ret = connect(s, (struct sockaddr *) &addr, sizeof(addr))) < 0) {
-        if (errno != EINPROGRESS) {
-            close(s);
-            return -1;
-        }
-    }
-
-    if (ret == 0)
-        goto done;
-
-    FD_ZERO(&rset);
-    FD_SET(s, &rset);
-    wset = rset;
-
-    if ((ret = select(s + 1, &rset, &wset, NULL, (timeout) ? &ts : NULL)) < 0) {
-        close(s);
-        return -1;
-    }
-    if (ret == 0) {   // we had a timeout
+    if (rc == 0) {   // we had a timeout
         errno = ETIMEDOUT;
         close(s);
         return -1;
     }
 
-    if (FD_ISSET(s, &rset) || FD_ISSET(s, &wset)) {
+    int error = 0;
+    socklen_t len = sizeof(error);
+    if (FD_ISSET(s, &r_set) || FD_ISSET(s, &w_set)) {
         if (getsockopt(s, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
             close(s);
             return -1;
@@ -115,11 +116,10 @@
         return -1;
     }
 
-done:
-    if (fcntl(s, F_SETFL, flags) < 0) {
-        close(s);
-        return -1;
-    }
+    return toggle_O_NONBLOCK(s);
+}
 
-    return s;
+int socket_network_client(const char* host, int port, int type) {
+    int getaddrinfo_error;
+    return socket_network_client_timeout(host, port, type, 0, &getaddrinfo_error);
 }
diff --git a/libcutils/sockets.c b/libcutils/sockets.c
index 15ede2b..d438782 100644
--- a/libcutils/sockets.c
+++ b/libcutils/sockets.c
@@ -17,7 +17,7 @@
 #include <cutils/sockets.h>
 #include <log/log.h>
 
-#ifdef HAVE_ANDROID_OS
+#if defined(__ANDROID__)
 /* For the socket trust (credentials) check */
 #include <private/android_filesystem_config.h>
 #define __android_unused
@@ -27,7 +27,7 @@
 
 bool socket_peer_is_trusted(int fd __android_unused)
 {
-#ifdef HAVE_ANDROID_OS
+#if defined(__ANDROID__)
     struct ucred cr;
     socklen_t len = sizeof(cr);
     int n = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cr, &len);
diff --git a/libcutils/str_parms.c b/libcutils/str_parms.c
index 924289a..4f23d09 100644
--- a/libcutils/str_parms.c
+++ b/libcutils/str_parms.c
@@ -42,6 +42,9 @@
 }
 
 /* use djb hash unless we find it inadequate */
+#ifdef __clang__
+__attribute__((no_sanitize("integer")))
+#endif
 static int str_hash_fn(void *str)
 {
     uint32_t hash = 5381;
diff --git a/libcutils/strdup16to8.c b/libcutils/strdup16to8.c
index 1a8ba86..4dc987e 100644
--- a/libcutils/strdup16to8.c
+++ b/libcutils/strdup16to8.c
@@ -55,7 +55,8 @@
     /* Fast path for the usual case where 3*len is < SIZE_MAX-1.
      */
     if (len < (SIZE_MAX-1)/3) {
-        while (len--) {
+        while (len != 0) {
+            len--;
             unsigned int uic = *utf16Str++;
 
             if (uic > 0x07ff)
@@ -69,7 +70,8 @@
     }
 
     /* The slower but paranoid version */
-    while (len--) {
+    while (len != 0) {
+        len--;
         unsigned int  uic     = *utf16Str++;
         size_t        utf8Cur = utf8Len;
 
@@ -112,7 +114,8 @@
      * strnlen16to8() properly or at a minimum checked the result of
      * its malloc(SIZE_MAX) in case of overflow.
      */
-    while (len--) {
+    while (len != 0) {
+        len--;
         unsigned int uic = *utf16Str++;
 
         if (uic > 0x07ff) {
diff --git a/liblog/Android.mk b/liblog/Android.mk
index d7766f5..3a1a9db 100644
--- a/liblog/Android.mk
+++ b/liblog/Android.mk
@@ -24,50 +24,32 @@
 # so make sure we do not regret hard-coding it as follows:
 liblog_cflags := -DLIBLOG_LOG_TAG=1005
 
-ifneq ($(TARGET_USES_LOGD),false)
-liblog_sources := logd_write.c
-else
-liblog_sources := logd_write_kern.c
-endif
-
-# 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
-
-ifeq ($(strip $(USE_MINGW)),)
-    liblog_sources += \
-        event_tag_map.c
-else
-    liblog_sources += \
-        uio.c
-endif
-
-liblog_host_sources := $(liblog_sources) fake_log_device.c event.logtags
-liblog_target_sources := $(liblog_sources) log_time.cpp log_is_loggable.c
-ifeq ($(strip $(USE_MINGW)),)
+liblog_host_sources := logd_write.c log_event_write.c fake_log_device.c event.logtags
+liblog_target_sources := logd_write.c log_event_write.c event_tag_map.c log_time.cpp log_is_loggable.c
 liblog_target_sources += logprint.c
-endif
-ifneq ($(TARGET_USES_LOGD),false)
 liblog_target_sources += log_read.c
-else
-liblog_target_sources += log_read_kern.c
-endif
 
 # Shared and static library for host
 # ========================================================
 LOCAL_MODULE := liblog
 LOCAL_SRC_FILES := $(liblog_host_sources)
+# 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
+LOCAL_SRC_FILES_darwin := event_tag_map.c
+LOCAL_SRC_FILES_linux := event_tag_map.c
+LOCAL_SRC_FILES_windows := uio.c
 LOCAL_CFLAGS := -DFAKE_LOG_DEVICE=1 -Werror $(liblog_cflags)
 LOCAL_MULTILIB := both
+LOCAL_MODULE_HOST_OS := darwin linux windows
 include $(BUILD_HOST_STATIC_LIBRARY)
 
 include $(CLEAR_VARS)
 LOCAL_MODULE := liblog
 LOCAL_WHOLE_STATIC_LIBRARIES := liblog
-ifeq ($(strip $(HOST_OS)),linux)
-LOCAL_LDLIBS := -lrt
-endif
+LOCAL_LDLIBS_linux := -lrt
 LOCAL_MULTILIB := both
+LOCAL_CXX_STL := none
 include $(BUILD_HOST_SHARED_LIBRARY)
 
 
@@ -77,6 +59,8 @@
 LOCAL_MODULE := liblog
 LOCAL_SRC_FILES := $(liblog_target_sources)
 LOCAL_CFLAGS := -Werror $(liblog_cflags)
+# AddressSanitizer runtime library depends on liblog.
+LOCAL_SANITIZE := never
 include $(BUILD_STATIC_LIBRARY)
 
 include $(CLEAR_VARS)
@@ -87,6 +71,9 @@
 # TODO: This is to work around b/19059885. Remove after root cause is fixed
 LOCAL_LDFLAGS_arm := -Wl,--hash-style=both
 
+LOCAL_SANITIZE := never
+LOCAL_CXX_STL := none
+
 include $(BUILD_SHARED_LIBRARY)
 
 include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/liblog/log_event_write.c b/liblog/log_event_write.c
new file mode 100644
index 0000000..0bc42d5
--- /dev/null
+++ b/liblog/log_event_write.c
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2015 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 <errno.h>
+#include <string.h>
+
+#include <log/log.h>
+#include <log/logger.h>
+
+#define MAX_EVENT_PAYLOAD 512
+#define MAX_SUBTAG_LEN 32
+
+static inline void copy4LE(uint8_t *buf, size_t pos, int val)
+{
+    buf[pos] = val & 0xFF;
+    buf[pos+1] = (val >> 8) & 0xFF;
+    buf[pos+2] = (val >> 16) & 0xFF;
+    buf[pos+3] = (val >> 24) & 0xFF;
+}
+
+int __android_log_error_write(int tag, const char *subTag, int32_t uid, const char *data,
+                              uint32_t dataLen)
+{
+    uint8_t buf[MAX_EVENT_PAYLOAD];
+    size_t pos = 0;
+    uint32_t subTagLen = 0;
+    uint32_t roomLeftForData = 0;
+
+    if ((subTag == NULL) || ((data == NULL) && (dataLen != 0))) return -EINVAL;
+
+    subTagLen = strlen(subTag);
+
+    // Truncate subtags that are too long.
+    subTagLen = subTagLen > MAX_SUBTAG_LEN ? MAX_SUBTAG_LEN : subTagLen;
+
+    // Truncate dataLen if it is too long.
+    roomLeftForData = MAX_EVENT_PAYLOAD -
+            (1 + // EVENT_TYPE_LIST
+             1 + // Number of elements in list
+             1 + // EVENT_TYPE_STRING
+             sizeof(subTagLen) +
+             subTagLen +
+             1 + // EVENT_TYPE_INT
+             sizeof(uid) +
+             1 + // EVENT_TYPE_STRING
+             sizeof(dataLen));
+    dataLen = dataLen > roomLeftForData ? roomLeftForData : dataLen;
+
+    buf[pos++] = EVENT_TYPE_LIST;
+    buf[pos++] = 3; // Number of elements in the list (subTag, uid, data)
+
+    // Write sub tag.
+    buf[pos++] = EVENT_TYPE_STRING;
+    copy4LE(buf, pos, subTagLen);
+    pos += 4;
+    memcpy(&buf[pos], subTag, subTagLen);
+    pos += subTagLen;
+
+    // Write UID.
+    buf[pos++] = EVENT_TYPE_INT;
+    copy4LE(buf, pos, uid);
+    pos += 4;
+
+    // Write data.
+    buf[pos++] = EVENT_TYPE_STRING;
+    copy4LE(buf, pos, dataLen);
+    pos += 4;
+    if (dataLen != 0)
+    {
+        memcpy(&buf[pos], data, dataLen);
+        pos += dataLen;
+    }
+
+    return __android_log_bwrite(tag, buf, pos);
+}
diff --git a/liblog/log_read.c b/liblog/log_read.c
index 9c4af30..cfc8a7a 100644
--- a/liblog/log_read.c
+++ b/liblog/log_read.c
@@ -37,7 +37,7 @@
 /* branchless on many architectures. */
 #define min(x,y) ((y) ^ (((x) ^ (y)) & -((x) < (y))))
 
-#if (defined(USE_MINGW) || defined(HAVE_WINSOCK))
+#if defined(_WIN32)
 #define WEAK static
 #else
 #define WEAK __attribute__((weak))
@@ -48,7 +48,7 @@
 
 /* Private copy of ../libcutils/socket_local_client.c prevent library loops */
 
-#ifdef HAVE_WINSOCK
+#if defined(_WIN32)
 
 int WEAK socket_local_client(const char *name, int namespaceId, int type)
 {
@@ -56,7 +56,7 @@
     return -ENOSYS;
 }
 
-#else /* !HAVE_WINSOCK */
+#else /* !_WIN32 */
 
 #include <sys/socket.h>
 #include <sys/un.h>
@@ -193,7 +193,7 @@
     return s;
 }
 
-#endif /* !HAVE_WINSOCK */
+#endif /* !_WIN32 */
 /* End of ../libcutils/socket_local_client.c */
 
 #define logger_for_each(logger, logger_list) \
diff --git a/liblog/log_read_kern.c b/liblog/log_read_kern.c
deleted file mode 100644
index 69b405c..0000000
--- a/liblog/log_read_kern.c
+++ /dev/null
@@ -1,742 +0,0 @@
-/*
-** Copyright 2013-2014, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-
-#define _GNU_SOURCE /* asprintf for x86 host */
-#include <errno.h>
-#include <fcntl.h>
-#include <poll.h>
-#include <string.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/cdefs.h>
-#include <sys/ioctl.h>
-
-#include <cutils/list.h>
-#include <log/log.h>
-#include <log/logger.h>
-
-#define __LOGGERIO     0xAE
-
-#define LOGGER_GET_LOG_BUF_SIZE    _IO(__LOGGERIO, 1) /* size of log */
-#define LOGGER_GET_LOG_LEN         _IO(__LOGGERIO, 2) /* used log len */
-#define LOGGER_GET_NEXT_ENTRY_LEN  _IO(__LOGGERIO, 3) /* next entry len */
-#define LOGGER_FLUSH_LOG           _IO(__LOGGERIO, 4) /* flush log */
-#define LOGGER_GET_VERSION         _IO(__LOGGERIO, 5) /* abi version */
-#define LOGGER_SET_VERSION         _IO(__LOGGERIO, 6) /* abi version */
-
-typedef char bool;
-#define false (const bool)0
-#define true (const bool)1
-
-#define LOG_FILE_DIR "/dev/log/"
-
-/* timeout in milliseconds */
-#define LOG_TIMEOUT_FLUSH 5
-#define LOG_TIMEOUT_NEVER -1
-
-#define logger_for_each(logger, logger_list) \
-    for (logger = node_to_item((logger_list)->node.next, struct logger, node); \
-         logger != node_to_item(&(logger_list)->node, struct logger, node); \
-         logger = node_to_item((logger)->node.next, struct logger, node))
-
-#ifndef __unused
-#define __unused __attribute__((unused))
-#endif
-
-/* In the future, we would like to make this list extensible */
-static const char *LOG_NAME[LOG_ID_MAX] = {
-    [LOG_ID_MAIN] = "main",
-    [LOG_ID_RADIO] = "radio",
-    [LOG_ID_EVENTS] = "events",
-    [LOG_ID_SYSTEM] = "system",
-    [LOG_ID_CRASH] = "crash",
-    [LOG_ID_KERNEL] = "kernel",
-};
-
-const char *android_log_id_to_name(log_id_t log_id)
-{
-    if (log_id >= LOG_ID_MAX) {
-        log_id = LOG_ID_MAIN;
-    }
-    return LOG_NAME[log_id];
-}
-
-static int accessmode(int mode)
-{
-    if ((mode & ANDROID_LOG_ACCMODE) == ANDROID_LOG_WRONLY) {
-        return W_OK;
-    }
-    if ((mode & ANDROID_LOG_ACCMODE) == ANDROID_LOG_RDWR) {
-        return R_OK | W_OK;
-    }
-    return R_OK;
-}
-
-/* repeated fragment */
-static int check_allocate_accessible(char **n, const char *b, int mode)
-{
-    *n = NULL;
-
-    if (!b) {
-        return -EINVAL;
-    }
-
-    asprintf(n, LOG_FILE_DIR "%s", b);
-    if (!*n) {
-        return -1;
-    }
-
-    return access(*n, accessmode(mode));
-}
-
-log_id_t android_name_to_log_id(const char *logName)
-{
-    const char *b;
-    char *n;
-    int ret;
-
-    if (!logName) {
-        return -1; /* NB: log_id_t is unsigned */
-    }
-    b = strrchr(logName, '/');
-    if (!b) {
-        b = logName;
-    } else {
-        ++b;
-    }
-
-    ret = check_allocate_accessible(&n, b, ANDROID_LOG_RDONLY);
-    free(n);
-    if (ret) {
-        return ret;
-    }
-
-    for(ret = LOG_ID_MIN; ret < LOG_ID_MAX; ++ret) {
-        const char *l = LOG_NAME[ret];
-        if (l && !strcmp(b, l)) {
-            return ret;
-        }
-    }
-    return -1;   /* should never happen */
-}
-
-struct logger_list {
-    struct listnode node;
-    int mode;
-    unsigned int tail;
-    pid_t pid;
-    unsigned int queued_lines;
-    int timeout_ms;
-    int error;
-    bool flush;
-    bool valid_entry; /* valiant(?) effort to deal with memory starvation */
-    struct log_msg entry;
-};
-
-struct log_list {
-    struct listnode node;
-    struct log_msg entry; /* Truncated to event->len() + 1 to save space */
-};
-
-struct logger {
-    struct listnode node;
-    struct logger_list *top;
-    int fd;
-    log_id_t id;
-    short *revents;
-    struct listnode log_list;
-};
-
-/* android_logger_alloc unimplemented, no use case */
-/* android_logger_free not exported */
-static void android_logger_free(struct logger *logger)
-{
-    if (!logger) {
-        return;
-    }
-
-    while (!list_empty(&logger->log_list)) {
-        struct log_list *entry = node_to_item(
-            list_head(&logger->log_list), struct log_list, node);
-        list_remove(&entry->node);
-        free(entry);
-        if (logger->top->queued_lines) {
-            logger->top->queued_lines--;
-        }
-    }
-
-    if (logger->fd >= 0) {
-        close(logger->fd);
-    }
-
-    list_remove(&logger->node);
-
-    free(logger);
-}
-
-log_id_t android_logger_get_id(struct logger *logger)
-{
-    return logger->id;
-}
-
-/* worker for sending the command to the logger */
-static int logger_ioctl(struct logger *logger, int cmd, int mode)
-{
-    char *n;
-    int  f, ret;
-
-    if (!logger || !logger->top) {
-        return -EFAULT;
-    }
-
-    if (((mode & ANDROID_LOG_ACCMODE) == ANDROID_LOG_RDWR)
-            || (((mode ^ logger->top->mode) & ANDROID_LOG_ACCMODE) == 0)) {
-        return ioctl(logger->fd, cmd);
-    }
-
-    /* We go here if android_logger_list_open got mode wrong for this ioctl */
-    ret = check_allocate_accessible(&n, android_log_id_to_name(logger->id), mode);
-    if (ret) {
-        free(n);
-        return ret;
-    }
-
-    f = open(n, mode);
-    free(n);
-    if (f < 0) {
-        return f;
-    }
-
-    ret = ioctl(f, cmd);
-    close (f);
-
-    return ret;
-}
-
-int android_logger_clear(struct logger *logger)
-{
-    return logger_ioctl(logger, LOGGER_FLUSH_LOG, ANDROID_LOG_WRONLY);
-}
-
-/* returns the total size of the log's ring buffer */
-long android_logger_get_log_size(struct logger *logger)
-{
-    return logger_ioctl(logger, LOGGER_GET_LOG_BUF_SIZE, ANDROID_LOG_RDWR);
-}
-
-int android_logger_set_log_size(struct logger *logger __unused,
-                                unsigned long size __unused)
-{
-    return -ENOTSUP;
-}
-
-/*
- * returns the readable size of the log's ring buffer (that is, amount of the
- * log consumed)
- */
-long android_logger_get_log_readable_size(struct logger *logger)
-{
-    return logger_ioctl(logger, LOGGER_GET_LOG_LEN, ANDROID_LOG_RDONLY);
-}
-
-/*
- * returns the logger version
- */
-int android_logger_get_log_version(struct logger *logger)
-{
-    int ret = logger_ioctl(logger, LOGGER_GET_VERSION, ANDROID_LOG_RDWR);
-    return (ret < 0) ? 1 : ret;
-}
-
-/*
- * returns statistics
- */
-static const char unsupported[] = "18\nNot Supported\n\f";
-
-ssize_t android_logger_get_statistics(struct logger_list *logger_list __unused,
-                                      char *buf, size_t len)
-{
-    strncpy(buf, unsupported, len);
-    return -ENOTSUP;
-}
-
-ssize_t android_logger_get_prune_list(struct logger_list *logger_list __unused,
-                                      char *buf, size_t len)
-{
-    strncpy(buf, unsupported, len);
-    return -ENOTSUP;
-}
-
-int android_logger_set_prune_list(struct logger_list *logger_list __unused,
-                                  char *buf, size_t len)
-{
-    static const char unsupported_error[] = "Unsupported";
-    strncpy(buf, unsupported, len);
-    return -ENOTSUP;
-}
-
-struct logger_list *android_logger_list_alloc(int mode,
-                                              unsigned int tail,
-                                              pid_t pid)
-{
-    struct logger_list *logger_list;
-
-    logger_list = calloc(1, sizeof(*logger_list));
-    if (!logger_list) {
-        return NULL;
-    }
-    list_init(&logger_list->node);
-    logger_list->mode = mode;
-    logger_list->tail = tail;
-    logger_list->pid = pid;
-    return logger_list;
-}
-
-struct logger_list *android_logger_list_alloc_time(int mode,
-                                                   log_time start __unused,
-                                                   pid_t pid)
-{
-    return android_logger_list_alloc(mode, 0, pid);
-}
-
-/* android_logger_list_register unimplemented, no use case */
-/* android_logger_list_unregister unimplemented, no use case */
-
-/* Open the named log and add it to the logger list */
-struct logger *android_logger_open(struct logger_list *logger_list,
-                                   log_id_t id)
-{
-    struct listnode *node;
-    struct logger *logger;
-    char *n;
-
-    if (!logger_list || (id >= LOG_ID_MAX)) {
-        goto err;
-    }
-
-    logger_for_each(logger, logger_list) {
-        if (logger->id == id) {
-            goto ok;
-        }
-    }
-
-    logger = calloc(1, sizeof(*logger));
-    if (!logger) {
-        goto err;
-    }
-
-    if (check_allocate_accessible(&n, android_log_id_to_name(id),
-                                  logger_list->mode)) {
-        goto err_name;
-    }
-
-    logger->fd = open(n, logger_list->mode & (ANDROID_LOG_ACCMODE | ANDROID_LOG_NONBLOCK));
-    if (logger->fd < 0) {
-        goto err_name;
-    }
-
-    free(n);
-    logger->id = id;
-    list_init(&logger->log_list);
-    list_add_tail(&logger_list->node, &logger->node);
-    logger->top = logger_list;
-    logger_list->timeout_ms = LOG_TIMEOUT_FLUSH;
-    goto ok;
-
-err_name:
-    free(n);
-err_logger:
-    free(logger);
-err:
-    logger = NULL;
-ok:
-    return logger;
-}
-
-/* Open the single named log and make it part of a new logger list */
-struct logger_list *android_logger_list_open(log_id_t id,
-                                             int mode,
-                                             unsigned int tail,
-                                             pid_t pid)
-{
-    struct logger_list *logger_list = android_logger_list_alloc(mode, tail, pid);
-    if (!logger_list) {
-        return NULL;
-    }
-
-    if (!android_logger_open(logger_list, id)) {
-        android_logger_list_free(logger_list);
-        return NULL;
-    }
-
-    return logger_list;
-}
-
-/* prevent memory starvation when backfilling */
-static unsigned int queue_threshold(struct logger_list *logger_list)
-{
-    return (logger_list->tail < 64) ? 64 : logger_list->tail;
-}
-
-static bool low_queue(struct listnode *node)
-{
-    /* low is considered less than 2 */
-    return list_head(node) == list_tail(node);
-}
-
-/* Flush queues in sequential order, one at a time */
-static int android_logger_list_flush(struct logger_list *logger_list,
-                                     struct log_msg *log_msg)
-{
-    int ret = 0;
-    struct log_list *firstentry = NULL;
-
-    while ((ret == 0)
-            && (logger_list->flush
-                || (logger_list->queued_lines > logger_list->tail))) {
-        struct logger *logger;
-
-        /* Merge sort */
-        bool at_least_one_is_low = false;
-        struct logger *firstlogger = NULL;
-        firstentry = NULL;
-
-        logger_for_each(logger, logger_list) {
-            struct listnode *node;
-            struct log_list *oldest = NULL;
-
-            /* kernel logger channels not necessarily time-sort order */
-            list_for_each(node, &logger->log_list) {
-                struct log_list *entry = node_to_item(node,
-                                                      struct log_list, node);
-                if (!oldest
-                        || (entry->entry.entry.sec < oldest->entry.entry.sec)
-                        || ((entry->entry.entry.sec == oldest->entry.entry.sec)
-                            && (entry->entry.entry.nsec < oldest->entry.entry.nsec))) {
-                    oldest = entry;
-                }
-            }
-
-            if (!oldest) {
-                at_least_one_is_low = true;
-                continue;
-            } else if (low_queue(&logger->log_list)) {
-                at_least_one_is_low = true;
-            }
-
-            if (!firstentry
-                    || (oldest->entry.entry.sec < firstentry->entry.entry.sec)
-                    || ((oldest->entry.entry.sec == firstentry->entry.entry.sec)
-                        && (oldest->entry.entry.nsec < firstentry->entry.entry.nsec))) {
-                firstentry = oldest;
-                firstlogger = logger;
-            }
-        }
-
-        if (!firstentry) {
-            break;
-        }
-
-        /* when trimming list, tries to keep one entry behind in each bucket */
-        if (!logger_list->flush
-                && at_least_one_is_low
-                && (logger_list->queued_lines < queue_threshold(logger_list))) {
-            break;
-        }
-
-        /* within tail?, send! */
-        if ((logger_list->tail == 0)
-                || (logger_list->queued_lines <= logger_list->tail)) {
-            int diff;
-            ret = firstentry->entry.entry.hdr_size;
-            if (!ret) {
-                ret = sizeof(firstentry->entry.entry_v1);
-            }
-
-            /* Promote entry to v3 format */
-            memcpy(log_msg->buf, firstentry->entry.buf, ret);
-            diff = sizeof(firstentry->entry.entry_v3) - ret;
-            if (diff < 0) {
-                diff = 0;
-            } else if (diff > 0) {
-                memset(log_msg->buf + ret, 0, diff);
-            }
-            memcpy(log_msg->buf + ret + diff, firstentry->entry.buf + ret,
-                   firstentry->entry.entry.len + 1);
-            ret += diff;
-            log_msg->entry.hdr_size = ret;
-            log_msg->entry.lid = firstlogger->id;
-
-            ret += firstentry->entry.entry.len;
-        }
-
-        /* next entry */
-        list_remove(&firstentry->node);
-        free(firstentry);
-        if (logger_list->queued_lines) {
-            logger_list->queued_lines--;
-        }
-    }
-
-    /* Flushed the list, no longer in tail mode for continuing content */
-    if (logger_list->flush && !firstentry) {
-        logger_list->tail = 0;
-    }
-    return ret;
-}
-
-/* Read from the selected logs */
-int android_logger_list_read(struct logger_list *logger_list,
-                             struct log_msg *log_msg)
-{
-    struct logger *logger;
-    nfds_t nfds;
-    struct pollfd *p, *pollfds = NULL;
-    int error = 0, ret = 0;
-
-    memset(log_msg, 0, sizeof(struct log_msg));
-
-    if (!logger_list) {
-        return -ENODEV;
-    }
-
-    if (!(accessmode(logger_list->mode) & R_OK)) {
-        logger_list->error = EPERM;
-        goto done;
-    }
-
-    nfds = 0;
-    logger_for_each(logger, logger_list) {
-        ++nfds;
-    }
-    if (nfds <= 0) {
-        error = ENODEV;
-        goto done;
-    }
-
-    /* Do we have anything to offer from the buffer or state? */
-    if (logger_list->valid_entry) { /* implies we are also in a flush state */
-        goto flush;
-    }
-
-    ret = android_logger_list_flush(logger_list, log_msg);
-    if (ret) {
-        goto done;
-    }
-
-    if (logger_list->error) { /* implies we are also in a flush state */
-        goto done;
-    }
-
-    /* Lets start grinding on metal */
-    pollfds = calloc(nfds, sizeof(struct pollfd));
-    if (!pollfds) {
-        error = ENOMEM;
-        goto flush;
-    }
-
-    p = pollfds;
-    logger_for_each(logger, logger_list) {
-        p->fd = logger->fd;
-        p->events = POLLIN;
-        logger->revents = &p->revents;
-        ++p;
-    }
-
-    while (!ret && !error) {
-        int result;
-
-        /* If we oversleep it's ok, i.e. ignore EINTR. */
-        result = TEMP_FAILURE_RETRY(
-                    poll(pollfds, nfds, logger_list->timeout_ms));
-
-        if (result <= 0) {
-            if (result) {
-                error = errno;
-            } else if (logger_list->mode & ANDROID_LOG_NONBLOCK) {
-                error = EAGAIN;
-            } else {
-                logger_list->timeout_ms = LOG_TIMEOUT_NEVER;
-            }
-
-            logger_list->flush = true;
-            goto try_flush;
-        }
-
-        logger_list->timeout_ms = LOG_TIMEOUT_FLUSH;
-
-        /* Anti starvation */
-        if (!logger_list->flush
-                && (logger_list->queued_lines > (queue_threshold(logger_list) / 2))) {
-            /* Any queues with input pending that is low? */
-            bool starving = false;
-            logger_for_each(logger, logger_list) {
-                if ((*(logger->revents) & POLLIN)
-                        && low_queue(&logger->log_list)) {
-                    starving = true;
-                    break;
-                }
-            }
-
-            /* pushback on any queues that are not low */
-            if (starving) {
-                logger_for_each(logger, logger_list) {
-                    if ((*(logger->revents) & POLLIN)
-                            && !low_queue(&logger->log_list)) {
-                        *(logger->revents) &= ~POLLIN;
-                    }
-                }
-            }
-        }
-
-        logger_for_each(logger, logger_list) {
-            unsigned int hdr_size;
-            struct log_list *entry;
-            int diff;
-
-            if (!(*(logger->revents) & POLLIN)) {
-                continue;
-            }
-
-            memset(logger_list->entry.buf, 0, sizeof(struct log_msg));
-            /* NOTE: driver guarantees we read exactly one full entry */
-            result = read(logger->fd, logger_list->entry.buf,
-                          LOGGER_ENTRY_MAX_LEN);
-            if (result <= 0) {
-                if (!result) {
-                    error = EIO;
-                } else if (errno != EINTR) {
-                    error = errno;
-                }
-                continue;
-            }
-
-            if (logger_list->pid
-                    && (logger_list->pid != logger_list->entry.entry.pid)) {
-                continue;
-            }
-
-            hdr_size = logger_list->entry.entry.hdr_size;
-            if (!hdr_size) {
-                hdr_size = sizeof(logger_list->entry.entry_v1);
-            }
-
-            if ((hdr_size > sizeof(struct log_msg))
-                    || (logger_list->entry.entry.len
-                        > sizeof(logger_list->entry.buf) - hdr_size)
-                    || (logger_list->entry.entry.len != result - hdr_size)) {
-                error = EINVAL;
-                continue;
-            }
-
-            /* Promote entry to v3 format */
-            diff = sizeof(logger_list->entry.entry_v3) - hdr_size;
-            if (diff > 0) {
-                if (logger_list->entry.entry.len
-                        > sizeof(logger_list->entry.buf) - hdr_size - diff) {
-                    error = EINVAL;
-                    continue;
-                }
-                result += diff;
-                memmove(logger_list->entry.buf + hdr_size + diff,
-                        logger_list->entry.buf + hdr_size,
-                        logger_list->entry.entry.len + 1);
-                memset(logger_list->entry.buf + hdr_size, 0, diff);
-                logger_list->entry.entry.hdr_size = hdr_size + diff;
-            }
-            logger_list->entry.entry.lid = logger->id;
-
-            /* speedup: If not tail, and only one list, send directly */
-            if (!logger_list->tail
-                    && (list_head(&logger_list->node)
-                        == list_tail(&logger_list->node))) {
-                ret = result;
-                memcpy(log_msg->buf, logger_list->entry.buf, result + 1);
-                break;
-            }
-
-            entry = malloc(sizeof(*entry) - sizeof(entry->entry) + result + 1);
-
-            if (!entry) {
-                logger_list->valid_entry = true;
-                error = ENOMEM;
-                break;
-            }
-
-            logger_list->queued_lines++;
-
-            memcpy(entry->entry.buf, logger_list->entry.buf, result);
-            entry->entry.buf[result] = '\0';
-            list_add_tail(&logger->log_list, &entry->node);
-        }
-
-        if (ret <= 0) {
-try_flush:
-            ret = android_logger_list_flush(logger_list, log_msg);
-        }
-    }
-
-    free(pollfds);
-
-flush:
-    if (error) {
-        logger_list->flush = true;
-    }
-
-    if (ret <= 0) {
-        ret = android_logger_list_flush(logger_list, log_msg);
-
-        if (!ret && logger_list->valid_entry) {
-            ret = logger_list->entry.entry.hdr_size;
-            if (!ret) {
-                ret = sizeof(logger_list->entry.entry_v1);
-            }
-            ret += logger_list->entry.entry.len;
-
-            memcpy(log_msg->buf, logger_list->entry.buf,
-                   sizeof(struct log_msg));
-            logger_list->valid_entry = false;
-        }
-    }
-
-done:
-    if (logger_list->error) {
-        error = logger_list->error;
-    }
-    if (error) {
-        logger_list->error = error;
-        if (!ret) {
-            ret = -error;
-        }
-    }
-    return ret;
-}
-
-/* Close all the logs */
-void android_logger_list_free(struct logger_list *logger_list)
-{
-    if (logger_list == NULL) {
-        return;
-    }
-
-    while (!list_empty(&logger_list->node)) {
-        struct listnode *node = list_head(&logger_list->node);
-        struct logger *logger = node_to_item(node, struct logger, node);
-        android_logger_free(logger);
-    }
-
-    free(logger_list);
-}
diff --git a/liblog/logd_write_kern.c b/liblog/logd_write_kern.c
deleted file mode 100644
index 8742b34..0000000
--- a/liblog/logd_write_kern.c
+++ /dev/null
@@ -1,295 +0,0 @@
-/*
- * Copyright (C) 2007-2014 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 <errno.h>
-#include <fcntl.h>
-#include <pthread.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <time.h>
-#include <unistd.h>
-
-#include <android/set_abort_message.h>
-
-#include <log/log.h>
-#include <log/logd.h>
-#include <log/logger.h>
-
-#define LOGGER_LOG_MAIN		"log/main"
-#define LOGGER_LOG_RADIO	"log/radio"
-#define LOGGER_LOG_EVENTS	"log/events"
-#define LOGGER_LOG_SYSTEM	"log/system"
-
-#define LOG_BUF_SIZE 1024
-
-#define log_open(pathname, flags) open(pathname, (flags) | O_CLOEXEC)
-#define log_writev(filedes, vector, count) writev(filedes, vector, count)
-#define log_close(filedes) close(filedes)
-
-static int __write_to_log_init(log_id_t, struct iovec *vec, size_t nr);
-static int (*write_to_log)(log_id_t, struct iovec *vec, size_t nr) = __write_to_log_init;
-
-static pthread_mutex_t log_init_lock = PTHREAD_MUTEX_INITIALIZER;
-
-#ifndef __unused
-#define __unused  __attribute__((__unused__))
-#endif
-
-static int log_fds[(int)LOG_ID_MAX] = { -1, -1, -1, -1 };
-
-/*
- * This is used by the C++ code to decide if it should write logs through
- * the C code.  Basically, if /dev/log/... is available, we're running in
- * the simulator rather than a desktop tool and want to use the device.
- */
-static enum {
-    kLogUninitialized, kLogNotAvailable, kLogAvailable
-} g_log_status = kLogUninitialized;
-int __android_log_dev_available(void)
-{
-    if (g_log_status == kLogUninitialized) {
-        if (access("/dev/"LOGGER_LOG_MAIN, W_OK) == 0)
-            g_log_status = kLogAvailable;
-        else
-            g_log_status = kLogNotAvailable;
-    }
-
-    return (g_log_status == kLogAvailable);
-}
-
-static int __write_to_log_null(log_id_t log_fd __unused, struct iovec *vec __unused,
-                               size_t nr __unused)
-{
-    return -1;
-}
-
-static int __write_to_log_kernel(log_id_t log_id, struct iovec *vec, size_t nr)
-{
-    ssize_t ret;
-    int log_fd;
-
-    if (/*(int)log_id >= 0 &&*/ (int)log_id < (int)LOG_ID_MAX) {
-        if (log_id == LOG_ID_CRASH) {
-            log_id = LOG_ID_MAIN;
-        }
-        log_fd = log_fds[(int)log_id];
-    } else {
-        return -EBADF;
-    }
-
-    do {
-        ret = log_writev(log_fd, vec, nr);
-        if (ret < 0) {
-            ret = -errno;
-        }
-    } while (ret == -EINTR);
-
-    return ret;
-}
-
-static int __write_to_log_init(log_id_t log_id, struct iovec *vec, size_t nr)
-{
-    pthread_mutex_lock(&log_init_lock);
-
-    if (write_to_log == __write_to_log_init) {
-        log_fds[LOG_ID_MAIN] = log_open("/dev/"LOGGER_LOG_MAIN, O_WRONLY);
-        log_fds[LOG_ID_RADIO] = log_open("/dev/"LOGGER_LOG_RADIO, O_WRONLY);
-        log_fds[LOG_ID_EVENTS] = log_open("/dev/"LOGGER_LOG_EVENTS, O_WRONLY);
-        log_fds[LOG_ID_SYSTEM] = log_open("/dev/"LOGGER_LOG_SYSTEM, O_WRONLY);
-
-        write_to_log = __write_to_log_kernel;
-
-        if (log_fds[LOG_ID_MAIN] < 0 || log_fds[LOG_ID_RADIO] < 0 ||
-                log_fds[LOG_ID_EVENTS] < 0) {
-            log_close(log_fds[LOG_ID_MAIN]);
-            log_close(log_fds[LOG_ID_RADIO]);
-            log_close(log_fds[LOG_ID_EVENTS]);
-            log_fds[LOG_ID_MAIN] = -1;
-            log_fds[LOG_ID_RADIO] = -1;
-            log_fds[LOG_ID_EVENTS] = -1;
-            write_to_log = __write_to_log_null;
-        }
-
-        if (log_fds[LOG_ID_SYSTEM] < 0) {
-            log_fds[LOG_ID_SYSTEM] = log_fds[LOG_ID_MAIN];
-        }
-    }
-
-    pthread_mutex_unlock(&log_init_lock);
-
-    return write_to_log(log_id, vec, nr);
-}
-
-int __android_log_write(int prio, const char *tag, const char *msg)
-{
-    return __android_log_buf_write(LOG_ID_MAIN, prio, tag, msg);
-}
-
-int __android_log_buf_write(int bufID, int prio, const char *tag, const char *msg)
-{
-    struct iovec vec[3];
-    char tmp_tag[32];
-
-    if (!tag)
-        tag = "";
-
-    /* XXX: This needs to go! */
-    if ((bufID != LOG_ID_RADIO) &&
-         (!strcmp(tag, "HTC_RIL") ||
-        !strncmp(tag, "RIL", 3) || /* Any log tag with "RIL" as the prefix */
-        !strncmp(tag, "IMS", 3) || /* Any log tag with "IMS" as the prefix */
-        !strcmp(tag, "AT") ||
-        !strcmp(tag, "GSM") ||
-        !strcmp(tag, "STK") ||
-        !strcmp(tag, "CDMA") ||
-        !strcmp(tag, "PHONE") ||
-        !strcmp(tag, "SMS"))) {
-            bufID = LOG_ID_RADIO;
-            /* Inform third party apps/ril/radio.. to use Rlog or RLOG */
-            snprintf(tmp_tag, sizeof(tmp_tag), "use-Rlog/RLOG-%s", tag);
-            tag = tmp_tag;
-    }
-
-    if (prio == ANDROID_LOG_FATAL) {
-        android_set_abort_message(msg);
-    }
-
-    vec[0].iov_base   = (unsigned char *) &prio;
-    vec[0].iov_len    = 1;
-    vec[1].iov_base   = (void *) tag;
-    vec[1].iov_len    = strlen(tag) + 1;
-    vec[2].iov_base   = (void *) msg;
-    vec[2].iov_len    = strlen(msg) + 1;
-
-    return write_to_log(bufID, vec, 3);
-}
-
-int __android_log_vprint(int prio, const char *tag, const char *fmt, va_list ap)
-{
-    char buf[LOG_BUF_SIZE];
-
-    vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
-
-    return __android_log_write(prio, tag, buf);
-}
-
-int __android_log_print(int prio, const char *tag, const char *fmt, ...)
-{
-    va_list ap;
-    char buf[LOG_BUF_SIZE];
-
-    va_start(ap, fmt);
-    vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
-    va_end(ap);
-
-    return __android_log_write(prio, tag, buf);
-}
-
-int __android_log_buf_print(int bufID, int prio, const char *tag, const char *fmt, ...)
-{
-    va_list ap;
-    char buf[LOG_BUF_SIZE];
-
-    va_start(ap, fmt);
-    vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
-    va_end(ap);
-
-    return __android_log_buf_write(bufID, prio, tag, buf);
-}
-
-void __android_log_assert(const char *cond, const char *tag,
-                          const char *fmt, ...)
-{
-    char buf[LOG_BUF_SIZE];
-
-    if (fmt) {
-        va_list ap;
-        va_start(ap, fmt);
-        vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
-        va_end(ap);
-    } else {
-        /* Msg not provided, log condition.  N.B. Do not use cond directly as
-         * format string as it could contain spurious '%' syntax (e.g.
-         * "%d" in "blocks%devs == 0").
-         */
-        if (cond)
-            snprintf(buf, LOG_BUF_SIZE, "Assertion failed: %s", cond);
-        else
-            strcpy(buf, "Unspecified assertion failed");
-    }
-
-    __android_log_write(ANDROID_LOG_FATAL, tag, buf);
-    abort(); /* abort so we have a chance to debug the situation */
-    /* NOTREACHED */
-}
-
-int __android_log_bwrite(int32_t tag, const void *payload, size_t len)
-{
-    struct iovec vec[2];
-
-    vec[0].iov_base = &tag;
-    vec[0].iov_len = sizeof(tag);
-    vec[1].iov_base = (void*)payload;
-    vec[1].iov_len = len;
-
-    return write_to_log(LOG_ID_EVENTS, vec, 2);
-}
-
-/*
- * Like __android_log_bwrite, but takes the type as well.  Doesn't work
- * for the general case where we're generating lists of stuff, but very
- * handy if we just want to dump an integer into the log.
- */
-int __android_log_btwrite(int32_t tag, char type, const void *payload,
-                          size_t len)
-{
-    struct iovec vec[3];
-
-    vec[0].iov_base = &tag;
-    vec[0].iov_len = sizeof(tag);
-    vec[1].iov_base = &type;
-    vec[1].iov_len = sizeof(type);
-    vec[2].iov_base = (void*)payload;
-    vec[2].iov_len = len;
-
-    return write_to_log(LOG_ID_EVENTS, vec, 3);
-}
-
-/*
- * Like __android_log_bwrite, but used for writing strings to the
- * event log.
- */
-int __android_log_bswrite(int32_t tag, const char *payload)
-{
-    struct iovec vec[4];
-    char type = EVENT_TYPE_STRING;
-    uint32_t len = strlen(payload);
-
-    vec[0].iov_base = &tag;
-    vec[0].iov_len = sizeof(tag);
-    vec[1].iov_base = &type;
-    vec[1].iov_len = sizeof(type);
-    vec[2].iov_base = &len;
-    vec[2].iov_len = sizeof(len);
-    vec[3].iov_base = (void*)payload;
-    vec[3].iov_len = len;
-
-    return write_to_log(LOG_ID_EVENTS, vec, 4);
-}
diff --git a/liblog/tests/Android.mk b/liblog/tests/Android.mk
index a407c50..8229859 100644
--- a/liblog/tests/Android.mk
+++ b/liblog/tests/Android.mk
@@ -65,10 +65,6 @@
 test_src_files += \
     libc_test.cpp
 
-ifneq ($(TARGET_USES_LOGD),false)
-test_c_flags += -DTARGET_USES_LOGD
-endif
-
 endif
 
 # Build tests for the device (with .so). Run with:
diff --git a/liblog/tests/libc_test.cpp b/liblog/tests/libc_test.cpp
index 0e84f4e..9dd6f03 100644
--- a/liblog/tests/libc_test.cpp
+++ b/liblog/tests/libc_test.cpp
@@ -25,9 +25,7 @@
 #define _ANDROID_LOG_H // Priorities redefined
 #define _LIBS_LOG_LOG_H // log ids redefined
 typedef unsigned char log_id_t; // log_id_t missing as a result
-#ifdef TARGET_USES_LOGD
 #define _LIBS_LOG_LOG_READ_H // log_time redefined
-#endif
 
 #include <log/log.h>
 #include <log/logger.h>
diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp
index abe0239..c987041 100644
--- a/liblog/tests/liblog_test.cpp
+++ b/liblog/tests/liblog_test.cpp
@@ -17,6 +17,7 @@
 #include <fcntl.h>
 #include <inttypes.h>
 #include <signal.h>
+#include <string.h>
 
 #include <cutils/properties.h>
 #include <gtest/gtest.h>
@@ -876,3 +877,398 @@
     property_set(key, hold[2]);
     property_set(key + base_offset, hold[3]);
 }
+
+static inline int32_t get4LE(const char* src)
+{
+    return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
+}
+
+TEST(liblog, android_errorWriteWithInfoLog__android_logger_list_read__typical) {
+    const int TAG = 123456781;
+    const char SUBTAG[] = "test-subtag";
+    const int UID = -1;
+    const int DATA_LEN = 200;
+    struct logger_list *logger_list;
+
+    pid_t pid = getpid();
+
+    ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
+        LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid)));
+
+    ASSERT_LT(0, android_errorWriteWithInfoLog(
+            TAG, SUBTAG, UID, max_payload_buf, DATA_LEN));
+
+    sleep(2);
+
+    int count = 0;
+
+    for (;;) {
+        log_msg log_msg;
+        if (android_logger_list_read(logger_list, &log_msg) <= 0) {
+            break;
+        }
+
+        char *eventData = log_msg.msg();
+
+        // Tag
+        int tag = get4LE(eventData);
+        eventData += 4;
+
+        if (tag != TAG) {
+            continue;
+        }
+
+        // List type
+        ASSERT_EQ(EVENT_TYPE_LIST, eventData[0]);
+        eventData++;
+
+        // Number of elements in list
+        ASSERT_EQ(3, eventData[0]);
+        eventData++;
+
+        // Element #1: string type for subtag
+        ASSERT_EQ(EVENT_TYPE_STRING, eventData[0]);
+        eventData++;
+
+        ASSERT_EQ((int) strlen(SUBTAG), get4LE(eventData));
+        eventData +=4;
+
+        if (memcmp(SUBTAG, eventData, strlen(SUBTAG))) {
+            continue;
+        }
+        eventData += strlen(SUBTAG);
+
+        // Element #2: int type for uid
+        ASSERT_EQ(EVENT_TYPE_INT, eventData[0]);
+        eventData++;
+
+        ASSERT_EQ(UID, get4LE(eventData));
+        eventData += 4;
+
+        // Element #3: string type for data
+        ASSERT_EQ(EVENT_TYPE_STRING, eventData[0]);
+        eventData++;
+
+        ASSERT_EQ(DATA_LEN, get4LE(eventData));
+        eventData += 4;
+
+        if (memcmp(max_payload_buf, eventData, DATA_LEN)) {
+            continue;
+        }
+
+        ++count;
+    }
+
+    EXPECT_EQ(1, count);
+
+    android_logger_list_close(logger_list);
+}
+
+TEST(liblog, android_errorWriteWithInfoLog__android_logger_list_read__data_too_large) {
+    const int TAG = 123456782;
+    const char SUBTAG[] = "test-subtag";
+    const int UID = -1;
+    const int DATA_LEN = sizeof(max_payload_buf);
+    struct logger_list *logger_list;
+
+    pid_t pid = getpid();
+
+    ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
+        LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid)));
+
+    ASSERT_LT(0, android_errorWriteWithInfoLog(
+            TAG, SUBTAG, UID, max_payload_buf, DATA_LEN));
+
+    sleep(2);
+
+    int count = 0;
+
+    for (;;) {
+        log_msg log_msg;
+        if (android_logger_list_read(logger_list, &log_msg) <= 0) {
+            break;
+        }
+
+        char *eventData = log_msg.msg();
+        char *original = eventData;
+
+        // Tag
+        int tag = get4LE(eventData);
+        eventData += 4;
+
+        if (tag != TAG) {
+            continue;
+        }
+
+        // List type
+        ASSERT_EQ(EVENT_TYPE_LIST, eventData[0]);
+        eventData++;
+
+        // Number of elements in list
+        ASSERT_EQ(3, eventData[0]);
+        eventData++;
+
+        // Element #1: string type for subtag
+        ASSERT_EQ(EVENT_TYPE_STRING, eventData[0]);
+        eventData++;
+
+        ASSERT_EQ((int) strlen(SUBTAG), get4LE(eventData));
+        eventData +=4;
+
+        if (memcmp(SUBTAG, eventData, strlen(SUBTAG))) {
+            continue;
+        }
+        eventData += strlen(SUBTAG);
+
+        // Element #2: int type for uid
+        ASSERT_EQ(EVENT_TYPE_INT, eventData[0]);
+        eventData++;
+
+        ASSERT_EQ(UID, get4LE(eventData));
+        eventData += 4;
+
+        // Element #3: string type for data
+        ASSERT_EQ(EVENT_TYPE_STRING, eventData[0]);
+        eventData++;
+
+        size_t dataLen = get4LE(eventData);
+        eventData += 4;
+
+        if (memcmp(max_payload_buf, eventData, dataLen)) {
+            continue;
+        }
+        eventData += dataLen;
+
+        // 4 bytes for the tag, and 512 bytes for the log since the max_payload_buf should be
+        // truncated.
+        ASSERT_EQ(4 + 512, eventData - original);
+
+        ++count;
+    }
+
+    EXPECT_EQ(1, count);
+
+    android_logger_list_close(logger_list);
+}
+
+TEST(liblog, android_errorWriteWithInfoLog__android_logger_list_read__null_data) {
+    const int TAG = 123456783;
+    const char SUBTAG[] = "test-subtag";
+    const int UID = -1;
+    const int DATA_LEN = 200;
+    struct logger_list *logger_list;
+
+    pid_t pid = getpid();
+
+    ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
+        LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid)));
+
+    ASSERT_GT(0, android_errorWriteWithInfoLog(
+            TAG, SUBTAG, UID, NULL, DATA_LEN));
+
+    sleep(2);
+
+    int count = 0;
+
+    for (;;) {
+        log_msg log_msg;
+        if (android_logger_list_read(logger_list, &log_msg) <= 0) {
+            break;
+        }
+
+        char *eventData = log_msg.msg();
+
+        // Tag
+        int tag = get4LE(eventData);
+        eventData += 4;
+
+        if (tag == TAG) {
+            // This tag should not have been written because the data was null
+            count++;
+            break;
+        }
+    }
+
+    EXPECT_EQ(0, count);
+
+    android_logger_list_close(logger_list);
+}
+
+TEST(liblog, android_errorWriteWithInfoLog__android_logger_list_read__subtag_too_long) {
+    const int TAG = 123456784;
+    const char SUBTAG[] = "abcdefghijklmnopqrstuvwxyz now i know my abc";
+    const int UID = -1;
+    const int DATA_LEN = 200;
+    struct logger_list *logger_list;
+
+    pid_t pid = getpid();
+
+    ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
+        LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid)));
+
+    ASSERT_LT(0, android_errorWriteWithInfoLog(
+            TAG, SUBTAG, UID, max_payload_buf, DATA_LEN));
+
+    sleep(2);
+
+    int count = 0;
+
+    for (;;) {
+        log_msg log_msg;
+        if (android_logger_list_read(logger_list, &log_msg) <= 0) {
+            break;
+        }
+
+        char *eventData = log_msg.msg();
+
+        // Tag
+        int tag = get4LE(eventData);
+        eventData += 4;
+
+        if (tag != TAG) {
+            continue;
+        }
+
+        // List type
+        ASSERT_EQ(EVENT_TYPE_LIST, eventData[0]);
+        eventData++;
+
+        // Number of elements in list
+        ASSERT_EQ(3, eventData[0]);
+        eventData++;
+
+        // Element #1: string type for subtag
+        ASSERT_EQ(EVENT_TYPE_STRING, eventData[0]);
+        eventData++;
+
+        // The subtag is longer than 32 and should be truncated to that.
+        ASSERT_EQ(32, get4LE(eventData));
+        eventData +=4;
+
+        if (memcmp(SUBTAG, eventData, 32)) {
+            continue;
+        }
+        eventData += 32;
+
+        // Element #2: int type for uid
+        ASSERT_EQ(EVENT_TYPE_INT, eventData[0]);
+        eventData++;
+
+        ASSERT_EQ(UID, get4LE(eventData));
+        eventData += 4;
+
+        // Element #3: string type for data
+        ASSERT_EQ(EVENT_TYPE_STRING, eventData[0]);
+        eventData++;
+
+        ASSERT_EQ(DATA_LEN, get4LE(eventData));
+        eventData += 4;
+
+        if (memcmp(max_payload_buf, eventData, DATA_LEN)) {
+            continue;
+        }
+
+        ++count;
+    }
+
+    EXPECT_EQ(1, count);
+
+    android_logger_list_close(logger_list);
+}
+
+TEST(liblog, android_errorWriteLog__android_logger_list_read__success) {
+    const int TAG = 123456785;
+    const char SUBTAG[] = "test-subtag";
+    struct logger_list *logger_list;
+
+    pid_t pid = getpid();
+
+    ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
+        LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid)));
+
+    ASSERT_LT(0, android_errorWriteLog(TAG, SUBTAG));
+
+    sleep(2);
+
+    int count = 0;
+
+    for (;;) {
+        log_msg log_msg;
+        if (android_logger_list_read(logger_list, &log_msg) <= 0) {
+            break;
+        }
+
+        char *eventData = log_msg.msg();
+
+        // Tag
+        int tag = get4LE(eventData);
+        eventData += 4;
+
+        if (tag != TAG) {
+            continue;
+        }
+
+        // List type
+        ASSERT_EQ(EVENT_TYPE_LIST, eventData[0]);
+        eventData++;
+
+        // Number of elements in list
+        ASSERT_EQ(3, eventData[0]);
+        eventData++;
+
+        // Element #1: string type for subtag
+        ASSERT_EQ(EVENT_TYPE_STRING, eventData[0]);
+        eventData++;
+
+        ASSERT_EQ((int) strlen(SUBTAG), get4LE(eventData));
+        eventData +=4;
+
+        if (memcmp(SUBTAG, eventData, strlen(SUBTAG))) {
+            continue;
+        }
+        ++count;
+    }
+
+    EXPECT_EQ(1, count);
+
+    android_logger_list_close(logger_list);
+}
+
+TEST(liblog, android_errorWriteLog__android_logger_list_read__null_subtag) {
+    const int TAG = 123456786;
+    struct logger_list *logger_list;
+
+    pid_t pid = getpid();
+
+    ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
+        LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid)));
+
+    ASSERT_GT(0, android_errorWriteLog(TAG, NULL));
+
+    sleep(2);
+
+    int count = 0;
+
+    for (;;) {
+        log_msg log_msg;
+        if (android_logger_list_read(logger_list, &log_msg) <= 0) {
+            break;
+        }
+
+        char *eventData = log_msg.msg();
+
+        // Tag
+        int tag = get4LE(eventData);
+        eventData += 4;
+
+        if (tag == TAG) {
+            // This tag should not have been written because the data was null
+            count++;
+            break;
+        }
+    }
+
+    EXPECT_EQ(0, count);
+
+    android_logger_list_close(logger_list);
+}
diff --git a/libnativebridge/Android.mk b/libnativebridge/Android.mk
index 83169eb..d20d44c 100644
--- a/libnativebridge/Android.mk
+++ b/libnativebridge/Android.mk
@@ -37,4 +37,22 @@
 
 include $(BUILD_HOST_SHARED_LIBRARY)
 
+# Static library for host
+# ========================================================
+include $(CLEAR_VARS)
+
+LOCAL_MODULE:= libnativebridge
+
+LOCAL_SRC_FILES:= $(NATIVE_BRIDGE_COMMON_SRC_FILES)
+LOCAL_STATIC_LIBRARIES := liblog
+LOCAL_CLANG := true
+LOCAL_CPP_EXTENSION := .cc
+LOCAL_CFLAGS += -Werror -Wall
+LOCAL_CPPFLAGS := -std=gnu++11 -fvisibility=protected
+LOCAL_LDFLAGS := -ldl
+LOCAL_MULTILIB := both
+
+include $(BUILD_HOST_STATIC_LIBRARY)
+
+
 include $(LOCAL_PATH)/tests/Android.mk
diff --git a/libnativebridge/native_bridge.cc b/libnativebridge/native_bridge.cc
index f63497b..32a65ea 100644
--- a/libnativebridge/native_bridge.cc
+++ b/libnativebridge/native_bridge.cc
@@ -109,6 +109,13 @@
   }
 }
 
+static void ReleaseAppCodeCacheDir() {
+  if (app_code_cache_dir != nullptr) {
+    delete[] app_code_cache_dir;
+    app_code_cache_dir = nullptr;
+  }
+}
+
 // We only allow simple names for the library. It is supposed to be a file in
 // /system/lib or /vendor/lib. Only allow a small range of characters, that is
 // names consisting of [a-zA-Z0-9._-] and starting with [a-zA-Z].
@@ -162,8 +169,7 @@
 static void CloseNativeBridge(bool with_error) {
   state = NativeBridgeState::kClosed;
   had_error |= with_error;
-  delete[] app_code_cache_dir;
-  app_code_cache_dir = nullptr;
+  ReleaseAppCodeCacheDir();
 }
 
 bool LoadNativeBridge(const char* nb_library_filename,
@@ -289,13 +295,13 @@
   // so we save the extra file existence check.
   char cpuinfo_path[1024];
 
-#ifdef HAVE_ANDROID_OS
+#if defined(__ANDROID__)
   snprintf(cpuinfo_path, sizeof(cpuinfo_path), "/system/lib"
 #ifdef __LP64__
       "64"
 #endif  // __LP64__
       "/%s/cpuinfo", instruction_set);
-#else   // !HAVE_ANDROID_OS
+#else   // !__ANDROID__
   // To be able to test on the host, we hardwire a relative path.
   snprintf(cpuinfo_path, sizeof(cpuinfo_path), "./cpuinfo");
 #endif
@@ -406,16 +412,16 @@
     if (stat(app_code_cache_dir, &st) == -1) {
       if (errno == ENOENT) {
         if (mkdir(app_code_cache_dir, S_IRWXU | S_IRWXG | S_IXOTH) == -1) {
-          ALOGE("Cannot create code cache directory %s: %s.", app_code_cache_dir, strerror(errno));
-          CloseNativeBridge(true);
+          ALOGW("Cannot create code cache directory %s: %s.", app_code_cache_dir, strerror(errno));
+          ReleaseAppCodeCacheDir();
         }
       } else {
-        ALOGE("Cannot stat code cache directory %s: %s.", app_code_cache_dir, strerror(errno));
-        CloseNativeBridge(true);
+        ALOGW("Cannot stat code cache directory %s: %s.", app_code_cache_dir, strerror(errno));
+        ReleaseAppCodeCacheDir();
       }
     } else if (!S_ISDIR(st.st_mode)) {
-      ALOGE("Code cache is not a directory %s.", app_code_cache_dir);
-      CloseNativeBridge(true);
+      ALOGW("Code cache is not a directory %s.", app_code_cache_dir);
+      ReleaseAppCodeCacheDir();
     }
 
     // If we're still PreInitialized (dind't fail the code cache checks) try to initialize.
@@ -424,8 +430,7 @@
         SetupEnvironment(callbacks, env, instruction_set);
         state = NativeBridgeState::kInitialized;
         // We no longer need the code cache path, release the memory.
-        delete[] app_code_cache_dir;
-        app_code_cache_dir = nullptr;
+        ReleaseAppCodeCacheDir();
       } else {
         // Unload the library.
         dlclose(native_bridge_handle);
diff --git a/libnativebridge/tests/Android.mk b/libnativebridge/tests/Android.mk
index 285e8c2..7265939 100644
--- a/libnativebridge/tests/Android.mk
+++ b/libnativebridge/tests/Android.mk
@@ -9,6 +9,7 @@
 test_src_files := \
     CodeCacheCreate_test.cpp \
     CodeCacheExists_test.cpp \
+    CodeCacheStatFail_test.cpp \
     CompleteFlow_test.cpp \
     InvalidCharsNativeBridge_test.cpp \
     NativeBridge2Signal_test.cpp \
diff --git a/libnativebridge/tests/CodeCacheStatFail_test.cpp b/libnativebridge/tests/CodeCacheStatFail_test.cpp
new file mode 100644
index 0000000..4ea519e
--- /dev/null
+++ b/libnativebridge/tests/CodeCacheStatFail_test.cpp
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2014 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 "NativeBridgeTest.h"
+
+#include <errno.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+namespace android {
+
+// Tests that the bridge is initialized without errors if the code_cache is
+// existed as a file.
+TEST_F(NativeBridgeTest, CodeCacheStatFail) {
+    int fd = creat(kCodeCache, O_RDWR);
+    ASSERT_NE(-1, fd);
+    close(fd);
+
+    struct stat st;
+    ASSERT_EQ(-1, stat(kCodeCacheStatFail, &st));
+    ASSERT_EQ(ENOTDIR, errno);
+
+    // Init
+    ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary, nullptr));
+    ASSERT_TRUE(PreInitializeNativeBridge(kCodeCacheStatFail, "isa"));
+    ASSERT_TRUE(InitializeNativeBridge(nullptr, nullptr));
+    ASSERT_TRUE(NativeBridgeAvailable());
+    ASSERT_FALSE(NativeBridgeError());
+
+    // Clean up
+    UnloadNativeBridge();
+
+    ASSERT_FALSE(NativeBridgeError());
+    unlink(kCodeCache);
+}
+
+}  // namespace android
diff --git a/libnativebridge/tests/NativeBridgeTest.h b/libnativebridge/tests/NativeBridgeTest.h
index 6a5c126..d489420 100644
--- a/libnativebridge/tests/NativeBridgeTest.h
+++ b/libnativebridge/tests/NativeBridgeTest.h
@@ -24,6 +24,7 @@
 
 constexpr const char* kNativeBridgeLibrary = "libnativebridge-dummy.so";
 constexpr const char* kCodeCache = "./code_cache";
+constexpr const char* kCodeCacheStatFail = "./code_cache/temp";
 
 namespace android {
 
diff --git a/libnativebridge/tests/PreInitializeNativeBridge_test.cpp b/libnativebridge/tests/PreInitializeNativeBridge_test.cpp
index cec26ce..d3bbebe 100644
--- a/libnativebridge/tests/PreInitializeNativeBridge_test.cpp
+++ b/libnativebridge/tests/PreInitializeNativeBridge_test.cpp
@@ -32,8 +32,8 @@
 
 TEST_F(NativeBridgeTest, PreInitializeNativeBridge) {
     ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary, nullptr));
-#ifndef __APPLE__         // Mac OS does not support bind-mount.
-#ifndef HAVE_ANDROID_OS   // Cannot write into the hard-wired location.
+#if !defined(__APPLE__)         // Mac OS does not support bind-mount.
+#if !defined(__ANDROID__)       // Cannot write into the hard-wired location.
     // Try to create our mount namespace.
     if (unshare(CLONE_NEWNS) != -1) {
         // Create a dummy file.
diff --git a/libnetutils/ifc_utils.c b/libnetutils/ifc_utils.c
index 7d2a5fb..956ed30 100644
--- a/libnetutils/ifc_utils.c
+++ b/libnetutils/ifc_utils.c
@@ -50,7 +50,7 @@
 #define ALOGW printf
 #endif
 
-#ifdef HAVE_ANDROID_OS
+#if defined(__ANDROID__)
 /* SIOCKILLADDR is an Android extension. */
 #define SIOCKILLADDR 0x8939
 #endif
@@ -596,7 +596,7 @@
 
 int ifc_reset_connections(const char *ifname, const int reset_mask)
 {
-#ifdef HAVE_ANDROID_OS
+#if defined(__ANDROID__)
     int result, success;
     in_addr_t myaddr = 0;
     struct ifreq ifr;
diff --git a/libpixelflinger/Android.mk b/libpixelflinger/Android.mk
index 697db25..11e7988 100644
--- a/libpixelflinger/Android.mk
+++ b/libpixelflinger/Android.mk
@@ -70,10 +70,6 @@
 # libhardware, but this at least gets us built.
 LOCAL_SHARED_LIBRARIES += libhardware_legacy
 LOCAL_CFLAGS += -DWITH_LIB_HARDWARE
-# t32cb16blend.S does not compile with Clang.
-LOCAL_CLANG_ASFLAGS_arm += -no-integrated-as
-# arch-arm64/col32cb16blend.S does not compile with Clang.
-LOCAL_CLANG_ASFLAGS_arm64 += -no-integrated-as
 include $(BUILD_SHARED_LIBRARY)
 
 include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/libpixelflinger/arch-arm64/col32cb16blend.S b/libpixelflinger/arch-arm64/col32cb16blend.S
index 18a01fd..8d9c7c4 100644
--- a/libpixelflinger/arch-arm64/col32cb16blend.S
+++ b/libpixelflinger/arch-arm64/col32cb16blend.S
@@ -26,7 +26,7 @@
  * SUCH DAMAGE.
  */
     .text
-    .align
+    .align 0
 
     .global scanline_col32cb16blend_arm64
 
diff --git a/libpixelflinger/arch-arm64/t32cb16blend.S b/libpixelflinger/arch-arm64/t32cb16blend.S
index 7da8cf5..230f47b 100644
--- a/libpixelflinger/arch-arm64/t32cb16blend.S
+++ b/libpixelflinger/arch-arm64/t32cb16blend.S
@@ -26,7 +26,7 @@
  * SUCH DAMAGE.
  */
     .text
-    .align
+    .align 0
 
     .global scanline_t32cb16blend_arm64
 
diff --git a/libpixelflinger/t32cb16blend.S b/libpixelflinger/t32cb16blend.S
index caf9eb7..1d40ad4 100644
--- a/libpixelflinger/t32cb16blend.S
+++ b/libpixelflinger/t32cb16blend.S
@@ -17,6 +17,7 @@
 
 
 	.text
+	.syntax unified
 	.align
 	
 	.global scanline_t32cb16blend_arm
@@ -146,7 +147,7 @@
     tst     r0, #0x3
     beq     aligned
     subs    r2, r2, #1
-    ldmlofd	sp!, {r4-r7, lr}        // return
+    ldmfdlo sp!, {r4-r7, lr}        // return
     bxlo    lr
 
 last:
@@ -197,6 +198,6 @@
     mov     r4, r5
 
 9:  adds    r2, r2, #1
-    ldmlofd sp!, {r4-r7, lr}        // return
+    ldmfdlo sp!, {r4-r7, lr}        // return
     bxlo    lr
     b       last
diff --git a/libpixelflinger/tests/arch-arm64/assembler/Android.mk b/libpixelflinger/tests/arch-arm64/assembler/Android.mk
index 448d298..bd0f24b 100644
--- a/libpixelflinger/tests/arch-arm64/assembler/Android.mk
+++ b/libpixelflinger/tests/arch-arm64/assembler/Android.mk
@@ -5,9 +5,6 @@
     arm64_assembler_test.cpp\
     asm_test_jacket.S
 
-# asm_test_jacket.S does not compile with Clang.
-LOCAL_CLANG_ASFLAGS_arm64 += -no-integrated-as
-
 LOCAL_SHARED_LIBRARIES := \
     libcutils \
     libpixelflinger
diff --git a/libpixelflinger/tests/arch-arm64/assembler/asm_test_jacket.S b/libpixelflinger/tests/arch-arm64/assembler/asm_test_jacket.S
index a1392c2..f44859f 100644
--- a/libpixelflinger/tests/arch-arm64/assembler/asm_test_jacket.S
+++ b/libpixelflinger/tests/arch-arm64/assembler/asm_test_jacket.S
@@ -27,7 +27,7 @@
  */
 
     .text
-    .align
+    .align 0
 
     .global asm_test_jacket
 
diff --git a/libpixelflinger/tests/arch-arm64/col32cb16blend/Android.mk b/libpixelflinger/tests/arch-arm64/col32cb16blend/Android.mk
index 5d69203..3368eb0 100644
--- a/libpixelflinger/tests/arch-arm64/col32cb16blend/Android.mk
+++ b/libpixelflinger/tests/arch-arm64/col32cb16blend/Android.mk
@@ -5,8 +5,6 @@
     col32cb16blend_test.c \
     ../../../arch-arm64/col32cb16blend.S
 
-LOCAL_CLANG_ASFLAGS_arm64 += -no-integrated-as
-
 LOCAL_SHARED_LIBRARIES :=
 
 LOCAL_C_INCLUDES :=
diff --git a/libpixelflinger/tests/arch-arm64/t32cb16blend/Android.mk b/libpixelflinger/tests/arch-arm64/t32cb16blend/Android.mk
index 2c1379b..8e5ec5e 100644
--- a/libpixelflinger/tests/arch-arm64/t32cb16blend/Android.mk
+++ b/libpixelflinger/tests/arch-arm64/t32cb16blend/Android.mk
@@ -5,8 +5,6 @@
     t32cb16blend_test.c \
     ../../../arch-arm64/t32cb16blend.S
 
-LOCAL_CLANG_ASFLAGS_arm64 += -no-integrated-as
-
 LOCAL_SHARED_LIBRARIES :=
 
 LOCAL_C_INCLUDES :=
diff --git a/libsparse/Android.mk b/libsparse/Android.mk
index 925b98b..c77eba9 100644
--- a/libsparse/Android.mk
+++ b/libsparse/Android.mk
@@ -18,6 +18,7 @@
 LOCAL_STATIC_LIBRARIES := libz
 LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
 LOCAL_CFLAGS := -Werror
+LOCAL_MODULE_HOST_OS := darwin linux windows
 include $(BUILD_HOST_STATIC_LIBRARY)
 
 
diff --git a/libsparse/backed_block.c b/libsparse/backed_block.c
index 3e72b57..794cd6b 100644
--- a/libsparse/backed_block.c
+++ b/libsparse/backed_block.c
@@ -221,7 +221,8 @@
 		}
 		break;
 	case BACKED_BLOCK_FILE:
-		if (a->file.filename != b->file.filename ||
+		/* Already make sure b->type is BACKED_BLOCK_FILE */
+		if (strcmp(a->file.filename, b->file.filename) ||
 				a->file.offset + a->len != b->file.offset) {
 			return -EINVAL;
 		}
@@ -279,7 +280,10 @@
 	}
 
 	merge_bb(bbl, new_bb, new_bb->next);
-	merge_bb(bbl, bb, new_bb);
+	if (!merge_bb(bbl, bb, new_bb)) {
+		/* new_bb destroyed, point to retained as last_used */
+		bbl->last_used = bb;
+	}
 
 	return 0;
 }
diff --git a/libsparse/simg_dump.py b/libsparse/simg_dump.py
index 6ece31d..c70d45f 100755
--- a/libsparse/simg_dump.py
+++ b/libsparse/simg_dump.py
@@ -135,7 +135,7 @@
           break;
         else:
           crc_bin = FH.read(4)
-          crc = struct.unpack("<I", crc)
+          crc = struct.unpack("<I", crc_bin)
           print("Unverified CRC32 0x%08X" % (crc))
       else:
           print("Unknown chunk type 0x%04X" % (chunk_type), end="")
diff --git a/libsparse/sparse_crc32.h b/libsparse/sparse_crc32.h
index cad8a86..50cd9e9 100644
--- a/libsparse/sparse_crc32.h
+++ b/libsparse/sparse_crc32.h
@@ -14,7 +14,19 @@
  * limitations under the License.
  */
 
+#ifndef _LIBSPARSE_SPARSE_CRC32_H_
+#define _LIBSPARSE_SPARSE_CRC32_H_
+
 #include <stdint.h>
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 uint32_t sparse_crc32(uint32_t crc, const void *buf, size_t size);
 
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libutils/Android.mk b/libutils/Android.mk
index d1ed997..d610b8c 100644
--- a/libutils/Android.mk
+++ b/libutils/Android.mk
@@ -41,27 +41,19 @@
 
 host_commonCflags := -DLIBUTILS_NATIVE=1 $(TOOL_CFLAGS) -Werror
 
-ifeq ($(HOST_OS),windows)
-ifeq ($(strip $(USE_CYGWIN),),)
-# Under MinGW, ctype.h doesn't need multi-byte support
-host_commonCflags += -DMB_CUR_MAX=1
-endif
-endif
-
 # For the host
 # =====================================================
 include $(CLEAR_VARS)
 LOCAL_SRC_FILES:= $(commonSources)
-ifeq ($(HOST_OS), linux)
-LOCAL_SRC_FILES += Looper.cpp
-endif
-ifeq ($(HOST_OS),darwin)
-LOCAL_CFLAGS += -Wno-unused-parameter
-endif
+LOCAL_SRC_FILES_linux := Looper.cpp
+LOCAL_CFLAGS_darwin := -Wno-unused-parameter
 LOCAL_MODULE:= libutils
 LOCAL_STATIC_LIBRARIES := liblog
 LOCAL_CFLAGS += $(host_commonCflags)
+# Under MinGW, ctype.h doesn't need multi-byte support
+LOCAL_CFLAGS_windows := -DMB_CUR_MAX=1
 LOCAL_MULTILIB := both
+LOCAL_MODULE_HOST_OS := darwin linux windows
 include $(BUILD_HOST_STATIC_LIBRARY)
 
 
@@ -91,7 +83,9 @@
         liblog \
         libdl
 
-LOCAL_MODULE:= libutils
+LOCAL_MODULE := libutils
+LOCAL_CLANG := true
+LOCAL_SANITIZE := integer
 include $(BUILD_STATIC_LIBRARY)
 
 # For the device, shared
@@ -106,6 +100,8 @@
         liblog
 LOCAL_CFLAGS := -Werror
 
+LOCAL_CLANG := true
+LOCAL_SANITIZE := integer
 include $(BUILD_SHARED_LIBRARY)
 
 # Include subdirectory makefiles
diff --git a/libutils/JenkinsHash.cpp b/libutils/JenkinsHash.cpp
index 52c9bb7..ff5d252 100644
--- a/libutils/JenkinsHash.cpp
+++ b/libutils/JenkinsHash.cpp
@@ -19,10 +19,14 @@
  * should still be quite good.
  **/
 
+#include <stdlib.h>
 #include <utils/JenkinsHash.h>
 
 namespace android {
 
+#ifdef __clang__
+__attribute__((no_sanitize("integer")))
+#endif
 hash_t JenkinsHashWhiten(uint32_t hash) {
     hash += (hash << 3);
     hash ^= (hash >> 11);
@@ -31,6 +35,9 @@
 }
 
 uint32_t JenkinsHashMixBytes(uint32_t hash, const uint8_t* bytes, size_t size) {
+    if (size > UINT32_MAX) {
+        abort();
+    }
     hash = JenkinsHashMix(hash, (uint32_t)size);
     size_t i;
     for (i = 0; i < (size & -4); i += 4) {
@@ -47,6 +54,9 @@
 }
 
 uint32_t JenkinsHashMixShorts(uint32_t hash, const uint16_t* shorts, size_t size) {
+    if (size > UINT32_MAX) {
+        abort();
+    }
     hash = JenkinsHashMix(hash, (uint32_t)size);
     size_t i;
     for (i = 0; i < (size & -2); i += 2) {
diff --git a/libutils/Looper.cpp b/libutils/Looper.cpp
index 5b0ff3a..b14884b 100644
--- a/libutils/Looper.cpp
+++ b/libutils/Looper.cpp
@@ -17,11 +17,13 @@
 #include <utils/Looper.h>
 #include <utils/Timers.h>
 
-#include <unistd.h>
+#include <errno.h>
 #include <fcntl.h>
 #include <limits.h>
 #include <inttypes.h>
+#include <string.h>
 #include <sys/eventfd.h>
+#include <unistd.h>
 
 
 namespace android {
@@ -73,7 +75,8 @@
         mPolling(false), mEpollFd(-1), mEpollRebuildRequired(false),
         mNextRequestSeq(0), mResponseIndex(0), mNextMessageUptime(LLONG_MAX) {
     mWakeEventFd = eventfd(0, EFD_NONBLOCK);
-    LOG_ALWAYS_FATAL_IF(mWakeEventFd < 0, "Could not make wake event fd.  errno=%d", errno);
+    LOG_ALWAYS_FATAL_IF(mWakeEventFd < 0, "Could not make wake event fd: %s",
+                        strerror(errno));
 
     AutoMutex _l(mLock);
     rebuildEpollLocked();
@@ -148,15 +151,15 @@
 
     // Allocate the new epoll instance and register the wake pipe.
     mEpollFd = epoll_create(EPOLL_SIZE_HINT);
-    LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance.  errno=%d", errno);
+    LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance: %s", strerror(errno));
 
     struct epoll_event eventItem;
     memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union
     eventItem.events = EPOLLIN;
     eventItem.data.fd = mWakeEventFd;
     int result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeEventFd, & eventItem);
-    LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake event fd to epoll instance.  errno=%d",
-            errno);
+    LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake event fd to epoll instance: %s",
+                        strerror(errno));
 
     for (size_t i = 0; i < mRequests.size(); i++) {
         const Request& request = mRequests.valueAt(i);
@@ -165,8 +168,8 @@
 
         int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, request.fd, & eventItem);
         if (epollResult < 0) {
-            ALOGE("Error adding epoll events for fd %d while rebuilding epoll set, errno=%d",
-                    request.fd, errno);
+            ALOGE("Error adding epoll events for fd %d while rebuilding epoll set: %s",
+                  request.fd, strerror(errno));
         }
     }
 }
@@ -265,7 +268,7 @@
         if (errno == EINTR) {
             goto Done;
         }
-        ALOGW("Poll failed with an unexpected error, errno=%d", errno);
+        ALOGW("Poll failed with an unexpected error: %s", strerror(errno));
         result = POLL_ERROR;
         goto Done;
     }
@@ -410,7 +413,7 @@
     ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd, &inc, sizeof(uint64_t)));
     if (nWrite != sizeof(uint64_t)) {
         if (errno != EAGAIN) {
-            ALOGW("Could not write wake signal, errno=%d", errno);
+            ALOGW("Could not write wake signal: %s", strerror(errno));
         }
     }
 }
@@ -474,7 +477,7 @@
         if (requestIndex < 0) {
             int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, & eventItem);
             if (epollResult < 0) {
-                ALOGE("Error adding epoll events for fd %d, errno=%d", fd, errno);
+                ALOGE("Error adding epoll events for fd %d: %s", fd, strerror(errno));
                 return -1;
             }
             mRequests.add(fd, request);
@@ -497,18 +500,18 @@
                     // call instead, but that approach carries others disadvantages.
 #if DEBUG_CALLBACKS
                     ALOGD("%p ~ addFd - EPOLL_CTL_MOD failed due to file descriptor "
-                            "being recycled, falling back on EPOLL_CTL_ADD, errno=%d",
-                            this, errno);
+                            "being recycled, falling back on EPOLL_CTL_ADD: %s",
+                            this, strerror(errno));
 #endif
                     epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, & eventItem);
                     if (epollResult < 0) {
-                        ALOGE("Error modifying or adding epoll events for fd %d, errno=%d",
-                                fd, errno);
+                        ALOGE("Error modifying or adding epoll events for fd %d: %s",
+                                fd, strerror(errno));
                         return -1;
                     }
                     scheduleEpollRebuildLocked();
                 } else {
-                    ALOGE("Error modifying epoll events for fd %d, errno=%d", fd, errno);
+                    ALOGE("Error modifying epoll events for fd %d: %s", fd, strerror(errno));
                     return -1;
                 }
             }
@@ -563,7 +566,7 @@
                 // call instead, but that approach carries others disadvantages.
 #if DEBUG_CALLBACKS
                 ALOGD("%p ~ removeFd - EPOLL_CTL_DEL failed due to file descriptor "
-                        "being closed, errno=%d", this, errno);
+                        "being closed: %s", this, strerror(errno));
 #endif
                 scheduleEpollRebuildLocked();
             } else {
@@ -571,7 +574,7 @@
                 // our list of callbacks got out of sync with the epoll set somehow.
                 // We defensively rebuild the epoll set to avoid getting spurious
                 // notifications with nowhere to go.
-                ALOGE("Error removing epoll events for fd %d, errno=%d", fd, errno);
+                ALOGE("Error removing epoll events for fd %d: %s", fd, strerror(errno));
                 scheduleEpollRebuildLocked();
                 return -1;
             }
diff --git a/libutils/ProcessCallStack.cpp b/libutils/ProcessCallStack.cpp
index db07e56..011c302 100644
--- a/libutils/ProcessCallStack.cpp
+++ b/libutils/ProcessCallStack.cpp
@@ -17,9 +17,10 @@
 #define LOG_TAG "ProcessCallStack"
 // #define LOG_NDEBUG 0
 
-#include <string.h>
-#include <stdio.h>
 #include <dirent.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
 
 #include <utils/Log.h>
 #include <utils/Errors.h>
@@ -135,8 +136,8 @@
 
     dp = opendir(PATH_SELF_TASK);
     if (dp == NULL) {
-        ALOGE("%s: Failed to update the process's call stacks (errno = %d, '%s')",
-              __FUNCTION__, errno, strerror(errno));
+        ALOGE("%s: Failed to update the process's call stacks: %s",
+              __FUNCTION__, strerror(errno));
         return;
     }
 
@@ -172,8 +173,8 @@
 
         ssize_t idx = mThreadMap.add(tid, ThreadInfo());
         if (idx < 0) { // returns negative error value on error
-            ALOGE("%s: Failed to add new ThreadInfo (errno = %zd, '%s')",
-                  __FUNCTION__, idx, strerror(-idx));
+            ALOGE("%s: Failed to add new ThreadInfo: %s",
+                  __FUNCTION__, strerror(-idx));
             continue;
         }
 
@@ -195,8 +196,8 @@
               __FUNCTION__, tid, threadInfo.callStack.size());
     }
     if (code != 0) { // returns positive error value on error
-        ALOGE("%s: Failed to readdir from %s (errno = %d, '%s')",
-              __FUNCTION__, PATH_SELF_TASK, -code, strerror(code));
+        ALOGE("%s: Failed to readdir from %s: %s",
+              __FUNCTION__, PATH_SELF_TASK, strerror(code));
     }
 #endif
 
diff --git a/libutils/String8.cpp b/libutils/String8.cpp
index ad65fdb..2d06023 100644
--- a/libutils/String8.cpp
+++ b/libutils/String8.cpp
@@ -19,6 +19,7 @@
 
 #include <utils/String8.h>
 
+#include <utils/Compat.h>
 #include <utils/Log.h>
 #include <utils/Unicode.h>
 #include <utils/SharedBuffer.h>
diff --git a/libutils/SystemClock.cpp b/libutils/SystemClock.cpp
index ac3dd98..1fca2b2 100644
--- a/libutils/SystemClock.cpp
+++ b/libutils/SystemClock.cpp
@@ -19,7 +19,7 @@
  * System clock functions.
  */
 
-#ifdef HAVE_ANDROID_OS
+#if defined(__ANDROID__)
 #include <linux/ioctl.h>
 #include <linux/rtc.h>
 #include <utils/Atomic.h>
@@ -29,7 +29,6 @@
 #include <sys/time.h>
 #include <limits.h>
 #include <fcntl.h>
-#include <errno.h>
 #include <string.h>
 
 #include <utils/SystemClock.h>
@@ -108,7 +107,7 @@
  */
 int64_t elapsedRealtimeNano()
 {
-#ifdef HAVE_ANDROID_OS
+#if defined(__ANDROID__)
     struct timespec ts;
     int result;
     int64_t timestamp;
diff --git a/libutils/Threads.cpp b/libutils/Threads.cpp
index 1e014c6..6dda6b5 100644
--- a/libutils/Threads.cpp
+++ b/libutils/Threads.cpp
@@ -22,6 +22,7 @@
 #include <memory.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <string.h>
 #include <unistd.h>
 
 #if !defined(_WIN32)
@@ -44,7 +45,7 @@
 
 #include <cutils/sched_policy.h>
 
-#ifdef HAVE_ANDROID_OS
+#if defined(__ANDROID__)
 # define __android_unused
 #else
 # define __android_unused __attribute__((__unused__))
@@ -131,7 +132,7 @@
     pthread_attr_init(&attr);
     pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
 
-#ifdef HAVE_ANDROID_OS  /* valgrind is rejecting RT-priority create reqs */
+#if defined(__ANDROID__)  /* valgrind is rejecting RT-priority create reqs */
     if (threadPriority != PRIORITY_DEFAULT || threadName != NULL) {
         // Now that the pthread_t has a method to find the associated
         // android_thread_id_t (pid) from pthread_t, it would be possible to avoid
@@ -160,9 +161,9 @@
                     (android_pthread_entry)entryFunction, userData);
     pthread_attr_destroy(&attr);
     if (result != 0) {
-        ALOGE("androidCreateRawThreadEtc failed (entry=%p, res=%d, errno=%d)\n"
+        ALOGE("androidCreateRawThreadEtc failed (entry=%p, res=%d, %s)\n"
              "(android threadPriority=%d)",
-            entryFunction, result, errno, threadPriority);
+            entryFunction, result, strerror(errno), threadPriority);
         return 0;
     }
 
@@ -175,7 +176,7 @@
     return 1;
 }
 
-#ifdef HAVE_ANDROID_OS
+#if defined(__ANDROID__)
 static pthread_t android_thread_id_t_to_pthread(android_thread_id_t thread)
 {
     return (pthread_t) thread;
@@ -301,12 +302,10 @@
     gCreateThreadFn = func;
 }
 
-#ifdef HAVE_ANDROID_OS
+#if defined(__ANDROID__)
 int androidSetThreadPriority(pid_t tid, int pri)
 {
     int rc = 0;
-
-#if !defined(_WIN32)
     int lasterr = 0;
 
     if (pri >= ANDROID_PRIORITY_BACKGROUND) {
@@ -324,17 +323,12 @@
     } else {
         errno = lasterr;
     }
-#endif
 
     return rc;
 }
 
 int androidGetThreadPriority(pid_t tid) {
-#if !defined(_WIN32)
     return getpriority(PRIO_PROCESS, tid);
-#else
-    return ANDROID_PRIORITY_NORMAL;
-#endif
 }
 
 #endif
@@ -657,7 +651,7 @@
         mLock("Thread::mLock"),
         mStatus(NO_ERROR),
         mExitPending(false), mRunning(false)
-#ifdef HAVE_ANDROID_OS
+#if defined(__ANDROID__)
         , mTid(-1)
 #endif
 {
@@ -727,7 +721,7 @@
     wp<Thread> weak(strong);
     self->mHoldSelf.clear();
 
-#ifdef HAVE_ANDROID_OS
+#if defined(__ANDROID__)
     // this is very useful for debugging with gdb
     self->mTid = gettid();
 #endif
@@ -838,7 +832,7 @@
     return mRunning;
 }
 
-#ifdef HAVE_ANDROID_OS
+#if defined(__ANDROID__)
 pid_t Thread::getTid() const
 {
     // mTid is not defined until the child initializes it, and the caller may need it earlier
diff --git a/libutils/Timers.cpp b/libutils/Timers.cpp
index fb70e15..201bc41 100644
--- a/libutils/Timers.cpp
+++ b/libutils/Timers.cpp
@@ -23,7 +23,7 @@
 #include <sys/time.h>
 #include <time.h>
 
-#if defined(HAVE_ANDROID_OS)
+#if defined(__ANDROID__)
 nsecs_t systemTime(int clock)
 {
     static const clockid_t clocks[] = {
diff --git a/libutils/Tokenizer.cpp b/libutils/Tokenizer.cpp
index 610002f..2d0e83d 100644
--- a/libutils/Tokenizer.cpp
+++ b/libutils/Tokenizer.cpp
@@ -56,12 +56,12 @@
     int fd = ::open(filename.string(), O_RDONLY);
     if (fd < 0) {
         result = -errno;
-        ALOGE("Error opening file '%s', %s.", filename.string(), strerror(errno));
+        ALOGE("Error opening file '%s': %s", filename.string(), strerror(errno));
     } else {
         struct stat stat;
         if (fstat(fd, &stat)) {
             result = -errno;
-            ALOGE("Error getting size of file '%s', %s.", filename.string(), strerror(errno));
+            ALOGE("Error getting size of file '%s': %s", filename.string(), strerror(errno));
         } else {
             size_t length = size_t(stat.st_size);
 
@@ -83,7 +83,7 @@
                 ssize_t nrd = read(fd, buffer, length);
                 if (nrd < 0) {
                     result = -errno;
-                    ALOGE("Error reading file '%s', %s.", filename.string(), strerror(errno));
+                    ALOGE("Error reading file '%s': %s", filename.string(), strerror(errno));
                     delete[] buffer;
                     buffer = NULL;
                 } else {
diff --git a/libutils/Unicode.cpp b/libutils/Unicode.cpp
index fb876c9..6f4b721 100644
--- a/libutils/Unicode.cpp
+++ b/libutils/Unicode.cpp
@@ -18,7 +18,7 @@
 
 #include <stddef.h>
 
-#ifdef HAVE_WINSOCK
+#if defined(_WIN32)
 # undef  nhtol
 # undef  htonl
 # undef  nhtos
diff --git a/libutils/VectorImpl.cpp b/libutils/VectorImpl.cpp
index 30ca663..2f770f5 100644
--- a/libutils/VectorImpl.cpp
+++ b/libutils/VectorImpl.cpp
@@ -198,7 +198,10 @@
                     _do_copy(next, curr, 1);
                     next = curr;
                     --j;
-                    curr = reinterpret_cast<char*>(array) + mItemSize*(j);                    
+                    curr = NULL;
+                    if (j >= 0) {
+                        curr = reinterpret_cast<char*>(array) + mItemSize*(j);
+                    }
                 } while (j>=0 && (cmp(curr, temp, state) > 0));
 
                 _do_destroy(next, 1);
@@ -551,6 +554,10 @@
 
 ssize_t SortedVectorImpl::_indexOrderOf(const void* item, size_t* order) const
 {
+    if (order) *order = 0;
+    if (isEmpty()) {
+        return NAME_NOT_FOUND;
+    }
     // binary search
     ssize_t err = NAME_NOT_FOUND;
     ssize_t l = 0;
diff --git a/libutils/misc.cpp b/libutils/misc.cpp
index ed1ba23..216dc14 100644
--- a/libutils/misc.cpp
+++ b/libutils/misc.cpp
@@ -24,7 +24,6 @@
 
 #include <sys/stat.h>
 #include <string.h>
-#include <errno.h>
 #include <stdio.h>
 
 #if !defined(_WIN32)
diff --git a/libutils/tests/BitSet_test.cpp b/libutils/tests/BitSet_test.cpp
index 38b668a..59d913e 100644
--- a/libutils/tests/BitSet_test.cpp
+++ b/libutils/tests/BitSet_test.cpp
@@ -138,11 +138,11 @@
 TEST_F(BitSet32Test, GetIndexOfBit) {
     b1.markBit(1);
     b1.markBit(4);
-    EXPECT_EQ(b1.getIndexOfBit(1), 0);
-    EXPECT_EQ(b1.getIndexOfBit(4), 1);
+    EXPECT_EQ(0U, b1.getIndexOfBit(1));
+    EXPECT_EQ(1U, b1.getIndexOfBit(4));
     b1.markFirstUnmarkedBit();
-    EXPECT_EQ(b1.getIndexOfBit(1), 1);
-    EXPECT_EQ(b1.getIndexOfBit(4), 2);
+    EXPECT_EQ(1U, b1.getIndexOfBit(1));
+    EXPECT_EQ(2U, b1.getIndexOfBit(4));
 }
 
 class BitSet64Test : public testing::Test {
@@ -260,11 +260,11 @@
 TEST_F(BitSet64Test, GetIndexOfBit) {
     b1.markBit(10);
     b1.markBit(40);
-    EXPECT_EQ(b1.getIndexOfBit(10), 0);
-    EXPECT_EQ(b1.getIndexOfBit(40), 1);
+    EXPECT_EQ(0U, b1.getIndexOfBit(10));
+    EXPECT_EQ(1U, b1.getIndexOfBit(40));
     b1.markFirstUnmarkedBit();
-    EXPECT_EQ(b1.getIndexOfBit(10), 1);
-    EXPECT_EQ(b1.getIndexOfBit(40), 2);
+    EXPECT_EQ(1U, b1.getIndexOfBit(10));
+    EXPECT_EQ(2U, b1.getIndexOfBit(40));
 }
 
 } // namespace android
diff --git a/libutils/tests/LruCache_test.cpp b/libutils/tests/LruCache_test.cpp
index 6534211..6155def 100644
--- a/libutils/tests/LruCache_test.cpp
+++ b/libutils/tests/LruCache_test.cpp
@@ -220,7 +220,7 @@
 
     cache.put(ComplexKey(0), ComplexValue(0));
     cache.put(ComplexKey(1), ComplexValue(1));
-    EXPECT_EQ(2, cache.size());
+    EXPECT_EQ(2U, cache.size());
     assertInstanceCount(2, 3);  // the null value counts as an instance
 }
 
@@ -229,7 +229,7 @@
 
     cache.put(ComplexKey(0), ComplexValue(0));
     cache.put(ComplexKey(1), ComplexValue(1));
-    EXPECT_EQ(2, cache.size());
+    EXPECT_EQ(2U, cache.size());
     assertInstanceCount(2, 3);
     cache.clear();
     assertInstanceCount(0, 1);
@@ -241,7 +241,7 @@
 
         cache.put(ComplexKey(0), ComplexValue(0));
         cache.put(ComplexKey(1), ComplexValue(1));
-        EXPECT_EQ(2, cache.size());
+        EXPECT_EQ(2U, cache.size());
         assertInstanceCount(2, 3);
         cache.removeOldest();
         cache.clear();
@@ -255,13 +255,13 @@
 
     cache.put(ComplexKey(0), ComplexValue(0));
     cache.put(ComplexKey(1), ComplexValue(1));
-    EXPECT_EQ(2, cache.size());
+    EXPECT_EQ(2U, cache.size());
     assertInstanceCount(2, 3);
     cache.clear();
     assertInstanceCount(0, 1);
     cache.put(ComplexKey(0), ComplexValue(0));
     cache.put(ComplexKey(1), ComplexValue(1));
-    EXPECT_EQ(2, cache.size());
+    EXPECT_EQ(2U, cache.size());
     assertInstanceCount(2, 3);
 }
 
@@ -273,7 +273,7 @@
     cache.put(1, "one");
     cache.put(2, "two");
     cache.put(3, "three");
-    EXPECT_EQ(3, cache.size());
+    EXPECT_EQ(3U, cache.size());
     cache.removeOldest();
     EXPECT_EQ(1, callback.callbackCount);
     EXPECT_EQ(1, callback.lastKey);
@@ -288,7 +288,7 @@
     cache.put(1, "one");
     cache.put(2, "two");
     cache.put(3, "three");
-    EXPECT_EQ(3, cache.size());
+    EXPECT_EQ(3U, cache.size());
     cache.clear();
     EXPECT_EQ(3, callback.callbackCount);
 }
diff --git a/libutils/tests/Vector_test.cpp b/libutils/tests/Vector_test.cpp
index d29c054..0ba7161 100644
--- a/libutils/tests/Vector_test.cpp
+++ b/libutils/tests/Vector_test.cpp
@@ -45,26 +45,26 @@
     vector.add(2);
     vector.add(3);
 
-    EXPECT_EQ(vector.size(), 3);
+    EXPECT_EQ(3U, vector.size());
 
     // copy the vector
     other = vector;
 
-    EXPECT_EQ(other.size(), 3);
+    EXPECT_EQ(3U, other.size());
 
     // add an element to the first vector
     vector.add(4);
 
     // make sure the sizes are correct
-    EXPECT_EQ(vector.size(), 4);
-    EXPECT_EQ(other.size(), 3);
+    EXPECT_EQ(4U, vector.size());
+    EXPECT_EQ(3U, other.size());
 
     // add an element to the copy
     other.add(5);
 
     // make sure the sizes are correct
-    EXPECT_EQ(vector.size(), 4);
-    EXPECT_EQ(other.size(), 4);
+    EXPECT_EQ(4U, vector.size());
+    EXPECT_EQ(4U, other.size());
 
     // make sure the content of both vectors are correct
     EXPECT_EQ(vector[3], 4);
diff --git a/libziparchive/Android.mk b/libziparchive/Android.mk
index a3087ee..608ff1c 100644
--- a/libziparchive/Android.mk
+++ b/libziparchive/Android.mk
@@ -33,17 +33,16 @@
 LOCAL_STATIC_LIBRARIES := libz libutils libbase
 LOCAL_MODULE:= libziparchive-host
 LOCAL_CFLAGS := -Werror
-ifneq ($(strip $(USE_MINGW)),)
-	LOCAL_CFLAGS += -mno-ms-bitfields
-endif
+LOCAL_CFLAGS_windows := -mno-ms-bitfields
 LOCAL_MULTILIB := both
+LOCAL_MODULE_HOST_OS := darwin linux windows
 include $(BUILD_HOST_STATIC_LIBRARY)
 
 include $(CLEAR_VARS)
 LOCAL_CPP_EXTENSION := .cc
 LOCAL_SRC_FILES := ${source_files}
-LOCAL_STATIC_LIBRARIES := libz libutils
-LOCAL_SHARED_LIBRARIES := liblog libbase
+LOCAL_STATIC_LIBRARIES := libutils
+LOCAL_SHARED_LIBRARIES := libz-host liblog libbase
 LOCAL_MODULE:= libziparchive-host
 LOCAL_CFLAGS := -Werror
 LOCAL_MULTILIB := both
diff --git a/libziparchive/zip_archive.cc b/libziparchive/zip_archive.cc
index cc39aa5..3716343 100644
--- a/libziparchive/zip_archive.cc
+++ b/libziparchive/zip_archive.cc
@@ -307,7 +307,7 @@
    * ((4 * UINT16_MAX) / 3 + 1) which can safely fit into a uint32_t.
    */
   uint32_t hash_table_size;
-  ZipEntryName* hash_table;
+  ZipString* hash_table;
 
   ZipArchive(const int fd, bool assume_ownership) :
       fd(fd),
@@ -343,7 +343,7 @@
   return val;
 }
 
-static uint32_t ComputeHash(const ZipEntryName& name) {
+static uint32_t ComputeHash(const ZipString& name) {
   uint32_t hash = 0;
   uint16_t len = name.name_length;
   const uint8_t* str = name.name;
@@ -359,16 +359,15 @@
  * Convert a ZipEntry to a hash table index, verifying that it's in a
  * valid range.
  */
-static int64_t EntryToIndex(const ZipEntryName* hash_table,
+static int64_t EntryToIndex(const ZipString* hash_table,
                             const uint32_t hash_table_size,
-                            const ZipEntryName& name) {
+                            const ZipString& name) {
   const uint32_t hash = ComputeHash(name);
 
   // NOTE: (hash_table_size - 1) is guaranteed to be non-negative.
   uint32_t ent = hash & (hash_table_size - 1);
   while (hash_table[ent].name != NULL) {
-    if (hash_table[ent].name_length == name.name_length &&
-        memcmp(hash_table[ent].name, name.name, name.name_length) == 0) {
+    if (hash_table[ent] == name) {
       return ent;
     }
 
@@ -382,8 +381,8 @@
 /*
  * Add a new entry to the hash table.
  */
-static int32_t AddToHash(ZipEntryName *hash_table, const uint64_t hash_table_size,
-                         const ZipEntryName& name) {
+static int32_t AddToHash(ZipString *hash_table, const uint64_t hash_table_size,
+                         const ZipString& name) {
   const uint64_t hash = ComputeHash(name);
   uint32_t ent = hash & (hash_table_size - 1);
 
@@ -392,8 +391,7 @@
    * Further, we guarantee that the hashtable size is not 0.
    */
   while (hash_table[ent].name != NULL) {
-    if (hash_table[ent].name_length == name.name_length &&
-        memcmp(hash_table[ent].name, name.name, name.name_length) == 0) {
+    if (hash_table[ent] == name) {
       // We've found a duplicate entry. We don't accept it
       ALOGW("Zip: Found duplicate entry %.*s", name.name_length, name.name);
       return kDuplicateEntry;
@@ -473,7 +471,7 @@
     return kEmptyArchive;
   }
 
-  ALOGV("+++ num_entries=%" PRIu32 "dir_size=%" PRIu32 " dir_offset=%" PRIu32,
+  ALOGV("+++ num_entries=%" PRIu32 " dir_size=%" PRIu32 " dir_offset=%" PRIu32,
         eocd->num_records, eocd->cd_size, eocd->cd_start_offset);
 
   /*
@@ -565,8 +563,8 @@
    * least one unused entry to avoid an infinite loop during creation.
    */
   archive->hash_table_size = RoundUpPower2(1 + (num_entries * 4) / 3);
-  archive->hash_table = reinterpret_cast<ZipEntryName*>(calloc(archive->hash_table_size,
-      sizeof(ZipEntryName)));
+  archive->hash_table = reinterpret_cast<ZipString*>(calloc(archive->hash_table_size,
+      sizeof(ZipString)));
 
   /*
    * Walk through the central directory, adding entries to the hash
@@ -605,7 +603,7 @@
     }
 
     /* add the CDE filename to the hash table */
-    ZipEntryName entry_name;
+    ZipString entry_name;
     entry_name.name = file_name;
     entry_name.name_length = file_name_length;
     const int add_result = AddToHash(archive->hash_table,
@@ -851,39 +849,41 @@
   uint32_t position;
   // We're not using vector here because this code is used in the Windows SDK
   // where the STL is not available.
-  const uint8_t* prefix;
-  const uint16_t prefix_len;
-  const uint8_t* suffix;
-  const uint16_t suffix_len;
+  ZipString prefix;
+  ZipString suffix;
   ZipArchive* archive;
 
-  IterationHandle(const ZipEntryName* prefix_name,
-                  const ZipEntryName* suffix_name)
-    : prefix(NULL),
-      prefix_len(prefix_name ? prefix_name->name_length : 0),
-      suffix(NULL),
-      suffix_len(suffix_name ? suffix_name->name_length : 0) {
-    if (prefix_name) {
-      uint8_t* prefix_copy = new uint8_t[prefix_len];
-      memcpy(prefix_copy, prefix_name->name, prefix_len);
-      prefix = prefix_copy;
+  IterationHandle(const ZipString* in_prefix,
+                  const ZipString* in_suffix) {
+    if (in_prefix) {
+      uint8_t* name_copy = new uint8_t[in_prefix->name_length];
+      memcpy(name_copy, in_prefix->name, in_prefix->name_length);
+      prefix.name = name_copy;
+      prefix.name_length = in_prefix->name_length;
+    } else {
+      prefix.name = NULL;
+      prefix.name_length = 0;
     }
-    if (suffix_name) {
-      uint8_t* suffix_copy = new uint8_t[suffix_len];
-      memcpy(suffix_copy, suffix_name->name, suffix_len);
-      suffix = suffix_copy;
+    if (in_suffix) {
+      uint8_t* name_copy = new uint8_t[in_suffix->name_length];
+      memcpy(name_copy, in_suffix->name, in_suffix->name_length);
+      suffix.name = name_copy;
+      suffix.name_length = in_suffix->name_length;
+    } else {
+      suffix.name = NULL;
+      suffix.name_length = 0;
     }
   }
 
   ~IterationHandle() {
-    delete[] prefix;
-    delete[] suffix;
+    delete[] prefix.name;
+    delete[] suffix.name;
   }
 };
 
 int32_t StartIteration(ZipArchiveHandle handle, void** cookie_ptr,
-                       const ZipEntryName* optional_prefix,
-                       const ZipEntryName* optional_suffix) {
+                       const ZipString* optional_prefix,
+                       const ZipString* optional_suffix) {
   ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle);
 
   if (archive == NULL || archive->hash_table == NULL) {
@@ -903,7 +903,7 @@
   delete reinterpret_cast<IterationHandle*>(cookie);
 }
 
-int32_t FindEntry(const ZipArchiveHandle handle, const ZipEntryName& entryName,
+int32_t FindEntry(const ZipArchiveHandle handle, const ZipString& entryName,
                   ZipEntry* data) {
   const ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle);
   if (entryName.name_length == 0) {
@@ -922,7 +922,7 @@
   return FindEntry(archive, ent, data);
 }
 
-int32_t Next(void* cookie, ZipEntry* data, ZipEntryName* name) {
+int32_t Next(void* cookie, ZipEntry* data, ZipString* name) {
   IterationHandle* handle = reinterpret_cast<IterationHandle*>(cookie);
   if (handle == NULL) {
     return kInvalidHandle;
@@ -936,18 +936,14 @@
 
   const uint32_t currentOffset = handle->position;
   const uint32_t hash_table_length = archive->hash_table_size;
-  const ZipEntryName *hash_table = archive->hash_table;
+  const ZipString* hash_table = archive->hash_table;
 
   for (uint32_t i = currentOffset; i < hash_table_length; ++i) {
     if (hash_table[i].name != NULL &&
-        (handle->prefix_len == 0 ||
-         (hash_table[i].name_length >= handle->prefix_len &&
-          memcmp(handle->prefix, hash_table[i].name, handle->prefix_len) == 0)) &&
-        (handle->suffix_len == 0 ||
-         (hash_table[i].name_length >= handle->suffix_len &&
-          memcmp(handle->suffix,
-                 hash_table[i].name + hash_table[i].name_length - handle->suffix_len,
-                 handle->suffix_len) == 0))) {
+        (handle->prefix.name_length == 0 ||
+         hash_table[i].StartsWith(handle->prefix)) &&
+        (handle->suffix.name_length == 0 ||
+         hash_table[i].EndsWith(handle->suffix))) {
       handle->position = (i + 1);
       const int error = FindEntry(archive, i, data);
       if (!error) {
diff --git a/libziparchive/zip_archive_test.cc b/libziparchive/zip_archive_test.cc
index c799869..9a3cdb4 100644
--- a/libziparchive/zip_archive_test.cc
+++ b/libziparchive/zip_archive_test.cc
@@ -70,7 +70,7 @@
 }
 
 static void AssertNameEquals(const std::string& name_str,
-                             const ZipEntryName& name) {
+                             const ZipString& name) {
   ASSERT_EQ(name_str.size(), name.name_length);
   ASSERT_EQ(0, memcmp(name_str.c_str(), name.name, name.name_length));
 }
@@ -118,7 +118,7 @@
   ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, NULL, NULL));
 
   ZipEntry data;
-  ZipEntryName name;
+  ZipString name;
 
   // b/c.txt
   ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
@@ -151,11 +151,11 @@
   ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
 
   void* iteration_cookie;
-  ZipEntryName prefix("b/");
+  ZipString prefix("b/");
   ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, &prefix, NULL));
 
   ZipEntry data;
-  ZipEntryName name;
+  ZipString name;
 
   // b/c.txt
   ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
@@ -180,11 +180,11 @@
   ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
 
   void* iteration_cookie;
-  ZipEntryName suffix(".txt");
+  ZipString suffix(".txt");
   ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, NULL, &suffix));
 
   ZipEntry data;
-  ZipEntryName name;
+  ZipString name;
 
   // b/c.txt
   ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
@@ -213,12 +213,12 @@
   ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
 
   void* iteration_cookie;
-  ZipEntryName prefix("b");
-  ZipEntryName suffix(".txt");
+  ZipString prefix("b");
+  ZipString suffix(".txt");
   ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, &prefix, &suffix));
 
   ZipEntry data;
-  ZipEntryName name;
+  ZipString name;
 
   // b/c.txt
   ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
@@ -243,12 +243,12 @@
   ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
 
   void* iteration_cookie;
-  ZipEntryName prefix("x");
-  ZipEntryName suffix("y");
+  ZipString prefix("x");
+  ZipString suffix("y");
   ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, &prefix, &suffix));
 
   ZipEntry data;
-  ZipEntryName name;
+  ZipString name;
 
   // End of iteration.
   ASSERT_EQ(-1, Next(iteration_cookie, &data, &name));
@@ -261,7 +261,7 @@
   ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
 
   ZipEntry data;
-  ZipEntryName name;
+  ZipString name;
   name.name = kATxtName;
   name.name_length = kATxtNameLength;
   ASSERT_EQ(0, FindEntry(handle, name, &data));
@@ -274,7 +274,7 @@
   ASSERT_EQ(0x950821c5, data.crc32);
 
   // An entry that doesn't exist. Should be a negative return code.
-  ZipEntryName absent_name;
+  ZipString absent_name;
   absent_name.name = kNonexistentTxtName;
   absent_name.name_length = kNonexistentTxtNameLength;
   ASSERT_LT(FindEntry(handle, absent_name, &data), 0);
@@ -287,9 +287,9 @@
   ASSERT_EQ(0, OpenArchiveWrapper("declaredlength.zip", &handle));
 
   void* iteration_cookie;
-  ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, NULL));
+  ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, NULL, NULL));
 
-  ZipEntryName name;
+  ZipString name;
   ZipEntry data;
 
   ASSERT_EQ(Next(iteration_cookie, &data, &name), 0);
@@ -304,7 +304,7 @@
 
   // An entry that's deflated.
   ZipEntry data;
-  ZipEntryName a_name;
+  ZipString a_name;
   a_name.name = kATxtName;
   a_name.name_length = kATxtNameLength;
   ASSERT_EQ(0, FindEntry(handle, a_name, &data));
@@ -316,7 +316,7 @@
   delete[] buffer;
 
   // An entry that's stored.
-  ZipEntryName b_name;
+  ZipString b_name;
   b_name.name = kBTxtName;
   b_name.name_length = kBTxtNameLength;
   ASSERT_EQ(0, FindEntry(handle, b_name, &data));
@@ -403,7 +403,7 @@
   ASSERT_EQ(0, OpenArchiveFd(fd, "EmptyEntriesTest", &handle));
 
   ZipEntry entry;
-  ZipEntryName empty_name;
+  ZipString empty_name;
   empty_name.name = kEmptyTxtName;
   empty_name.name_length = kEmptyTxtNameLength;
   ASSERT_EQ(0, FindEntry(handle, empty_name, &entry));
@@ -434,7 +434,7 @@
   ASSERT_EQ(0, OpenArchiveFd(fd, "EntryLargerThan32KTest", &handle));
 
   ZipEntry entry;
-  ZipEntryName ab_name;
+  ZipString ab_name;
   ab_name.name = kAbTxtName;
   ab_name.name_length = kAbTxtNameLength;
   ASSERT_EQ(0, FindEntry(handle, ab_name, &entry));
@@ -502,7 +502,7 @@
   ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
 
   ZipEntry entry;
-  ZipEntryName name;
+  ZipString name;
   name.name = kATxtName;
   name.name_length = kATxtNameLength;
   ASSERT_EQ(0, FindEntry(handle, name, &entry));
diff --git a/lmkd/Android.mk b/lmkd/Android.mk
index 39081d6..8c88661 100644
--- a/lmkd/Android.mk
+++ b/lmkd/Android.mk
@@ -7,4 +7,6 @@
 
 LOCAL_MODULE := lmkd
 
+LOCAL_INIT_RC := lmkd.rc
+
 include $(BUILD_EXECUTABLE)
diff --git a/lmkd/lmkd.rc b/lmkd/lmkd.rc
new file mode 100644
index 0000000..83c5ff0
--- /dev/null
+++ b/lmkd/lmkd.rc
@@ -0,0 +1,4 @@
+service lmkd /system/bin/lmkd
+    class core
+    critical
+    socket lmkd seqpacket 0660 system system
diff --git a/logcat/Android.mk b/logcat/Android.mk
index 7115f9b..844ab8b 100644
--- a/logcat/Android.mk
+++ b/logcat/Android.mk
@@ -11,6 +11,8 @@
 
 LOCAL_CFLAGS := -Werror
 
+LOCAL_INIT_RC := logcatd.rc
+
 include $(BUILD_EXECUTABLE)
 
 include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/logcat/logcatd.rc b/logcat/logcatd.rc
new file mode 100644
index 0000000..0bc581e
--- /dev/null
+++ b/logcat/logcatd.rc
@@ -0,0 +1,13 @@
+on property:persist.logd.logpersistd=logcatd
+    # all exec/services are called with umask(077), so no gain beyond 0700
+    mkdir /data/misc/logd 0700 logd log
+    # logd for write to /data/misc/logd, log group for read from pstore (-L)
+    exec - logd log -- /system/bin/logcat -L -b all -v threadtime -v usec -v printable -D -f /data/misc/logd/logcat -r 64 -n 256
+    start logcatd
+
+service logcatd /system/bin/logcat -b all -v threadtime -v usec -v printable -D -f /data/misc/logd/logcat -r 64 -n 256
+    class late_start
+    disabled
+    # logd for write to /data/misc/logd, log group for read from log daemon
+    user logd
+    group log
diff --git a/logd/Android.mk b/logd/Android.mk
index 615d030..01c51c7 100644
--- a/logd/Android.mk
+++ b/logd/Android.mk
@@ -4,6 +4,8 @@
 
 LOCAL_MODULE:= logd
 
+LOCAL_INIT_RC := logd.rc
+
 LOCAL_SRC_FILES := \
     main.cpp \
     LogCommand.cpp \
@@ -25,7 +27,7 @@
     libsysutils \
     liblog \
     libcutils \
-    libutils
+    libbase
 
 # This is what we want to do:
 #  event_logtags = $(shell \
diff --git a/logd/CommandListener.cpp b/logd/CommandListener.cpp
index 5489cc9..031c740 100644
--- a/logd/CommandListener.cpp
+++ b/logd/CommandListener.cpp
@@ -25,6 +25,9 @@
 #include <sys/socket.h>
 #include <sys/types.h>
 
+#include <string>
+
+#include <base/stringprintf.h>
 #include <cutils/sockets.h>
 #include <private/android_filesystem_config.h>
 #include <sysutils/SocketClient.h>
@@ -34,8 +37,7 @@
 
 CommandListener::CommandListener(LogBuffer *buf, LogReader * /*reader*/,
                                  LogListener * /*swl*/) :
-        FrameworkListener(getLogSocket()),
-        mBuf(*buf) {
+        FrameworkListener(getLogSocket()) {
     // registerCmd(new ShutdownCmd(buf, writer, swl));
     registerCmd(new ClearCmd(buf));
     registerCmd(new GetBufSizeCmd(buf));
@@ -47,10 +49,9 @@
     registerCmd(new ReinitCmd());
 }
 
-CommandListener::ShutdownCmd::ShutdownCmd(LogBuffer *buf, LogReader *reader,
+CommandListener::ShutdownCmd::ShutdownCmd(LogReader *reader,
                                           LogListener *swl) :
         LogCommand("shutdown"),
-        mBuf(*buf),
         mReader(*reader),
         mSwl(*swl) {
 }
@@ -191,22 +192,13 @@
         mBuf(*buf) {
 }
 
-static void package_string(char **strp) {
-    const char *a = *strp;
-    if (!a) {
-        a = "";
-    }
-
+static std::string package_string(const std::string &str) {
     // Calculate total buffer size prefix, count is the string length w/o nul
     char fmt[32];
-    for(size_t l = strlen(a), y = 0, x = 6; y != x; y = x, x = strlen(fmt) - 2) {
+    for(size_t l = str.length(), y = 0, x = 6; y != x; y = x, x = strlen(fmt) - 2) {
         snprintf(fmt, sizeof(fmt), "%zu\n%%s\n\f", l + x);
     }
-
-    char *b = *strp;
-    *strp = NULL;
-    asprintf(strp, fmt, a);
-    free(b);
+    return android::base::StringPrintf(fmt, str.c_str());
 }
 
 int CommandListener::GetStatisticsCmd::runCommand(SocketClient *cli,
@@ -230,16 +222,7 @@
         }
     }
 
-    char *buf = NULL;
-
-    mBuf.formatStatistics(&buf, uid, logMask);
-    if (!buf) {
-        cli->sendMsg("Failed");
-    } else {
-        package_string(&buf);
-        cli->sendMsg(buf);
-        free(buf);
-    }
+    cli->sendMsg(package_string(mBuf.formatStatistics(uid, logMask)).c_str());
     return 0;
 }
 
@@ -251,15 +234,7 @@
 int CommandListener::GetPruneListCmd::runCommand(SocketClient *cli,
                                          int /*argc*/, char ** /*argv*/) {
     setname();
-    char *buf = NULL;
-    mBuf.formatPrune(&buf);
-    if (!buf) {
-        cli->sendMsg("Failed");
-    } else {
-        package_string(&buf);
-        cli->sendMsg(buf);
-        free(buf);
-    }
+    cli->sendMsg(package_string(mBuf.formatPrune()).c_str());
     return 0;
 }
 
@@ -276,20 +251,15 @@
         return 0;
     }
 
-    char *cp = NULL;
+    std::string str;
     for (int i = 1; i < argc; ++i) {
-        char *p = cp;
-        if (p) {
-            cp = NULL;
-            asprintf(&cp, "%s %s", p, argv[i]);
-            free(p);
-        } else {
-            asprintf(&cp, "%s", argv[i]);
+        if (str.length()) {
+            str += " ";
         }
+        str += argv[i];
     }
 
-    int ret = mBuf.initPrune(cp);
-    free(cp);
+    int ret = mBuf.initPrune(str.c_str());
 
     if (ret) {
         cli->sendMsg("Invalid");
diff --git a/logd/CommandListener.h b/logd/CommandListener.h
index 83e06b4..3877675 100644
--- a/logd/CommandListener.h
+++ b/logd/CommandListener.h
@@ -27,7 +27,6 @@
 void reinit_signal_handler(int /*signal*/);
 
 class CommandListener : public FrameworkListener {
-    LogBuffer &mBuf;
 
 public:
     CommandListener(LogBuffer *buf, LogReader *reader, LogListener *swl);
@@ -37,12 +36,11 @@
     static int getLogSocket();
 
     class ShutdownCmd : public LogCommand {
-        LogBuffer &mBuf;
         LogReader &mReader;
         LogListener &mSwl;
 
     public:
-        ShutdownCmd(LogBuffer *buf, LogReader *reader, LogListener *swl);
+        ShutdownCmd(LogReader *reader, LogListener *swl);
         virtual ~ShutdownCmd() {}
         int runCommand(SocketClient *c, int argc, char ** argv);
     };
diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp
index 9605ef8..a93369e 100644
--- a/logd/LogBuffer.cpp
+++ b/logd/LogBuffer.cpp
@@ -693,10 +693,12 @@
     return max;
 }
 
-void LogBuffer::formatStatistics(char **strp, uid_t uid, unsigned int logMask) {
+std::string LogBuffer::formatStatistics(uid_t uid, unsigned int logMask) {
     pthread_mutex_lock(&mLogElementsLock);
 
-    stats.format(strp, uid, logMask);
+    std::string ret = stats.format(uid, logMask);
 
     pthread_mutex_unlock(&mLogElementsLock);
+
+    return ret;
 }
diff --git a/logd/LogBuffer.h b/logd/LogBuffer.h
index e94598c..fcb05f5 100644
--- a/logd/LogBuffer.h
+++ b/logd/LogBuffer.h
@@ -20,6 +20,7 @@
 #include <sys/types.h>
 
 #include <list>
+#include <string>
 
 #include <log/log.h>
 #include <sysutils/SocketClient.h>
@@ -67,15 +68,14 @@
     int setSize(log_id_t id, unsigned long size);
     unsigned long getSizeUsed(log_id_t id);
     // *strp uses malloc, use free to release.
-    void formatStatistics(char **strp, uid_t uid, unsigned int logMask);
+    std::string formatStatistics(uid_t uid, unsigned int logMask);
 
     void enableStatistics() {
         stats.enableStatistics();
     }
 
-    int initPrune(char *cp) { return mPrune.init(cp); }
-    // *strp uses malloc, use free to release.
-    void formatPrune(char **strp) { mPrune.format(strp); }
+    int initPrune(const char *cp) { return mPrune.init(cp); }
+    std::string formatPrune() { return mPrune.format(); }
 
     // helper must be protected directly or implicitly by lock()/unlock()
     char *pidToName(pid_t pid) { return stats.pidToName(pid); }
diff --git a/logd/LogKlog.cpp b/logd/LogKlog.cpp
index 39da87c..febf775 100644
--- a/logd/LogKlog.cpp
+++ b/logd/LogKlog.cpp
@@ -48,7 +48,7 @@
     char c;
     while (((c = *s++)) && (++len <= max_prio_len)) {
         if (!isdigit(c)) {
-            return (c == '>') ? s : NULL;
+            return ((c == '>') && (*s == '[')) ? s : NULL;
         }
     }
     return NULL;
@@ -177,8 +177,6 @@
         logbuf(buf),
         reader(reader),
         signature(CLOCK_MONOTONIC),
-        fdWrite(fdWrite),
-        fdRead(fdRead),
         initialized(false),
         enableLogging(true),
         auditd(auditd) {
@@ -296,6 +294,22 @@
     }
 }
 
+pid_t LogKlog::sniffPid(const char *cp) {
+    while (*cp) {
+        // Mediatek kernels with modified printk
+        if (*cp == '[') {
+            int pid = 0;
+            char dummy;
+            if (sscanf(cp, "[%d:%*[a-z_./0-9:A-Z]]%c", &pid, &dummy) == 2) {
+                return pid;
+            }
+            break; // Only the first one
+        }
+        ++cp;
+    }
+    return 0;
+}
+
 // Passed the entire SYSLOG_ACTION_READ_ALL buffer and interpret a
 // compensated start time.
 void LogKlog::synchronize(const char *buf) {
@@ -419,9 +433,9 @@
 
     // sniff for start marker
     const char klogd_message[] = "logd.klogd: ";
-    if (!strncmp(buf, klogd_message, sizeof(klogd_message) - 1)) {
-        char *endp;
-        uint64_t sig = strtoll(buf + sizeof(klogd_message) - 1, &endp, 10);
+    const char *start = strstr(buf, klogd_message);
+    if (start) {
+        uint64_t sig = strtoll(start + sizeof(klogd_message) - 1, NULL, 10);
         if (sig == signature.nsec()) {
             if (initialized) {
                 enableLogging = true;
@@ -437,10 +451,10 @@
         return 0;
     }
 
-    // Parse pid, tid and uid (not possible)
-    const pid_t pid = 0;
-    const pid_t tid = 0;
-    const uid_t uid = 0;
+    // Parse pid, tid and uid
+    const pid_t pid = sniffPid(buf);
+    const pid_t tid = pid;
+    const uid_t uid = pid ? logbuf->pidToUid(pid) : 0;
 
     // Parse (rules at top) to pull out a tag from the incoming kernel message.
     // Some may view the following as an ugly heuristic, the desire is to
@@ -452,7 +466,7 @@
     if (!*buf) {
         return 0;
     }
-    const char *start = buf;
+    start = buf;
     const char *tag = "";
     const char *etag = tag;
     if (!isspace(*buf)) {
@@ -463,7 +477,14 @@
             // <PRI>[<TIME>] "[INFO]"<tag> ":" message
             bt = buf + 6;
         }
-        for(et = bt; *et && (*et != ':') && !isspace(*et); ++et);
+        for(et = bt; *et && (*et != ':') && !isspace(*et); ++et) {
+           // skip ':' within [ ... ]
+           if (*et == '[') {
+               while (*et && *et != ']') {
+                   ++et;
+               }
+            }
+        }
         for(cp = et; isspace(*cp); ++cp);
         size_t size;
 
@@ -502,7 +523,6 @@
                     }
                 }
             } else if (isspace(cp[size])) {
-                const char *b = cp;
                 cp += size;
                 while (isspace(*++cp));
                 // <PRI>[<TIME>] <tag> <tag> : message
@@ -560,7 +580,17 @@
             etag = tag = "";
         }
     }
-    size_t l = etag - tag;
+    // Suppress additional stutter in tag:
+    //   eg: [143:healthd]healthd -> [143:healthd]
+    size_t taglen = etag - tag;
+    // Mediatek-special printk induced stutter
+    char *np = strrchr(tag, ']');
+    if (np && (++np < etag)) {
+        size_t s = etag - np;
+        if (((s + s) < taglen) && !strncmp(np, np - 1 - s, s)) {
+            taglen = np - tag;
+        }
+    }
     // skip leading space
     while (isspace(*buf)) {
         ++buf;
@@ -571,11 +601,11 @@
         --b;
     }
     // trick ... allow tag with empty content to be logged. log() drops empty
-    if (!b && l) {
+    if (!b && taglen) {
         buf = " ";
         b = 1;
     }
-    size_t n = 1 + l + 1 + b + 1;
+    size_t n = 1 + taglen + 1 + b + 1;
 
     // Allocate a buffer to hold the interpreted log message
     int rc = n;
@@ -584,15 +614,15 @@
         rc = -ENOMEM;
         return rc;
     }
-    char *np = newstr;
+    np = newstr;
 
     // Convert priority into single-byte Android logger priority
     *np = convertKernelPrioToAndroidPrio(pri);
     ++np;
 
     // Copy parsed tag following priority
-    strncpy(np, tag, l);
-    np += l;
+    strncpy(np, tag, taglen);
+    np += taglen;
     *np = '\0';
     ++np;
 
diff --git a/logd/LogKlog.h b/logd/LogKlog.h
index a898c63..7e4fde0 100644
--- a/logd/LogKlog.h
+++ b/logd/LogKlog.h
@@ -27,8 +27,6 @@
     LogBuffer *logbuf;
     LogReader *reader;
     const log_time signature;
-    const int fdWrite; // /dev/kmsg
-    const int fdRead;  // /proc/kmsg
     // Set once thread is started, separates KLOG_ACTION_READ_ALL
     // and KLOG_ACTION_READ phases.
     bool initialized;
@@ -49,6 +47,7 @@
 
 protected:
     void sniffTime(log_time &now, const char **buf, bool reverse);
+    pid_t sniffPid(const char *buf);
     void calculateCorrection(const log_time &monotonic, const char *real_string);
     virtual bool onDataAvailable(SocketClient *cli);
 
diff --git a/logd/LogStatistics.cpp b/logd/LogStatistics.cpp
index 48c2fe6..61fd559 100644
--- a/logd/LogStatistics.cpp
+++ b/logd/LogStatistics.cpp
@@ -20,9 +20,9 @@
 #include <string.h>
 #include <unistd.h>
 
+#include <base/stringprintf.h>
 #include <log/logger.h>
 #include <private/android_filesystem_config.h>
-#include <utils/String8.h>
 
 #include "LogStatistics.h"
 
@@ -183,8 +183,10 @@
     return name;
 }
 
-static void format_line(android::String8 &output,
-        android::String8 &name, android::String8 &size, android::String8 &pruned) {
+static std::string format_line(
+        const std::string &name,
+        const std::string &size,
+        const std::string &pruned) {
     static const size_t pruned_len = 6;
     static const size_t total_len = 70 + pruned_len;
 
@@ -193,26 +195,21 @@
                                 total_len - name.length() - drop_len - 1);
 
     if (pruned.length()) {
-        output.appendFormat("%s%*s%*s\n", name.string(),
-                                          (int)size_len, size.string(),
-                                          (int)drop_len, pruned.string());
+        return android::base::StringPrintf("%s%*s%*s\n", name.c_str(),
+                                           (int)size_len, size.c_str(),
+                                           (int)drop_len, pruned.c_str());
     } else {
-        output.appendFormat("%s%*s\n", name.string(),
-                                       (int)size_len, size.string());
+        return android::base::StringPrintf("%s%*s\n", name.c_str(),
+                                           (int)size_len, size.c_str());
     }
 }
 
-void LogStatistics::format(char **buf, uid_t uid, unsigned int logMask) {
+std::string LogStatistics::format(uid_t uid, unsigned int logMask) {
     static const unsigned short spaces_total = 19;
 
-    if (*buf) {
-        free(*buf);
-        *buf = NULL;
-    }
-
     // Report on total logging, current and for all time
 
-    android::String8 output("size/num");
+    std::string output = "size/num";
     size_t oldLength;
     short spaces = 1;
 
@@ -224,12 +221,13 @@
         if (spaces < 0) {
             spaces = 0;
         }
-        output.appendFormat("%*s%s", spaces, "", android_log_id_to_name(id));
+        output += android::base::StringPrintf("%*s%s", spaces, "",
+                                              android_log_id_to_name(id));
         spaces += spaces_total + oldLength - output.length();
     }
 
     spaces = 4;
-    output.appendFormat("\nTotal");
+    output += android::base::StringPrintf("\nTotal");
 
     log_id_for_each(id) {
         if (!(logMask & (1 << id))) {
@@ -239,13 +237,14 @@
         if (spaces < 0) {
             spaces = 0;
         }
-        output.appendFormat("%*s%zu/%zu", spaces, "",
-                            sizesTotal(id), elementsTotal(id));
+        output += android::base::StringPrintf("%*s%zu/%zu", spaces, "",
+                                              sizesTotal(id),
+                                              elementsTotal(id));
         spaces += spaces_total + oldLength - output.length();
     }
 
     spaces = 6;
-    output.appendFormat("\nNow");
+    output += android::base::StringPrintf("\nNow");
 
     log_id_for_each(id) {
         if (!(logMask & (1 << id))) {
@@ -258,7 +257,8 @@
             if (spaces < 0) {
                 spaces = 0;
             }
-            output.appendFormat("%*s%zu/%zu", spaces, "", sizes(id), els);
+            output += android::base::StringPrintf("%*s%zu/%zu", spaces, "",
+                                                  sizes(id), els);
             spaces -= output.length() - oldLength;
         }
         spaces += spaces_total;
@@ -284,53 +284,54 @@
             }
 
             if (!headerPrinted) {
-                output.appendFormat("\n\n");
-                android::String8 name("");
+                output += android::base::StringPrintf("\n\n");
+                std::string name;
                 if (uid == AID_ROOT) {
-                    name.appendFormat(
+                    name = android::base::StringPrintf(
                         "Chattiest UIDs in %s log buffer:",
                         android_log_id_to_name(id));
                 } else {
-                    name.appendFormat(
+                    name = android::base::StringPrintf(
                         "Logging for your UID in %s log buffer:",
                         android_log_id_to_name(id));
                 }
-                android::String8 size("Size");
-                android::String8 pruned("Pruned");
+                std::string size = "Size";
+                std::string pruned = "Pruned";
                 if (!worstUidEnabledForLogid(id)) {
-                    pruned.setTo("");
+                    pruned = "";
                 }
-                format_line(output, name, size, pruned);
+                output += format_line(name, size, pruned);
 
-                name.setTo("UID   PACKAGE");
-                size.setTo("BYTES");
-                pruned.setTo("LINES");
+                name = "UID   PACKAGE";
+                size = "BYTES";
+                pruned = "LINES";
                 if (!worstUidEnabledForLogid(id)) {
-                    pruned.setTo("");
+                    pruned = "";
                 }
-                format_line(output, name, size, pruned);
+                output += format_line(name, size, pruned);
 
                 headerPrinted = true;
             }
 
-            android::String8 name("");
-            name.appendFormat("%u", u);
+            std::string name = android::base::StringPrintf("%u", u);
             char *n = uidToName(u);
             if (n) {
-                name.appendFormat("%*s%s", (int)std::max(6 - name.length(), (size_t)1), "", n);
+                name += android::base::StringPrintf(
+                    "%*s%s", (int)std::max(6 - name.length(), (size_t)1),
+                    "", n);
                 free(n);
             }
 
-            android::String8 size("");
-            size.appendFormat("%zu", entry->getSizes());
+            std::string size = android::base::StringPrintf("%zu",
+                                                           entry->getSizes());
 
-            android::String8 pruned("");
+            std::string pruned = "";
             size_t dropped = entry->getDropped();
             if (dropped) {
-                pruned.appendFormat("%zu", dropped);
+                pruned = android::base::StringPrintf("%zu", dropped);
             }
 
-            format_line(output, name, size, pruned);
+            output += format_line(name, size, pruned);
         }
     }
 
@@ -347,48 +348,52 @@
             }
 
             if (!headerPrinted) {
-                output.appendFormat("\n\n");
-                android::String8 name("");
+                output += android::base::StringPrintf("\n\n");
+                std::string name;
                 if (uid == AID_ROOT) {
-                    name.appendFormat("Chattiest PIDs:");
+                    name = android::base::StringPrintf("Chattiest PIDs:");
                 } else {
-                    name.appendFormat("Logging for this PID:");
+                    name = android::base::StringPrintf("Logging for this PID:");
                 }
-                android::String8 size("Size");
-                android::String8 pruned("Pruned");
-                format_line(output, name, size, pruned);
+                std::string size = "Size";
+                std::string pruned = "Pruned";
+                output += format_line(name, size, pruned);
 
-                name.setTo("  PID/UID   COMMAND LINE");
-                size.setTo("BYTES");
-                pruned.setTo("LINES");
-                format_line(output, name, size, pruned);
+                name = "  PID/UID   COMMAND LINE";
+                size = "BYTES";
+                pruned = "LINES";
+                output += format_line(name, size, pruned);
 
                 headerPrinted = true;
             }
 
-            android::String8 name("");
-            name.appendFormat("%5u/%u", entry->getKey(), u);
+            std::string name = android::base::StringPrintf("%5u/%u",
+                                                           entry->getKey(), u);
             const char *n = entry->getName();
             if (n) {
-                name.appendFormat("%*s%s", (int)std::max(12 - name.length(), (size_t)1), "", n);
+                name += android::base::StringPrintf(
+                    "%*s%s", (int)std::max(12 - name.length(), (size_t)1),
+                    "", n);
             } else {
                 char *un = uidToName(u);
                 if (un) {
-                    name.appendFormat("%*s%s", (int)std::max(12 - name.length(), (size_t)1), "", un);
+                    name += android::base::StringPrintf(
+                        "%*s%s", (int)std::max(12 - name.length(), (size_t)1),
+                        "", un);
                     free(un);
                 }
             }
 
-            android::String8 size("");
-            size.appendFormat("%zu", entry->getSizes());
+            std::string size = android::base::StringPrintf("%zu",
+                                                           entry->getSizes());
 
-            android::String8 pruned("");
+            std::string pruned = "";
             size_t dropped = entry->getDropped();
             if (dropped) {
-                pruned.appendFormat("%zu", dropped);
+                pruned = android::base::StringPrintf("%zu", dropped);
             }
 
-            format_line(output, name, size, pruned);
+            output += format_line(name, size, pruned);
         }
     }
 
@@ -406,46 +411,50 @@
             }
 
             if (!headerPrinted) { // Only print header if we have table to print
-                output.appendFormat("\n\n");
-                android::String8 name("Chattiest TIDs:");
-                android::String8 size("Size");
-                android::String8 pruned("Pruned");
-                format_line(output, name, size, pruned);
+                output += android::base::StringPrintf("\n\n");
+                std::string name = "Chattiest TIDs:";
+                std::string size = "Size";
+                std::string pruned = "Pruned";
+                output += format_line(name, size, pruned);
 
-                name.setTo("  TID/UID   COMM");
-                size.setTo("BYTES");
-                pruned.setTo("LINES");
-                format_line(output, name, size, pruned);
+                name = "  TID/UID   COMM";
+                size = "BYTES";
+                pruned = "LINES";
+                output += format_line(name, size, pruned);
 
                 headerPrinted = true;
             }
 
-            android::String8 name("");
-            name.appendFormat("%5u/%u", entry->getKey(), u);
+            std::string name = android::base::StringPrintf("%5u/%u",
+                                                           entry->getKey(), u);
             const char *n = entry->getName();
             if (n) {
-                name.appendFormat("%*s%s", (int)std::max(12 - name.length(), (size_t)1), "", n);
+                name += android::base::StringPrintf(
+                    "%*s%s", (int)std::max(12 - name.length(), (size_t)1),
+                    "", n);
             } else {
                 // if we do not have a PID name, lets punt to try UID name?
                 char *un = uidToName(u);
                 if (un) {
-                    name.appendFormat("%*s%s", (int)std::max(12 - name.length(), (size_t)1), "", un);
+                    name += android::base::StringPrintf(
+                        "%*s%s", (int)std::max(12 - name.length(), (size_t)1),
+                        "", un);
                     free(un);
                 }
                 // We tried, better to not have a name at all, we still
                 // have TID/UID by number to report in any case.
             }
 
-            android::String8 size("");
-            size.appendFormat("%zu", entry->getSizes());
+            std::string size = android::base::StringPrintf("%zu",
+                                                           entry->getSizes());
 
-            android::String8 pruned("");
+            std::string pruned = "";
             size_t dropped = entry->getDropped();
             if (dropped) {
-                pruned.appendFormat("%zu", dropped);
+                pruned = android::base::StringPrintf("%zu", dropped);
             }
 
-            format_line(output, name, size, pruned);
+            output += format_line(name, size, pruned);
         }
     }
 
@@ -461,40 +470,44 @@
                 continue;
             }
 
-            android::String8 pruned("");
+            std::string pruned = "";
 
             if (!headerPrinted) {
-                output.appendFormat("\n\n");
-                android::String8 name("Chattiest events log buffer TAGs:");
-                android::String8 size("Size");
-                format_line(output, name, size, pruned);
+                output += android::base::StringPrintf("\n\n");
+                std::string name = "Chattiest events log buffer TAGs:";
+                std::string size = "Size";
+                output += format_line(name, size, pruned);
 
-                name.setTo("    TAG/UID   TAGNAME");
-                size.setTo("BYTES");
-                format_line(output, name, size, pruned);
+                name = "    TAG/UID   TAGNAME";
+                size = "BYTES";
+                output += format_line(name, size, pruned);
 
                 headerPrinted = true;
             }
 
-            android::String8 name("");
+            std::string name;
             if (u == (uid_t)-1) {
-                name.appendFormat("%7u", entry->getKey());
+                name = android::base::StringPrintf("%7u",
+                                                   entry->getKey());
             } else {
-                name.appendFormat("%7u/%u", entry->getKey(), u);
+                name = android::base::StringPrintf("%7u/%u",
+                                                   entry->getKey(), u);
             }
             const char *n = entry->getName();
             if (n) {
-                name.appendFormat("%*s%s", (int)std::max(14 - name.length(), (size_t)1), "", n);
+                name += android::base::StringPrintf(
+                    "%*s%s", (int)std::max(14 - name.length(), (size_t)1),
+                    "", n);
             }
 
-            android::String8 size("");
-            size.appendFormat("%zu", entry->getSizes());
+            std::string size = android::base::StringPrintf("%zu",
+                                                           entry->getSizes());
 
-            format_line(output, name, size, pruned);
+            output += format_line(name, size, pruned);
         }
     }
 
-    *buf = strdup(output.string());
+    return output;
 }
 
 namespace android {
diff --git a/logd/LogStatistics.h b/logd/LogStatistics.h
index 760d6b2..61000d2 100644
--- a/logd/LogStatistics.h
+++ b/logd/LogStatistics.h
@@ -331,8 +331,7 @@
     size_t sizesTotal(log_id_t id) const { return mSizesTotal[id]; }
     size_t elementsTotal(log_id_t id) const { return mElementsTotal[id]; }
 
-    // *strp = malloc, balance with free
-    void format(char **strp, uid_t uid, unsigned int logMask);
+    std::string format(uid_t uid, unsigned int logMask);
 
     // helper (must be locked directly or implicitly by mLogElementsLock)
     char *pidToName(pid_t pid);
diff --git a/logd/LogWhiteBlackList.cpp b/logd/LogWhiteBlackList.cpp
index 16dd6d2..ad005ec 100644
--- a/logd/LogWhiteBlackList.cpp
+++ b/logd/LogWhiteBlackList.cpp
@@ -16,7 +16,7 @@
 
 #include <ctype.h>
 
-#include <utils/String8.h>
+#include <base/stringprintf.h>
 
 #include "LogWhiteBlackList.h"
 
@@ -35,18 +35,18 @@
     return uid - mUid;
 }
 
-void Prune::format(char **strp) {
+std::string Prune::format() {
     if (mUid != uid_all) {
         if (mPid != pid_all) {
-            asprintf(strp, "%u/%u", mUid, mPid);
-        } else {
-            asprintf(strp, "%u", mUid);
+            return android::base::StringPrintf("%u/%u", mUid, mPid);
         }
-    } else if (mPid != pid_all) {
-        asprintf(strp, "/%u", mPid);
-    } else { // NB: mPid == pid_all can not happen if mUid == uid_all
-        asprintf(strp, "/");
+        return android::base::StringPrintf("%u", mUid);
     }
+    if (mPid != pid_all) {
+        return android::base::StringPrintf("/%u", mPid);
+    }
+    // NB: mPid == pid_all can not happen if mUid == uid_all
+    return std::string("/");
 }
 
 PruneList::PruneList() : mWorstUidEnabled(true) {
@@ -62,7 +62,7 @@
     }
 }
 
-int PruneList::init(char *str) {
+int PruneList::init(const char *str) {
     mWorstUidEnabled = true;
     PruneCollection::iterator it;
     for (it = mNice.begin(); it != mNice.end();) {
@@ -167,47 +167,32 @@
     return 0;
 }
 
-void PruneList::format(char **strp) {
-    if (*strp) {
-        free(*strp);
-        *strp = NULL;
-    }
-
+std::string PruneList::format() {
     static const char nice_format[] = " %s";
     const char *fmt = nice_format + 1;
 
-    android::String8 string;
+    std::string string;
 
     if (mWorstUidEnabled) {
-        string.setTo("~!");
+        string = "~!";
         fmt = nice_format;
     }
 
     PruneCollection::iterator it;
 
     for (it = mNice.begin(); it != mNice.end(); ++it) {
-        char *a = NULL;
-        (*it).format(&a);
-
-        string.appendFormat(fmt, a);
+        string += android::base::StringPrintf(fmt, (*it).format().c_str());
         fmt = nice_format;
-
-        free(a);
     }
 
     static const char naughty_format[] = " ~%s";
     fmt = naughty_format + (*fmt != ' ');
     for (it = mNaughty.begin(); it != mNaughty.end(); ++it) {
-        char *a = NULL;
-        (*it).format(&a);
-
-        string.appendFormat(fmt, a);
+        string += android::base::StringPrintf(fmt, (*it).format().c_str());
         fmt = naughty_format;
-
-        free(a);
     }
 
-    *strp = strdup(string.string());
+    return string;
 }
 
 // ToDo: Lists are in sorted order, Prune->cmp() returns + or -
diff --git a/logd/LogWhiteBlackList.h b/logd/LogWhiteBlackList.h
index 57cd03b..00e1cad 100644
--- a/logd/LogWhiteBlackList.h
+++ b/logd/LogWhiteBlackList.h
@@ -20,6 +20,7 @@
 #include <sys/types.h>
 
 #include <list>
+#include <string.h>
 
 #include <LogBufferElement.h>
 
@@ -43,8 +44,7 @@
 
     int cmp(LogBufferElement *e) const { return cmp(e->getUid(), e->getPid()); }
 
-    // *strp is malloc'd, use free to release
-    void format(char **strp);
+    std::string format();
 };
 
 typedef std::list<Prune> PruneCollection;
@@ -58,7 +58,7 @@
     PruneList();
     ~PruneList();
 
-    int init(char *str);
+    int init(const char *str);
 
     bool naughty(LogBufferElement *element);
     bool naughty(void) { return !mNaughty.empty(); }
@@ -66,8 +66,7 @@
     bool nice(void) { return !mNice.empty(); }
     bool worstUidEnabled() const { return mWorstUidEnabled; }
 
-    // *strp is malloc'd, use free to release
-    void format(char **strp);
+    std::string format();
 };
 
 #endif // _LOGD_LOG_WHITE_BLACK_LIST_H__
diff --git a/logd/logd.rc b/logd/logd.rc
new file mode 100644
index 0000000..da6a0bc
--- /dev/null
+++ b/logd/logd.rc
@@ -0,0 +1,10 @@
+service logd /system/bin/logd
+    class core
+    socket logd stream 0666 logd logd
+    socket logdr seqpacket 0666 logd logd
+    socket logdw dgram 0222 logd logd
+    group root system
+
+service logd-reinit /system/bin/logd --reinit
+    oneshot
+    disabled
diff --git a/logd/tests/Android.mk b/logd/tests/Android.mk
index 85ca4ac..a7c6b53 100644
--- a/logd/tests/Android.mk
+++ b/logd/tests/Android.mk
@@ -34,10 +34,6 @@
     -Werror \
     -fno-builtin \
 
-ifneq ($(TARGET_USES_LOGD),false)
-test_c_flags += -DTARGET_USES_LOGD=1
-endif
-
 test_src_files := \
     logd_test.cpp
 
diff --git a/logd/tests/logd_test.cpp b/logd/tests/logd_test.cpp
index 3266360..44fa95c 100644
--- a/logd/tests/logd_test.cpp
+++ b/logd/tests/logd_test.cpp
@@ -137,13 +137,7 @@
 
     alloc_statistics(&buf, &len);
 
-#ifdef TARGET_USES_LOGD
     ASSERT_TRUE(NULL != buf);
-#else
-    if (!buf) {
-        return;
-    }
-#endif
 
     // remove trailing FF
     char *cp = buf + len - 1;
@@ -167,7 +161,6 @@
 
     EXPECT_EQ(0, truncated);
 
-#ifdef TARGET_USES_LOGD
     char *main_logs = strstr(cp, "\nChattiest UIDs in main ");
     EXPECT_TRUE(NULL != main_logs);
 
@@ -179,7 +172,6 @@
 
     char *events_logs = strstr(cp, "\nChattiest UIDs in events ");
     EXPECT_TRUE(NULL != events_logs);
-#endif
 
     delete [] buf;
 }
@@ -419,37 +411,17 @@
         return;
     }
 
-#ifdef TARGET_USES_LOGD
     EXPECT_GE(200000UL, ns[log_maximum_retry]); // 104734 user
-#else
-    EXPECT_GE(10000UL, ns[log_maximum_retry]); // 5636 kernel
-#endif
 
-#ifdef TARGET_USES_LOGD
     EXPECT_GE(90000UL, ns[log_maximum]); // 46913 user
-#else
-    EXPECT_GE(10000UL, ns[log_maximum]); // 5637 kernel
-#endif
 
     EXPECT_GE(4096UL, ns[clock_overhead]); // 4095
 
-#ifdef TARGET_USES_LOGD
     EXPECT_GE(250000UL, ns[log_overhead]); // 126886 user
-#else
-    EXPECT_GE(100000UL, ns[log_overhead]); // 50945 kernel
-#endif
 
-#ifdef TARGET_USES_LOGD
     EXPECT_GE(10000UL, ns[log_latency]); // 5669 user space
-#else
-    EXPECT_GE(500000UL, ns[log_latency]); // 254200 kernel
-#endif
 
-#ifdef TARGET_USES_LOGD
     EXPECT_GE(20000000UL, ns[log_delay]); // 10500289 user
-#else
-    EXPECT_GE(55000UL, ns[log_delay]); // 27341 kernel
-#endif
 
     for (unsigned i = 0; i < sizeof(ns) / sizeof(ns[0]); ++i) {
         EXPECT_NE(0UL, ns[i]);
@@ -457,14 +429,8 @@
 
     alloc_statistics(&buf, &len);
 
-#ifdef TARGET_USES_LOGD
     bool collected_statistics = !!buf;
     EXPECT_EQ(true, collected_statistics);
-#else
-    if (!buf) {
-        return;
-    }
-#endif
 
     ASSERT_TRUE(NULL != buf);
 
diff --git a/logwrapper/Android.mk b/logwrapper/Android.mk
index 61b4659..ad45b2c 100644
--- a/logwrapper/Android.mk
+++ b/logwrapper/Android.mk
@@ -11,7 +11,7 @@
 LOCAL_SHARED_LIBRARIES := libcutils liblog
 LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
 LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-LOCAL_CFLAGS := -Werror
+LOCAL_CFLAGS := -Werror -std=gnu99
 include $(BUILD_STATIC_LIBRARY)
 
 # ========================================================
@@ -23,7 +23,7 @@
 LOCAL_WHOLE_STATIC_LIBRARIES := liblogwrap
 LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
 LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-LOCAL_CFLAGS := -Werror
+LOCAL_CFLAGS := -Werror -std=gnu99
 include $(BUILD_SHARED_LIBRARY)
 
 # ========================================================
@@ -33,5 +33,5 @@
 LOCAL_SRC_FILES:= logwrapper.c
 LOCAL_MODULE := logwrapper
 LOCAL_STATIC_LIBRARIES := liblog liblogwrap libcutils
-LOCAL_CFLAGS := -Werror
+LOCAL_CFLAGS := -Werror -std=gnu99
 include $(BUILD_EXECUTABLE)
diff --git a/logwrapper/include/logwrap/logwrap.h b/logwrapper/include/logwrap/logwrap.h
index 4307a30..89a8fdd 100644
--- a/logwrapper/include/logwrap/logwrap.h
+++ b/logwrapper/include/logwrap/logwrap.h
@@ -19,6 +19,7 @@
 #define __LIBS_LOGWRAP_H
 
 #include <stdbool.h>
+#include <stdint.h>
 
 __BEGIN_DECLS
 
@@ -53,6 +54,9 @@
  *           the specified log until the child has exited.
  *   file_path: if log_target has the LOG_FILE bit set, then this parameter
  *           must be set to the pathname of the file to log to.
+ *   opts: set to non-NULL if you want to use one or more of the
+ *           FORK_EXECVP_OPTION_* features.
+ *   opts_len: the length of the opts array. When opts is NULL, pass 0.
  *
  * Return value:
  *   0 when logwrap successfully run the child process and captured its status
@@ -68,8 +72,30 @@
 #define LOG_KLOG        2
 #define LOG_FILE        4
 
+/* Write data to child's stdin. */
+#define FORK_EXECVP_OPTION_INPUT             0
+/* Capture data from child's stdout and stderr. */
+#define FORK_EXECVP_OPTION_CAPTURE_OUTPUT    1
+
+struct AndroidForkExecvpOption {
+    int opt_type;
+    union {
+        struct {
+            const uint8_t* input;
+            size_t input_len;
+        } opt_input;
+        struct {
+            void (*on_output)(const uint8_t* /*output*/,
+                              size_t /*output_len*/,
+                              void* /* user_pointer */);
+            void* user_pointer;
+        } opt_capture_output;
+    };
+};
+
 int android_fork_execvp_ext(int argc, char* argv[], int *status, bool ignore_int_quit,
-        int log_target, bool abbreviated, char *file_path);
+        int log_target, bool abbreviated, char *file_path,
+        const struct AndroidForkExecvpOption* opts, size_t opts_len);
 
 /* Similar to above, except abbreviated logging is not available, and if logwrap
  * is true, logging is to the Android system log, and if false, there is no
@@ -79,7 +105,8 @@
                                      bool ignore_int_quit, bool logwrap)
 {
     return android_fork_execvp_ext(argc, argv, status, ignore_int_quit,
-                                   (logwrap ? LOG_ALOG : LOG_NONE), false, NULL);
+                                   (logwrap ? LOG_ALOG : LOG_NONE), false, NULL,
+                                   NULL, 0);
 }
 
 __END_DECLS
diff --git a/logwrapper/logwrap.c b/logwrapper/logwrap.c
index 83576fb..28d6de7 100644
--- a/logwrapper/logwrap.c
+++ b/logwrapper/logwrap.c
@@ -291,7 +291,8 @@
 }
 
 static int parent(const char *tag, int parent_read, pid_t pid,
-        int *chld_sts, int log_target, bool abbreviated, char *file_path) {
+        int *chld_sts, int log_target, bool abbreviated, char *file_path,
+        const struct AndroidForkExecvpOption* opts, size_t opts_len) {
     int status = 0;
     char buffer[4096];
     struct pollfd poll_fds[] = {
@@ -355,7 +356,15 @@
         }
 
         if (poll_fds[0].revents & POLLIN) {
-            sz = read(parent_read, &buffer[b], sizeof(buffer) - 1 - b);
+            sz = TEMP_FAILURE_RETRY(
+                read(parent_read, &buffer[b], sizeof(buffer) - 1 - b));
+
+            for (size_t i = 0; sz > 0 && i < opts_len; ++i) {
+                if (opts[i].opt_type == FORK_EXECVP_OPTION_CAPTURE_OUTPUT) {
+                  opts[i].opt_capture_output.on_output(
+                      (uint8_t*)&buffer[b], sz, opts[i].opt_capture_output.user_pointer);
+                }
+            }
 
             sz += b;
             // Log one line at a time
@@ -473,7 +482,8 @@
 }
 
 int android_fork_execvp_ext(int argc, char* argv[], int *status, bool ignore_int_quit,
-        int log_target, bool abbreviated, char *file_path) {
+        int log_target, bool abbreviated, char *file_path,
+        const struct AndroidForkExecvpOption* opts, size_t opts_len) {
     pid_t pid;
     int parent_ptty;
     int child_ptty;
@@ -490,7 +500,7 @@
     }
 
     /* Use ptty instead of socketpair so that STDOUT is not buffered */
-    parent_ptty = open("/dev/ptmx", O_RDWR);
+    parent_ptty = TEMP_FAILURE_RETRY(open("/dev/ptmx", O_RDWR));
     if (parent_ptty < 0) {
         ERROR("Cannot create parent ptty\n");
         rc = -1;
@@ -505,7 +515,7 @@
         goto err_ptty;
     }
 
-    child_ptty = open(child_devname, O_RDWR);
+    child_ptty = TEMP_FAILURE_RETRY(open(child_devname, O_RDWR));
     if (child_ptty < 0) {
         ERROR("Cannot open child_ptty\n");
         rc = -1;
@@ -528,7 +538,13 @@
         pthread_sigmask(SIG_SETMASK, &oldset, NULL);
         close(parent_ptty);
 
-        // redirect stdout and stderr
+        // redirect stdin, stdout and stderr
+        for (size_t i = 0; i < opts_len; ++i) {
+            if (opts[i].opt_type == FORK_EXECVP_OPTION_INPUT) {
+                dup2(child_ptty, 0);
+                break;
+            }
+        }
         dup2(child_ptty, 1);
         dup2(child_ptty, 2);
         close(child_ptty);
@@ -545,8 +561,24 @@
             sigaction(SIGQUIT, &ignact, &quitact);
         }
 
+        for (size_t i = 0; i < opts_len; ++i) {
+            if (opts[i].opt_type == FORK_EXECVP_OPTION_INPUT) {
+                size_t left = opts[i].opt_input.input_len;
+                const uint8_t* input = opts[i].opt_input.input;
+                while (left > 0) {
+                    ssize_t res =
+                        TEMP_FAILURE_RETRY(write(parent_ptty, input, left));
+                    if (res < 0) {
+                        break;
+                    }
+                    left -= res;
+                    input += res;
+                }
+            }
+        }
+
         rc = parent(argv[0], parent_ptty, pid, status, log_target,
-                    abbreviated, file_path);
+                    abbreviated, file_path, opts, opts_len);
     }
 
     if (ignore_int_quit) {
diff --git a/logwrapper/logwrapper.c b/logwrapper/logwrapper.c
index 9e0385d..55b71c7 100644
--- a/logwrapper/logwrapper.c
+++ b/logwrapper/logwrapper.c
@@ -81,7 +81,7 @@
     }
 
     rc = android_fork_execvp_ext(argc, &argv[0], &status, true,
-                                 log_target, abbreviated, NULL);
+                                 log_target, abbreviated, NULL, NULL, 0);
     if (!rc) {
         if (WIFEXITED(status))
             rc = WEXITSTATUS(status);
diff --git a/metricsd/Android.mk b/metricsd/Android.mk
new file mode 100644
index 0000000..12dfa18
--- /dev/null
+++ b/metricsd/Android.mk
@@ -0,0 +1,171 @@
+# Copyright (C) 2015 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 := $(call my-dir)
+
+ifeq ($(HOST_OS),linux)
+
+LOCAL_INIT_SERVICE := metrics_daemon
+
+metrics_cpp_extension := .cc
+libmetrics_sources := \
+  c_metrics_library.cc \
+  metrics_library.cc \
+  serialization/metric_sample.cc \
+  serialization/serialization_utils.cc
+
+metrics_client_sources := \
+  metrics_client.cc
+
+metrics_daemon_sources := \
+  metrics_daemon.cc \
+  metrics_daemon_main.cc \
+  persistent_integer.cc \
+  uploader/metrics_hashes.cc \
+  uploader/metrics_log_base.cc \
+  uploader/metrics_log.cc \
+  uploader/sender_http.cc \
+  uploader/system_profile_cache.cc \
+  uploader/upload_service.cc \
+  serialization/metric_sample.cc \
+  serialization/serialization_utils.cc
+
+metrics_tests_sources := \
+  metrics_daemon.cc \
+  metrics_daemon_test.cc \
+  metrics_library_test.cc \
+  persistent_integer.cc \
+  persistent_integer_test.cc \
+  serialization/metric_sample.cc \
+  serialization/serialization_utils.cc \
+  serialization/serialization_utils_unittest.cc \
+  timer.cc \
+  timer_test.cc \
+  uploader/metrics_hashes.cc \
+  uploader/metrics_hashes_unittest.cc \
+  uploader/metrics_log_base.cc \
+  uploader/metrics_log_base_unittest.cc \
+  uploader/metrics_log.cc \
+  uploader/mock/sender_mock.cc \
+  uploader/sender_http.cc \
+  uploader/system_profile_cache.cc \
+  uploader/upload_service.cc \
+  uploader/upload_service_test.cc \
+
+metrics_CFLAGS := -Wall \
+  -Wno-char-subscripts \
+  -Wno-missing-field-initializers \
+  -Wno-unused-function \
+  -Wno-unused-parameter \
+  -Werror \
+  -fvisibility=default
+metrics_CPPFLAGS := -Wno-non-virtual-dtor \
+  -Wno-sign-promo \
+  -Wno-strict-aliasing \
+  -fvisibility=default
+metrics_includes := external/gtest/include \
+  $(LOCAL_PATH)/include
+metrics_shared_libraries := libchrome libchromeos
+
+# Shared library for metrics.
+# ========================================================
+include $(CLEAR_VARS)
+LOCAL_MODULE := libmetrics
+LOCAL_C_INCLUDES := $(metrics_includes)
+LOCAL_CFLAGS := $(metrics_CFLAGS)
+LOCAL_CPP_EXTENSION := $(metrics_cpp_extension)
+LOCAL_CPPFLAGS := $(metrics_CPPFLAGS)
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_SHARED_LIBRARIES := $(metrics_shared_libraries)
+LOCAL_SRC_FILES := $(libmetrics_sources)
+include $(BUILD_SHARED_LIBRARY)
+
+# CLI client for metrics.
+# ========================================================
+include $(CLEAR_VARS)
+LOCAL_MODULE := metrics_client
+LOCAL_C_INCLUDES := $(metrics_includes)
+LOCAL_CFLAGS := $(metrics_CFLAGS)
+LOCAL_CPP_EXTENSION := $(metrics_cpp_extension)
+LOCAL_CPPFLAGS := $(metrics_CPPFLAGS)
+LOCAL_SHARED_LIBRARIES := $(metrics_shared_libraries) \
+  libmetrics
+LOCAL_SRC_FILES := $(metrics_client_sources)
+include $(BUILD_EXECUTABLE)
+
+# Protobuf library for metrics_daemon.
+# ========================================================
+include $(CLEAR_VARS)
+LOCAL_MODULE := metrics_daemon_protos
+LOCAL_MODULE_CLASS := STATIC_LIBRARIES
+generated_sources_dir := $(call local-generated-sources-dir)
+LOCAL_EXPORT_C_INCLUDE_DIRS += \
+    $(generated_sources_dir)/proto/system/core/metricsd
+LOCAL_SRC_FILES :=  $(call all-proto-files-under,uploader/proto)
+LOCAL_STATIC_LIBRARIES := libprotobuf-cpp-lite
+include $(BUILD_STATIC_LIBRARY)
+
+# metrics daemon.
+# ========================================================
+include $(CLEAR_VARS)
+LOCAL_MODULE := $(LOCAL_INIT_SERVICE)
+LOCAL_C_INCLUDES := $(metrics_includes) \
+  external/libchromeos
+LOCAL_CFLAGS := $(metrics_CFLAGS)
+LOCAL_CPP_EXTENSION := $(metrics_cpp_extension)
+LOCAL_CPPFLAGS := $(metrics_CPPFLAGS)
+LOCAL_REQUIRED_MODULES := init.$(LOCAL_INIT_SERVICE).rc
+LOCAL_RTTI_FLAG := -frtti
+LOCAL_SHARED_LIBRARIES := $(metrics_shared_libraries) \
+  libmetrics \
+  libprotobuf-cpp-lite \
+  libchromeos-http \
+  libchromeos-dbus \
+  libcutils \
+  libdbus
+LOCAL_SRC_FILES := $(metrics_daemon_sources)
+LOCAL_STATIC_LIBRARIES := metrics_daemon_protos
+include $(BUILD_EXECUTABLE)
+
+ifdef INITRC_TEMPLATE
+include $(CLEAR_VARS)
+LOCAL_MODULE := init.$(LOCAL_INIT_SERVICE).rc
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_INITRCD)
+LOCAL_SRC_FILES := init.$(LOCAL_INIT_SERVICE).rc
+include $(BUILD_PREBUILT)
+endif # INITRC_TEMPLATE
+
+# Unit tests for metrics.
+# ========================================================
+include $(CLEAR_VARS)
+LOCAL_MODULE := metrics_tests
+LOCAL_CFLAGS := $(metrics_CFLAGS)
+LOCAL_CPP_EXTENSION := $(metrics_cpp_extension)
+LOCAL_CPPFLAGS := $(metrics_CPPFLAGS) -Wno-sign-compare
+LOCAL_RTTI_FLAG := -frtti
+LOCAL_SHARED_LIBRARIES := $(metrics_shared_libraries) \
+  libmetrics \
+  libprotobuf-cpp-lite \
+  libchromeos-http \
+  libchromeos-dbus \
+  libcutils \
+  libdbus \
+
+LOCAL_SRC_FILES := $(metrics_tests_sources)
+LOCAL_STATIC_LIBRARIES := libBionicGtestMain libgmock metrics_daemon_protos
+
+include $(BUILD_NATIVE_TEST)
+
+endif # HOST_OS == linux
diff --git a/metricsd/OWNERS b/metricsd/OWNERS
new file mode 100644
index 0000000..7f5e50d
--- /dev/null
+++ b/metricsd/OWNERS
@@ -0,0 +1,3 @@
+semenzato@chromium.org
+derat@chromium.org
+bsimonnet@chromium.org
diff --git a/metricsd/README b/metricsd/README
new file mode 100644
index 0000000..d4c9a0e
--- /dev/null
+++ b/metricsd/README
@@ -0,0 +1,150 @@
+Copyright (C) 2015 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.
+
+================================================================================
+
+The Chrome OS "metrics" package contains utilities for client-side user metric
+collection.
+When Chrome is installed, Chrome will take care of aggregating and uploading the
+metrics to the UMA server.
+When Chrome is not installed (embedded build) and the metrics_uploader USE flag
+is set, metrics_daemon will aggregate and upload the metrics itself.
+
+
+================================================================================
+The Metrics Library: libmetrics
+================================================================================
+
+libmetrics is a small library that implements the basic C and C++ API for
+metrics collection. All metrics collection is funneled through this library. The
+easiest and recommended way for a client-side module to collect user metrics is
+to link libmetrics and use its APIs to send metrics to Chrome for transport to
+UMA. In order to use the library in a module, you need to do the following:
+
+- Add a dependence (DEPEND and RDEPEND) on chromeos-base/metrics to the module's
+  ebuild.
+
+- Link the module with libmetrics (for example, by passing -lmetrics to the
+  module's link command). Both libmetrics.so and libmetrics.a are built and
+  installed under $SYSROOT/usr/lib/. Note that by default -lmetrics will link
+  against libmetrics.so, which is preferred.
+
+- To access the metrics library API in the module, include the
+  <metrics/metrics_library.h> header file. The file is installed in
+  $SYSROOT/usr/include/ when the metrics library is built and installed.
+
+- The API is documented in metrics_library.h under src/platform/metrics/. Before
+  using the API methods, a MetricsLibrary object needs to be constructed and
+  initialized through its Init method.
+
+  For more information on the C API see c_metrics_library.h.
+
+- Samples are sent to Chrome only if the "/home/chronos/Consent To Send Stats"
+  file exists or the metrics are declared enabled in the policy file (see the
+  AreMetricsEnabled API method).
+
+- On the target platform, shortly after the sample is sent, it should be visible
+  in Chrome through "about:histograms".
+
+
+================================================================================
+Histogram Naming Convention
+================================================================================
+
+Use TrackerArea.MetricName. For example:
+
+Platform.DailyUseTime
+Network.TimeToDrop
+
+
+================================================================================
+Server Side
+================================================================================
+
+If the histogram data is visible in about:histograms, it will be sent by an
+official Chrome build to UMA, assuming the user has opted into metrics
+collection. To make the histogram visible on "chromedashboard", the histogram
+description XML file needs to be updated (steps 2 and 3 after following the
+"Details on how to add your own histograms" link under the Histograms tab).
+Include the string "Chrome OS" in the histogram description so that it's easier
+to distinguish Chrome OS specific metrics from general Chrome histograms.
+
+The UMA server logs and keeps the collected field data even if the metric's name
+is not added to the histogram XML. However, the dashboard histogram for that
+metric will show field data as of the histogram XML update date; it will not
+include data for older dates. If past data needs to be displayed, manual
+server-side intervention is required. In other words, one should assume that
+field data collection starts only after the histogram XML has been updated.
+
+
+================================================================================
+The Metrics Client: metrics_client
+================================================================================
+
+metrics_client is a simple shell command-line utility for sending histogram
+samples and user actions. It's installed under /usr/bin on the target platform
+and uses libmetrics to send the data to Chrome. The utility is useful for
+generating metrics from shell scripts.
+
+For usage information and command-line options, run "metrics_client" on the
+target platform or look for "Usage:" in metrics_client.cc.
+
+
+================================================================================
+The Metrics Daemon: metrics_daemon
+================================================================================
+
+metrics_daemon is a daemon that runs in the background on the target platform
+and is intended for passive or ongoing metrics collection, or metrics collection
+requiring feedback from multiple modules. For example, it listens to D-Bus
+signals related to the user session and screen saver states to determine if the
+user is actively using the device or not and generates the corresponding
+data. The metrics daemon uses libmetrics to send the data to Chrome.
+
+The recommended way to generate metrics data from a module is to link and use
+libmetrics directly. However, the module could instead send signals to or
+communicate in some alternative way with the metrics daemon. Then the metrics
+daemon needs to monitor for the relevant events and take appropriate action --
+for example, aggregate data and send the histogram samples.
+
+
+================================================================================
+FAQ
+================================================================================
+
+Q. What should my histogram's |min| and |max| values be set at?
+
+A. You should set the values to a range that covers the vast majority of samples
+   that would appear in the field. Note that samples below the |min| will still
+   be collected in the underflow bucket and samples above the |max| will end up
+   in the overflow bucket. Also, the reported mean of the data will be correct
+   regardless of the range.
+
+Q. How many buckets should I use in my histogram?
+
+A. You should allocate as many buckets as necessary to perform proper analysis
+   on the collected data. Note, however, that the memory allocated in Chrome for
+   each histogram is proportional to the number of buckets. Therefore, it is
+   strongly recommended to keep this number low (e.g., 50 is normal, while 100
+   is probably high).
+
+Q. When should I use an enumeration (linear) histogram vs. a regular
+   (exponential) histogram?
+
+A. Enumeration histograms should really be used only for sampling enumerated
+   events and, in some cases, percentages. Normally, you should use a regular
+   histogram with exponential bucket layout that provides higher resolution at
+   the low end of the range and lower resolution at the high end. Regular
+   histograms are generally used for collecting performance data (e.g., timing,
+   memory usage, power) as well as aggregated event counts.
diff --git a/metricsd/WATCHLISTS b/metricsd/WATCHLISTS
new file mode 100644
index 0000000..a051f35
--- /dev/null
+++ b/metricsd/WATCHLISTS
@@ -0,0 +1,16 @@
+# See http://dev.chromium.org/developers/contributing-code/watchlists for
+# a description of this file's format.
+# Please keep these keys in alphabetical order.
+
+{
+  'WATCHLIST_DEFINITIONS': {
+    'all': {
+      'filepath': '.',
+    },
+  },
+  'WATCHLISTS': {
+    'all': ['petkov@chromium.org',
+                'semenzato@chromium.org',
+                'sosa@chromium.org']
+  },
+}
diff --git a/metricsd/c_metrics_library.cc b/metricsd/c_metrics_library.cc
new file mode 100644
index 0000000..0503876
--- /dev/null
+++ b/metricsd/c_metrics_library.cc
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//
+// C wrapper to libmetrics
+//
+
+#include "metrics/c_metrics_library.h"
+
+#include <string>
+
+#include "metrics/metrics_library.h"
+
+extern "C" CMetricsLibrary CMetricsLibraryNew(void) {
+  MetricsLibrary* lib = new MetricsLibrary;
+  return reinterpret_cast<CMetricsLibrary>(lib);
+}
+
+extern "C" void CMetricsLibraryDelete(CMetricsLibrary handle) {
+  MetricsLibrary* lib = reinterpret_cast<MetricsLibrary*>(handle);
+  delete lib;
+}
+
+extern "C" void CMetricsLibraryInit(CMetricsLibrary handle) {
+  MetricsLibrary* lib = reinterpret_cast<MetricsLibrary*>(handle);
+  if (lib != NULL)
+    lib->Init();
+}
+
+extern "C" int CMetricsLibrarySendToUMA(CMetricsLibrary handle,
+                                        const char* name, int sample,
+                                        int min, int max, int nbuckets) {
+  MetricsLibrary* lib = reinterpret_cast<MetricsLibrary*>(handle);
+  if (lib == NULL)
+    return 0;
+  return lib->SendToUMA(std::string(name), sample, min, max, nbuckets);
+}
+
+extern "C" int CMetricsLibrarySendEnumToUMA(CMetricsLibrary handle,
+                                            const char* name, int sample,
+                                            int max) {
+  MetricsLibrary* lib = reinterpret_cast<MetricsLibrary*>(handle);
+  if (lib == NULL)
+    return 0;
+  return lib->SendEnumToUMA(std::string(name), sample, max);
+}
+
+extern "C" int CMetricsLibrarySendSparseToUMA(CMetricsLibrary handle,
+                                              const char* name, int sample) {
+  MetricsLibrary* lib = reinterpret_cast<MetricsLibrary*>(handle);
+  if (lib == NULL)
+    return 0;
+  return lib->SendSparseToUMA(std::string(name), sample);
+}
+
+extern "C" int CMetricsLibrarySendUserActionToUMA(CMetricsLibrary handle,
+                                                  const char* action) {
+  MetricsLibrary* lib = reinterpret_cast<MetricsLibrary*>(handle);
+  if (lib == NULL)
+    return 0;
+  return lib->SendUserActionToUMA(std::string(action));
+}
+
+extern "C" int CMetricsLibrarySendCrashToUMA(CMetricsLibrary handle,
+                                            const char* crash_kind) {
+  MetricsLibrary* lib = reinterpret_cast<MetricsLibrary*>(handle);
+  if (lib == NULL)
+    return 0;
+  return lib->SendCrashToUMA(crash_kind);
+}
+
+extern "C" int CMetricsLibraryAreMetricsEnabled(CMetricsLibrary handle) {
+  MetricsLibrary* lib = reinterpret_cast<MetricsLibrary*>(handle);
+  if (lib == NULL)
+    return 0;
+  return lib->AreMetricsEnabled();
+}
diff --git a/metricsd/constants.h b/metricsd/constants.h
new file mode 100644
index 0000000..15c15d9
--- /dev/null
+++ b/metricsd/constants.h
@@ -0,0 +1,34 @@
+//
+// Copyright (C) 2015 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 METRICS_CONSTANTS_H_
+#define METRICS_CONSTANTS_H_
+
+namespace metrics {
+static const char kMetricsDirectory[] = "/data/misc/metrics/";
+static const char kMetricsEventsFilePath[] = "/data/misc/metrics/uma-events";
+static const char kMetricsGUIDFilePath[] = "/data/misc/metrics/Sysinfo.GUID";
+static const char kMetricsServer[] = "https://clients4.google.com/uma/v2";
+static const char kConsentFilePath[] = "/data/misc/metrics/enabled";
+static const char kDefaultVersion[] = "0.0.0.0";
+
+// System properties used.
+static const char kBuildTargetIdProperty[] = "ro.product.build_target_id";
+static const char kChannelProperty[] = "ro.product.channel";
+static const char kProductVersionProperty[] = "ro.product.version";
+}  // namespace metrics
+
+#endif  // METRICS_CONSTANTS_H_
diff --git a/metricsd/include/metrics/c_metrics_library.h b/metricsd/include/metrics/c_metrics_library.h
new file mode 100644
index 0000000..4e7e666
--- /dev/null
+++ b/metricsd/include/metrics/c_metrics_library.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2015 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 METRICS_C_METRICS_LIBRARY_H_
+#define METRICS_C_METRICS_LIBRARY_H_
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+typedef struct CMetricsLibraryOpaque* CMetricsLibrary;
+
+// C wrapper for MetricsLibrary::MetricsLibrary.
+CMetricsLibrary CMetricsLibraryNew(void);
+
+// C wrapper for MetricsLibrary::~MetricsLibrary.
+void CMetricsLibraryDelete(CMetricsLibrary handle);
+
+// C wrapper for MetricsLibrary::Init.
+void CMetricsLibraryInit(CMetricsLibrary handle);
+
+// C wrapper for MetricsLibrary::SendToUMA.
+int CMetricsLibrarySendToUMA(CMetricsLibrary handle,
+                             const char* name, int sample,
+                             int min, int max, int nbuckets);
+
+// C wrapper for MetricsLibrary::SendEnumToUMA.
+int CMetricsLibrarySendEnumToUMA(CMetricsLibrary handle,
+                                 const char* name, int sample, int max);
+
+// C wrapper for MetricsLibrary::SendSparseToUMA.
+int CMetricsLibrarySendSparseToUMA(CMetricsLibrary handle,
+                                   const char* name, int sample);
+
+// C wrapper for MetricsLibrary::SendUserActionToUMA.
+int CMetricsLibrarySendUserActionToUMA(CMetricsLibrary handle,
+                                       const char* action);
+
+// C wrapper for MetricsLibrary::SendCrashToUMA.
+int CMetricsLibrarySendCrashToUMA(CMetricsLibrary handle,
+                                  const char* crash_kind);
+
+// C wrapper for MetricsLibrary::AreMetricsEnabled.
+int CMetricsLibraryAreMetricsEnabled(CMetricsLibrary handle);
+
+#if defined(__cplusplus)
+}
+#endif
+#endif  // METRICS_C_METRICS_LIBRARY_H_
diff --git a/metricsd/include/metrics/metrics_library.h b/metricsd/include/metrics/metrics_library.h
new file mode 100644
index 0000000..a956b69
--- /dev/null
+++ b/metricsd/include/metrics/metrics_library.h
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2015 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 METRICS_METRICS_LIBRARY_H_
+#define METRICS_METRICS_LIBRARY_H_
+
+#include <sys/types.h>
+#include <string>
+#include <unistd.h>
+
+#include <base/compiler_specific.h>
+#include <base/macros.h>
+#include <base/memory/scoped_ptr.h>
+#include <gtest/gtest_prod.h>  // for FRIEND_TEST
+
+class MetricsLibraryInterface {
+ public:
+  virtual void Init() = 0;
+  virtual bool AreMetricsEnabled() = 0;
+  virtual bool SendToUMA(const std::string& name, int sample,
+                         int min, int max, int nbuckets) = 0;
+  virtual bool SendEnumToUMA(const std::string& name, int sample, int max) = 0;
+  virtual bool SendSparseToUMA(const std::string& name, int sample) = 0;
+  virtual bool SendUserActionToUMA(const std::string& action) = 0;
+  virtual ~MetricsLibraryInterface() {}
+};
+
+// Library used to send metrics to Chrome/UMA.
+class MetricsLibrary : public MetricsLibraryInterface {
+ public:
+  MetricsLibrary();
+  virtual ~MetricsLibrary();
+
+  // Initializes the library.
+  void Init() override;
+
+  // Returns whether or not the machine is running in guest mode.
+  bool IsGuestMode();
+
+  // Returns whether or not metrics collection is enabled.
+  bool AreMetricsEnabled() override;
+
+  // Sends histogram data to Chrome for transport to UMA and returns
+  // true on success. This method results in the equivalent of an
+  // asynchronous non-blocking RPC to UMA_HISTOGRAM_CUSTOM_COUNTS
+  // inside Chrome (see base/histogram.h).
+  //
+  // |sample| is the sample value to be recorded (|min| <= |sample| < |max|).
+  // |min| is the minimum value of the histogram samples (|min| > 0).
+  // |max| is the maximum value of the histogram samples.
+  // |nbuckets| is the number of histogram buckets.
+  // [0,min) is the implicit underflow bucket.
+  // [|max|,infinity) is the implicit overflow bucket.
+  //
+  // Note that the memory allocated in Chrome for each histogram is
+  // proportional to the number of buckets. Therefore, it is strongly
+  // recommended to keep this number low (e.g., 50 is normal, while
+  // 100 is high).
+  bool SendToUMA(const std::string& name, int sample,
+                 int min, int max, int nbuckets) override;
+
+  // Sends linear histogram data to Chrome for transport to UMA and
+  // returns true on success. This method results in the equivalent of
+  // an asynchronous non-blocking RPC to UMA_HISTOGRAM_ENUMERATION
+  // inside Chrome (see base/histogram.h).
+  //
+  // |sample| is the sample value to be recorded (1 <= |sample| < |max|).
+  // |max| is the maximum value of the histogram samples.
+  // 0 is the implicit underflow bucket.
+  // [|max|,infinity) is the implicit overflow bucket.
+  //
+  // An enumeration histogram requires |max| + 1 number of
+  // buckets. Note that the memory allocated in Chrome for each
+  // histogram is proportional to the number of buckets. Therefore, it
+  // is strongly recommended to keep this number low (e.g., 50 is
+  // normal, while 100 is high).
+  bool SendEnumToUMA(const std::string& name, int sample, int max) override;
+
+  // Sends sparse histogram sample to Chrome for transport to UMA.  Returns
+  // true on success.
+  //
+  // |sample| is the 32-bit integer value to be recorded.
+  bool SendSparseToUMA(const std::string& name, int sample) override;
+
+  // Sends a user action to Chrome for transport to UMA and returns true on
+  // success. This method results in the equivalent of an asynchronous
+  // non-blocking RPC to UserMetrics::RecordAction.  The new metric must be
+  // added to chrome/tools/extract_actions.py in the Chromium repository, which
+  // should then be run to generate a hash for the new action.
+  //
+  // Until http://crosbug.com/11125 is fixed, the metric must also be added to
+  // chrome/browser/chromeos/external_metrics.cc.
+  //
+  // |action| is the user-generated event (e.g., "MuteKeyPressed").
+  bool SendUserActionToUMA(const std::string& action) override;
+
+  // Sends a signal to UMA that a crash of the given |crash_kind|
+  // has occurred.  Used by UMA to generate stability statistics.
+  bool SendCrashToUMA(const char *crash_kind);
+
+  // Sends a "generic Chrome OS event" to UMA.  This is an event name
+  // that is translated into an enumerated histogram entry.  Event names
+  // are added to metrics_library.cc.  Optionally, they can be added
+  // to histograms.xml---but part of the reason for this is to simplify
+  // the addition of events (at the cost of having to look them up by
+  // number in the histograms dashboard).
+  bool SendCrosEventToUMA(const std::string& event);
+
+ private:
+  friend class CMetricsLibraryTest;
+  friend class MetricsLibraryTest;
+  FRIEND_TEST(MetricsLibraryTest, AreMetricsEnabled);
+  FRIEND_TEST(MetricsLibraryTest, FormatChromeMessage);
+  FRIEND_TEST(MetricsLibraryTest, FormatChromeMessageTooLong);
+  FRIEND_TEST(MetricsLibraryTest, IsDeviceMounted);
+  FRIEND_TEST(MetricsLibraryTest, SendMessageToChrome);
+  FRIEND_TEST(MetricsLibraryTest, SendMessageToChromeUMAEventsBadFileLocation);
+
+  void InitForTest(const std::string& uma_events_file,
+                   const std::string& consent_file);
+
+  // Sets |*result| to whether or not the |mounts_file| indicates that
+  // the |device_name| is currently mounted.  Uses |buffer| of
+  // |buffer_size| to read the file.  Returns false if any error.
+  bool IsDeviceMounted(const char* device_name,
+                       const char* mounts_file,
+                       char* buffer, int buffer_size,
+                       bool* result);
+
+  // Time at which we last checked if metrics were enabled.
+  static time_t cached_enabled_time_;
+
+  // Cached state of whether or not metrics were enabled.
+  static bool cached_enabled_;
+
+  std::string uma_events_file_;
+  std::string consent_file_;
+
+  DISALLOW_COPY_AND_ASSIGN(MetricsLibrary);
+};
+
+#endif  // METRICS_METRICS_LIBRARY_H_
diff --git a/metricsd/init.metrics_daemon.rc b/metricsd/init.metrics_daemon.rc
new file mode 100644
index 0000000..73ce673
--- /dev/null
+++ b/metricsd/init.metrics_daemon.rc
@@ -0,0 +1,8 @@
+on boot
+    mkdir /data/misc/metrics 0770 system system
+
+service metrics_daemon /system/bin/metrics_daemon --uploader -nodaemon
+    class late_start
+    user system
+    group system dbus inet
+    seclabel u:r:brillo:s0
diff --git a/metricsd/libmetrics-334380.gyp b/metricsd/libmetrics-334380.gyp
new file mode 100644
index 0000000..9771821
--- /dev/null
+++ b/metricsd/libmetrics-334380.gyp
@@ -0,0 +1,8 @@
+{
+  'variables': {
+    'libbase_ver': 334380,
+  },
+  'includes': [
+    'libmetrics.gypi',
+  ],
+}
diff --git a/metricsd/libmetrics.gypi b/metricsd/libmetrics.gypi
new file mode 100644
index 0000000..5b90a55
--- /dev/null
+++ b/metricsd/libmetrics.gypi
@@ -0,0 +1,33 @@
+{
+  'target_defaults': {
+    'variables': {
+      'deps': [
+        'libchrome-<(libbase_ver)',
+        'libchromeos-<(libbase_ver)',
+      ]
+    },
+    'cflags_cc': [
+      '-fno-exceptions',
+    ],
+  },
+  'targets': [
+    {
+      'target_name': 'libmetrics-<(libbase_ver)',
+      'type': 'shared_library',
+      'cflags': [
+        '-fvisibility=default',
+      ],
+      'libraries+': [
+        '-lpolicy-<(libbase_ver)',
+      ],
+      'sources': [
+        'c_metrics_library.cc',
+        'metrics_library.cc',
+        'serialization/metric_sample.cc',
+        'serialization/serialization_utils.cc',
+        'timer.cc',
+      ],
+      'include_dirs': ['.'],
+    },
+  ],
+}
diff --git a/metricsd/libmetrics.pc.in b/metricsd/libmetrics.pc.in
new file mode 100644
index 0000000..233f318
--- /dev/null
+++ b/metricsd/libmetrics.pc.in
@@ -0,0 +1,7 @@
+bslot=@BSLOT@
+
+Name: libmetrics
+Description: Chrome OS metrics library
+Version: ${bslot}
+Requires.private: libchrome-${bslot}
+Libs: -lmetrics-${bslot}
diff --git a/metricsd/metrics.gyp b/metricsd/metrics.gyp
new file mode 100644
index 0000000..276ec78
--- /dev/null
+++ b/metricsd/metrics.gyp
@@ -0,0 +1,184 @@
+{
+  'target_defaults': {
+    'variables': {
+      'deps': [
+        'dbus-1',
+        'libchrome-<(libbase_ver)',
+        'libchromeos-<(libbase_ver)',
+      ]
+    },
+    'cflags_cc': [
+      '-fno-exceptions',
+    ],
+  },
+  'targets': [
+    {
+      'target_name': 'libmetrics_daemon',
+      'type': 'static_library',
+      'dependencies': [
+        '../metrics/libmetrics-<(libbase_ver).gyp:libmetrics-<(libbase_ver)',
+        'libupload_service',
+        'metrics_proto',
+      ],
+      'link_settings': {
+        'libraries': [
+          '-lrootdev',
+        ],
+      },
+      'sources': [
+        'persistent_integer.cc',
+        'metrics_daemon.cc',
+        'metrics_daemon_main.cc',
+      ],
+      'include_dirs': ['.'],
+    },
+    {
+      'target_name': 'metrics_client',
+      'type': 'executable',
+      'dependencies': [
+        '../metrics/libmetrics-<(libbase_ver).gyp:libmetrics-<(libbase_ver)',
+      ],
+      'sources': [
+        'metrics_client.cc',
+      ]
+    },
+    {
+      'target_name': 'libupload_service',
+      'type': 'static_library',
+      'dependencies': [
+        'metrics_proto',
+        '../metrics/libmetrics-<(libbase_ver).gyp:libmetrics-<(libbase_ver)',
+      ],
+      'link_settings': {
+        'libraries': [
+          '-lvboot_host',
+        ],
+      },
+      'variables': {
+        'exported_deps': [
+          'protobuf-lite',
+        ],
+        'deps': [
+          '<@(exported_deps)',
+        ],
+      },
+      'all_dependent_settings': {
+        'variables': {
+          'deps+': [
+            '<@(exported_deps)',
+          ],
+        },
+      },
+      'sources': [
+        'uploader/upload_service.cc',
+        'uploader/metrics_hashes.cc',
+        'uploader/metrics_log.cc',
+        'uploader/metrics_log_base.cc',
+        'uploader/system_profile_cache.cc',
+        'uploader/sender_http.cc',
+      ],
+      'include_dirs': ['.']
+    },
+    {
+      'target_name': 'metrics_proto',
+      'type': 'static_library',
+      'variables': {
+        'proto_in_dir': 'uploader/proto',
+        'proto_out_dir': 'include/metrics/uploader/proto',
+      },
+      'sources': [
+        '<(proto_in_dir)/chrome_user_metrics_extension.proto',
+        '<(proto_in_dir)/histogram_event.proto',
+        '<(proto_in_dir)/system_profile.proto',
+        '<(proto_in_dir)/user_action_event.proto',
+      ],
+      'includes': [
+        '../common-mk/protoc.gypi'
+      ],
+    },
+  ],
+  'conditions': [
+    ['USE_passive_metrics == 1', {
+      'targets': [
+        {
+          'target_name': 'metrics_daemon',
+          'type': 'executable',
+          'dependencies': ['libmetrics_daemon'],
+        },
+      ],
+    }],
+    ['USE_test == 1', {
+      'targets': [
+        {
+          'target_name': 'persistent_integer_test',
+          'type': 'executable',
+          'includes': ['../common-mk/common_test.gypi'],
+          'sources': [
+            'persistent_integer.cc',
+            'persistent_integer_test.cc',
+          ]
+        },
+        {
+          'target_name': 'metrics_library_test',
+          'type': 'executable',
+          'dependencies': [
+            '../metrics/libmetrics-<(libbase_ver).gyp:libmetrics-<(libbase_ver)',
+          ],
+          'includes': ['../common-mk/common_test.gypi'],
+          'sources': [
+            'metrics_library_test.cc',
+            'serialization/serialization_utils_unittest.cc',
+          ],
+          'link_settings': {
+            'libraries': [
+              '-lpolicy-<(libbase_ver)',
+            ]
+          }
+        },
+        {
+          'target_name': 'timer_test',
+          'type': 'executable',
+          'includes': ['../common-mk/common_test.gypi'],
+          'sources': [
+            'timer.cc',
+            'timer_test.cc',
+          ]
+        },
+        {
+          'target_name': 'upload_service_test',
+          'type': 'executable',
+          'sources': [
+            'persistent_integer.cc',
+            'uploader/metrics_hashes_unittest.cc',
+            'uploader/metrics_log_base_unittest.cc',
+            'uploader/mock/sender_mock.cc',
+            'uploader/upload_service_test.cc',
+          ],
+          'dependencies': [
+            'libupload_service',
+          ],
+          'includes':[
+            '../common-mk/common_test.gypi',
+          ],
+          'include_dirs': ['.']
+        },
+      ],
+    }],
+    ['USE_passive_metrics == 1 and USE_test == 1', {
+      'targets': [
+        {
+          'target_name': 'metrics_daemon_test',
+          'type': 'executable',
+          'dependencies': [
+            'libmetrics_daemon',
+          ],
+          'includes': ['../common-mk/common_test.gypi'],
+          'sources': [
+            'metrics_daemon_test.cc',
+          ],
+          'include_dirs': ['.'],
+        },
+      ],
+    }],
+  ]
+}
diff --git a/metricsd/metrics_client.cc b/metricsd/metrics_client.cc
new file mode 100644
index 0000000..57e96c2
--- /dev/null
+++ b/metricsd/metrics_client.cc
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2015 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 <cstdio>
+#include <cstdlib>
+
+#include "metrics/metrics_library.h"
+
+enum Mode {
+    kModeSendSample,
+    kModeSendEnumSample,
+    kModeSendSparseSample,
+    kModeSendUserAction,
+    kModeSendCrosEvent,
+    kModeHasConsent,
+    kModeIsGuestMode,
+};
+
+void ShowUsage() {
+  fprintf(stderr,
+          "Usage:  metrics_client [-t] name sample min max nbuckets\n"
+          "        metrics_client -e   name sample max\n"
+          "        metrics_client -s   name sample\n"
+          "        metrics_client -v   event\n"
+          "        metrics_client -u action\n"
+          "        metrics_client [-cg]\n"
+          "\n"
+          "  default: send metric with integer values \n"
+          "           |min| > 0, |min| <= sample < |max|\n"
+          "  -c: return exit status 0 if user consents to stats, 1 otherwise,\n"
+          "      in guest mode always return 1\n"
+          "  -e: send linear/enumeration histogram data\n"
+          "  -g: return exit status 0 if machine in guest mode, 1 otherwise\n"
+          "  -s: send a sparse histogram sample\n"
+          "  -t: convert sample from double seconds to int milliseconds\n"
+          "  -u: send a user action to Chrome\n"
+          "  -v: send a Platform.CrOSEvent enum histogram sample\n");
+  exit(1);
+}
+
+static int ParseInt(const char *arg) {
+  char *endptr;
+  int value = strtol(arg, &endptr, 0);
+  if (*endptr != '\0') {
+    fprintf(stderr, "metrics client: bad integer \"%s\"\n", arg);
+    ShowUsage();
+  }
+  return value;
+}
+
+static double ParseDouble(const char *arg) {
+  char *endptr;
+  double value = strtod(arg, &endptr);
+  if (*endptr != '\0') {
+    fprintf(stderr, "metrics client: bad double \"%s\"\n", arg);
+    ShowUsage();
+  }
+  return value;
+}
+
+static int SendStats(char* argv[],
+                     int name_index,
+                     enum Mode mode,
+                     bool secs_to_msecs) {
+  const char* name = argv[name_index];
+  int sample;
+  if (secs_to_msecs) {
+    sample = static_cast<int>(ParseDouble(argv[name_index + 1]) * 1000.0);
+  } else {
+    sample = ParseInt(argv[name_index + 1]);
+  }
+
+  MetricsLibrary metrics_lib;
+  metrics_lib.Init();
+  if (mode == kModeSendSparseSample) {
+    metrics_lib.SendSparseToUMA(name, sample);
+  } else if (mode == kModeSendEnumSample) {
+    int max = ParseInt(argv[name_index + 2]);
+    metrics_lib.SendEnumToUMA(name, sample, max);
+  } else {
+    int min = ParseInt(argv[name_index + 2]);
+    int max = ParseInt(argv[name_index + 3]);
+    int nbuckets = ParseInt(argv[name_index + 4]);
+    metrics_lib.SendToUMA(name, sample, min, max, nbuckets);
+  }
+  return 0;
+}
+
+static int SendUserAction(char* argv[], int action_index) {
+  const char* action = argv[action_index];
+  MetricsLibrary metrics_lib;
+  metrics_lib.Init();
+  metrics_lib.SendUserActionToUMA(action);
+  return 0;
+}
+
+static int SendCrosEvent(char* argv[], int action_index) {
+  const char* event = argv[action_index];
+  bool result;
+  MetricsLibrary metrics_lib;
+  metrics_lib.Init();
+  result = metrics_lib.SendCrosEventToUMA(event);
+  if (!result) {
+    fprintf(stderr, "metrics_client: could not send event %s\n", event);
+    return 1;
+  }
+  return 0;
+}
+
+static int HasConsent() {
+  MetricsLibrary metrics_lib;
+  metrics_lib.Init();
+  return metrics_lib.AreMetricsEnabled() ? 0 : 1;
+}
+
+static int IsGuestMode() {
+  MetricsLibrary metrics_lib;
+  metrics_lib.Init();
+  return metrics_lib.IsGuestMode() ? 0 : 1;
+}
+
+int main(int argc, char** argv) {
+  enum Mode mode = kModeSendSample;
+  bool secs_to_msecs = false;
+
+  // Parse arguments
+  int flag;
+  while ((flag = getopt(argc, argv, "abcegstuv")) != -1) {
+    switch (flag) {
+      case 'c':
+        mode = kModeHasConsent;
+        break;
+      case 'e':
+        mode = kModeSendEnumSample;
+        break;
+      case 'g':
+        mode = kModeIsGuestMode;
+        break;
+      case 's':
+        mode = kModeSendSparseSample;
+        break;
+      case 't':
+        secs_to_msecs = true;
+        break;
+      case 'u':
+        mode = kModeSendUserAction;
+        break;
+      case 'v':
+        mode = kModeSendCrosEvent;
+        break;
+      default:
+        ShowUsage();
+        break;
+    }
+  }
+  int arg_index = optind;
+
+  int expected_args = 0;
+  if (mode == kModeSendSample)
+    expected_args = 5;
+  else if (mode == kModeSendEnumSample)
+    expected_args = 3;
+  else if (mode == kModeSendSparseSample)
+    expected_args = 2;
+  else if (mode == kModeSendUserAction)
+    expected_args = 1;
+  else if (mode == kModeSendCrosEvent)
+    expected_args = 1;
+
+  if ((arg_index + expected_args) != argc) {
+    ShowUsage();
+  }
+
+  switch (mode) {
+    case kModeSendSample:
+    case kModeSendEnumSample:
+    case kModeSendSparseSample:
+      if ((mode != kModeSendSample) && secs_to_msecs) {
+        ShowUsage();
+      }
+      return SendStats(argv,
+                       arg_index,
+                       mode,
+                       secs_to_msecs);
+    case kModeSendUserAction:
+      return SendUserAction(argv, arg_index);
+    case kModeSendCrosEvent:
+      return SendCrosEvent(argv, arg_index);
+    case kModeHasConsent:
+      return HasConsent();
+    case kModeIsGuestMode:
+      return IsGuestMode();
+    default:
+      ShowUsage();
+      return 0;
+  }
+}
diff --git a/metricsd/metrics_daemon.cc b/metricsd/metrics_daemon.cc
new file mode 100644
index 0000000..cf4d79d
--- /dev/null
+++ b/metricsd/metrics_daemon.cc
@@ -0,0 +1,1077 @@
+/*
+ * Copyright (C) 2015 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 "metrics_daemon.h"
+
+#include <fcntl.h>
+#include <inttypes.h>
+#include <math.h>
+#include <string.h>
+#include <sysexits.h>
+#include <time.h>
+
+#include <base/bind.h>
+#include <base/files/file_path.h>
+#include <base/files/file_util.h>
+#include <base/hash.h>
+#include <base/logging.h>
+#include <base/strings/string_number_conversions.h>
+#include <base/strings/string_split.h>
+#include <base/strings/string_util.h>
+#include <base/strings/stringprintf.h>
+#include <cutils/properties.h>
+#include <dbus/dbus.h>
+#include <dbus/message.h>
+
+#include "constants.h"
+#include "uploader/upload_service.h"
+
+using base::FilePath;
+using base::StringPrintf;
+using base::Time;
+using base::TimeDelta;
+using base::TimeTicks;
+using chromeos_metrics::PersistentInteger;
+using std::map;
+using std::string;
+using std::vector;
+
+namespace {
+
+const char kCrashReporterInterface[] = "org.chromium.CrashReporter";
+const char kCrashReporterUserCrashSignal[] = "UserCrash";
+const char kCrashReporterMatchRule[] =
+    "type='signal',interface='%s',path='/',member='%s'";
+
+const int kSecondsPerMinute = 60;
+const int kMinutesPerHour = 60;
+const int kHoursPerDay = 24;
+const int kMinutesPerDay = kHoursPerDay * kMinutesPerHour;
+const int kSecondsPerDay = kSecondsPerMinute * kMinutesPerDay;
+const int kDaysPerWeek = 7;
+const int kSecondsPerWeek = kSecondsPerDay * kDaysPerWeek;
+
+// Interval between calls to UpdateStats().
+const uint32_t kUpdateStatsIntervalMs = 300000;
+
+const char kKernelCrashDetectedFile[] = "/var/run/kernel-crash-detected";
+const char kUncleanShutdownDetectedFile[] =
+    "/var/run/unclean-shutdown-detected";
+
+// disk stats metrics
+
+// The {Read,Write}Sectors numbers are in sectors/second.
+// A sector is usually 512 bytes.
+
+const char kMetricReadSectorsLongName[] = "Platform.ReadSectorsLong";
+const char kMetricWriteSectorsLongName[] = "Platform.WriteSectorsLong";
+const char kMetricReadSectorsShortName[] = "Platform.ReadSectorsShort";
+const char kMetricWriteSectorsShortName[] = "Platform.WriteSectorsShort";
+
+const int kMetricStatsShortInterval = 1;  // seconds
+const int kMetricStatsLongInterval = 30;  // seconds
+
+const int kMetricMeminfoInterval = 30;        // seconds
+
+// Assume a max rate of 250Mb/s for reads (worse for writes) and 512 byte
+// sectors.
+const int kMetricSectorsIOMax = 500000;  // sectors/second
+const int kMetricSectorsBuckets = 50;    // buckets
+// Page size is 4k, sector size is 0.5k.  We're not interested in page fault
+// rates that the disk cannot sustain.
+const int kMetricPageFaultsMax = kMetricSectorsIOMax / 8;
+const int kMetricPageFaultsBuckets = 50;
+
+// Major page faults, i.e. the ones that require data to be read from disk.
+
+const char kMetricPageFaultsLongName[] = "Platform.PageFaultsLong";
+const char kMetricPageFaultsShortName[] = "Platform.PageFaultsShort";
+
+// Swap in and Swap out
+
+const char kMetricSwapInLongName[] = "Platform.SwapInLong";
+const char kMetricSwapInShortName[] = "Platform.SwapInShort";
+
+const char kMetricSwapOutLongName[] = "Platform.SwapOutLong";
+const char kMetricSwapOutShortName[] = "Platform.SwapOutShort";
+
+const char kMetricsProcStatFileName[] = "/proc/stat";
+const char kVmStatFileName[] = "/proc/vmstat";
+const char kMeminfoFileName[] = "/proc/meminfo";
+const int kMetricsProcStatFirstLineItemsCount = 11;
+
+// Thermal CPU throttling.
+
+const char kMetricScaledCpuFrequencyName[] =
+    "Platform.CpuFrequencyThermalScaling";
+
+}  // namespace
+
+// Zram sysfs entries.
+
+const char MetricsDaemon::kComprDataSizeName[] = "compr_data_size";
+const char MetricsDaemon::kOrigDataSizeName[] = "orig_data_size";
+const char MetricsDaemon::kZeroPagesName[] = "zero_pages";
+
+// Memory use stats collection intervals.  We collect some memory use interval
+// at these intervals after boot, and we stop collecting after the last one,
+// with the assumption that in most cases the memory use won't change much
+// after that.
+static const int kMemuseIntervals[] = {
+  1 * kSecondsPerMinute,    // 1 minute mark
+  4 * kSecondsPerMinute,    // 5 minute mark
+  25 * kSecondsPerMinute,   // 0.5 hour mark
+  120 * kSecondsPerMinute,  // 2.5 hour mark
+  600 * kSecondsPerMinute,  // 12.5 hour mark
+};
+
+MetricsDaemon::MetricsDaemon()
+    : memuse_final_time_(0),
+      memuse_interval_index_(0),
+      read_sectors_(0),
+      write_sectors_(0),
+      vmstats_(),
+      stats_state_(kStatsShort),
+      stats_initial_time_(0),
+      ticks_per_second_(0),
+      latest_cpu_use_ticks_(0) {}
+
+MetricsDaemon::~MetricsDaemon() {
+}
+
+double MetricsDaemon::GetActiveTime() {
+  struct timespec ts;
+  int r = clock_gettime(CLOCK_MONOTONIC, &ts);
+  if (r < 0) {
+    PLOG(WARNING) << "clock_gettime(CLOCK_MONOTONIC) failed";
+    return 0;
+  } else {
+    return ts.tv_sec + static_cast<double>(ts.tv_nsec) / (1000 * 1000 * 1000);
+  }
+}
+
+int MetricsDaemon::Run() {
+  if (CheckSystemCrash(kKernelCrashDetectedFile)) {
+    ProcessKernelCrash();
+  }
+
+  if (CheckSystemCrash(kUncleanShutdownDetectedFile)) {
+    ProcessUncleanShutdown();
+  }
+
+  // On OS version change, clear version stats (which are reported daily).
+  int32_t version = GetOsVersionHash();
+  if (version_cycle_->Get() != version) {
+    version_cycle_->Set(version);
+    kernel_crashes_version_count_->Set(0);
+    version_cumulative_active_use_->Set(0);
+    version_cumulative_cpu_use_->Set(0);
+  }
+
+  return chromeos::DBusDaemon::Run();
+}
+
+void MetricsDaemon::RunUploaderTest() {
+  upload_service_.reset(new UploadService(
+      new SystemProfileCache(true, base::FilePath(config_root_)),
+      metrics_lib_,
+      server_));
+  upload_service_->Init(upload_interval_, metrics_file_);
+  upload_service_->UploadEvent();
+}
+
+uint32_t MetricsDaemon::GetOsVersionHash() {
+  static uint32_t cached_version_hash = 0;
+  static bool version_hash_is_cached = false;
+  if (version_hash_is_cached)
+    return cached_version_hash;
+  version_hash_is_cached = true;
+
+  char version[PROPERTY_VALUE_MAX];
+  // The version might not be set for development devices. In this case, use the
+  // zero version.
+  property_get(metrics::kProductVersionProperty, version,
+               metrics::kDefaultVersion);
+
+  cached_version_hash = base::Hash(version);
+  if (testing_) {
+    cached_version_hash = 42;  // return any plausible value for the hash
+  }
+  return cached_version_hash;
+}
+
+void MetricsDaemon::Init(bool testing,
+                         bool uploader_active,
+                         bool dbus_enabled,
+                         MetricsLibraryInterface* metrics_lib,
+                         const string& scaling_max_freq_path,
+                         const string& cpuinfo_max_freq_path,
+                         const base::TimeDelta& upload_interval,
+                         const string& server,
+                         const string& metrics_file,
+                         const string& config_root) {
+  CHECK(metrics_lib);
+  testing_ = testing;
+  uploader_active_ = uploader_active;
+  dbus_enabled_ = dbus_enabled;
+  config_root_ = config_root;
+  metrics_lib_ = metrics_lib;
+
+  upload_interval_ = upload_interval;
+  server_ = server;
+  metrics_file_ = metrics_file;
+
+  // Get ticks per second (HZ) on this system.
+  // Sysconf cannot fail, so no sanity checks are needed.
+  ticks_per_second_ = sysconf(_SC_CLK_TCK);
+
+  daily_active_use_.reset(
+      new PersistentInteger("Platform.DailyUseTime"));
+  version_cumulative_active_use_.reset(
+      new PersistentInteger("Platform.CumulativeDailyUseTime"));
+  version_cumulative_cpu_use_.reset(
+      new PersistentInteger("Platform.CumulativeCpuTime"));
+
+  kernel_crash_interval_.reset(
+      new PersistentInteger("Platform.KernelCrashInterval"));
+  unclean_shutdown_interval_.reset(
+      new PersistentInteger("Platform.UncleanShutdownInterval"));
+  user_crash_interval_.reset(
+      new PersistentInteger("Platform.UserCrashInterval"));
+
+  any_crashes_daily_count_.reset(
+      new PersistentInteger("Platform.AnyCrashesDaily"));
+  any_crashes_weekly_count_.reset(
+      new PersistentInteger("Platform.AnyCrashesWeekly"));
+  user_crashes_daily_count_.reset(
+      new PersistentInteger("Platform.UserCrashesDaily"));
+  user_crashes_weekly_count_.reset(
+      new PersistentInteger("Platform.UserCrashesWeekly"));
+  kernel_crashes_daily_count_.reset(
+      new PersistentInteger("Platform.KernelCrashesDaily"));
+  kernel_crashes_weekly_count_.reset(
+      new PersistentInteger("Platform.KernelCrashesWeekly"));
+  kernel_crashes_version_count_.reset(
+      new PersistentInteger("Platform.KernelCrashesSinceUpdate"));
+  unclean_shutdowns_daily_count_.reset(
+      new PersistentInteger("Platform.UncleanShutdownsDaily"));
+  unclean_shutdowns_weekly_count_.reset(
+      new PersistentInteger("Platform.UncleanShutdownsWeekly"));
+
+  daily_cycle_.reset(new PersistentInteger("daily.cycle"));
+  weekly_cycle_.reset(new PersistentInteger("weekly.cycle"));
+  version_cycle_.reset(new PersistentInteger("version.cycle"));
+
+  scaling_max_freq_path_ = scaling_max_freq_path;
+  cpuinfo_max_freq_path_ = cpuinfo_max_freq_path;
+}
+
+int MetricsDaemon::OnInit() {
+  int return_code = dbus_enabled_ ? chromeos::DBusDaemon::OnInit() :
+      chromeos::Daemon::OnInit();
+  if (return_code != EX_OK)
+    return return_code;
+
+  if (testing_)
+    return EX_OK;
+
+  if (dbus_enabled_) {
+    bus_->AssertOnDBusThread();
+    CHECK(bus_->SetUpAsyncOperations());
+
+    if (bus_->is_connected()) {
+      const std::string match_rule =
+          base::StringPrintf(kCrashReporterMatchRule,
+                             kCrashReporterInterface,
+                             kCrashReporterUserCrashSignal);
+
+      bus_->AddFilterFunction(&MetricsDaemon::MessageFilter, this);
+
+      DBusError error;
+      dbus_error_init(&error);
+      bus_->AddMatch(match_rule, &error);
+
+      if (dbus_error_is_set(&error)) {
+        LOG(ERROR) << "Failed to add match rule \"" << match_rule << "\". Got "
+            << error.name << ": " << error.message;
+        return EX_SOFTWARE;
+      }
+    } else {
+      LOG(ERROR) << "DBus isn't connected.";
+      return EX_UNAVAILABLE;
+    }
+  }
+
+  if (uploader_active_) {
+    upload_service_.reset(
+        new UploadService(new SystemProfileCache(), metrics_lib_, server_));
+    upload_service_->Init(upload_interval_, metrics_file_);
+  }
+
+  return EX_OK;
+}
+
+void MetricsDaemon::OnShutdown(int* return_code) {
+  if (!testing_ && dbus_enabled_ && bus_->is_connected()) {
+    const std::string match_rule =
+        base::StringPrintf(kCrashReporterMatchRule,
+                           kCrashReporterInterface,
+                           kCrashReporterUserCrashSignal);
+
+    bus_->RemoveFilterFunction(&MetricsDaemon::MessageFilter, this);
+
+    DBusError error;
+    dbus_error_init(&error);
+    bus_->RemoveMatch(match_rule, &error);
+
+    if (dbus_error_is_set(&error)) {
+      LOG(ERROR) << "Failed to remove match rule \"" << match_rule << "\". Got "
+          << error.name << ": " << error.message;
+    }
+  }
+  chromeos::DBusDaemon::OnShutdown(return_code);
+}
+
+// static
+DBusHandlerResult MetricsDaemon::MessageFilter(DBusConnection* connection,
+                                               DBusMessage* message,
+                                               void* user_data) {
+  int message_type = dbus_message_get_type(message);
+  if (message_type != DBUS_MESSAGE_TYPE_SIGNAL) {
+    DLOG(WARNING) << "unexpected message type " << message_type;
+    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+  }
+
+  // Signal messages always have interfaces.
+  const std::string interface(dbus_message_get_interface(message));
+  const std::string member(dbus_message_get_member(message));
+  DLOG(INFO) << "Got " << interface << "." << member << " D-Bus signal";
+
+  MetricsDaemon* daemon = static_cast<MetricsDaemon*>(user_data);
+
+  DBusMessageIter iter;
+  dbus_message_iter_init(message, &iter);
+  if (interface == kCrashReporterInterface) {
+    CHECK_EQ(member, kCrashReporterUserCrashSignal);
+    daemon->ProcessUserCrash();
+  } else {
+    // Ignore messages from the bus itself.
+    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+  }
+
+  return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+// One might argue that parts of this should go into
+// chromium/src/base/sys_info_chromeos.c instead, but put it here for now.
+
+TimeDelta MetricsDaemon::GetIncrementalCpuUse() {
+  FilePath proc_stat_path = FilePath(kMetricsProcStatFileName);
+  std::string proc_stat_string;
+  if (!base::ReadFileToString(proc_stat_path, &proc_stat_string)) {
+    LOG(WARNING) << "cannot open " << kMetricsProcStatFileName;
+    return TimeDelta();
+  }
+
+  std::vector<std::string> proc_stat_lines;
+  base::SplitString(proc_stat_string, '\n', &proc_stat_lines);
+  if (proc_stat_lines.empty()) {
+    LOG(WARNING) << "cannot parse " << kMetricsProcStatFileName
+                 << ": " << proc_stat_string;
+    return TimeDelta();
+  }
+  std::vector<std::string> proc_stat_totals;
+  base::SplitStringAlongWhitespace(proc_stat_lines[0], &proc_stat_totals);
+
+  uint64_t user_ticks, user_nice_ticks, system_ticks;
+  if (proc_stat_totals.size() != kMetricsProcStatFirstLineItemsCount ||
+      proc_stat_totals[0] != "cpu" ||
+      !base::StringToUint64(proc_stat_totals[1], &user_ticks) ||
+      !base::StringToUint64(proc_stat_totals[2], &user_nice_ticks) ||
+      !base::StringToUint64(proc_stat_totals[3], &system_ticks)) {
+    LOG(WARNING) << "cannot parse first line: " << proc_stat_lines[0];
+    return TimeDelta(base::TimeDelta::FromSeconds(0));
+  }
+
+  uint64_t total_cpu_use_ticks = user_ticks + user_nice_ticks + system_ticks;
+
+  // Sanity check.
+  if (total_cpu_use_ticks < latest_cpu_use_ticks_) {
+    LOG(WARNING) << "CPU time decreasing from " << latest_cpu_use_ticks_
+                 << " to " << total_cpu_use_ticks;
+    return TimeDelta();
+  }
+
+  uint64_t diff = total_cpu_use_ticks - latest_cpu_use_ticks_;
+  latest_cpu_use_ticks_ = total_cpu_use_ticks;
+  // Use microseconds to avoid significant truncations.
+  return base::TimeDelta::FromMicroseconds(
+      diff * 1000 * 1000 / ticks_per_second_);
+}
+
+void MetricsDaemon::ProcessUserCrash() {
+  // Counts the active time up to now.
+  UpdateStats(TimeTicks::Now(), Time::Now());
+
+  // Reports the active use time since the last crash and resets it.
+  SendCrashIntervalSample(user_crash_interval_);
+
+  any_crashes_daily_count_->Add(1);
+  any_crashes_weekly_count_->Add(1);
+  user_crashes_daily_count_->Add(1);
+  user_crashes_weekly_count_->Add(1);
+}
+
+void MetricsDaemon::ProcessKernelCrash() {
+  // Counts the active time up to now.
+  UpdateStats(TimeTicks::Now(), Time::Now());
+
+  // Reports the active use time since the last crash and resets it.
+  SendCrashIntervalSample(kernel_crash_interval_);
+
+  any_crashes_daily_count_->Add(1);
+  any_crashes_weekly_count_->Add(1);
+  kernel_crashes_daily_count_->Add(1);
+  kernel_crashes_weekly_count_->Add(1);
+
+  kernel_crashes_version_count_->Add(1);
+}
+
+void MetricsDaemon::ProcessUncleanShutdown() {
+  // Counts the active time up to now.
+  UpdateStats(TimeTicks::Now(), Time::Now());
+
+  // Reports the active use time since the last crash and resets it.
+  SendCrashIntervalSample(unclean_shutdown_interval_);
+
+  unclean_shutdowns_daily_count_->Add(1);
+  unclean_shutdowns_weekly_count_->Add(1);
+  any_crashes_daily_count_->Add(1);
+  any_crashes_weekly_count_->Add(1);
+}
+
+bool MetricsDaemon::CheckSystemCrash(const string& crash_file) {
+  FilePath crash_detected(crash_file);
+  if (!base::PathExists(crash_detected))
+    return false;
+
+  // Deletes the crash-detected file so that the daemon doesn't report
+  // another kernel crash in case it's restarted.
+  base::DeleteFile(crash_detected, false);  // not recursive
+  return true;
+}
+
+void MetricsDaemon::StatsReporterInit() {
+  DiskStatsReadStats(&read_sectors_, &write_sectors_);
+  VmStatsReadStats(&vmstats_);
+  // The first time around just run the long stat, so we don't delay boot.
+  stats_state_ = kStatsLong;
+  stats_initial_time_ = GetActiveTime();
+  if (stats_initial_time_ < 0) {
+    LOG(WARNING) << "not collecting disk stats";
+  } else {
+    ScheduleStatsCallback(kMetricStatsLongInterval);
+  }
+}
+
+void MetricsDaemon::ScheduleStatsCallback(int wait) {
+  if (testing_) {
+    return;
+  }
+  base::MessageLoop::current()->PostDelayedTask(FROM_HERE,
+      base::Bind(&MetricsDaemon::StatsCallback, base::Unretained(this)),
+      base::TimeDelta::FromSeconds(wait));
+}
+
+bool MetricsDaemon::VmStatsParseStats(const char* stats,
+                                      struct VmstatRecord* record) {
+  CHECK(stats);
+  CHECK(record);
+  base::StringPairs pairs;
+  base::SplitStringIntoKeyValuePairs(stats, ' ', '\n', &pairs);
+
+  for (base::StringPairs::iterator it = pairs.begin(); it != pairs.end(); ++it) {
+    if (it->first == "pgmajfault" &&
+        !base::StringToUint64(it->second, &record->page_faults_)) {
+      return false;
+    }
+    if (it->first == "pswpin" &&
+        !base::StringToUint64(it->second, &record->swap_in_)) {
+      return false;
+    }
+    if (it->first == "pswpout" &&
+        !base::StringToUint64(it->second, &record->swap_out_)) {
+      return false;
+    }
+  }
+  return true;
+}
+
+bool MetricsDaemon::VmStatsReadStats(struct VmstatRecord* stats) {
+  CHECK(stats);
+  string value_string;
+  if (!base::ReadFileToString(base::FilePath(kVmStatFileName), &value_string)) {
+    LOG(WARNING) << "cannot read " << kVmStatFileName;
+    return false;
+  }
+  return VmStatsParseStats(value_string.c_str(), stats);
+}
+
+bool MetricsDaemon::ReadFreqToInt(const string& sysfs_file_name, int* value) {
+  const FilePath sysfs_path(sysfs_file_name);
+  string value_string;
+  if (!base::ReadFileToString(sysfs_path, &value_string)) {
+    LOG(WARNING) << "cannot read " << sysfs_path.value().c_str();
+    return false;
+  }
+  if (!base::RemoveChars(value_string, "\n", &value_string)) {
+    LOG(WARNING) << "no newline in " << value_string;
+    // Continue even though the lack of newline is suspicious.
+  }
+  if (!base::StringToInt(value_string, value)) {
+    LOG(WARNING) << "cannot convert " << value_string << " to int";
+    return false;
+  }
+  return true;
+}
+
+void MetricsDaemon::SendCpuThrottleMetrics() {
+  // |max_freq| is 0 only the first time through.
+  static int max_freq = 0;
+  if (max_freq == -1)
+    // Give up, as sysfs did not report max_freq correctly.
+    return;
+  if (max_freq == 0 || testing_) {
+    // One-time initialization of max_freq.  (Every time when testing.)
+    if (!ReadFreqToInt(cpuinfo_max_freq_path_, &max_freq)) {
+      max_freq = -1;
+      return;
+    }
+    if (max_freq == 0) {
+      LOG(WARNING) << "sysfs reports 0 max CPU frequency\n";
+      max_freq = -1;
+      return;
+    }
+    if (max_freq % 10000 == 1000) {
+      // Special case: system has turbo mode, and max non-turbo frequency is
+      // max_freq - 1000.  This relies on "normal" (non-turbo) frequencies
+      // being multiples of (at least) 10 MHz.  Although there is no guarantee
+      // of this, it seems a fairly reasonable assumption.  Otherwise we should
+      // read scaling_available_frequencies, sort the frequencies, compare the
+      // two highest ones, and check if they differ by 1000 (kHz) (and that's a
+      // hack too, no telling when it will change).
+      max_freq -= 1000;
+    }
+  }
+  int scaled_freq = 0;
+  if (!ReadFreqToInt(scaling_max_freq_path_, &scaled_freq))
+    return;
+  // Frequencies are in kHz.  If scaled_freq > max_freq, turbo is on, but
+  // scaled_freq is not the actual turbo frequency.  We indicate this situation
+  // with a 101% value.
+  int percent = scaled_freq > max_freq ? 101 : scaled_freq / (max_freq / 100);
+  SendLinearSample(kMetricScaledCpuFrequencyName, percent, 101, 102);
+}
+
+// Collects disk and vm stats alternating over a short and a long interval.
+
+void MetricsDaemon::StatsCallback() {
+  uint64_t read_sectors_now, write_sectors_now;
+  struct VmstatRecord vmstats_now;
+  double time_now = GetActiveTime();
+  double delta_time = time_now - stats_initial_time_;
+  if (testing_) {
+    // Fake the time when testing.
+    delta_time = stats_state_ == kStatsShort ?
+        kMetricStatsShortInterval : kMetricStatsLongInterval;
+  }
+  bool diskstats_success = DiskStatsReadStats(&read_sectors_now,
+                                              &write_sectors_now);
+  int delta_read = read_sectors_now - read_sectors_;
+  int delta_write = write_sectors_now - write_sectors_;
+  int read_sectors_per_second = delta_read / delta_time;
+  int write_sectors_per_second = delta_write / delta_time;
+  bool vmstats_success = VmStatsReadStats(&vmstats_now);
+  uint64_t delta_faults = vmstats_now.page_faults_ - vmstats_.page_faults_;
+  uint64_t delta_swap_in = vmstats_now.swap_in_ - vmstats_.swap_in_;
+  uint64_t delta_swap_out = vmstats_now.swap_out_ - vmstats_.swap_out_;
+  uint64_t page_faults_per_second = delta_faults / delta_time;
+  uint64_t swap_in_per_second = delta_swap_in / delta_time;
+  uint64_t swap_out_per_second = delta_swap_out / delta_time;
+
+  switch (stats_state_) {
+    case kStatsShort:
+      if (diskstats_success) {
+        SendSample(kMetricReadSectorsShortName,
+                   read_sectors_per_second,
+                   1,
+                   kMetricSectorsIOMax,
+                   kMetricSectorsBuckets);
+        SendSample(kMetricWriteSectorsShortName,
+                   write_sectors_per_second,
+                   1,
+                   kMetricSectorsIOMax,
+                   kMetricSectorsBuckets);
+      }
+      if (vmstats_success) {
+        SendSample(kMetricPageFaultsShortName,
+                   page_faults_per_second,
+                   1,
+                   kMetricPageFaultsMax,
+                   kMetricPageFaultsBuckets);
+        SendSample(kMetricSwapInShortName,
+                   swap_in_per_second,
+                   1,
+                   kMetricPageFaultsMax,
+                   kMetricPageFaultsBuckets);
+        SendSample(kMetricSwapOutShortName,
+                   swap_out_per_second,
+                   1,
+                   kMetricPageFaultsMax,
+                   kMetricPageFaultsBuckets);
+      }
+      // Schedule long callback.
+      stats_state_ = kStatsLong;
+      ScheduleStatsCallback(kMetricStatsLongInterval -
+                            kMetricStatsShortInterval);
+      break;
+    case kStatsLong:
+      if (diskstats_success) {
+        SendSample(kMetricReadSectorsLongName,
+                   read_sectors_per_second,
+                   1,
+                   kMetricSectorsIOMax,
+                   kMetricSectorsBuckets);
+        SendSample(kMetricWriteSectorsLongName,
+                   write_sectors_per_second,
+                   1,
+                   kMetricSectorsIOMax,
+                   kMetricSectorsBuckets);
+        // Reset sector counters.
+        read_sectors_ = read_sectors_now;
+        write_sectors_ = write_sectors_now;
+      }
+      if (vmstats_success) {
+        SendSample(kMetricPageFaultsLongName,
+                   page_faults_per_second,
+                   1,
+                   kMetricPageFaultsMax,
+                   kMetricPageFaultsBuckets);
+        SendSample(kMetricSwapInLongName,
+                   swap_in_per_second,
+                   1,
+                   kMetricPageFaultsMax,
+                   kMetricPageFaultsBuckets);
+        SendSample(kMetricSwapOutLongName,
+                   swap_out_per_second,
+                   1,
+                   kMetricPageFaultsMax,
+                   kMetricPageFaultsBuckets);
+
+        vmstats_ = vmstats_now;
+      }
+      SendCpuThrottleMetrics();
+      // Set start time for new cycle.
+      stats_initial_time_ = time_now;
+      // Schedule short callback.
+      stats_state_ = kStatsShort;
+      ScheduleStatsCallback(kMetricStatsShortInterval);
+      break;
+    default:
+      LOG(FATAL) << "Invalid stats state";
+  }
+}
+
+void MetricsDaemon::ScheduleMeminfoCallback(int wait) {
+  if (testing_) {
+    return;
+  }
+  base::TimeDelta waitDelta = base::TimeDelta::FromSeconds(wait);
+  base::MessageLoop::current()->PostDelayedTask(FROM_HERE,
+      base::Bind(&MetricsDaemon::MeminfoCallback, base::Unretained(this),
+                 waitDelta),
+      waitDelta);
+}
+
+void MetricsDaemon::MeminfoCallback(base::TimeDelta wait) {
+  string meminfo_raw;
+  const FilePath meminfo_path(kMeminfoFileName);
+  if (!base::ReadFileToString(meminfo_path, &meminfo_raw)) {
+    LOG(WARNING) << "cannot read " << meminfo_path.value().c_str();
+    return;
+  }
+  // Make both calls even if the first one fails.
+  bool success = ProcessMeminfo(meminfo_raw);
+  bool reschedule =
+      ReportZram(base::FilePath(FILE_PATH_LITERAL("/sys/block/zram0"))) &&
+      success;
+  if (reschedule) {
+    base::MessageLoop::current()->PostDelayedTask(FROM_HERE,
+        base::Bind(&MetricsDaemon::MeminfoCallback, base::Unretained(this),
+                   wait),
+        wait);
+  }
+}
+
+// static
+bool MetricsDaemon::ReadFileToUint64(const base::FilePath& path,
+                                     uint64_t* value) {
+  std::string content;
+  if (!base::ReadFileToString(path, &content)) {
+    PLOG(WARNING) << "cannot read " << path.MaybeAsASCII();
+    return false;
+  }
+  // Remove final newline.
+  base::TrimWhitespaceASCII(content, base::TRIM_TRAILING, &content);
+  if (!base::StringToUint64(content, value)) {
+    LOG(WARNING) << "invalid integer: " << content;
+    return false;
+  }
+  return true;
+}
+
+bool MetricsDaemon::ReportZram(const base::FilePath& zram_dir) {
+  // Data sizes are in bytes.  |zero_pages| is in number of pages.
+  uint64_t compr_data_size, orig_data_size, zero_pages;
+  const size_t page_size = 4096;
+
+  if (!ReadFileToUint64(zram_dir.Append(kComprDataSizeName),
+                        &compr_data_size) ||
+      !ReadFileToUint64(zram_dir.Append(kOrigDataSizeName), &orig_data_size) ||
+      !ReadFileToUint64(zram_dir.Append(kZeroPagesName), &zero_pages)) {
+    return false;
+  }
+
+  // |orig_data_size| does not include zero-filled pages.
+  orig_data_size += zero_pages * page_size;
+
+  const int compr_data_size_mb = compr_data_size >> 20;
+  const int savings_mb = (orig_data_size - compr_data_size) >> 20;
+  const int zero_ratio_percent = zero_pages * page_size * 100 / orig_data_size;
+
+  // Report compressed size in megabytes.  100 MB or less has little impact.
+  SendSample("Platform.ZramCompressedSize", compr_data_size_mb, 100, 4000, 50);
+  SendSample("Platform.ZramSavings", savings_mb, 100, 4000, 50);
+  // The compression ratio is multiplied by 100 for better resolution.  The
+  // ratios of interest are between 1 and 6 (100% and 600% as reported).  We
+  // don't want samples when very little memory is being compressed.
+  if (compr_data_size_mb >= 1) {
+    SendSample("Platform.ZramCompressionRatioPercent",
+               orig_data_size * 100 / compr_data_size, 100, 600, 50);
+  }
+  // The values of interest for zero_pages are between 1MB and 1GB.  The units
+  // are number of pages.
+  SendSample("Platform.ZramZeroPages", zero_pages, 256, 256 * 1024, 50);
+  SendSample("Platform.ZramZeroRatioPercent", zero_ratio_percent, 1, 50, 50);
+
+  return true;
+}
+
+bool MetricsDaemon::ProcessMeminfo(const string& meminfo_raw) {
+  static const MeminfoRecord fields_array[] = {
+    { "MemTotal", "MemTotal" },  // SPECIAL CASE: total system memory
+    { "MemFree", "MemFree" },
+    { "Buffers", "Buffers" },
+    { "Cached", "Cached" },
+    // { "SwapCached", "SwapCached" },
+    { "Active", "Active" },
+    { "Inactive", "Inactive" },
+    { "ActiveAnon", "Active(anon)" },
+    { "InactiveAnon", "Inactive(anon)" },
+    { "ActiveFile" , "Active(file)" },
+    { "InactiveFile", "Inactive(file)" },
+    { "Unevictable", "Unevictable", kMeminfoOp_HistLog },
+    // { "Mlocked", "Mlocked" },
+    { "SwapTotal", "SwapTotal", kMeminfoOp_SwapTotal },
+    { "SwapFree", "SwapFree", kMeminfoOp_SwapFree },
+    // { "Dirty", "Dirty" },
+    // { "Writeback", "Writeback" },
+    { "AnonPages", "AnonPages" },
+    { "Mapped", "Mapped" },
+    { "Shmem", "Shmem", kMeminfoOp_HistLog },
+    { "Slab", "Slab", kMeminfoOp_HistLog },
+    // { "SReclaimable", "SReclaimable" },
+    // { "SUnreclaim", "SUnreclaim" },
+  };
+  vector<MeminfoRecord> fields(fields_array,
+                               fields_array + arraysize(fields_array));
+  if (!FillMeminfo(meminfo_raw, &fields)) {
+    return false;
+  }
+  int total_memory = fields[0].value;
+  if (total_memory == 0) {
+    // this "cannot happen"
+    LOG(WARNING) << "borked meminfo parser";
+    return false;
+  }
+  int swap_total = 0;
+  int swap_free = 0;
+  // Send all fields retrieved, except total memory.
+  for (unsigned int i = 1; i < fields.size(); i++) {
+    string metrics_name = base::StringPrintf("Platform.Meminfo%s",
+                                             fields[i].name);
+    int percent;
+    switch (fields[i].op) {
+      case kMeminfoOp_HistPercent:
+        // report value as percent of total memory
+        percent = fields[i].value * 100 / total_memory;
+        SendLinearSample(metrics_name, percent, 100, 101);
+        break;
+      case kMeminfoOp_HistLog:
+        // report value in kbytes, log scale, 4Gb max
+        SendSample(metrics_name, fields[i].value, 1, 4 * 1000 * 1000, 100);
+        break;
+      case kMeminfoOp_SwapTotal:
+        swap_total = fields[i].value;
+      case kMeminfoOp_SwapFree:
+        swap_free = fields[i].value;
+        break;
+    }
+  }
+  if (swap_total > 0) {
+    int swap_used = swap_total - swap_free;
+    int swap_used_percent = swap_used * 100 / swap_total;
+    SendSample("Platform.MeminfoSwapUsed", swap_used, 1, 8 * 1000 * 1000, 100);
+    SendLinearSample("Platform.MeminfoSwapUsedPercent", swap_used_percent,
+                     100, 101);
+  }
+  return true;
+}
+
+bool MetricsDaemon::FillMeminfo(const string& meminfo_raw,
+                                vector<MeminfoRecord>* fields) {
+  vector<string> lines;
+  unsigned int nlines = Tokenize(meminfo_raw, "\n", &lines);
+
+  // Scan meminfo output and collect field values.  Each field name has to
+  // match a meminfo entry (case insensitive) after removing non-alpha
+  // characters from the entry.
+  unsigned int ifield = 0;
+  for (unsigned int iline = 0;
+       iline < nlines && ifield < fields->size();
+       iline++) {
+    vector<string> tokens;
+    Tokenize(lines[iline], ": ", &tokens);
+    if (strcmp((*fields)[ifield].match, tokens[0].c_str()) == 0) {
+      // Name matches. Parse value and save.
+      if (!base::StringToInt(tokens[1], &(*fields)[ifield].value)) {
+        LOG(WARNING) << "Cound not convert " << tokens[1] << " to int";
+        return false;
+      }
+      ifield++;
+    }
+  }
+  if (ifield < fields->size()) {
+    // End of input reached while scanning.
+    LOG(WARNING) << "cannot find field " << (*fields)[ifield].match
+                 << " and following";
+    return false;
+  }
+  return true;
+}
+
+void MetricsDaemon::ScheduleMemuseCallback(double interval) {
+  if (testing_) {
+    return;
+  }
+  base::MessageLoop::current()->PostDelayedTask(FROM_HERE,
+      base::Bind(&MetricsDaemon::MemuseCallback, base::Unretained(this)),
+      base::TimeDelta::FromSeconds(interval));
+}
+
+void MetricsDaemon::MemuseCallback() {
+  // Since we only care about active time (i.e. uptime minus sleep time) but
+  // the callbacks are driven by real time (uptime), we check if we should
+  // reschedule this callback due to intervening sleep periods.
+  double now = GetActiveTime();
+  // Avoid intervals of less than one second.
+  double remaining_time = ceil(memuse_final_time_ - now);
+  if (remaining_time > 0) {
+    ScheduleMemuseCallback(remaining_time);
+  } else {
+    // Report stats and advance the measurement interval unless there are
+    // errors or we've completed the last interval.
+    if (MemuseCallbackWork() &&
+        memuse_interval_index_ < arraysize(kMemuseIntervals)) {
+      double interval = kMemuseIntervals[memuse_interval_index_++];
+      memuse_final_time_ = now + interval;
+      ScheduleMemuseCallback(interval);
+    }
+  }
+}
+
+bool MetricsDaemon::MemuseCallbackWork() {
+  string meminfo_raw;
+  const FilePath meminfo_path(kMeminfoFileName);
+  if (!base::ReadFileToString(meminfo_path, &meminfo_raw)) {
+    LOG(WARNING) << "cannot read " << meminfo_path.value().c_str();
+    return false;
+  }
+  return ProcessMemuse(meminfo_raw);
+}
+
+bool MetricsDaemon::ProcessMemuse(const string& meminfo_raw) {
+  static const MeminfoRecord fields_array[] = {
+    { "MemTotal", "MemTotal" },  // SPECIAL CASE: total system memory
+    { "ActiveAnon", "Active(anon)" },
+    { "InactiveAnon", "Inactive(anon)" },
+  };
+  vector<MeminfoRecord> fields(fields_array,
+                               fields_array + arraysize(fields_array));
+  if (!FillMeminfo(meminfo_raw, &fields)) {
+    return false;
+  }
+  int total = fields[0].value;
+  int active_anon = fields[1].value;
+  int inactive_anon = fields[2].value;
+  if (total == 0) {
+    // this "cannot happen"
+    LOG(WARNING) << "borked meminfo parser";
+    return false;
+  }
+  string metrics_name = base::StringPrintf("Platform.MemuseAnon%d",
+                                           memuse_interval_index_);
+  SendLinearSample(metrics_name, (active_anon + inactive_anon) * 100 / total,
+                   100, 101);
+  return true;
+}
+
+void MetricsDaemon::SendSample(const string& name, int sample,
+                               int min, int max, int nbuckets) {
+  metrics_lib_->SendToUMA(name, sample, min, max, nbuckets);
+}
+
+void MetricsDaemon::SendKernelCrashesCumulativeCountStats() {
+  // Report the number of crashes for this OS version, but don't clear the
+  // counter.  It is cleared elsewhere on version change.
+  int64_t crashes_count = kernel_crashes_version_count_->Get();
+  SendSample(kernel_crashes_version_count_->Name(),
+             crashes_count,
+             1,                         // value of first bucket
+             500,                       // value of last bucket
+             100);                      // number of buckets
+
+
+  int64_t cpu_use_ms = version_cumulative_cpu_use_->Get();
+  SendSample(version_cumulative_cpu_use_->Name(),
+             cpu_use_ms / 1000,         // stat is in seconds
+             1,                         // device may be used very little...
+             8 * 1000 * 1000,           // ... or a lot (a little over 90 days)
+             100);
+
+  // On the first run after an autoupdate, cpu_use_ms and active_use_seconds
+  // can be zero.  Avoid division by zero.
+  if (cpu_use_ms > 0) {
+    // Send the crash frequency since update in number of crashes per CPU year.
+    SendSample("Logging.KernelCrashesPerCpuYear",
+               crashes_count * kSecondsPerDay * 365 * 1000 / cpu_use_ms,
+               1,
+               1000 * 1000,     // about one crash every 30s of CPU time
+               100);
+  }
+
+  int64_t active_use_seconds = version_cumulative_active_use_->Get();
+  if (active_use_seconds > 0) {
+    SendSample(version_cumulative_active_use_->Name(),
+               active_use_seconds / 1000,  // stat is in seconds
+               1,                          // device may be used very little...
+               8 * 1000 * 1000,            // ... or a lot (about 90 days)
+               100);
+    // Same as above, but per year of active time.
+    SendSample("Logging.KernelCrashesPerActiveYear",
+               crashes_count * kSecondsPerDay * 365 / active_use_seconds,
+               1,
+               1000 * 1000,     // about one crash every 30s of active time
+               100);
+  }
+}
+
+void MetricsDaemon::SendDailyUseSample(
+    const scoped_ptr<PersistentInteger>& use) {
+  SendSample(use->Name(),
+             use->GetAndClear(),
+             1,                        // value of first bucket
+             kSecondsPerDay,           // value of last bucket
+             50);                      // number of buckets
+}
+
+void MetricsDaemon::SendCrashIntervalSample(
+    const scoped_ptr<PersistentInteger>& interval) {
+  SendSample(interval->Name(),
+             interval->GetAndClear(),
+             1,                        // value of first bucket
+             4 * kSecondsPerWeek,      // value of last bucket
+             50);                      // number of buckets
+}
+
+void MetricsDaemon::SendCrashFrequencySample(
+    const scoped_ptr<PersistentInteger>& frequency) {
+  SendSample(frequency->Name(),
+             frequency->GetAndClear(),
+             1,                        // value of first bucket
+             100,                      // value of last bucket
+             50);                      // number of buckets
+}
+
+void MetricsDaemon::SendLinearSample(const string& name, int sample,
+                                     int max, int nbuckets) {
+  // TODO(semenzato): add a proper linear histogram to the Chrome external
+  // metrics API.
+  LOG_IF(FATAL, nbuckets != max + 1) << "unsupported histogram scale";
+  metrics_lib_->SendEnumToUMA(name, sample, max);
+}
+
+void MetricsDaemon::UpdateStats(TimeTicks now_ticks,
+                                Time now_wall_time) {
+  const int elapsed_seconds = (now_ticks - last_update_stats_time_).InSeconds();
+  daily_active_use_->Add(elapsed_seconds);
+  version_cumulative_active_use_->Add(elapsed_seconds);
+  user_crash_interval_->Add(elapsed_seconds);
+  kernel_crash_interval_->Add(elapsed_seconds);
+  version_cumulative_cpu_use_->Add(GetIncrementalCpuUse().InMilliseconds());
+  last_update_stats_time_ = now_ticks;
+
+  const TimeDelta since_epoch = now_wall_time - Time::UnixEpoch();
+  const int day = since_epoch.InDays();
+  const int week = day / 7;
+
+  if (daily_cycle_->Get() != day) {
+    daily_cycle_->Set(day);
+    SendDailyUseSample(daily_active_use_);
+    SendDailyUseSample(version_cumulative_active_use_);
+    SendCrashFrequencySample(any_crashes_daily_count_);
+    SendCrashFrequencySample(user_crashes_daily_count_);
+    SendCrashFrequencySample(kernel_crashes_daily_count_);
+    SendCrashFrequencySample(unclean_shutdowns_daily_count_);
+    SendKernelCrashesCumulativeCountStats();
+  }
+
+  if (weekly_cycle_->Get() != week) {
+    weekly_cycle_->Set(week);
+    SendCrashFrequencySample(any_crashes_weekly_count_);
+    SendCrashFrequencySample(user_crashes_weekly_count_);
+    SendCrashFrequencySample(kernel_crashes_weekly_count_);
+    SendCrashFrequencySample(unclean_shutdowns_weekly_count_);
+  }
+}
+
+void MetricsDaemon::HandleUpdateStatsTimeout() {
+  UpdateStats(TimeTicks::Now(), Time::Now());
+  base::MessageLoop::current()->PostDelayedTask(FROM_HERE,
+      base::Bind(&MetricsDaemon::HandleUpdateStatsTimeout,
+                 base::Unretained(this)),
+      base::TimeDelta::FromMilliseconds(kUpdateStatsIntervalMs));
+}
diff --git a/metricsd/metrics_daemon.h b/metricsd/metrics_daemon.h
new file mode 100644
index 0000000..9180e23
--- /dev/null
+++ b/metricsd/metrics_daemon.h
@@ -0,0 +1,337 @@
+/*
+ * Copyright (C) 2015 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 METRICS_METRICS_DAEMON_H_
+#define METRICS_METRICS_DAEMON_H_
+
+#include <stdint.h>
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include <base/files/file_path.h>
+#include <base/memory/scoped_ptr.h>
+#include <base/time/time.h>
+#include <chromeos/daemons/dbus_daemon.h>
+#include <gtest/gtest_prod.h>  // for FRIEND_TEST
+
+#include "metrics/metrics_library.h"
+#include "persistent_integer.h"
+#include "uploader/upload_service.h"
+
+using chromeos_metrics::PersistentInteger;
+
+class MetricsDaemon : public chromeos::DBusDaemon {
+ public:
+  MetricsDaemon();
+  ~MetricsDaemon();
+
+  // Initializes metrics class variables.
+  void Init(bool testing,
+            bool uploader_active,
+            bool dbus_enabled,
+            MetricsLibraryInterface* metrics_lib,
+            const std::string& cpuinfo_max_freq_path,
+            const std::string& scaling_max_freq_path,
+            const base::TimeDelta& upload_interval,
+            const std::string& server,
+            const std::string& metrics_file,
+            const std::string& config_root);
+
+  // Initializes DBus and MessageLoop variables before running the MessageLoop.
+  int OnInit() override;
+
+  // Clean up data set up in OnInit before shutting down message loop.
+  void OnShutdown(int* return_code) override;
+
+  // Does all the work.
+  int Run() override;
+
+  // Triggers an upload event and exit. (Used to test UploadService)
+  void RunUploaderTest();
+
+ protected:
+  // Used also by the unit tests.
+  static const char kComprDataSizeName[];
+  static const char kOrigDataSizeName[];
+  static const char kZeroPagesName[];
+
+ private:
+  friend class MetricsDaemonTest;
+  FRIEND_TEST(MetricsDaemonTest, CheckSystemCrash);
+  FRIEND_TEST(MetricsDaemonTest, ComputeEpochNoCurrent);
+  FRIEND_TEST(MetricsDaemonTest, ComputeEpochNoLast);
+  FRIEND_TEST(MetricsDaemonTest, GetHistogramPath);
+  FRIEND_TEST(MetricsDaemonTest, IsNewEpoch);
+  FRIEND_TEST(MetricsDaemonTest, MessageFilter);
+  FRIEND_TEST(MetricsDaemonTest, ParseVmStats);
+  FRIEND_TEST(MetricsDaemonTest, ProcessKernelCrash);
+  FRIEND_TEST(MetricsDaemonTest, ProcessMeminfo);
+  FRIEND_TEST(MetricsDaemonTest, ProcessMeminfo2);
+  FRIEND_TEST(MetricsDaemonTest, ProcessUncleanShutdown);
+  FRIEND_TEST(MetricsDaemonTest, ProcessUserCrash);
+  FRIEND_TEST(MetricsDaemonTest, ReportCrashesDailyFrequency);
+  FRIEND_TEST(MetricsDaemonTest, ReadFreqToInt);
+  FRIEND_TEST(MetricsDaemonTest, ReportDiskStats);
+  FRIEND_TEST(MetricsDaemonTest, ReportKernelCrashInterval);
+  FRIEND_TEST(MetricsDaemonTest, ReportUncleanShutdownInterval);
+  FRIEND_TEST(MetricsDaemonTest, ReportUserCrashInterval);
+  FRIEND_TEST(MetricsDaemonTest, SendSample);
+  FRIEND_TEST(MetricsDaemonTest, SendCpuThrottleMetrics);
+  FRIEND_TEST(MetricsDaemonTest, SendZramMetrics);
+
+  // State for disk stats collector callback.
+  enum StatsState {
+    kStatsShort,    // short wait before short interval collection
+    kStatsLong,     // final wait before new collection
+  };
+
+  // Type of scale to use for meminfo histograms.  For most of them we use
+  // percent of total RAM, but for some we use absolute numbers, usually in
+  // megabytes, on a log scale from 0 to 4000, and 0 to 8000 for compressed
+  // swap (since it can be larger than total RAM).
+  enum MeminfoOp {
+    kMeminfoOp_HistPercent = 0,
+    kMeminfoOp_HistLog,
+    kMeminfoOp_SwapTotal,
+    kMeminfoOp_SwapFree,
+  };
+
+  // Record for retrieving and reporting values from /proc/meminfo.
+  struct MeminfoRecord {
+    const char* name;        // print name
+    const char* match;       // string to match in output of /proc/meminfo
+    MeminfoOp op;            // histogram scale selector, or other operator
+    int value;               // value from /proc/meminfo
+  };
+
+  // Record for retrieving and reporting values from /proc/vmstat
+  struct VmstatRecord {
+    uint64_t page_faults_;    // major faults
+    uint64_t swap_in_;        // pages swapped in
+    uint64_t swap_out_;       // pages swapped out
+  };
+
+  // Returns the active time since boot (uptime minus sleep time) in seconds.
+  double GetActiveTime();
+
+  // D-Bus filter callback.
+  static DBusHandlerResult MessageFilter(DBusConnection* connection,
+                                         DBusMessage* message,
+                                         void* user_data);
+
+  // Updates the active use time and logs time between user-space
+  // process crashes.
+  void ProcessUserCrash();
+
+  // Updates the active use time and logs time between kernel crashes.
+  void ProcessKernelCrash();
+
+  // Updates the active use time and logs time between unclean shutdowns.
+  void ProcessUncleanShutdown();
+
+  // Checks if a kernel crash has been detected and returns true if
+  // so.  The method assumes that a kernel crash has happened if
+  // |crash_file| exists.  It removes the file immediately if it
+  // exists, so it must not be called more than once.
+  bool CheckSystemCrash(const std::string& crash_file);
+
+  // Sends a regular (exponential) histogram sample to Chrome for
+  // transport to UMA. See MetricsLibrary::SendToUMA in
+  // metrics_library.h for a description of the arguments.
+  void SendSample(const std::string& name, int sample,
+                  int min, int max, int nbuckets);
+
+  // Sends a linear histogram sample to Chrome for transport to UMA. See
+  // MetricsLibrary::SendToUMA in metrics_library.h for a description of the
+  // arguments.
+  void SendLinearSample(const std::string& name, int sample,
+                        int max, int nbuckets);
+
+  // Sends various cumulative kernel crash-related stats, for instance the
+  // total number of kernel crashes since the last version update.
+  void SendKernelCrashesCumulativeCountStats();
+
+  // Returns the total (system-wide) CPU usage between the time of the most
+  // recent call to this function and now.
+  base::TimeDelta GetIncrementalCpuUse();
+
+  // Sends a sample representing the number of seconds of active use
+  // for a 24-hour period.
+  void SendDailyUseSample(const scoped_ptr<PersistentInteger>& use);
+
+  // Sends a sample representing a time interval between two crashes of the
+  // same type.
+  void SendCrashIntervalSample(const scoped_ptr<PersistentInteger>& interval);
+
+  // Sends a sample representing a frequency of crashes of some type.
+  void SendCrashFrequencySample(const scoped_ptr<PersistentInteger>& frequency);
+
+  // Initializes vm and disk stats reporting.
+  void StatsReporterInit();
+
+  // Schedules a callback for the next vm and disk stats collection.
+  void ScheduleStatsCallback(int wait);
+
+  // Reads cumulative disk statistics from sysfs.  Returns true for success.
+  bool DiskStatsReadStats(uint64_t* read_sectors, uint64_t* write_sectors);
+
+  // Reads cumulative vm statistics from procfs.  Returns true for success.
+  bool VmStatsReadStats(struct VmstatRecord* stats);
+
+  // Parse cumulative vm statistics from a C string.  Returns true for success.
+  bool VmStatsParseStats(const char* stats, struct VmstatRecord* record);
+
+  // Reports disk and vm statistics.
+  void StatsCallback();
+
+  // Schedules meminfo collection callback.
+  void ScheduleMeminfoCallback(int wait);
+
+  // Reports memory statistics.  Reschedules callback on success.
+  void MeminfoCallback(base::TimeDelta wait);
+
+  // Parses content of /proc/meminfo and sends fields of interest to UMA.
+  // Returns false on errors.  |meminfo_raw| contains the content of
+  // /proc/meminfo.
+  bool ProcessMeminfo(const std::string& meminfo_raw);
+
+  // Parses meminfo data from |meminfo_raw|.  |fields| is a vector containing
+  // the fields of interest.  The order of the fields must be the same in which
+  // /proc/meminfo prints them.  The result of parsing fields[i] is placed in
+  // fields[i].value.
+  bool FillMeminfo(const std::string& meminfo_raw,
+                   std::vector<MeminfoRecord>* fields);
+
+  // Schedule a memory use callback in |interval| seconds.
+  void ScheduleMemuseCallback(double interval);
+
+  // Calls MemuseCallbackWork, and possibly schedules next callback, if enough
+  // active time has passed.  Otherwise reschedules itself to simulate active
+  // time callbacks (i.e. wall clock time minus sleep time).
+  void MemuseCallback();
+
+  // Reads /proc/meminfo and sends total anonymous memory usage to UMA.
+  bool MemuseCallbackWork();
+
+  // Parses meminfo data and sends it to UMA.
+  bool ProcessMemuse(const std::string& meminfo_raw);
+
+  // Sends stats for thermal CPU throttling.
+  void SendCpuThrottleMetrics();
+
+  // Reads an integer CPU frequency value from sysfs.
+  bool ReadFreqToInt(const std::string& sysfs_file_name, int* value);
+
+  // Reads the current OS version from /etc/lsb-release and hashes it
+  // to a unsigned 32-bit int.
+  uint32_t GetOsVersionHash();
+
+  // Updates stats, additionally sending them to UMA if enough time has elapsed
+  // since the last report.
+  void UpdateStats(base::TimeTicks now_ticks, base::Time now_wall_time);
+
+  // Invoked periodically by |update_stats_timeout_id_| to call UpdateStats().
+  void HandleUpdateStatsTimeout();
+
+  // Reports zram statistics.
+  bool ReportZram(const base::FilePath& zram_dir);
+
+  // Reads a string from a file and converts it to uint64_t.
+  static bool ReadFileToUint64(const base::FilePath& path, uint64_t* value);
+
+  // VARIABLES
+
+  // Test mode.
+  bool testing_;
+
+  // Whether the uploader is enabled or disabled.
+  bool uploader_active_;
+
+  // Whether or not dbus should be used.
+  // If disabled, we will not collect the frequency of crashes.
+  bool dbus_enabled_;
+
+  // Root of the configuration files to use.
+  std::string config_root_;
+
+  // The metrics library handle.
+  MetricsLibraryInterface* metrics_lib_;
+
+  // The last time that UpdateStats() was called.
+  base::TimeTicks last_update_stats_time_;
+
+  // End time of current memuse stat collection interval.
+  double memuse_final_time_;
+
+  // Selects the wait time for the next memory use callback.
+  unsigned int memuse_interval_index_;
+
+  // Contain the most recent disk and vm cumulative stats.
+  uint64_t read_sectors_;
+  uint64_t write_sectors_;
+  struct VmstatRecord vmstats_;
+
+  StatsState stats_state_;
+  double stats_initial_time_;
+
+  // The system "HZ", or frequency of ticks.  Some system data uses ticks as a
+  // unit, and this is used to convert to standard time units.
+  uint32_t ticks_per_second_;
+  // Used internally by GetIncrementalCpuUse() to return the CPU utilization
+  // between calls.
+  uint64_t latest_cpu_use_ticks_;
+
+  // Persistent values and accumulators for crash statistics.
+  scoped_ptr<PersistentInteger> daily_cycle_;
+  scoped_ptr<PersistentInteger> weekly_cycle_;
+  scoped_ptr<PersistentInteger> version_cycle_;
+
+  // Active use accumulated in a day.
+  scoped_ptr<PersistentInteger> daily_active_use_;
+  // Active use accumulated since the latest version update.
+  scoped_ptr<PersistentInteger> version_cumulative_active_use_;
+
+  // The CPU time accumulator.  This contains the CPU time, in milliseconds,
+  // used by the system since the most recent OS version update.
+  scoped_ptr<PersistentInteger> version_cumulative_cpu_use_;
+
+  scoped_ptr<PersistentInteger> user_crash_interval_;
+  scoped_ptr<PersistentInteger> kernel_crash_interval_;
+  scoped_ptr<PersistentInteger> unclean_shutdown_interval_;
+
+  scoped_ptr<PersistentInteger> any_crashes_daily_count_;
+  scoped_ptr<PersistentInteger> any_crashes_weekly_count_;
+  scoped_ptr<PersistentInteger> user_crashes_daily_count_;
+  scoped_ptr<PersistentInteger> user_crashes_weekly_count_;
+  scoped_ptr<PersistentInteger> kernel_crashes_daily_count_;
+  scoped_ptr<PersistentInteger> kernel_crashes_weekly_count_;
+  scoped_ptr<PersistentInteger> kernel_crashes_version_count_;
+  scoped_ptr<PersistentInteger> unclean_shutdowns_daily_count_;
+  scoped_ptr<PersistentInteger> unclean_shutdowns_weekly_count_;
+
+  std::string scaling_max_freq_path_;
+  std::string cpuinfo_max_freq_path_;
+
+  base::TimeDelta upload_interval_;
+  std::string server_;
+  std::string metrics_file_;
+
+  scoped_ptr<UploadService> upload_service_;
+};
+
+#endif  // METRICS_METRICS_DAEMON_H_
diff --git a/metricsd/metrics_daemon_main.cc b/metricsd/metrics_daemon_main.cc
new file mode 100644
index 0000000..7f9ec43
--- /dev/null
+++ b/metricsd/metrics_daemon_main.cc
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2015 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 <base/at_exit.h>
+#include <base/command_line.h>
+#include <base/logging.h>
+#include <base/strings/string_util.h>
+#include <chromeos/flag_helper.h>
+#include <chromeos/syslog_logging.h>
+
+#include "constants.h"
+#include "metrics_daemon.h"
+
+const char kScalingMaxFreqPath[] =
+    "/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq";
+const char kCpuinfoMaxFreqPath[] =
+    "/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq";
+
+int main(int argc, char** argv) {
+  DEFINE_bool(daemon, true, "run as daemon (use -nodaemon for debugging)");
+
+  // The uploader is disabled by default on ChromeOS as Chrome is responsible
+  // for sending the metrics.
+  DEFINE_bool(uploader, false, "activate the uploader");
+
+  // Upload the metrics once and exit. (used for testing)
+  DEFINE_bool(uploader_test,
+              false,
+              "run the uploader once and exit");
+
+  // Enable dbus.
+  DEFINE_bool(withdbus, true, "Enable dbus");
+
+  // Upload Service flags.
+  DEFINE_int32(upload_interval_secs,
+               1800,
+               "Interval at which metrics_daemon sends the metrics. (needs "
+               "-uploader)");
+  DEFINE_string(server,
+                metrics::kMetricsServer,
+                "Server to upload the metrics to. (needs -uploader)");
+  DEFINE_string(metrics_file,
+                metrics::kMetricsEventsFilePath,
+                "File to use as a proxy for uploading the metrics");
+  DEFINE_string(config_root,
+                "/", "Root of the configuration files (testing only)");
+
+  chromeos::FlagHelper::Init(argc, argv, "Chromium OS Metrics Daemon");
+
+  // Also log to stderr when not running as daemon.
+  chromeos::InitLog(chromeos::kLogToSyslog | chromeos::kLogHeader |
+                    (FLAGS_daemon ? 0 : chromeos::kLogToStderr));
+
+  if (FLAGS_daemon && daemon(0, 0) != 0) {
+    return errno;
+  }
+
+  MetricsLibrary metrics_lib;
+  metrics_lib.Init();
+  MetricsDaemon daemon;
+  daemon.Init(FLAGS_uploader_test,
+              FLAGS_uploader | FLAGS_uploader_test,
+              FLAGS_withdbus,
+              &metrics_lib,
+              kScalingMaxFreqPath,
+              kCpuinfoMaxFreqPath,
+              base::TimeDelta::FromSeconds(FLAGS_upload_interval_secs),
+              FLAGS_server,
+              FLAGS_metrics_file,
+              FLAGS_config_root);
+
+  if (FLAGS_uploader_test) {
+    daemon.RunUploaderTest();
+    return 0;
+  }
+
+  daemon.Run();
+}
diff --git a/metricsd/metrics_daemon_test.cc b/metricsd/metrics_daemon_test.cc
new file mode 100644
index 0000000..0d2229c
--- /dev/null
+++ b/metricsd/metrics_daemon_test.cc
@@ -0,0 +1,315 @@
+/*
+ * Copyright (C) 2015 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 <inttypes.h>
+#include <utime.h>
+
+#include <string>
+#include <vector>
+
+#include <base/at_exit.h>
+#include <base/files/file_util.h>
+#include <base/files/scoped_temp_dir.h>
+#include <base/strings/string_number_conversions.h>
+#include <base/strings/stringprintf.h>
+#include <gtest/gtest.h>
+
+#include "constants.h"
+#include "metrics_daemon.h"
+#include "metrics_library_mock.h"
+#include "persistent_integer_mock.h"
+
+using base::FilePath;
+using base::StringPrintf;
+using base::Time;
+using base::TimeDelta;
+using base::TimeTicks;
+using std::string;
+using std::vector;
+using ::testing::_;
+using ::testing::AnyNumber;
+using ::testing::AtLeast;
+using ::testing::Return;
+using ::testing::StrictMock;
+using chromeos_metrics::PersistentIntegerMock;
+
+static const char kFakeDiskStatsFormat[] =
+    "    1793     1788    %" PRIu64 "   105580    "
+    "    196      175     %" PRIu64 "    30290    "
+    "    0    44060   135850\n";
+static const uint64_t kFakeReadSectors[] = {80000, 100000};
+static const uint64_t kFakeWriteSectors[] = {3000, 4000};
+
+
+class MetricsDaemonTest : public testing::Test {
+ protected:
+  std::string kFakeDiskStats0;
+  std::string kFakeDiskStats1;
+
+  virtual void SetUp() {
+    EXPECT_TRUE(temp_dir_.CreateUniqueTempDir());
+    scaling_max_freq_path_ = temp_dir_.path().Append("scaling_max");
+    cpu_max_freq_path_ = temp_dir_.path().Append("cpu_freq_max");
+    disk_stats_path_ = temp_dir_.path().Append("disk_stats");
+
+    kFakeDiskStats0 = base::StringPrintf(kFakeDiskStatsFormat,
+                                           kFakeReadSectors[0],
+                                           kFakeWriteSectors[0]);
+    kFakeDiskStats1 = base::StringPrintf(kFakeDiskStatsFormat,
+                                           kFakeReadSectors[1],
+                                           kFakeWriteSectors[1]);
+
+    CreateFakeDiskStatsFile(kFakeDiskStats0);
+    CreateUint64ValueFile(cpu_max_freq_path_, 10000000);
+    CreateUint64ValueFile(scaling_max_freq_path_, 10000000);
+
+    chromeos_metrics::PersistentInteger::SetMetricsDirectory(
+        temp_dir_.path().value());
+    daemon_.Init(true,
+                 false,
+                 true,
+                 &metrics_lib_,
+                 scaling_max_freq_path_.value(),
+                 cpu_max_freq_path_.value(),
+                 base::TimeDelta::FromMinutes(30),
+                 metrics::kMetricsServer,
+                 metrics::kMetricsEventsFilePath,
+                 "/");
+  }
+
+  // Adds a metrics library mock expectation that the specified metric
+  // will be generated.
+  void ExpectSample(const std::string& name, int sample) {
+    EXPECT_CALL(metrics_lib_, SendToUMA(name, sample, _, _, _))
+        .Times(1)
+        .WillOnce(Return(true))
+        .RetiresOnSaturation();
+  }
+
+  // Creates a new DBus signal message with zero or more string arguments.
+  // The message can be deallocated through DeleteDBusMessage.
+  //
+  // |path| is the object emitting the signal.
+  // |interface| is the interface the signal is emitted from.
+  // |name| is the name of the signal.
+  // |arg_values| contains the values of the string arguments.
+  DBusMessage* NewDBusSignalString(const string& path,
+                                   const string& interface,
+                                   const string& name,
+                                   const vector<string>& arg_values) {
+    DBusMessage* msg = dbus_message_new_signal(path.c_str(),
+                                               interface.c_str(),
+                                               name.c_str());
+    DBusMessageIter iter;
+    dbus_message_iter_init_append(msg, &iter);
+    for (vector<string>::const_iterator it = arg_values.begin();
+         it != arg_values.end(); ++it) {
+      const char* str_value = it->c_str();
+      dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &str_value);
+    }
+    return msg;
+  }
+
+  // Deallocates the DBus message |msg| previously allocated through
+  // dbus_message_new*.
+  void DeleteDBusMessage(DBusMessage* msg) {
+    dbus_message_unref(msg);
+  }
+
+  // Creates or overwrites an input file containing fake disk stats.
+  void CreateFakeDiskStatsFile(const string& fake_stats) {
+    EXPECT_EQ(base::WriteFile(disk_stats_path_,
+                              fake_stats.data(), fake_stats.size()),
+              fake_stats.size());
+  }
+
+  // Creates or overwrites the file in |path| so that it contains the printable
+  // representation of |value|.
+  void CreateUint64ValueFile(const base::FilePath& path, uint64_t value) {
+    std::string value_string = base::Uint64ToString(value);
+    ASSERT_EQ(value_string.length(),
+              base::WriteFile(path, value_string.c_str(),
+                              value_string.length()));
+  }
+
+  // The MetricsDaemon under test.
+  MetricsDaemon daemon_;
+
+  // Temporary directory used for tests.
+  base::ScopedTempDir temp_dir_;
+
+  // Path for the fake files.
+  base::FilePath scaling_max_freq_path_;
+  base::FilePath cpu_max_freq_path_;
+  base::FilePath disk_stats_path_;
+
+  // Mocks. They are strict mock so that all unexpected
+  // calls are marked as failures.
+  StrictMock<MetricsLibraryMock> metrics_lib_;
+};
+
+TEST_F(MetricsDaemonTest, MessageFilter) {
+  // Ignore calls to SendToUMA.
+  EXPECT_CALL(metrics_lib_, SendToUMA(_, _, _, _, _)).Times(AnyNumber());
+
+  DBusMessage* msg = dbus_message_new(DBUS_MESSAGE_TYPE_METHOD_CALL);
+  DBusHandlerResult res =
+      MetricsDaemon::MessageFilter(/* connection */ nullptr, msg, &daemon_);
+  EXPECT_EQ(DBUS_HANDLER_RESULT_NOT_YET_HANDLED, res);
+  DeleteDBusMessage(msg);
+
+  vector<string> signal_args;
+  msg = NewDBusSignalString("/",
+                            "org.chromium.CrashReporter",
+                            "UserCrash",
+                            signal_args);
+  res = MetricsDaemon::MessageFilter(/* connection */ nullptr, msg, &daemon_);
+  EXPECT_EQ(DBUS_HANDLER_RESULT_HANDLED, res);
+  DeleteDBusMessage(msg);
+
+  signal_args.clear();
+  signal_args.push_back("randomstate");
+  signal_args.push_back("bob");  // arbitrary username
+  msg = NewDBusSignalString("/",
+                            "org.chromium.UnknownService.Manager",
+                            "StateChanged",
+                            signal_args);
+  res = MetricsDaemon::MessageFilter(/* connection */ nullptr, msg, &daemon_);
+  EXPECT_EQ(DBUS_HANDLER_RESULT_NOT_YET_HANDLED, res);
+  DeleteDBusMessage(msg);
+}
+
+TEST_F(MetricsDaemonTest, SendSample) {
+  ExpectSample("Dummy.Metric", 3);
+  daemon_.SendSample("Dummy.Metric", /* sample */ 3,
+                     /* min */ 1, /* max */ 100, /* buckets */ 50);
+}
+
+TEST_F(MetricsDaemonTest, ProcessMeminfo) {
+  string meminfo =
+      "MemTotal:        2000000 kB\nMemFree:          500000 kB\n"
+      "Buffers:         1000000 kB\nCached:           213652 kB\n"
+      "SwapCached:            0 kB\nActive:           133400 kB\n"
+      "Inactive:         183396 kB\nActive(anon):      92984 kB\n"
+      "Inactive(anon):    58860 kB\nActive(file):      40416 kB\n"
+      "Inactive(file):   124536 kB\nUnevictable:           0 kB\n"
+      "Mlocked:               0 kB\nSwapTotal:             0 kB\n"
+      "SwapFree:              0 kB\nDirty:                40 kB\n"
+      "Writeback:             0 kB\nAnonPages:         92652 kB\n"
+      "Mapped:            59716 kB\nShmem:             59196 kB\n"
+      "Slab:              16656 kB\nSReclaimable:       6132 kB\n"
+      "SUnreclaim:        10524 kB\nKernelStack:        1648 kB\n"
+      "PageTables:         2780 kB\nNFS_Unstable:          0 kB\n"
+      "Bounce:                0 kB\nWritebackTmp:          0 kB\n"
+      "CommitLimit:      970656 kB\nCommitted_AS:    1260528 kB\n"
+      "VmallocTotal:     122880 kB\nVmallocUsed:       12144 kB\n"
+      "VmallocChunk:     103824 kB\nDirectMap4k:        9636 kB\n"
+      "DirectMap2M:     1955840 kB\n";
+
+  // All enum calls must report percents.
+  EXPECT_CALL(metrics_lib_, SendEnumToUMA(_, _, 100)).Times(AtLeast(1));
+  // Check that MemFree is correctly computed at 25%.
+  EXPECT_CALL(metrics_lib_, SendEnumToUMA("Platform.MeminfoMemFree", 25, 100))
+      .Times(AtLeast(1));
+  // Check that we call SendToUma at least once (log histogram).
+  EXPECT_CALL(metrics_lib_, SendToUMA(_, _, _, _, _))
+      .Times(AtLeast(1));
+  // Make sure we don't report fields not in the list.
+  EXPECT_CALL(metrics_lib_, SendToUMA("Platform.MeminfoMlocked", _, _, _, _))
+      .Times(0);
+  EXPECT_CALL(metrics_lib_, SendEnumToUMA("Platform.MeminfoMlocked", _, _))
+      .Times(0);
+  EXPECT_TRUE(daemon_.ProcessMeminfo(meminfo));
+}
+
+TEST_F(MetricsDaemonTest, ProcessMeminfo2) {
+  string meminfo = "MemTotal:        2000000 kB\nMemFree:         1000000 kB\n";
+  // Not enough fields.
+  EXPECT_FALSE(daemon_.ProcessMeminfo(meminfo));
+}
+
+TEST_F(MetricsDaemonTest, ParseVmStats) {
+  static char kVmStats[] = "pswpin 1345\npswpout 8896\n"
+    "foo 100\nbar 200\npgmajfault 42\netcetc 300\n";
+  struct MetricsDaemon::VmstatRecord stats;
+  EXPECT_TRUE(daemon_.VmStatsParseStats(kVmStats, &stats));
+  EXPECT_EQ(stats.page_faults_, 42);
+  EXPECT_EQ(stats.swap_in_, 1345);
+  EXPECT_EQ(stats.swap_out_, 8896);
+}
+
+TEST_F(MetricsDaemonTest, ReadFreqToInt) {
+  const int fake_scaled_freq = 1666999;
+  const int fake_max_freq = 2000000;
+  int scaled_freq = 0;
+  int max_freq = 0;
+  CreateUint64ValueFile(scaling_max_freq_path_, fake_scaled_freq);
+  CreateUint64ValueFile(cpu_max_freq_path_, fake_max_freq);
+  EXPECT_TRUE(daemon_.testing_);
+  EXPECT_TRUE(daemon_.ReadFreqToInt(scaling_max_freq_path_.value(),
+                                    &scaled_freq));
+  EXPECT_TRUE(daemon_.ReadFreqToInt(cpu_max_freq_path_.value(), &max_freq));
+  EXPECT_EQ(fake_scaled_freq, scaled_freq);
+  EXPECT_EQ(fake_max_freq, max_freq);
+}
+
+TEST_F(MetricsDaemonTest, SendCpuThrottleMetrics) {
+  CreateUint64ValueFile(cpu_max_freq_path_, 2001000);
+  // Test the 101% and 100% cases.
+  CreateUint64ValueFile(scaling_max_freq_path_, 2001000);
+  EXPECT_TRUE(daemon_.testing_);
+  EXPECT_CALL(metrics_lib_, SendEnumToUMA(_, 101, 101));
+  daemon_.SendCpuThrottleMetrics();
+  CreateUint64ValueFile(scaling_max_freq_path_, 2000000);
+  EXPECT_CALL(metrics_lib_, SendEnumToUMA(_, 100, 101));
+  daemon_.SendCpuThrottleMetrics();
+}
+
+TEST_F(MetricsDaemonTest, SendZramMetrics) {
+  EXPECT_TRUE(daemon_.testing_);
+
+  // |compr_data_size| is the size in bytes of compressed data.
+  const uint64_t compr_data_size = 50 * 1000 * 1000;
+  // The constant '3' is a realistic but random choice.
+  // |orig_data_size| does not include zero pages.
+  const uint64_t orig_data_size = compr_data_size * 3;
+  const uint64_t page_size = 4096;
+  const uint64_t zero_pages = 10 * 1000 * 1000 / page_size;
+
+  CreateUint64ValueFile(
+      temp_dir_.path().Append(MetricsDaemon::kComprDataSizeName),
+      compr_data_size);
+  CreateUint64ValueFile(
+      temp_dir_.path().Append(MetricsDaemon::kOrigDataSizeName),
+      orig_data_size);
+  CreateUint64ValueFile(
+      temp_dir_.path().Append(MetricsDaemon::kZeroPagesName), zero_pages);
+
+  const uint64_t real_orig_size = orig_data_size + zero_pages * page_size;
+  const uint64_t zero_ratio_percent =
+      zero_pages * page_size * 100 / real_orig_size;
+  // Ratio samples are in percents.
+  const uint64_t actual_ratio_sample = real_orig_size * 100 / compr_data_size;
+
+  EXPECT_CALL(metrics_lib_, SendToUMA(_, compr_data_size >> 20, _, _, _));
+  EXPECT_CALL(metrics_lib_,
+              SendToUMA(_, (real_orig_size - compr_data_size) >> 20, _, _, _));
+  EXPECT_CALL(metrics_lib_, SendToUMA(_, actual_ratio_sample, _, _, _));
+  EXPECT_CALL(metrics_lib_, SendToUMA(_, zero_pages, _, _, _));
+  EXPECT_CALL(metrics_lib_, SendToUMA(_, zero_ratio_percent, _, _, _));
+
+  EXPECT_TRUE(daemon_.ReportZram(temp_dir_.path()));
+}
diff --git a/metricsd/metrics_library.cc b/metricsd/metrics_library.cc
new file mode 100644
index 0000000..5687f1b
--- /dev/null
+++ b/metricsd/metrics_library.cc
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2015 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 "metrics/metrics_library.h"
+
+#include <base/logging.h>
+#include <base/strings/stringprintf.h>
+#include <errno.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+
+#include <cstdio>
+#include <cstring>
+
+#include "constants.h"
+#include "serialization/metric_sample.h"
+#include "serialization/serialization_utils.h"
+
+static const char kCrosEventHistogramName[] = "Platform.CrOSEvent";
+static const int kCrosEventHistogramMax = 100;
+
+/* Add new cros events here.
+ *
+ * The index of the event is sent in the message, so please do not
+ * reorder the names.
+ */
+static const char *kCrosEventNames[] = {
+  "ModemManagerCommandSendFailure",  // 0
+  "HwWatchdogReboot",  // 1
+  "Cras.NoCodecsFoundAtBoot",  // 2
+  "Chaps.DatabaseCorrupted",  // 3
+  "Chaps.DatabaseRepairFailure",  // 4
+  "Chaps.DatabaseCreateFailure",  // 5
+  "Attestation.OriginSpecificExhausted",  // 6
+  "SpringPowerSupply.Original.High",  // 7
+  "SpringPowerSupply.Other.High",  // 8
+  "SpringPowerSupply.Original.Low",  // 9
+  "SpringPowerSupply.ChargerIdle",  // 10
+  "TPM.NonZeroDictionaryAttackCounter",  // 11
+  "TPM.EarlyResetDuringCommand",  // 12
+};
+
+time_t MetricsLibrary::cached_enabled_time_ = 0;
+bool MetricsLibrary::cached_enabled_ = false;
+
+MetricsLibrary::MetricsLibrary() : consent_file_(metrics::kConsentFilePath) {}
+MetricsLibrary::~MetricsLibrary() {}
+
+// We take buffer and buffer_size as parameters in order to simplify testing
+// of various alignments of the |device_name| with |buffer_size|.
+bool MetricsLibrary::IsDeviceMounted(const char* device_name,
+                                     const char* mounts_file,
+                                     char* buffer,
+                                     int buffer_size,
+                                     bool* result) {
+  if (buffer == nullptr || buffer_size < 1)
+    return false;
+  int mounts_fd = open(mounts_file, O_RDONLY);
+  if (mounts_fd < 0)
+    return false;
+  // match_offset describes:
+  //   -1 -- not beginning of line
+  //   0..strlen(device_name)-1 -- this offset in device_name is next to match
+  //   strlen(device_name) -- matched full name, just need a space.
+  int match_offset = 0;
+  bool match = false;
+  while (!match) {
+    int read_size = read(mounts_fd, buffer, buffer_size);
+    if (read_size <= 0) {
+      if (errno == -EINTR)
+        continue;
+      break;
+    }
+    for (int i = 0; i < read_size; ++i) {
+      if (buffer[i] == '\n') {
+        match_offset = 0;
+        continue;
+      }
+      if (match_offset < 0) {
+        continue;
+      }
+      if (device_name[match_offset] == '\0') {
+        if (buffer[i] == ' ') {
+          match = true;
+          break;
+        }
+        match_offset = -1;
+        continue;
+      }
+
+      if (buffer[i] == device_name[match_offset]) {
+        ++match_offset;
+      } else {
+        match_offset = -1;
+      }
+    }
+  }
+  close(mounts_fd);
+  *result = match;
+  return true;
+}
+
+bool MetricsLibrary::IsGuestMode() {
+  char buffer[256];
+  bool result = false;
+  if (!IsDeviceMounted("guestfs",
+                       "/proc/mounts",
+                       buffer,
+                       sizeof(buffer),
+                       &result)) {
+    return false;
+  }
+  return result && (access("/var/run/state/logged-in", F_OK) == 0);
+}
+
+bool MetricsLibrary::AreMetricsEnabled() {
+  static struct stat stat_buffer;
+  time_t this_check_time = time(nullptr);
+  if (this_check_time != cached_enabled_time_) {
+    cached_enabled_time_ = this_check_time;
+    cached_enabled_ = stat(consent_file_.c_str(), &stat_buffer) >= 0;
+  }
+  return cached_enabled_;
+}
+
+void MetricsLibrary::Init() {
+  uma_events_file_ = metrics::kMetricsEventsFilePath;
+}
+
+void MetricsLibrary::InitForTest(const std::string& uma_events_file,
+                                 const std::string& consent_file) {
+  uma_events_file_ = uma_events_file;
+  consent_file_ = consent_file;
+}
+
+bool MetricsLibrary::SendToUMA(const std::string& name,
+                               int sample,
+                               int min,
+                               int max,
+                               int nbuckets) {
+  return metrics::SerializationUtils::WriteMetricToFile(
+      *metrics::MetricSample::HistogramSample(name, sample, min, max, nbuckets)
+           .get(),
+      metrics::kMetricsEventsFilePath);
+}
+
+bool MetricsLibrary::SendEnumToUMA(const std::string& name, int sample,
+                                   int max) {
+  return metrics::SerializationUtils::WriteMetricToFile(
+      *metrics::MetricSample::LinearHistogramSample(name, sample, max).get(),
+      metrics::kMetricsEventsFilePath);
+}
+
+bool MetricsLibrary::SendSparseToUMA(const std::string& name, int sample) {
+  return metrics::SerializationUtils::WriteMetricToFile(
+      *metrics::MetricSample::SparseHistogramSample(name, sample).get(),
+      metrics::kMetricsEventsFilePath);
+}
+
+bool MetricsLibrary::SendUserActionToUMA(const std::string& action) {
+  return metrics::SerializationUtils::WriteMetricToFile(
+      *metrics::MetricSample::UserActionSample(action).get(), metrics::kMetricsEventsFilePath);
+}
+
+bool MetricsLibrary::SendCrashToUMA(const char *crash_kind) {
+  return metrics::SerializationUtils::WriteMetricToFile(
+      *metrics::MetricSample::CrashSample(crash_kind).get(), metrics::kMetricsEventsFilePath);
+}
+
+bool MetricsLibrary::SendCrosEventToUMA(const std::string& event) {
+  for (size_t i = 0; i < arraysize(kCrosEventNames); i++) {
+    if (strcmp(event.c_str(), kCrosEventNames[i]) == 0) {
+      return SendEnumToUMA(kCrosEventHistogramName, i, kCrosEventHistogramMax);
+    }
+  }
+  return false;
+}
diff --git a/metricsd/metrics_library_mock.h b/metricsd/metrics_library_mock.h
new file mode 100644
index 0000000..3de87a9
--- /dev/null
+++ b/metricsd/metrics_library_mock.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2015 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 METRICS_METRICS_LIBRARY_MOCK_H_
+#define METRICS_METRICS_LIBRARY_MOCK_H_
+
+#include <string>
+
+#include "metrics/metrics_library.h"
+
+#include <gmock/gmock.h>
+
+class MetricsLibraryMock : public MetricsLibraryInterface {
+ public:
+  bool metrics_enabled_ = true;
+
+  MOCK_METHOD0(Init, void());
+  MOCK_METHOD5(SendToUMA, bool(const std::string& name, int sample,
+                               int min, int max, int nbuckets));
+  MOCK_METHOD3(SendEnumToUMA, bool(const std::string& name, int sample,
+                                   int max));
+  MOCK_METHOD2(SendSparseToUMA, bool(const std::string& name, int sample));
+  MOCK_METHOD1(SendUserActionToUMA, bool(const std::string& action));
+
+  bool AreMetricsEnabled() override {return metrics_enabled_;};
+};
+
+#endif  // METRICS_METRICS_LIBRARY_MOCK_H_
diff --git a/metricsd/metrics_library_test.cc b/metricsd/metrics_library_test.cc
new file mode 100644
index 0000000..7ade6ee
--- /dev/null
+++ b/metricsd/metrics_library_test.cc
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2015 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 <base/files/file_util.h>
+#include <base/files/scoped_temp_dir.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "metrics/c_metrics_library.h"
+#include "metrics/metrics_library.h"
+
+
+class MetricsLibraryTest : public testing::Test {
+ protected:
+  virtual void SetUp() {
+    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+    consent_file_ = temp_dir_.path().Append("consent");
+    uma_events_file_ = temp_dir_.path().Append("events");
+    lib_.InitForTest(uma_events_file_.value(), consent_file_.value());
+    EXPECT_EQ(0, WriteFile(uma_events_file_, "", 0));
+    // Defeat metrics enabled caching between tests.
+    lib_.cached_enabled_time_ = 0;
+  }
+
+  void SetMetricsConsent(bool enabled) {
+    if (enabled) {
+      ASSERT_EQ(base::WriteFile(consent_file_, "", 0), 0);
+    } else {
+      ASSERT_TRUE(base::DeleteFile(consent_file_, false));
+    }
+  }
+
+  void VerifyEnabledCacheHit(bool to_value);
+  void VerifyEnabledCacheEviction(bool to_value);
+
+  MetricsLibrary lib_;
+  base::ScopedTempDir temp_dir_;
+  base::FilePath consent_file_;
+  base::FilePath uma_events_file_;
+};
+
+TEST_F(MetricsLibraryTest, AreMetricsEnabledFalse) {
+  SetMetricsConsent(false);
+  EXPECT_FALSE(lib_.AreMetricsEnabled());
+}
+
+TEST_F(MetricsLibraryTest, AreMetricsEnabledTrue) {
+  SetMetricsConsent(true);
+  EXPECT_TRUE(lib_.AreMetricsEnabled());
+}
+
+void MetricsLibraryTest::VerifyEnabledCacheHit(bool to_value) {
+  // We might step from one second to the next one time, but not 100
+  // times in a row.
+  for (int i = 0; i < 100; ++i) {
+    lib_.cached_enabled_time_ = 0;
+    SetMetricsConsent(to_value);
+    lib_.AreMetricsEnabled();
+    // If we check the metrics status twice in a row, we use the cached value
+    // the second time.
+    SetMetricsConsent(!to_value);
+    if (lib_.AreMetricsEnabled() == to_value)
+      return;
+  }
+  ADD_FAILURE() << "Did not see evidence of caching";
+}
+
+void MetricsLibraryTest::VerifyEnabledCacheEviction(bool to_value) {
+  lib_.cached_enabled_time_ = 0;
+  SetMetricsConsent(!to_value);
+  ASSERT_EQ(!to_value, lib_.AreMetricsEnabled());
+
+  SetMetricsConsent(to_value);
+  // Sleep one second (or cheat to be faster) and check that we are not using
+  // the cached value.
+  --lib_.cached_enabled_time_;
+  ASSERT_EQ(to_value, lib_.AreMetricsEnabled());
+}
+
+TEST_F(MetricsLibraryTest, AreMetricsEnabledCaching) {
+  VerifyEnabledCacheHit(false);
+  VerifyEnabledCacheHit(true);
+  VerifyEnabledCacheEviction(false);
+  VerifyEnabledCacheEviction(true);
+}
diff --git a/metricsd/persistent_integer.cc b/metricsd/persistent_integer.cc
new file mode 100644
index 0000000..e849f00
--- /dev/null
+++ b/metricsd/persistent_integer.cc
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2015 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 "persistent_integer.h"
+
+#include <fcntl.h>
+
+#include <base/logging.h>
+#include <base/posix/eintr_wrapper.h>
+
+#include "constants.h"
+#include "metrics/metrics_library.h"
+
+
+namespace chromeos_metrics {
+
+// Static class member instantiation.
+std::string PersistentInteger::metrics_directory_ = metrics::kMetricsDirectory;
+
+PersistentInteger::PersistentInteger(const std::string& name) :
+      value_(0),
+      version_(kVersion),
+      name_(name),
+      synced_(false) {
+  backing_file_name_ = metrics_directory_ + name_;
+}
+
+PersistentInteger::~PersistentInteger() {}
+
+void PersistentInteger::Set(int64_t value) {
+  value_ = value;
+  Write();
+}
+
+int64_t PersistentInteger::Get() {
+  // If not synced, then read.  If the read fails, it's a good idea to write.
+  if (!synced_ && !Read())
+    Write();
+  return value_;
+}
+
+int64_t PersistentInteger::GetAndClear() {
+  int64_t v = Get();
+  Set(0);
+  return v;
+}
+
+void PersistentInteger::Add(int64_t x) {
+  Set(Get() + x);
+}
+
+void PersistentInteger::Write() {
+  int fd = HANDLE_EINTR(open(backing_file_name_.c_str(),
+                             O_WRONLY | O_CREAT | O_TRUNC,
+                             S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH));
+  PCHECK(fd >= 0) << "cannot open " << backing_file_name_ << " for writing";
+  PCHECK((HANDLE_EINTR(write(fd, &version_, sizeof(version_))) ==
+          sizeof(version_)) &&
+         (HANDLE_EINTR(write(fd, &value_, sizeof(value_))) ==
+          sizeof(value_)))
+      << "cannot write to " << backing_file_name_;
+  close(fd);
+  synced_ = true;
+}
+
+bool PersistentInteger::Read() {
+  int fd = HANDLE_EINTR(open(backing_file_name_.c_str(), O_RDONLY));
+  if (fd < 0) {
+    PLOG(WARNING) << "cannot open " << backing_file_name_ << " for reading";
+    return false;
+  }
+  int32_t version;
+  int64_t value;
+  bool read_succeeded = false;
+  if (HANDLE_EINTR(read(fd, &version, sizeof(version))) == sizeof(version) &&
+      version == version_ &&
+      HANDLE_EINTR(read(fd, &value, sizeof(value))) == sizeof(value)) {
+    value_ = value;
+    read_succeeded = true;
+    synced_ = true;
+  }
+  close(fd);
+  return read_succeeded;
+}
+
+void PersistentInteger::SetMetricsDirectory(const std::string& directory) {
+  metrics_directory_ = directory;
+}
+
+
+}  // namespace chromeos_metrics
diff --git a/metricsd/persistent_integer.h b/metricsd/persistent_integer.h
new file mode 100644
index 0000000..ecef3d1
--- /dev/null
+++ b/metricsd/persistent_integer.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2015 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 METRICS_PERSISTENT_INTEGER_H_
+#define METRICS_PERSISTENT_INTEGER_H_
+
+#include <stdint.h>
+
+#include <string>
+
+namespace chromeos_metrics {
+
+// PersistentIntegers is a named 64-bit integer value backed by a file.
+// The in-memory value acts as a write-through cache of the file value.
+// If the backing file doesn't exist or has bad content, the value is 0.
+
+class PersistentInteger {
+ public:
+  explicit PersistentInteger(const std::string& name);
+
+  // Virtual only because of mock.
+  virtual ~PersistentInteger();
+
+  // Sets the value.  This writes through to the backing file.
+  void Set(int64_t v);
+
+  // Gets the value.  May sync from backing file first.
+  int64_t Get();
+
+  // Returns the name of the object.
+  std::string Name() { return name_; }
+
+  // Convenience function for Get() followed by Set(0).
+  int64_t GetAndClear();
+
+  // Convenience function for v = Get, Set(v + x).
+  // Virtual only because of mock.
+  virtual void Add(int64_t x);
+
+  // Sets the directory path for all persistent integers.
+  // This is used in unittests to change where the counters are stored.
+  static void SetMetricsDirectory(const std::string& directory);
+
+ private:
+  static const int kVersion = 1001;
+
+  // Writes |value_| to the backing file, creating it if necessary.
+  void Write();
+
+  // Reads the value from the backing file, stores it in |value_|, and returns
+  // true if the backing file is valid.  Returns false otherwise, and creates
+  // a valid backing file as a side effect.
+  bool Read();
+
+  int64_t value_;
+  int32_t version_;
+  std::string name_;
+  std::string backing_file_name_;
+  static std::string metrics_directory_;
+  bool synced_;
+};
+
+}  // namespace chromeos_metrics
+
+#endif  // METRICS_PERSISTENT_INTEGER_H_
diff --git a/metricsd/persistent_integer_mock.h b/metricsd/persistent_integer_mock.h
new file mode 100644
index 0000000..acc5389
--- /dev/null
+++ b/metricsd/persistent_integer_mock.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2015 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 METRICS_PERSISTENT_INTEGER_MOCK_H_
+#define METRICS_PERSISTENT_INTEGER_MOCK_H_
+
+#include <string>
+
+#include <gmock/gmock.h>
+
+#include "persistent_integer.h"
+
+namespace chromeos_metrics {
+
+class PersistentIntegerMock : public PersistentInteger {
+ public:
+  explicit PersistentIntegerMock(const std::string& name)
+      : PersistentInteger(name) {}
+    MOCK_METHOD1(Add, void(int64_t count));
+};
+
+}  // namespace chromeos_metrics
+
+#endif  // METRICS_PERSISTENT_INTEGER_MOCK_H_
diff --git a/metricsd/persistent_integer_test.cc b/metricsd/persistent_integer_test.cc
new file mode 100644
index 0000000..5e2067f
--- /dev/null
+++ b/metricsd/persistent_integer_test.cc
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2015 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 <gtest/gtest.h>
+
+#include <base/compiler_specific.h>
+#include <base/files/file_enumerator.h>
+#include <base/files/file_util.h>
+#include <base/files/scoped_temp_dir.h>
+
+#include "persistent_integer.h"
+
+const char kBackingFileName[] = "1.pibakf";
+const char kBackingFilePattern[] = "*.pibakf";
+
+using chromeos_metrics::PersistentInteger;
+
+class PersistentIntegerTest : public testing::Test {
+  void SetUp() override {
+    // Set testing mode.
+    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+    chromeos_metrics::PersistentInteger::SetMetricsDirectory(
+        temp_dir_.path().value());
+  }
+
+  void TearDown() override {
+    // Remove backing files.  The convention is that they all end in ".pibakf".
+    base::FileEnumerator f_enum(base::FilePath("."),
+                                false,
+                                base::FileEnumerator::FILES,
+                                FILE_PATH_LITERAL(kBackingFilePattern));
+    for (base::FilePath name = f_enum.Next();
+         !name.empty();
+         name = f_enum.Next()) {
+      base::DeleteFile(name, false);
+    }
+  }
+
+  base::ScopedTempDir temp_dir_;
+};
+
+TEST_F(PersistentIntegerTest, BasicChecks) {
+  scoped_ptr<PersistentInteger> pi(new PersistentInteger(kBackingFileName));
+
+  // Test initialization.
+  EXPECT_EQ(0, pi->Get());
+  EXPECT_EQ(kBackingFileName, pi->Name());  // boring
+
+  // Test set and add.
+  pi->Set(2);
+  pi->Add(3);
+  EXPECT_EQ(5, pi->Get());
+
+  // Test persistence.
+  pi.reset(new PersistentInteger(kBackingFileName));
+  EXPECT_EQ(5, pi->Get());
+
+  // Test GetAndClear.
+  EXPECT_EQ(5, pi->GetAndClear());
+  EXPECT_EQ(pi->Get(), 0);
+
+  // Another persistence test.
+  pi.reset(new PersistentInteger(kBackingFileName));
+  EXPECT_EQ(0, pi->Get());
+}
diff --git a/metricsd/serialization/metric_sample.cc b/metricsd/serialization/metric_sample.cc
new file mode 100644
index 0000000..76a47c0
--- /dev/null
+++ b/metricsd/serialization/metric_sample.cc
@@ -0,0 +1,209 @@
+/*
+ * Copyright (C) 2015 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 "serialization/metric_sample.h"
+
+#include <string>
+#include <vector>
+
+#include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_split.h"
+#include "base/strings/stringprintf.h"
+
+namespace metrics {
+
+MetricSample::MetricSample(MetricSample::SampleType sample_type,
+                           const std::string& metric_name,
+                           int sample,
+                           int min,
+                           int max,
+                           int bucket_count)
+    : type_(sample_type),
+      name_(metric_name),
+      sample_(sample),
+      min_(min),
+      max_(max),
+      bucket_count_(bucket_count) {
+}
+
+MetricSample::~MetricSample() {
+}
+
+bool MetricSample::IsValid() const {
+  return name().find(' ') == std::string::npos &&
+         name().find('\0') == std::string::npos && !name().empty();
+}
+
+std::string MetricSample::ToString() const {
+  if (type_ == CRASH) {
+    return base::StringPrintf("crash%c%s%c",
+                              '\0',
+                              name().c_str(),
+                              '\0');
+  } else if (type_ == SPARSE_HISTOGRAM) {
+    return base::StringPrintf("sparsehistogram%c%s %d%c",
+                              '\0',
+                              name().c_str(),
+                              sample_,
+                              '\0');
+  } else if (type_ == LINEAR_HISTOGRAM) {
+    return base::StringPrintf("linearhistogram%c%s %d %d%c",
+                              '\0',
+                              name().c_str(),
+                              sample_,
+                              max_,
+                              '\0');
+  } else if (type_ == HISTOGRAM) {
+    return base::StringPrintf("histogram%c%s %d %d %d %d%c",
+                              '\0',
+                              name().c_str(),
+                              sample_,
+                              min_,
+                              max_,
+                              bucket_count_,
+                              '\0');
+  } else {
+    // The type can only be USER_ACTION.
+    CHECK_EQ(type_, USER_ACTION);
+    return base::StringPrintf("useraction%c%s%c",
+                              '\0',
+                              name().c_str(),
+                              '\0');
+  }
+}
+
+int MetricSample::sample() const {
+  CHECK_NE(type_, USER_ACTION);
+  CHECK_NE(type_, CRASH);
+  return sample_;
+}
+
+int MetricSample::min() const {
+  CHECK_EQ(type_, HISTOGRAM);
+  return min_;
+}
+
+int MetricSample::max() const {
+  CHECK_NE(type_, CRASH);
+  CHECK_NE(type_, USER_ACTION);
+  CHECK_NE(type_, SPARSE_HISTOGRAM);
+  return max_;
+}
+
+int MetricSample::bucket_count() const {
+  CHECK_EQ(type_, HISTOGRAM);
+  return bucket_count_;
+}
+
+// static
+scoped_ptr<MetricSample> MetricSample::CrashSample(
+    const std::string& crash_name) {
+  return scoped_ptr<MetricSample>(
+      new MetricSample(CRASH, crash_name, 0, 0, 0, 0));
+}
+
+// static
+scoped_ptr<MetricSample> MetricSample::HistogramSample(
+    const std::string& histogram_name,
+    int sample,
+    int min,
+    int max,
+    int bucket_count) {
+  return scoped_ptr<MetricSample>(new MetricSample(
+      HISTOGRAM, histogram_name, sample, min, max, bucket_count));
+}
+
+// static
+scoped_ptr<MetricSample> MetricSample::ParseHistogram(
+    const std::string& serialized_histogram) {
+  std::vector<std::string> parts;
+  base::SplitString(serialized_histogram, ' ', &parts);
+
+  if (parts.size() != 5)
+    return scoped_ptr<MetricSample>();
+  int sample, min, max, bucket_count;
+  if (parts[0].empty() || !base::StringToInt(parts[1], &sample) ||
+      !base::StringToInt(parts[2], &min) ||
+      !base::StringToInt(parts[3], &max) ||
+      !base::StringToInt(parts[4], &bucket_count)) {
+    return scoped_ptr<MetricSample>();
+  }
+
+  return HistogramSample(parts[0], sample, min, max, bucket_count);
+}
+
+// static
+scoped_ptr<MetricSample> MetricSample::SparseHistogramSample(
+    const std::string& histogram_name,
+    int sample) {
+  return scoped_ptr<MetricSample>(
+      new MetricSample(SPARSE_HISTOGRAM, histogram_name, sample, 0, 0, 0));
+}
+
+// static
+scoped_ptr<MetricSample> MetricSample::ParseSparseHistogram(
+    const std::string& serialized_histogram) {
+  std::vector<std::string> parts;
+  base::SplitString(serialized_histogram, ' ', &parts);
+  if (parts.size() != 2)
+    return scoped_ptr<MetricSample>();
+  int sample;
+  if (parts[0].empty() || !base::StringToInt(parts[1], &sample))
+    return scoped_ptr<MetricSample>();
+
+  return SparseHistogramSample(parts[0], sample);
+}
+
+// static
+scoped_ptr<MetricSample> MetricSample::LinearHistogramSample(
+    const std::string& histogram_name,
+    int sample,
+    int max) {
+  return scoped_ptr<MetricSample>(
+      new MetricSample(LINEAR_HISTOGRAM, histogram_name, sample, 0, max, 0));
+}
+
+// static
+scoped_ptr<MetricSample> MetricSample::ParseLinearHistogram(
+    const std::string& serialized_histogram) {
+  std::vector<std::string> parts;
+  int sample, max;
+  base::SplitString(serialized_histogram, ' ', &parts);
+  if (parts.size() != 3)
+    return scoped_ptr<MetricSample>();
+  if (parts[0].empty() || !base::StringToInt(parts[1], &sample) ||
+      !base::StringToInt(parts[2], &max)) {
+    return scoped_ptr<MetricSample>();
+  }
+
+  return LinearHistogramSample(parts[0], sample, max);
+}
+
+// static
+scoped_ptr<MetricSample> MetricSample::UserActionSample(
+    const std::string& action_name) {
+  return scoped_ptr<MetricSample>(
+      new MetricSample(USER_ACTION, action_name, 0, 0, 0, 0));
+}
+
+bool MetricSample::IsEqual(const MetricSample& metric) {
+  return type_ == metric.type_ && name_ == metric.name_ &&
+         sample_ == metric.sample_ && min_ == metric.min_ &&
+         max_ == metric.max_ && bucket_count_ == metric.bucket_count_;
+}
+
+}  // namespace metrics
diff --git a/metricsd/serialization/metric_sample.h b/metricsd/serialization/metric_sample.h
new file mode 100644
index 0000000..5a4e4ae
--- /dev/null
+++ b/metricsd/serialization/metric_sample.h
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2015 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 METRICS_SERIALIZATION_METRIC_SAMPLE_H_
+#define METRICS_SERIALIZATION_METRIC_SAMPLE_H_
+
+#include <string>
+
+#include "base/gtest_prod_util.h"
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+
+namespace metrics {
+
+// This class is used by libmetrics (ChromeOS) to serialize
+// and deserialize measurements to send them to a metrics sending service.
+// It is meant to be a simple container with serialization functions.
+class MetricSample {
+ public:
+  // Types of metric sample used.
+  enum SampleType {
+    CRASH,
+    HISTOGRAM,
+    LINEAR_HISTOGRAM,
+    SPARSE_HISTOGRAM,
+    USER_ACTION
+  };
+
+  ~MetricSample();
+
+  // Returns true if the sample is valid (can be serialized without ambiguity).
+  //
+  // This function should be used to filter bad samples before serializing them.
+  bool IsValid() const;
+
+  // Getters for type and name. All types of metrics have these so we do not
+  // need to check the type.
+  SampleType type() const { return type_; }
+  const std::string& name() const { return name_; }
+
+  // Getters for sample, min, max, bucket_count.
+  // Check the metric type to make sure the request make sense. (ex: a crash
+  // sample does not have a bucket_count so we crash if we call bucket_count()
+  // on it.)
+  int sample() const;
+  int min() const;
+  int max() const;
+  int bucket_count() const;
+
+  // Returns a serialized version of the sample.
+  //
+  // The serialized message for each type is:
+  // crash: crash\0|name_|\0
+  // user action: useraction\0|name_|\0
+  // histogram: histogram\0|name_| |sample_| |min_| |max_| |bucket_count_|\0
+  // sparsehistogram: sparsehistogram\0|name_| |sample_|\0
+  // linearhistogram: linearhistogram\0|name_| |sample_| |max_|\0
+  std::string ToString() const;
+
+  // Builds a crash sample.
+  static scoped_ptr<MetricSample> CrashSample(const std::string& crash_name);
+
+  // Builds a histogram sample.
+  static scoped_ptr<MetricSample> HistogramSample(
+      const std::string& histogram_name,
+      int sample,
+      int min,
+      int max,
+      int bucket_count);
+  // Deserializes a histogram sample.
+  static scoped_ptr<MetricSample> ParseHistogram(const std::string& serialized);
+
+  // Builds a sparse histogram sample.
+  static scoped_ptr<MetricSample> SparseHistogramSample(
+      const std::string& histogram_name,
+      int sample);
+  // Deserializes a sparse histogram sample.
+  static scoped_ptr<MetricSample> ParseSparseHistogram(
+      const std::string& serialized);
+
+  // Builds a linear histogram sample.
+  static scoped_ptr<MetricSample> LinearHistogramSample(
+      const std::string& histogram_name,
+      int sample,
+      int max);
+  // Deserializes a linear histogram sample.
+  static scoped_ptr<MetricSample> ParseLinearHistogram(
+      const std::string& serialized);
+
+  // Builds a user action sample.
+  static scoped_ptr<MetricSample> UserActionSample(
+      const std::string& action_name);
+
+  // Returns true if sample and this object represent the same sample (type,
+  // name, sample, min, max, bucket_count match).
+  bool IsEqual(const MetricSample& sample);
+
+ private:
+  MetricSample(SampleType sample_type,
+               const std::string& metric_name,
+               const int sample,
+               const int min,
+               const int max,
+               const int bucket_count);
+
+  const SampleType type_;
+  const std::string name_;
+  const int sample_;
+  const int min_;
+  const int max_;
+  const int bucket_count_;
+
+  DISALLOW_COPY_AND_ASSIGN(MetricSample);
+};
+
+}  // namespace metrics
+
+#endif  // METRICS_SERIALIZATION_METRIC_SAMPLE_H_
diff --git a/metricsd/serialization/serialization_utils.cc b/metricsd/serialization/serialization_utils.cc
new file mode 100644
index 0000000..6dd8258
--- /dev/null
+++ b/metricsd/serialization/serialization_utils.cc
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2015 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 "serialization/serialization_utils.h"
+
+#include <sys/file.h>
+
+#include <string>
+#include <vector>
+
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_file.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/scoped_vector.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
+#include "serialization/metric_sample.h"
+
+#define READ_WRITE_ALL_FILE_FLAGS \
+  (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)
+
+namespace metrics {
+namespace {
+
+// Reads the next message from |file_descriptor| into |message|.
+//
+// |message| will be set to the empty string if no message could be read (EOF)
+// or the message was badly constructed.
+//
+// Returns false if no message can be read from this file anymore (EOF or
+// unrecoverable error).
+bool ReadMessage(int fd, std::string* message) {
+  CHECK(message);
+
+  int result;
+  int32_t message_size;
+  const int32_t message_hdr_size = sizeof(message_size);
+  // The file containing the metrics do not leave the device so the writer and
+  // the reader will always have the same endianness.
+  result = HANDLE_EINTR(read(fd, &message_size, sizeof(message_size)));
+  if (result < 0) {
+    DPLOG(ERROR) << "reading metrics message header";
+    return false;
+  }
+  if (result == 0) {
+    // This indicates a normal EOF.
+    return false;
+  }
+  if (result < message_hdr_size) {
+    DLOG(ERROR) << "bad read size " << result << ", expecting "
+                << sizeof(message_size);
+    return false;
+  }
+
+  // kMessageMaxLength applies to the entire message: the 4-byte
+  // length field and the content.
+  if (message_size > SerializationUtils::kMessageMaxLength) {
+    DLOG(ERROR) << "message too long : " << message_size;
+    if (HANDLE_EINTR(lseek(fd, message_size - 4, SEEK_CUR)) == -1) {
+      DLOG(ERROR) << "error while skipping message. abort";
+      return false;
+    }
+    // Badly formatted message was skipped. Treat the badly formatted sample as
+    // an empty sample.
+    message->clear();
+    return true;
+  }
+
+  if (message_size < message_hdr_size) {
+    DLOG(ERROR) << "message too short : " << message_size;
+    return false;
+  }
+
+  message_size -= message_hdr_size;  // The message size includes itself.
+  char buffer[SerializationUtils::kMessageMaxLength];
+  if (!base::ReadFromFD(fd, buffer, message_size)) {
+    DPLOG(ERROR) << "reading metrics message body";
+    return false;
+  }
+  *message = std::string(buffer, message_size);
+  return true;
+}
+
+}  // namespace
+
+scoped_ptr<MetricSample> SerializationUtils::ParseSample(
+    const std::string& sample) {
+  if (sample.empty())
+    return scoped_ptr<MetricSample>();
+
+  std::vector<std::string> parts;
+  base::SplitString(sample, '\0', &parts);
+  // We should have two null terminated strings so split should produce
+  // three chunks.
+  if (parts.size() != 3) {
+    DLOG(ERROR) << "splitting message on \\0 produced " << parts.size()
+                << " parts (expected 3)";
+    return scoped_ptr<MetricSample>();
+  }
+  const std::string& name = parts[0];
+  const std::string& value = parts[1];
+
+  if (base::LowerCaseEqualsASCII(name, "crash")) {
+    return MetricSample::CrashSample(value);
+  } else if (base::LowerCaseEqualsASCII(name, "histogram")) {
+    return MetricSample::ParseHistogram(value);
+  } else if (base::LowerCaseEqualsASCII(name, "linearhistogram")) {
+    return MetricSample::ParseLinearHistogram(value);
+  } else if (base::LowerCaseEqualsASCII(name, "sparsehistogram")) {
+    return MetricSample::ParseSparseHistogram(value);
+  } else if (base::LowerCaseEqualsASCII(name, "useraction")) {
+    return MetricSample::UserActionSample(value);
+  } else {
+    DLOG(ERROR) << "invalid event type: " << name << ", value: " << value;
+  }
+  return scoped_ptr<MetricSample>();
+}
+
+void SerializationUtils::ReadAndTruncateMetricsFromFile(
+    const std::string& filename,
+    ScopedVector<MetricSample>* metrics) {
+  struct stat stat_buf;
+  int result;
+
+  result = stat(filename.c_str(), &stat_buf);
+  if (result < 0) {
+    if (errno != ENOENT)
+      DPLOG(ERROR) << filename << ": bad metrics file stat";
+
+    // Nothing to collect---try later.
+    return;
+  }
+  if (stat_buf.st_size == 0) {
+    // Also nothing to collect.
+    return;
+  }
+  base::ScopedFD fd(open(filename.c_str(), O_RDWR));
+  if (fd.get() < 0) {
+    DPLOG(ERROR) << filename << ": cannot open";
+    return;
+  }
+  result = flock(fd.get(), LOCK_EX);
+  if (result < 0) {
+    DPLOG(ERROR) << filename << ": cannot lock";
+    return;
+  }
+
+  // This processes all messages in the log. When all messages are
+  // read and processed, or an error occurs, truncate the file to zero size.
+  for (;;) {
+    std::string message;
+
+    if (!ReadMessage(fd.get(), &message))
+      break;
+
+    scoped_ptr<MetricSample> sample = ParseSample(message);
+    if (sample)
+      metrics->push_back(sample.release());
+  }
+
+  result = ftruncate(fd.get(), 0);
+  if (result < 0)
+    DPLOG(ERROR) << "truncate metrics log";
+
+  result = flock(fd.get(), LOCK_UN);
+  if (result < 0)
+    DPLOG(ERROR) << "unlock metrics log";
+}
+
+bool SerializationUtils::WriteMetricToFile(const MetricSample& sample,
+                                           const std::string& filename) {
+  if (!sample.IsValid())
+    return false;
+
+  base::ScopedFD file_descriptor(open(filename.c_str(),
+                                      O_WRONLY | O_APPEND | O_CREAT,
+                                      READ_WRITE_ALL_FILE_FLAGS));
+
+  if (file_descriptor.get() < 0) {
+    DPLOG(ERROR) << filename << ": cannot open";
+    return false;
+  }
+
+  fchmod(file_descriptor.get(), READ_WRITE_ALL_FILE_FLAGS);
+  // Grab a lock to avoid chrome truncating the file
+  // underneath us. Keep the file locked as briefly as possible.
+  // Freeing file_descriptor will close the file and and remove the lock.
+  if (HANDLE_EINTR(flock(file_descriptor.get(), LOCK_EX)) < 0) {
+    DPLOG(ERROR) << filename << ": cannot lock";
+    return false;
+  }
+
+  std::string msg = sample.ToString();
+  int32 size = msg.length() + sizeof(int32);
+  if (size > kMessageMaxLength) {
+    DLOG(ERROR) << "cannot write message: too long";
+    return false;
+  }
+
+  // The file containing the metrics samples will only be read by programs on
+  // the same device so we do not check endianness.
+  if (!base::WriteFileDescriptor(file_descriptor.get(),
+                                 reinterpret_cast<char*>(&size),
+                                 sizeof(size))) {
+    DPLOG(ERROR) << "error writing message length";
+    return false;
+  }
+
+  if (!base::WriteFileDescriptor(
+          file_descriptor.get(), msg.c_str(), msg.size())) {
+    DPLOG(ERROR) << "error writing message";
+    return false;
+  }
+
+  return true;
+}
+
+}  // namespace metrics
diff --git a/metricsd/serialization/serialization_utils.h b/metricsd/serialization/serialization_utils.h
new file mode 100644
index 0000000..67d4675
--- /dev/null
+++ b/metricsd/serialization/serialization_utils.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2015 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 METRICS_SERIALIZATION_SERIALIZATION_UTILS_H_
+#define METRICS_SERIALIZATION_SERIALIZATION_UTILS_H_
+
+#include <string>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/scoped_vector.h"
+
+namespace metrics {
+
+class MetricSample;
+
+// Metrics helpers to serialize and deserialize metrics collected by
+// ChromeOS.
+namespace SerializationUtils {
+
+// Deserializes a sample passed as a string and return a sample.
+// The return value will either be a scoped_ptr to a Metric sample (if the
+// deserialization was successful) or a NULL scoped_ptr.
+scoped_ptr<MetricSample> ParseSample(const std::string& sample);
+
+// Reads all samples from a file and truncate the file when done.
+void ReadAndTruncateMetricsFromFile(const std::string& filename,
+                                    ScopedVector<MetricSample>* metrics);
+
+// Serializes a sample and write it to filename.
+// The format for the message is:
+//  message_size, serialized_message
+// where
+//  * message_size is the total length of the message (message_size +
+//    serialized_message) on 4 bytes
+//  * serialized_message is the serialized version of sample (using ToString)
+//
+//  NB: the file will never leave the device so message_size will be written
+//  with the architecture's endianness.
+bool WriteMetricToFile(const MetricSample& sample, const std::string& filename);
+
+// Maximum length of a serialized message
+static const int kMessageMaxLength = 1024;
+
+}  // namespace SerializationUtils
+}  // namespace metrics
+
+#endif  // METRICS_SERIALIZATION_SERIALIZATION_UTILS_H_
diff --git a/metricsd/serialization/serialization_utils_unittest.cc b/metricsd/serialization/serialization_utils_unittest.cc
new file mode 100644
index 0000000..7a572de
--- /dev/null
+++ b/metricsd/serialization/serialization_utils_unittest.cc
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2015 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 "serialization/serialization_utils.h"
+
+#include <base/files/file_util.h>
+#include <base/files/scoped_temp_dir.h>
+#include <base/logging.h>
+#include <base/strings/stringprintf.h>
+#include <gtest/gtest.h>
+
+#include "serialization/metric_sample.h"
+
+namespace metrics {
+namespace {
+
+class SerializationUtilsTest : public testing::Test {
+ protected:
+  SerializationUtilsTest() {
+    bool success = temporary_dir.CreateUniqueTempDir();
+    if (success) {
+      base::FilePath dir_path = temporary_dir.path();
+      filename = dir_path.value() + "chromeossampletest";
+      filepath = base::FilePath(filename);
+    }
+  }
+
+  void SetUp() override { base::DeleteFile(filepath, false); }
+
+  void TestSerialization(MetricSample* sample) {
+    std::string serialized(sample->ToString());
+    ASSERT_EQ('\0', serialized[serialized.length() - 1]);
+    scoped_ptr<MetricSample> deserialized =
+        SerializationUtils::ParseSample(serialized);
+    ASSERT_TRUE(deserialized);
+    EXPECT_TRUE(sample->IsEqual(*deserialized.get()));
+  }
+
+  std::string filename;
+  base::ScopedTempDir temporary_dir;
+  base::FilePath filepath;
+};
+
+TEST_F(SerializationUtilsTest, CrashSerializeTest) {
+  TestSerialization(MetricSample::CrashSample("test").get());
+}
+
+TEST_F(SerializationUtilsTest, HistogramSerializeTest) {
+  TestSerialization(
+      MetricSample::HistogramSample("myhist", 13, 1, 100, 10).get());
+}
+
+TEST_F(SerializationUtilsTest, LinearSerializeTest) {
+  TestSerialization(
+      MetricSample::LinearHistogramSample("linearhist", 12, 30).get());
+}
+
+TEST_F(SerializationUtilsTest, SparseSerializeTest) {
+  TestSerialization(MetricSample::SparseHistogramSample("mysparse", 30).get());
+}
+
+TEST_F(SerializationUtilsTest, UserActionSerializeTest) {
+  TestSerialization(MetricSample::UserActionSample("myaction").get());
+}
+
+TEST_F(SerializationUtilsTest, IllegalNameAreFilteredTest) {
+  scoped_ptr<MetricSample> sample1 =
+      MetricSample::SparseHistogramSample("no space", 10);
+  scoped_ptr<MetricSample> sample2 = MetricSample::LinearHistogramSample(
+      base::StringPrintf("here%cbhe", '\0'), 1, 3);
+
+  EXPECT_FALSE(SerializationUtils::WriteMetricToFile(*sample1.get(), filename));
+  EXPECT_FALSE(SerializationUtils::WriteMetricToFile(*sample2.get(), filename));
+  int64 size = 0;
+
+  ASSERT_TRUE(!PathExists(filepath) || base::GetFileSize(filepath, &size));
+
+  EXPECT_EQ(0, size);
+}
+
+TEST_F(SerializationUtilsTest, BadInputIsCaughtTest) {
+  std::string input(
+      base::StringPrintf("sparsehistogram%cname foo%c", '\0', '\0'));
+  EXPECT_EQ(NULL, MetricSample::ParseSparseHistogram(input).get());
+}
+
+TEST_F(SerializationUtilsTest, MessageSeparatedByZero) {
+  scoped_ptr<MetricSample> crash = MetricSample::CrashSample("mycrash");
+
+  SerializationUtils::WriteMetricToFile(*crash.get(), filename);
+  int64 size = 0;
+  ASSERT_TRUE(base::GetFileSize(filepath, &size));
+  // 4 bytes for the size
+  // 5 bytes for crash
+  // 7 bytes for mycrash
+  // 2 bytes for the \0
+  // -> total of 18
+  EXPECT_EQ(size, 18);
+}
+
+TEST_F(SerializationUtilsTest, MessagesTooLongAreDiscardedTest) {
+  // Creates a message that is bigger than the maximum allowed size.
+  // As we are adding extra character (crash, \0s, etc), if the name is
+  // kMessageMaxLength long, it will be too long.
+  std::string name(SerializationUtils::kMessageMaxLength, 'c');
+
+  scoped_ptr<MetricSample> crash = MetricSample::CrashSample(name);
+  EXPECT_FALSE(SerializationUtils::WriteMetricToFile(*crash.get(), filename));
+  int64 size = 0;
+  ASSERT_TRUE(base::GetFileSize(filepath, &size));
+  EXPECT_EQ(0, size);
+}
+
+TEST_F(SerializationUtilsTest, ReadLongMessageTest) {
+  base::File test_file(filepath,
+                       base::File::FLAG_OPEN_ALWAYS | base::File::FLAG_APPEND);
+  std::string message(SerializationUtils::kMessageMaxLength + 1, 'c');
+
+  int32 message_size = message.length() + sizeof(int32);
+  test_file.WriteAtCurrentPos(reinterpret_cast<const char*>(&message_size),
+                              sizeof(message_size));
+  test_file.WriteAtCurrentPos(message.c_str(), message.length());
+  test_file.Close();
+
+  scoped_ptr<MetricSample> crash = MetricSample::CrashSample("test");
+  SerializationUtils::WriteMetricToFile(*crash.get(), filename);
+
+  ScopedVector<MetricSample> samples;
+  SerializationUtils::ReadAndTruncateMetricsFromFile(filename, &samples);
+  ASSERT_EQ(size_t(1), samples.size());
+  ASSERT_TRUE(samples[0] != NULL);
+  EXPECT_TRUE(crash->IsEqual(*samples[0]));
+}
+
+TEST_F(SerializationUtilsTest, WriteReadTest) {
+  scoped_ptr<MetricSample> hist =
+      MetricSample::HistogramSample("myhist", 1, 2, 3, 4);
+  scoped_ptr<MetricSample> crash = MetricSample::CrashSample("mycrash");
+  scoped_ptr<MetricSample> lhist =
+      MetricSample::LinearHistogramSample("linear", 1, 10);
+  scoped_ptr<MetricSample> shist =
+      MetricSample::SparseHistogramSample("mysparse", 30);
+  scoped_ptr<MetricSample> action = MetricSample::UserActionSample("myaction");
+
+  SerializationUtils::WriteMetricToFile(*hist.get(), filename);
+  SerializationUtils::WriteMetricToFile(*crash.get(), filename);
+  SerializationUtils::WriteMetricToFile(*lhist.get(), filename);
+  SerializationUtils::WriteMetricToFile(*shist.get(), filename);
+  SerializationUtils::WriteMetricToFile(*action.get(), filename);
+  ScopedVector<MetricSample> vect;
+  SerializationUtils::ReadAndTruncateMetricsFromFile(filename, &vect);
+  ASSERT_EQ(vect.size(), size_t(5));
+  for (int i = 0; i < 5; i++) {
+    ASSERT_TRUE(vect[0] != NULL);
+  }
+  EXPECT_TRUE(hist->IsEqual(*vect[0]));
+  EXPECT_TRUE(crash->IsEqual(*vect[1]));
+  EXPECT_TRUE(lhist->IsEqual(*vect[2]));
+  EXPECT_TRUE(shist->IsEqual(*vect[3]));
+  EXPECT_TRUE(action->IsEqual(*vect[4]));
+
+  int64 size = 0;
+  ASSERT_TRUE(base::GetFileSize(filepath, &size));
+  ASSERT_EQ(0, size);
+}
+
+}  // namespace
+}  // namespace metrics
diff --git a/metricsd/timer.cc b/metricsd/timer.cc
new file mode 100644
index 0000000..7b00cc0
--- /dev/null
+++ b/metricsd/timer.cc
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2015 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 "timer.h"
+
+#include <string>
+
+#include <base/memory/scoped_ptr.h>
+
+#include "metrics/metrics_library.h"
+
+namespace chromeos_metrics {
+
+base::TimeTicks ClockWrapper::GetCurrentTime() const {
+  return base::TimeTicks::Now();
+}
+
+Timer::Timer()
+    : timer_state_(kTimerStopped),
+      clock_wrapper_(new ClockWrapper()) {}
+
+bool Timer::Start() {
+  elapsed_time_ = base::TimeDelta();  // Sets elapsed_time_ to zero.
+  start_time_ = clock_wrapper_->GetCurrentTime();
+  timer_state_ = kTimerRunning;
+  return true;
+}
+
+bool Timer::Stop() {
+  if (timer_state_ == kTimerStopped)
+    return false;
+  if (timer_state_ == kTimerRunning)
+    elapsed_time_ += clock_wrapper_->GetCurrentTime() - start_time_;
+  timer_state_ = kTimerStopped;
+  return true;
+}
+
+bool Timer::Pause() {
+  switch (timer_state_) {
+    case kTimerStopped:
+      if (!Start())
+        return false;
+      timer_state_ = kTimerPaused;
+      return true;
+    case kTimerRunning:
+      timer_state_ = kTimerPaused;
+      elapsed_time_ += clock_wrapper_->GetCurrentTime() - start_time_;
+      return true;
+    default:
+      return false;
+  }
+}
+
+bool Timer::Resume() {
+  switch (timer_state_) {
+    case kTimerStopped:
+      return Start();
+    case kTimerPaused:
+      start_time_ = clock_wrapper_->GetCurrentTime();
+      timer_state_ = kTimerRunning;
+      return true;
+    default:
+      return false;
+  }
+}
+
+bool Timer::Reset() {
+  elapsed_time_ = base::TimeDelta();  // Sets elapsed_time_ to zero.
+  timer_state_ = kTimerStopped;
+  return true;
+}
+
+bool Timer::HasStarted() const {
+  return timer_state_ != kTimerStopped;
+}
+
+bool Timer::GetElapsedTime(base::TimeDelta* elapsed_time) const {
+  if (start_time_.is_null() || !elapsed_time)
+    return false;
+  *elapsed_time = elapsed_time_;
+  if (timer_state_ == kTimerRunning) {
+    *elapsed_time += clock_wrapper_->GetCurrentTime() - start_time_;
+  }
+  return true;
+}
+
+// static
+MetricsLibraryInterface* TimerReporter::metrics_lib_ = nullptr;
+
+TimerReporter::TimerReporter(const std::string& histogram_name, int min,
+                             int max, int num_buckets)
+    : histogram_name_(histogram_name),
+      min_(min),
+      max_(max),
+      num_buckets_(num_buckets) {}
+
+bool TimerReporter::ReportMilliseconds() const {
+  base::TimeDelta elapsed_time;
+  if (!metrics_lib_ || !GetElapsedTime(&elapsed_time)) return false;
+  return metrics_lib_->SendToUMA(histogram_name_,
+                                 elapsed_time.InMilliseconds(),
+                                 min_,
+                                 max_,
+                                 num_buckets_);
+}
+
+}  // namespace chromeos_metrics
diff --git a/metricsd/timer.h b/metricsd/timer.h
new file mode 100644
index 0000000..b36ffff
--- /dev/null
+++ b/metricsd/timer.h
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+// Timer - class that provides timer tracking.
+
+#ifndef METRICS_TIMER_H_
+#define METRICS_TIMER_H_
+
+#include <string>
+
+#include <base/macros.h>
+#include <base/memory/scoped_ptr.h>
+#include <base/time/time.h>
+#include <gtest/gtest_prod.h>  // for FRIEND_TEST
+
+class MetricsLibraryInterface;
+
+namespace chromeos_metrics {
+
+class TimerInterface {
+ public:
+  virtual ~TimerInterface() {}
+
+  virtual bool Start() = 0;
+  virtual bool Stop() = 0;
+  virtual bool Reset() = 0;
+  virtual bool HasStarted() const = 0;
+};
+
+// Wrapper for calls to the system clock.
+class ClockWrapper {
+ public:
+  ClockWrapper() {}
+  virtual ~ClockWrapper() {}
+
+  // Returns the current time from the system.
+  virtual base::TimeTicks GetCurrentTime() const;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ClockWrapper);
+};
+
+// Implements a Timer.
+class Timer : public TimerInterface {
+ public:
+  Timer();
+  virtual ~Timer() {}
+
+  // Starts the timer. If a timer is already running, also resets current
+  // timer. Always returns true.
+  virtual bool Start();
+
+  // Stops the timer and calculates the total time elapsed between now and when
+  // Start() was called. Note that this method needs a prior call to Start().
+  // Otherwise, it fails (returns false).
+  virtual bool Stop();
+
+  // Pauses a timer.  If the timer is stopped, this call starts the timer in
+  // the paused state. Fails (returns false) if the timer is already paused.
+  virtual bool Pause();
+
+  // Restarts a paused timer (or starts a stopped timer). This method fails
+  // (returns false) if the timer is already running; otherwise, returns true.
+  virtual bool Resume();
+
+  // Resets the timer, erasing the current duration being tracked. Always
+  // returns true.
+  virtual bool Reset();
+
+  // Returns whether the timer has started or not.
+  virtual bool HasStarted() const;
+
+  // Stores the current elapsed time in |elapsed_time|. If timer is stopped,
+  // stores the elapsed time from when Stop() was last called. Otherwise,
+  // calculates and stores the elapsed time since the last Start().
+  // Returns false if the timer was never Start()'ed or if called with a null
+  // pointer argument.
+  virtual bool GetElapsedTime(base::TimeDelta* elapsed_time) const;
+
+ private:
+  enum TimerState { kTimerStopped, kTimerRunning, kTimerPaused };
+  friend class TimerTest;
+  friend class TimerReporterTest;
+  FRIEND_TEST(TimerReporterTest, StartStopReport);
+  FRIEND_TEST(TimerTest, InvalidElapsedTime);
+  FRIEND_TEST(TimerTest, InvalidStop);
+  FRIEND_TEST(TimerTest, PauseResumeStop);
+  FRIEND_TEST(TimerTest, PauseStartStopResume);
+  FRIEND_TEST(TimerTest, PauseStop);
+  FRIEND_TEST(TimerTest, Reset);
+  FRIEND_TEST(TimerTest, ReStart);
+  FRIEND_TEST(TimerTest, ResumeStartStopPause);
+  FRIEND_TEST(TimerTest, SeparatedTimers);
+  FRIEND_TEST(TimerTest, StartPauseResumePauseResumeStop);
+  FRIEND_TEST(TimerTest, StartPauseResumePauseStop);
+  FRIEND_TEST(TimerTest, StartPauseResumeStop);
+  FRIEND_TEST(TimerTest, StartPauseStop);
+  FRIEND_TEST(TimerTest, StartResumeStop);
+  FRIEND_TEST(TimerTest, StartStop);
+
+  // Elapsed time of the last use of the timer.
+  base::TimeDelta elapsed_time_;
+
+  // Starting time value.
+  base::TimeTicks start_time_;
+
+  // Whether the timer is running, stopped, or paused.
+  TimerState timer_state_;
+
+  // Wrapper for the calls to the system clock.
+  scoped_ptr<ClockWrapper> clock_wrapper_;
+
+  DISALLOW_COPY_AND_ASSIGN(Timer);
+};
+
+// Extends the Timer class to report the elapsed time in milliseconds through
+// the UMA metrics library.
+class TimerReporter : public Timer {
+ public:
+  // Initializes the timer by providing a |histogram_name| to report to with
+  // |min|, |max| and |num_buckets| attributes for the histogram.
+  TimerReporter(const std::string& histogram_name, int min, int max,
+                int num_buckets);
+  virtual ~TimerReporter() {}
+
+  // Sets the metrics library used by all instances of this class.
+  static void set_metrics_lib(MetricsLibraryInterface* metrics_lib) {
+    metrics_lib_ = metrics_lib;
+  }
+
+  // Reports the current duration to UMA, in milliseconds. Returns false if
+  // there is nothing to report, e.g. a metrics library is not set.
+  virtual bool ReportMilliseconds() const;
+
+  // Accessor methods.
+  const std::string& histogram_name() const { return histogram_name_; }
+  int min() const { return min_; }
+  int max() const { return max_; }
+  int num_buckets() const { return num_buckets_; }
+
+ private:
+  friend class TimerReporterTest;
+  FRIEND_TEST(TimerReporterTest, StartStopReport);
+  FRIEND_TEST(TimerReporterTest, InvalidReport);
+
+  static MetricsLibraryInterface* metrics_lib_;
+  std::string histogram_name_;
+  int min_;
+  int max_;
+  int num_buckets_;
+
+  DISALLOW_COPY_AND_ASSIGN(TimerReporter);
+};
+
+}  // namespace chromeos_metrics
+
+#endif  // METRICS_TIMER_H_
diff --git a/metricsd/timer_mock.h b/metricsd/timer_mock.h
new file mode 100644
index 0000000..8c9e8d8
--- /dev/null
+++ b/metricsd/timer_mock.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2015 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 METRICS_TIMER_MOCK_H_
+#define METRICS_TIMER_MOCK_H_
+
+#include <string>
+
+#include <gmock/gmock.h>
+
+#include "timer.h"
+
+namespace chromeos_metrics {
+
+class TimerMock : public Timer {
+ public:
+  MOCK_METHOD0(Start, bool());
+  MOCK_METHOD0(Stop, bool());
+  MOCK_METHOD0(Reset, bool());
+  MOCK_CONST_METHOD0(HasStarted, bool());
+  MOCK_CONST_METHOD1(GetElapsedTime, bool(base::TimeDelta* elapsed_time));
+};
+
+class TimerReporterMock : public TimerReporter {
+ public:
+  TimerReporterMock() : TimerReporter("", 0, 0, 0) {}
+  MOCK_METHOD0(Start, bool());
+  MOCK_METHOD0(Stop, bool());
+  MOCK_METHOD0(Reset, bool());
+  MOCK_CONST_METHOD0(HasStarted, bool());
+  MOCK_CONST_METHOD1(GetElapsedTime, bool(base::TimeDelta* elapsed_time));
+  MOCK_CONST_METHOD0(ReportMilliseconds, bool());
+  MOCK_CONST_METHOD0(histogram_name, std::string&());
+  MOCK_CONST_METHOD0(min, int());
+  MOCK_CONST_METHOD0(max, int());
+  MOCK_CONST_METHOD0(num_buckets, int());
+};
+
+class ClockWrapperMock : public ClockWrapper {
+ public:
+  MOCK_CONST_METHOD0(GetCurrentTime, base::TimeTicks());
+};
+
+}  // namespace chromeos_metrics
+
+#endif  // METRICS_TIMER_MOCK_H_
diff --git a/metricsd/timer_test.cc b/metricsd/timer_test.cc
new file mode 100644
index 0000000..ab027d4
--- /dev/null
+++ b/metricsd/timer_test.cc
@@ -0,0 +1,464 @@
+/*
+ * Copyright (C) 2015 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 <stdint.h>
+
+#include <base/memory/scoped_ptr.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "metrics_library_mock.h"
+#include "timer.h"
+#include "timer_mock.h"
+
+using ::testing::_;
+using ::testing::Return;
+
+namespace chromeos_metrics {
+
+namespace {
+const int64_t kStime1MSec = 1400;
+const int64_t kEtime1MSec = 3000;
+const int64_t kDelta1MSec = 1600;
+
+const int64_t kStime2MSec = 4200;
+const int64_t kEtime2MSec = 5000;
+const int64_t kDelta2MSec = 800;
+
+const int64_t kStime3MSec = 6600;
+const int64_t kEtime3MSec = 6800;
+const int64_t kDelta3MSec = 200;
+}  // namespace
+
+class TimerTest : public testing::Test {
+ public:
+  TimerTest() : clock_wrapper_mock_(new ClockWrapperMock()) {}
+
+ protected:
+  virtual void SetUp() {
+    EXPECT_EQ(Timer::kTimerStopped, timer_.timer_state_);
+    stime += base::TimeDelta::FromMilliseconds(kStime1MSec);
+    etime += base::TimeDelta::FromMilliseconds(kEtime1MSec);
+    stime2 += base::TimeDelta::FromMilliseconds(kStime2MSec);
+    etime2 += base::TimeDelta::FromMilliseconds(kEtime2MSec);
+    stime3 += base::TimeDelta::FromMilliseconds(kStime3MSec);
+    etime3 += base::TimeDelta::FromMilliseconds(kEtime3MSec);
+  }
+
+  virtual void TearDown() {}
+
+  Timer timer_;
+  scoped_ptr<ClockWrapperMock> clock_wrapper_mock_;
+  base::TimeTicks stime, etime, stime2, etime2, stime3, etime3;
+};
+
+TEST_F(TimerTest, StartStop) {
+  EXPECT_CALL(*clock_wrapper_mock_, GetCurrentTime())
+      .WillOnce(Return(stime))
+      .WillOnce(Return(etime));
+  timer_.clock_wrapper_.reset(clock_wrapper_mock_.release());
+  ASSERT_TRUE(timer_.Start());
+  ASSERT_TRUE(timer_.start_time_ == stime);
+  ASSERT_TRUE(timer_.HasStarted());
+  ASSERT_TRUE(timer_.Stop());
+  ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(), kDelta1MSec);
+
+  base::TimeDelta elapsed_time;
+  ASSERT_TRUE(timer_.GetElapsedTime(&elapsed_time));
+  ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(),
+            elapsed_time.InMilliseconds());
+
+  ASSERT_FALSE(timer_.HasStarted());
+}
+
+TEST_F(TimerTest, ReStart) {
+  EXPECT_CALL(*clock_wrapper_mock_, GetCurrentTime())
+      .WillOnce(Return(stime))
+      .WillOnce(Return(etime));
+  timer_.clock_wrapper_.reset(clock_wrapper_mock_.release());
+  timer_.Start();
+  base::TimeTicks buffer = timer_.start_time_;
+  timer_.Start();
+  ASSERT_FALSE(timer_.start_time_ == buffer);
+}
+
+TEST_F(TimerTest, Reset) {
+  EXPECT_CALL(*clock_wrapper_mock_, GetCurrentTime())
+      .WillOnce(Return(stime));
+  timer_.clock_wrapper_.reset(clock_wrapper_mock_.release());
+  timer_.Start();
+  ASSERT_TRUE(timer_.Reset());
+  ASSERT_FALSE(timer_.HasStarted());
+}
+
+TEST_F(TimerTest, SeparatedTimers) {
+  EXPECT_CALL(*clock_wrapper_mock_, GetCurrentTime())
+      .WillOnce(Return(stime))
+      .WillOnce(Return(etime))
+      .WillOnce(Return(stime2))
+      .WillOnce(Return(etime2));
+  timer_.clock_wrapper_.reset(clock_wrapper_mock_.release());
+  ASSERT_TRUE(timer_.Start());
+  ASSERT_TRUE(timer_.Stop());
+  ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(), kDelta1MSec);
+  ASSERT_TRUE(timer_.Start());
+  ASSERT_TRUE(timer_.start_time_ == stime2);
+  ASSERT_TRUE(timer_.Stop());
+  ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(), kDelta2MSec);
+  ASSERT_FALSE(timer_.HasStarted());
+
+  base::TimeDelta elapsed_time;
+  ASSERT_TRUE(timer_.GetElapsedTime(&elapsed_time));
+  ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(),
+            elapsed_time.InMilliseconds());
+}
+
+TEST_F(TimerTest, InvalidStop) {
+  EXPECT_CALL(*clock_wrapper_mock_, GetCurrentTime())
+      .WillOnce(Return(stime))
+      .WillOnce(Return(etime));
+  timer_.clock_wrapper_.reset(clock_wrapper_mock_.release());
+  ASSERT_FALSE(timer_.Stop());
+  // Now we try it again, but after a valid start/stop.
+  timer_.Start();
+  timer_.Stop();
+  base::TimeDelta elapsed_time = timer_.elapsed_time_;
+  ASSERT_FALSE(timer_.Stop());
+  ASSERT_TRUE(elapsed_time == timer_.elapsed_time_);
+}
+
+TEST_F(TimerTest, InvalidElapsedTime) {
+  base::TimeDelta elapsed_time;
+  ASSERT_FALSE(timer_.GetElapsedTime(&elapsed_time));
+}
+
+TEST_F(TimerTest, PauseStartStopResume) {
+  EXPECT_CALL(*clock_wrapper_mock_, GetCurrentTime())
+      .WillOnce(Return(stime))
+      .WillOnce(Return(stime2))
+      .WillOnce(Return(etime2))
+      .WillOnce(Return(stime3))
+      .WillOnce(Return(etime3));
+  timer_.clock_wrapper_.reset(clock_wrapper_mock_.release());
+  ASSERT_TRUE(timer_.Pause());  // Starts timer paused.
+  ASSERT_TRUE(timer_.start_time_ == stime);
+  ASSERT_TRUE(timer_.HasStarted());
+
+  ASSERT_TRUE(timer_.Start());  // Restarts timer.
+  ASSERT_TRUE(timer_.start_time_ == stime2);
+  ASSERT_TRUE(timer_.HasStarted());
+
+  ASSERT_TRUE(timer_.Stop());
+  ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(), kDelta2MSec);
+  ASSERT_FALSE(timer_.HasStarted());
+  base::TimeDelta elapsed_time;
+  ASSERT_TRUE(timer_.GetElapsedTime(&elapsed_time));
+  ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(),
+            elapsed_time.InMilliseconds());
+
+  ASSERT_TRUE(timer_.Resume());
+  ASSERT_TRUE(timer_.HasStarted());
+  ASSERT_TRUE(timer_.GetElapsedTime(&elapsed_time));
+  ASSERT_EQ(kDelta3MSec, elapsed_time.InMilliseconds());
+}
+
+TEST_F(TimerTest, ResumeStartStopPause) {
+  EXPECT_CALL(*clock_wrapper_mock_, GetCurrentTime())
+      .WillOnce(Return(stime))
+      .WillOnce(Return(stime2))
+      .WillOnce(Return(etime2))
+      .WillOnce(Return(stime3));
+  timer_.clock_wrapper_.reset(clock_wrapper_mock_.release());
+  ASSERT_TRUE(timer_.Resume());
+  ASSERT_TRUE(timer_.start_time_ == stime);
+  ASSERT_TRUE(timer_.HasStarted());
+
+  ASSERT_TRUE(timer_.Start());
+  ASSERT_TRUE(timer_.start_time_ == stime2);
+  ASSERT_TRUE(timer_.HasStarted());
+
+  ASSERT_TRUE(timer_.Stop());
+  ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(), kDelta2MSec);
+  ASSERT_FALSE(timer_.HasStarted());
+  base::TimeDelta elapsed_time;
+  ASSERT_TRUE(timer_.GetElapsedTime(&elapsed_time));
+  ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(),
+            elapsed_time.InMilliseconds());
+
+  ASSERT_TRUE(timer_.Pause());
+  ASSERT_TRUE(timer_.HasStarted());
+  ASSERT_TRUE(timer_.GetElapsedTime(&elapsed_time));
+  ASSERT_EQ(0, elapsed_time.InMilliseconds());
+}
+
+TEST_F(TimerTest, StartResumeStop) {
+  EXPECT_CALL(*clock_wrapper_mock_, GetCurrentTime())
+      .WillOnce(Return(stime))
+      .WillOnce(Return(etime));
+  timer_.clock_wrapper_.reset(clock_wrapper_mock_.release());
+  ASSERT_TRUE(timer_.Start());
+  ASSERT_TRUE(timer_.start_time_ == stime);
+  ASSERT_TRUE(timer_.HasStarted());
+
+  ASSERT_FALSE(timer_.Resume());
+  ASSERT_TRUE(timer_.start_time_ == stime);
+  ASSERT_TRUE(timer_.HasStarted());
+
+  ASSERT_TRUE(timer_.Stop());
+  ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(), kDelta1MSec);
+  ASSERT_FALSE(timer_.HasStarted());
+  base::TimeDelta elapsed_time;
+  ASSERT_TRUE(timer_.GetElapsedTime(&elapsed_time));
+  ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(),
+            elapsed_time.InMilliseconds());
+}
+
+TEST_F(TimerTest, StartPauseStop) {
+  EXPECT_CALL(*clock_wrapper_mock_, GetCurrentTime())
+      .WillOnce(Return(stime))
+      .WillOnce(Return(etime));
+  timer_.clock_wrapper_.reset(clock_wrapper_mock_.release());
+  ASSERT_TRUE(timer_.Start());
+  ASSERT_TRUE(timer_.start_time_ == stime);
+  ASSERT_TRUE(timer_.HasStarted());
+
+  ASSERT_TRUE(timer_.Pause());
+  ASSERT_TRUE(timer_.HasStarted());
+  ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(), kDelta1MSec);
+  base::TimeDelta elapsed_time;
+  ASSERT_TRUE(timer_.GetElapsedTime(&elapsed_time));
+  ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(),
+            elapsed_time.InMilliseconds());
+
+  ASSERT_TRUE(timer_.Stop());
+  ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(), kDelta1MSec);
+  ASSERT_FALSE(timer_.HasStarted());
+  ASSERT_TRUE(timer_.GetElapsedTime(&elapsed_time));
+  ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(),
+            elapsed_time.InMilliseconds());
+}
+
+TEST_F(TimerTest, StartPauseResumeStop) {
+  EXPECT_CALL(*clock_wrapper_mock_, GetCurrentTime())
+      .WillOnce(Return(stime))
+      .WillOnce(Return(etime))
+      .WillOnce(Return(stime2))
+      .WillOnce(Return(etime2));
+  timer_.clock_wrapper_.reset(clock_wrapper_mock_.release());
+  ASSERT_TRUE(timer_.Start());
+  ASSERT_TRUE(timer_.start_time_ == stime);
+  ASSERT_TRUE(timer_.HasStarted());
+
+  ASSERT_TRUE(timer_.Pause());
+  ASSERT_TRUE(timer_.HasStarted());
+  ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(), kDelta1MSec);
+  base::TimeDelta elapsed_time;
+  ASSERT_TRUE(timer_.GetElapsedTime(&elapsed_time));
+  ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(),
+            elapsed_time.InMilliseconds());
+
+  ASSERT_TRUE(timer_.Resume());
+  ASSERT_TRUE(timer_.HasStarted());
+
+  ASSERT_TRUE(timer_.Stop());
+  ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(), kDelta1MSec + kDelta2MSec);
+  ASSERT_FALSE(timer_.HasStarted());
+  ASSERT_TRUE(timer_.GetElapsedTime(&elapsed_time));
+  ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(),
+            elapsed_time.InMilliseconds());
+}
+
+TEST_F(TimerTest, PauseStop) {
+  EXPECT_CALL(*clock_wrapper_mock_, GetCurrentTime())
+      .WillOnce(Return(stime));
+  timer_.clock_wrapper_.reset(clock_wrapper_mock_.release());
+  ASSERT_TRUE(timer_.Pause());
+  ASSERT_TRUE(timer_.start_time_ == stime);
+  ASSERT_TRUE(timer_.HasStarted());
+  ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(), 0);
+
+  ASSERT_TRUE(timer_.Stop());
+  ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(), 0);
+  ASSERT_FALSE(timer_.HasStarted());
+  base::TimeDelta elapsed_time;
+  ASSERT_TRUE(timer_.GetElapsedTime(&elapsed_time));
+  ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(),
+            elapsed_time.InMilliseconds());
+}
+
+TEST_F(TimerTest, PauseResumeStop) {
+  EXPECT_CALL(*clock_wrapper_mock_, GetCurrentTime())
+      .WillOnce(Return(stime))
+      .WillOnce(Return(stime2))
+      .WillOnce(Return(etime2));
+  timer_.clock_wrapper_.reset(clock_wrapper_mock_.release());
+  ASSERT_TRUE(timer_.Pause());
+  ASSERT_TRUE(timer_.start_time_ == stime);
+  ASSERT_TRUE(timer_.HasStarted());
+
+  ASSERT_TRUE(timer_.Resume());
+  ASSERT_TRUE(timer_.HasStarted());
+
+  ASSERT_TRUE(timer_.Stop());
+  ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(), kDelta2MSec);
+  ASSERT_FALSE(timer_.HasStarted());
+  base::TimeDelta elapsed_time;
+  ASSERT_TRUE(timer_.GetElapsedTime(&elapsed_time));
+  ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(),
+            elapsed_time.InMilliseconds());
+}
+
+TEST_F(TimerTest, StartPauseResumePauseStop) {
+  EXPECT_CALL(*clock_wrapper_mock_, GetCurrentTime())
+      .WillOnce(Return(stime))
+      .WillOnce(Return(etime))
+      .WillOnce(Return(stime2))
+      .WillOnce(Return(stime3))
+      .WillOnce(Return(etime3));
+  timer_.clock_wrapper_.reset(clock_wrapper_mock_.release());
+  ASSERT_TRUE(timer_.Start());
+  ASSERT_TRUE(timer_.start_time_ == stime);
+  ASSERT_TRUE(timer_.HasStarted());
+
+  ASSERT_TRUE(timer_.Pause());
+  ASSERT_TRUE(timer_.HasStarted());
+  ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(), kDelta1MSec);
+  base::TimeDelta elapsed_time;
+  ASSERT_TRUE(timer_.GetElapsedTime(&elapsed_time));
+  ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(),
+            elapsed_time.InMilliseconds());
+
+  ASSERT_TRUE(timer_.Resume());
+  ASSERT_TRUE(timer_.HasStarted());
+  // Make sure GetElapsedTime works while we're running.
+  ASSERT_TRUE(timer_.GetElapsedTime(&elapsed_time));
+  ASSERT_EQ(kDelta1MSec + kStime3MSec - kStime2MSec,
+            elapsed_time.InMilliseconds());
+
+  ASSERT_TRUE(timer_.Pause());
+  ASSERT_TRUE(timer_.HasStarted());
+  ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(),
+            kDelta1MSec + kEtime3MSec - kStime2MSec);
+  ASSERT_TRUE(timer_.GetElapsedTime(&elapsed_time));
+  ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(),
+            elapsed_time.InMilliseconds());
+
+  ASSERT_TRUE(timer_.Stop());
+  ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(),
+            kDelta1MSec + kEtime3MSec - kStime2MSec);
+  ASSERT_FALSE(timer_.HasStarted());
+  ASSERT_TRUE(timer_.GetElapsedTime(&elapsed_time));
+  ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(),
+            elapsed_time.InMilliseconds());
+}
+
+TEST_F(TimerTest, StartPauseResumePauseResumeStop) {
+  EXPECT_CALL(*clock_wrapper_mock_, GetCurrentTime())
+      .WillOnce(Return(stime))
+      .WillOnce(Return(etime))
+      .WillOnce(Return(stime2))
+      .WillOnce(Return(etime2))
+      .WillOnce(Return(stime3))
+      .WillOnce(Return(etime3));
+  timer_.clock_wrapper_.reset(clock_wrapper_mock_.release());
+  ASSERT_TRUE(timer_.Start());
+  ASSERT_TRUE(timer_.start_time_ == stime);
+  ASSERT_TRUE(timer_.HasStarted());
+
+  ASSERT_TRUE(timer_.Pause());
+  ASSERT_TRUE(timer_.HasStarted());
+  ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(), kDelta1MSec);
+  base::TimeDelta elapsed_time;
+  ASSERT_TRUE(timer_.GetElapsedTime(&elapsed_time));
+  ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(),
+            elapsed_time.InMilliseconds());
+
+  ASSERT_TRUE(timer_.Resume());
+  ASSERT_TRUE(timer_.HasStarted());
+
+  ASSERT_TRUE(timer_.Pause());
+  ASSERT_TRUE(timer_.HasStarted());
+  ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(), kDelta1MSec + kDelta2MSec);
+  ASSERT_TRUE(timer_.GetElapsedTime(&elapsed_time));
+  ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(),
+            elapsed_time.InMilliseconds());
+
+  ASSERT_TRUE(timer_.Resume());
+  ASSERT_TRUE(timer_.HasStarted());
+
+  ASSERT_TRUE(timer_.Stop());
+  ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(),
+            kDelta1MSec + kDelta2MSec + kDelta3MSec);
+  ASSERT_FALSE(timer_.HasStarted());
+  ASSERT_TRUE(timer_.GetElapsedTime(&elapsed_time));
+  ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(),
+            elapsed_time.InMilliseconds());
+}
+
+static const char kMetricName[] = "test-timer";
+static const int kMinSample = 0;
+static const int kMaxSample = 120 * 1E6;
+static const int kNumBuckets = 50;
+
+class TimerReporterTest : public testing::Test {
+ public:
+  TimerReporterTest() : timer_reporter_(kMetricName, kMinSample, kMaxSample,
+                                        kNumBuckets),
+                        clock_wrapper_mock_(new ClockWrapperMock()) {}
+
+ protected:
+  virtual void SetUp() {
+    timer_reporter_.set_metrics_lib(&lib_);
+    EXPECT_EQ(timer_reporter_.histogram_name_, kMetricName);
+    EXPECT_EQ(timer_reporter_.min_, kMinSample);
+    EXPECT_EQ(timer_reporter_.max_, kMaxSample);
+    EXPECT_EQ(timer_reporter_.num_buckets_, kNumBuckets);
+    stime += base::TimeDelta::FromMilliseconds(kStime1MSec);
+    etime += base::TimeDelta::FromMilliseconds(kEtime1MSec);
+  }
+
+  virtual void TearDown() {
+    timer_reporter_.set_metrics_lib(nullptr);
+  }
+
+  TimerReporter timer_reporter_;
+  MetricsLibraryMock lib_;
+  scoped_ptr<ClockWrapperMock> clock_wrapper_mock_;
+  base::TimeTicks stime, etime;
+};
+
+TEST_F(TimerReporterTest, StartStopReport) {
+  EXPECT_CALL(*clock_wrapper_mock_, GetCurrentTime())
+      .WillOnce(Return(stime))
+      .WillOnce(Return(etime));
+  timer_reporter_.clock_wrapper_.reset(clock_wrapper_mock_.release());
+  EXPECT_CALL(lib_, SendToUMA(kMetricName, kDelta1MSec, kMinSample, kMaxSample,
+                              kNumBuckets)).WillOnce(Return(true));
+  ASSERT_TRUE(timer_reporter_.Start());
+  ASSERT_TRUE(timer_reporter_.Stop());
+  ASSERT_TRUE(timer_reporter_.ReportMilliseconds());
+}
+
+TEST_F(TimerReporterTest, InvalidReport) {
+  ASSERT_FALSE(timer_reporter_.ReportMilliseconds());
+}
+
+}  // namespace chromeos_metrics
+
+int main(int argc, char **argv) {
+  testing::InitGoogleTest(&argc, argv);
+  return RUN_ALL_TESTS();
+}
diff --git a/metricsd/uploader/metrics_hashes.cc b/metricsd/uploader/metrics_hashes.cc
new file mode 100644
index 0000000..208c560
--- /dev/null
+++ b/metricsd/uploader/metrics_hashes.cc
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2015 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 "uploader/metrics_hashes.h"
+
+#include "base/logging.h"
+#include "base/md5.h"
+#include "base/sys_byteorder.h"
+
+namespace metrics {
+
+namespace {
+
+// Converts the 8-byte prefix of an MD5 hash into a uint64 value.
+inline uint64_t HashToUInt64(const std::string& hash) {
+  uint64_t value;
+  DCHECK_GE(hash.size(), sizeof(value));
+  memcpy(&value, hash.data(), sizeof(value));
+  return base::HostToNet64(value);
+}
+
+}  // namespace
+
+uint64_t HashMetricName(const std::string& name) {
+  // Create an MD5 hash of the given |name|, represented as a byte buffer
+  // encoded as an std::string.
+  base::MD5Context context;
+  base::MD5Init(&context);
+  base::MD5Update(&context, name);
+
+  base::MD5Digest digest;
+  base::MD5Final(&digest, &context);
+
+  std::string hash_str(reinterpret_cast<char*>(digest.a), arraysize(digest.a));
+  return HashToUInt64(hash_str);
+}
+
+}  // namespace metrics
diff --git a/base/test_utils.h b/metricsd/uploader/metrics_hashes.h
similarity index 64%
copy from base/test_utils.h
copy to metricsd/uploader/metrics_hashes.h
index 132d3a7..1082b42 100644
--- a/base/test_utils.h
+++ b/metricsd/uploader/metrics_hashes.h
@@ -14,19 +14,17 @@
  * limitations under the License.
  */
 
-#ifndef TEST_UTILS_H
-#define TEST_UTILS_H
+#ifndef METRICS_UPLOADER_METRICS_HASHES_H_
+#define METRICS_UPLOADER_METRICS_HASHES_H_
 
-class TemporaryFile {
- public:
-  TemporaryFile();
-  ~TemporaryFile();
+#include <string>
 
-  int fd;
-  char filename[1024];
+namespace metrics {
 
- private:
-  void init(const char* tmp_dir);
-};
+// Computes a uint64 hash of a given string based on its MD5 hash. Suitable for
+// metric names.
+uint64_t HashMetricName(const std::string& name);
 
-#endif // TEST_UTILS_H
+}  // namespace metrics
+
+#endif  // METRICS_UPLOADER_METRICS_HASHES_H_
diff --git a/metricsd/uploader/metrics_hashes_unittest.cc b/metricsd/uploader/metrics_hashes_unittest.cc
new file mode 100644
index 0000000..b8c2575
--- /dev/null
+++ b/metricsd/uploader/metrics_hashes_unittest.cc
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2015 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 "uploader/metrics_hashes.h"
+
+#include <base/format_macros.h>
+#include <base/macros.h>
+#include <base/strings/stringprintf.h>
+#include <gtest/gtest.h>
+
+namespace metrics {
+
+// Make sure our ID hashes are the same as what we see on the server side.
+TEST(MetricsUtilTest, HashMetricName) {
+  static const struct {
+    std::string input;
+    std::string output;
+  } cases[] = {
+    {"Back", "0x0557fa923dcee4d0"},
+    {"Forward", "0x67d2f6740a8eaebf"},
+    {"NewTab", "0x290eb683f96572f1"},
+  };
+
+  for (size_t i = 0; i < arraysize(cases); ++i) {
+    uint64_t hash = HashMetricName(cases[i].input);
+    std::string hash_hex = base::StringPrintf("0x%016" PRIx64, hash);
+    EXPECT_EQ(cases[i].output, hash_hex);
+  }
+}
+
+}  // namespace metrics
diff --git a/metricsd/uploader/metrics_log.cc b/metricsd/uploader/metrics_log.cc
new file mode 100644
index 0000000..1f16ca1
--- /dev/null
+++ b/metricsd/uploader/metrics_log.cc
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2015 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 "uploader/metrics_log.h"
+
+#include <string>
+
+#include "uploader/proto/system_profile.pb.h"
+#include "uploader/system_profile_setter.h"
+
+// We use default values for the MetricsLogBase constructor as the setter will
+// override them.
+MetricsLog::MetricsLog()
+    : MetricsLogBase("", 0, metrics::MetricsLogBase::ONGOING_LOG, "") {
+}
+
+void MetricsLog::IncrementUserCrashCount() {
+  metrics::SystemProfileProto::Stability* stability(
+      uma_proto()->mutable_system_profile()->mutable_stability());
+  int current = stability->other_user_crash_count();
+  stability->set_other_user_crash_count(current + 1);
+}
+
+void MetricsLog::IncrementKernelCrashCount() {
+  metrics::SystemProfileProto::Stability* stability(
+      uma_proto()->mutable_system_profile()->mutable_stability());
+  int current = stability->kernel_crash_count();
+  stability->set_kernel_crash_count(current + 1);
+}
+
+void MetricsLog::IncrementUncleanShutdownCount() {
+  metrics::SystemProfileProto::Stability* stability(
+      uma_proto()->mutable_system_profile()->mutable_stability());
+  int current = stability->unclean_system_shutdown_count();
+  stability->set_unclean_system_shutdown_count(current + 1);
+}
+
+bool MetricsLog::PopulateSystemProfile(SystemProfileSetter* profile_setter) {
+  return profile_setter->Populate(uma_proto());
+}
diff --git a/metricsd/uploader/metrics_log.h b/metricsd/uploader/metrics_log.h
new file mode 100644
index 0000000..5e09070
--- /dev/null
+++ b/metricsd/uploader/metrics_log.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2015 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 METRICS_UPLOADER_METRICS_LOG_H_
+#define METRICS_UPLOADER_METRICS_LOG_H_
+
+#include <string>
+
+#include <base/macros.h>
+
+#include "uploader/metrics_log_base.h"
+
+// This file defines a set of user experience metrics data recorded by
+// the MetricsService. This is the unit of data that is sent to the server.
+class SystemProfileSetter;
+
+// This class provides base functionality for logging metrics data.
+class MetricsLog : public metrics::MetricsLogBase {
+ public:
+  // The constructor doesn't set any metadata. The metadata is only set by a
+  // SystemProfileSetter.
+  MetricsLog();
+
+  void IncrementUserCrashCount();
+  void IncrementKernelCrashCount();
+  void IncrementUncleanShutdownCount();
+
+  // Populate the system profile with system information using setter.
+  bool PopulateSystemProfile(SystemProfileSetter* setter);
+
+ private:
+  FRIEND_TEST(UploadServiceTest, LogContainsAggregatedValues);
+  FRIEND_TEST(UploadServiceTest, LogKernelCrash);
+  FRIEND_TEST(UploadServiceTest, LogUncleanShutdown);
+  FRIEND_TEST(UploadServiceTest, LogUserCrash);
+  FRIEND_TEST(UploadServiceTest, UnknownCrashIgnored);
+
+  DISALLOW_COPY_AND_ASSIGN(MetricsLog);
+};
+
+#endif  // METRICS_UPLOADER_METRICS_LOG_H_
diff --git a/metricsd/uploader/metrics_log_base.cc b/metricsd/uploader/metrics_log_base.cc
new file mode 100644
index 0000000..ee325ae
--- /dev/null
+++ b/metricsd/uploader/metrics_log_base.cc
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2015 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 "uploader/metrics_log_base.h"
+
+#include "base/metrics/histogram_base.h"
+#include "base/metrics/histogram_samples.h"
+#include "uploader/metrics_hashes.h"
+#include "uploader/proto/histogram_event.pb.h"
+#include "uploader/proto/system_profile.pb.h"
+#include "uploader/proto/user_action_event.pb.h"
+
+using base::Histogram;
+using base::HistogramBase;
+using base::HistogramSamples;
+using base::SampleCountIterator;
+using base::Time;
+using base::TimeDelta;
+using metrics::HistogramEventProto;
+using metrics::SystemProfileProto;
+using metrics::UserActionEventProto;
+
+namespace metrics {
+namespace {
+
+// Any id less than 16 bytes is considered to be a testing id.
+bool IsTestingID(const std::string& id) {
+  return id.size() < 16;
+}
+
+}  // namespace
+
+MetricsLogBase::MetricsLogBase(const std::string& client_id,
+                               int session_id,
+                               LogType log_type,
+                               const std::string& version_string)
+    : num_events_(0),
+      locked_(false),
+      log_type_(log_type) {
+  DCHECK_NE(NO_LOG, log_type);
+  if (IsTestingID(client_id))
+    uma_proto_.set_client_id(0);
+  else
+    uma_proto_.set_client_id(Hash(client_id));
+
+  uma_proto_.set_session_id(session_id);
+  uma_proto_.mutable_system_profile()->set_build_timestamp(GetBuildTime());
+  uma_proto_.mutable_system_profile()->set_app_version(version_string);
+}
+
+MetricsLogBase::~MetricsLogBase() {}
+
+// static
+uint64_t MetricsLogBase::Hash(const std::string& value) {
+  uint64_t hash = metrics::HashMetricName(value);
+
+  // The following log is VERY helpful when folks add some named histogram into
+  // the code, but forgot to update the descriptive list of histograms.  When
+  // that happens, all we get to see (server side) is a hash of the histogram
+  // name.  We can then use this logging to find out what histogram name was
+  // being hashed to a given MD5 value by just running the version of Chromium
+  // in question with --enable-logging.
+  DVLOG(1) << "Metrics: Hash numeric [" << value << "]=[" << hash << "]";
+
+  return hash;
+}
+
+// static
+int64_t MetricsLogBase::GetBuildTime() {
+  static int64_t integral_build_time = 0;
+  if (!integral_build_time) {
+    Time time;
+    const char* kDateTime = __DATE__ " " __TIME__ " GMT";
+    bool result = Time::FromString(kDateTime, &time);
+    DCHECK(result);
+    integral_build_time = static_cast<int64_t>(time.ToTimeT());
+  }
+  return integral_build_time;
+}
+
+// static
+int64_t MetricsLogBase::GetCurrentTime() {
+  return (base::TimeTicks::Now() - base::TimeTicks()).InSeconds();
+}
+
+void MetricsLogBase::CloseLog() {
+  DCHECK(!locked_);
+  locked_ = true;
+}
+
+void MetricsLogBase::GetEncodedLog(std::string* encoded_log) {
+  DCHECK(locked_);
+  uma_proto_.SerializeToString(encoded_log);
+}
+
+void MetricsLogBase::RecordUserAction(const std::string& key) {
+  DCHECK(!locked_);
+
+  UserActionEventProto* user_action = uma_proto_.add_user_action_event();
+  user_action->set_name_hash(Hash(key));
+  user_action->set_time(GetCurrentTime());
+
+  ++num_events_;
+}
+
+void MetricsLogBase::RecordHistogramDelta(const std::string& histogram_name,
+                                          const HistogramSamples& snapshot) {
+  DCHECK(!locked_);
+  DCHECK_NE(0, snapshot.TotalCount());
+
+  // We will ignore the MAX_INT/infinite value in the last element of range[].
+
+  HistogramEventProto* histogram_proto = uma_proto_.add_histogram_event();
+  histogram_proto->set_name_hash(Hash(histogram_name));
+  histogram_proto->set_sum(snapshot.sum());
+
+  for (scoped_ptr<SampleCountIterator> it = snapshot.Iterator(); !it->Done();
+       it->Next()) {
+    HistogramBase::Sample min;
+    HistogramBase::Sample max;
+    HistogramBase::Count count;
+    it->Get(&min, &max, &count);
+    HistogramEventProto::Bucket* bucket = histogram_proto->add_bucket();
+    bucket->set_min(min);
+    bucket->set_max(max);
+    bucket->set_count(count);
+  }
+
+  // Omit fields to save space (see rules in histogram_event.proto comments).
+  for (int i = 0; i < histogram_proto->bucket_size(); ++i) {
+    HistogramEventProto::Bucket* bucket = histogram_proto->mutable_bucket(i);
+    if (i + 1 < histogram_proto->bucket_size() &&
+        bucket->max() == histogram_proto->bucket(i + 1).min()) {
+      bucket->clear_max();
+    } else if (bucket->max() == bucket->min() + 1) {
+      bucket->clear_min();
+    }
+  }
+}
+
+}  // namespace metrics
diff --git a/metricsd/uploader/metrics_log_base.h b/metricsd/uploader/metrics_log_base.h
new file mode 100644
index 0000000..f4e1995
--- /dev/null
+++ b/metricsd/uploader/metrics_log_base.h
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// This file defines a set of user experience metrics data recorded by
+// the MetricsService.  This is the unit of data that is sent to the server.
+
+#ifndef METRICS_UPLOADER_METRICS_LOG_BASE_H_
+#define METRICS_UPLOADER_METRICS_LOG_BASE_H_
+
+#include <string>
+
+#include "base/macros.h"
+#include "base/metrics/histogram.h"
+#include "base/time/time.h"
+#include "uploader/proto/chrome_user_metrics_extension.pb.h"
+
+namespace base {
+class HistogramSamples;
+}  // namespace base
+
+namespace metrics {
+
+// This class provides base functionality for logging metrics data.
+class MetricsLogBase {
+ public:
+  // TODO(asvitkine): Remove the NO_LOG value.
+  enum LogType {
+    INITIAL_STABILITY_LOG,  // The initial log containing stability stats.
+    ONGOING_LOG,            // Subsequent logs in a session.
+    NO_LOG,                 // Placeholder value for when there is no log.
+  };
+
+  // Creates a new metrics log of the specified type.
+  // client_id is the identifier for this profile on this installation
+  // session_id is an integer that's incremented on each application launch
+  MetricsLogBase(const std::string& client_id,
+                 int session_id,
+                 LogType log_type,
+                 const std::string& version_string);
+  virtual ~MetricsLogBase();
+
+  // Computes the MD5 hash of the given string, and returns the first 8 bytes of
+  // the hash.
+  static uint64_t Hash(const std::string& value);
+
+  // Get the GMT buildtime for the current binary, expressed in seconds since
+  // January 1, 1970 GMT.
+  // The value is used to identify when a new build is run, so that previous
+  // reliability stats, from other builds, can be abandoned.
+  static int64_t GetBuildTime();
+
+  // Convenience function to return the current time at a resolution in seconds.
+  // This wraps base::TimeTicks, and hence provides an abstract time that is
+  // always incrementing for use in measuring time durations.
+  static int64_t GetCurrentTime();
+
+  // Records a user-initiated action.
+  void RecordUserAction(const std::string& key);
+
+  // Record any changes in a given histogram for transmission.
+  void RecordHistogramDelta(const std::string& histogram_name,
+                            const base::HistogramSamples& snapshot);
+
+  // Stop writing to this record and generate the encoded representation.
+  // None of the Record* methods can be called after this is called.
+  void CloseLog();
+
+  // Fills |encoded_log| with the serialized protobuf representation of the
+  // record.  Must only be called after CloseLog() has been called.
+  void GetEncodedLog(std::string* encoded_log);
+
+  int num_events() { return num_events_; }
+
+  void set_hardware_class(const std::string& hardware_class) {
+    uma_proto_.mutable_system_profile()->mutable_hardware()->set_hardware_class(
+        hardware_class);
+  }
+
+  LogType log_type() const { return log_type_; }
+
+ protected:
+  bool locked() const { return locked_; }
+
+  metrics::ChromeUserMetricsExtension* uma_proto() { return &uma_proto_; }
+  const metrics::ChromeUserMetricsExtension* uma_proto() const {
+    return &uma_proto_;
+  }
+
+  // TODO(isherman): Remove this once the XML pipeline is outta here.
+  int num_events_;  // the number of events recorded in this log
+
+ private:
+  // locked_ is true when record has been packed up for sending, and should
+  // no longer be written to.  It is only used for sanity checking and is
+  // not a real lock.
+  bool locked_;
+
+  // The type of the log, i.e. initial or ongoing.
+  const LogType log_type_;
+
+  // Stores the protocol buffer representation for this log.
+  metrics::ChromeUserMetricsExtension uma_proto_;
+
+  DISALLOW_COPY_AND_ASSIGN(MetricsLogBase);
+};
+
+}  // namespace metrics
+
+#endif  // METRICS_UPLOADER_METRICS_LOG_BASE_H_
diff --git a/metricsd/uploader/metrics_log_base_unittest.cc b/metricsd/uploader/metrics_log_base_unittest.cc
new file mode 100644
index 0000000..980afd5
--- /dev/null
+++ b/metricsd/uploader/metrics_log_base_unittest.cc
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2015 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 "uploader/metrics_log_base.h"
+
+#include <string>
+
+#include <base/metrics/bucket_ranges.h>
+#include <base/metrics/sample_vector.h>
+#include <gtest/gtest.h>
+
+#include "uploader/proto/chrome_user_metrics_extension.pb.h"
+
+namespace metrics {
+
+namespace {
+
+class TestMetricsLogBase : public MetricsLogBase {
+ public:
+  TestMetricsLogBase()
+      : MetricsLogBase("client_id", 1, MetricsLogBase::ONGOING_LOG, "1.2.3.4") {
+  }
+  virtual ~TestMetricsLogBase() {}
+
+  using MetricsLogBase::uma_proto;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(TestMetricsLogBase);
+};
+
+}  // namespace
+
+TEST(MetricsLogBaseTest, LogType) {
+  MetricsLogBase log1("id", 0, MetricsLogBase::ONGOING_LOG, "1.2.3");
+  EXPECT_EQ(MetricsLogBase::ONGOING_LOG, log1.log_type());
+
+  MetricsLogBase log2("id", 0, MetricsLogBase::INITIAL_STABILITY_LOG, "1.2.3");
+  EXPECT_EQ(MetricsLogBase::INITIAL_STABILITY_LOG, log2.log_type());
+}
+
+TEST(MetricsLogBaseTest, EmptyRecord) {
+  MetricsLogBase log("totally bogus client ID", 137,
+                     MetricsLogBase::ONGOING_LOG, "bogus version");
+  log.set_hardware_class("sample-class");
+  log.CloseLog();
+
+  std::string encoded;
+  log.GetEncodedLog(&encoded);
+
+  // A couple of fields are hard to mock, so these will be copied over directly
+  // for the expected output.
+  metrics::ChromeUserMetricsExtension parsed;
+  ASSERT_TRUE(parsed.ParseFromString(encoded));
+
+  metrics::ChromeUserMetricsExtension expected;
+  expected.set_client_id(5217101509553811875);  // Hashed bogus client ID
+  expected.set_session_id(137);
+  expected.mutable_system_profile()->set_build_timestamp(
+      parsed.system_profile().build_timestamp());
+  expected.mutable_system_profile()->set_app_version("bogus version");
+  expected.mutable_system_profile()->mutable_hardware()->set_hardware_class(
+      "sample-class");
+
+  EXPECT_EQ(expected.SerializeAsString(), encoded);
+}
+
+TEST(MetricsLogBaseTest, HistogramBucketFields) {
+  // Create buckets: 1-5, 5-7, 7-8, 8-9, 9-10, 10-11, 11-12.
+  base::BucketRanges ranges(8);
+  ranges.set_range(0, 1);
+  ranges.set_range(1, 5);
+  ranges.set_range(2, 7);
+  ranges.set_range(3, 8);
+  ranges.set_range(4, 9);
+  ranges.set_range(5, 10);
+  ranges.set_range(6, 11);
+  ranges.set_range(7, 12);
+
+  base::SampleVector samples(&ranges);
+  samples.Accumulate(3, 1);   // Bucket 1-5.
+  samples.Accumulate(6, 1);   // Bucket 5-7.
+  samples.Accumulate(8, 1);   // Bucket 8-9. (7-8 skipped)
+  samples.Accumulate(10, 1);  // Bucket 10-11. (9-10 skipped)
+  samples.Accumulate(11, 1);  // Bucket 11-12.
+
+  TestMetricsLogBase log;
+  log.RecordHistogramDelta("Test", samples);
+
+  const metrics::ChromeUserMetricsExtension* uma_proto = log.uma_proto();
+  const metrics::HistogramEventProto& histogram_proto =
+      uma_proto->histogram_event(uma_proto->histogram_event_size() - 1);
+
+  // Buckets with samples: 1-5, 5-7, 8-9, 10-11, 11-12.
+  // Should become: 1-/, 5-7, /-9, 10-/, /-12.
+  ASSERT_EQ(5, histogram_proto.bucket_size());
+
+  // 1-5 becomes 1-/ (max is same as next min).
+  EXPECT_TRUE(histogram_proto.bucket(0).has_min());
+  EXPECT_FALSE(histogram_proto.bucket(0).has_max());
+  EXPECT_EQ(1, histogram_proto.bucket(0).min());
+
+  // 5-7 stays 5-7 (no optimization possible).
+  EXPECT_TRUE(histogram_proto.bucket(1).has_min());
+  EXPECT_TRUE(histogram_proto.bucket(1).has_max());
+  EXPECT_EQ(5, histogram_proto.bucket(1).min());
+  EXPECT_EQ(7, histogram_proto.bucket(1).max());
+
+  // 8-9 becomes /-9 (min is same as max - 1).
+  EXPECT_FALSE(histogram_proto.bucket(2).has_min());
+  EXPECT_TRUE(histogram_proto.bucket(2).has_max());
+  EXPECT_EQ(9, histogram_proto.bucket(2).max());
+
+  // 10-11 becomes 10-/ (both optimizations apply, omit max is prioritized).
+  EXPECT_TRUE(histogram_proto.bucket(3).has_min());
+  EXPECT_FALSE(histogram_proto.bucket(3).has_max());
+  EXPECT_EQ(10, histogram_proto.bucket(3).min());
+
+  // 11-12 becomes /-12 (last record must keep max, min is same as max - 1).
+  EXPECT_FALSE(histogram_proto.bucket(4).has_min());
+  EXPECT_TRUE(histogram_proto.bucket(4).has_max());
+  EXPECT_EQ(12, histogram_proto.bucket(4).max());
+}
+
+}  // namespace metrics
diff --git a/metricsd/uploader/mock/mock_system_profile_setter.h b/metricsd/uploader/mock/mock_system_profile_setter.h
new file mode 100644
index 0000000..9b20291
--- /dev/null
+++ b/metricsd/uploader/mock/mock_system_profile_setter.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2015 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 METRICS_UPLOADER_MOCK_MOCK_SYSTEM_PROFILE_SETTER_H_
+#define METRICS_UPLOADER_MOCK_MOCK_SYSTEM_PROFILE_SETTER_H_
+
+#include "uploader/system_profile_setter.h"
+
+namespace metrics {
+class ChromeUserMetricsExtension;
+}
+
+// Mock profile setter used for testing.
+class MockSystemProfileSetter : public SystemProfileSetter {
+ public:
+  bool Populate(metrics::ChromeUserMetricsExtension* profile_proto) override {
+    return true;
+  }
+};
+
+#endif  // METRICS_UPLOADER_MOCK_MOCK_SYSTEM_PROFILE_SETTER_H_
diff --git a/metricsd/uploader/mock/sender_mock.cc b/metricsd/uploader/mock/sender_mock.cc
new file mode 100644
index 0000000..bb4dc7d
--- /dev/null
+++ b/metricsd/uploader/mock/sender_mock.cc
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2015 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 "uploader/mock/sender_mock.h"
+
+SenderMock::SenderMock() {
+  Reset();
+}
+
+bool SenderMock::Send(const std::string& content, const std::string& hash) {
+  send_call_count_ += 1;
+  last_message_ = content;
+  is_good_proto_ = last_message_proto_.ParseFromString(content);
+  return should_succeed_;
+}
+
+void SenderMock::Reset() {
+  send_call_count_ = 0;
+  last_message_ = "";
+  should_succeed_ = true;
+  last_message_proto_.Clear();
+  is_good_proto_ = false;
+}
diff --git a/metricsd/uploader/mock/sender_mock.h b/metricsd/uploader/mock/sender_mock.h
new file mode 100644
index 0000000..e79233f
--- /dev/null
+++ b/metricsd/uploader/mock/sender_mock.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2015 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 METRICS_UPLOADER_MOCK_SENDER_MOCK_H_
+#define METRICS_UPLOADER_MOCK_SENDER_MOCK_H_
+
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "uploader/proto/chrome_user_metrics_extension.pb.h"
+#include "uploader/sender.h"
+
+class SenderMock : public Sender {
+ public:
+  SenderMock();
+
+  bool Send(const std::string& content, const std::string& hash) override;
+  void Reset();
+
+  bool is_good_proto() { return is_good_proto_; }
+  int send_call_count() { return send_call_count_; }
+  const std::string last_message() { return last_message_; }
+  metrics::ChromeUserMetricsExtension last_message_proto() {
+    return last_message_proto_;
+  }
+  void set_should_succeed(bool succeed) { should_succeed_ = succeed; }
+
+ private:
+  // Is set to true if the proto was parsed successfully.
+  bool is_good_proto_;
+
+  // If set to true, the Send method will return true to simulate a successful
+  // send.
+  bool should_succeed_;
+
+  // Count of how many times Send was called since the last reset.
+  int send_call_count_;
+
+  // Last message received by Send.
+  std::string last_message_;
+
+  // If is_good_proto is true, last_message_proto is the deserialized
+  // representation of last_message.
+  metrics::ChromeUserMetricsExtension last_message_proto_;
+};
+
+#endif  // METRICS_UPLOADER_MOCK_SENDER_MOCK_H_
diff --git a/metricsd/uploader/proto/README b/metricsd/uploader/proto/README
new file mode 100644
index 0000000..4292a40
--- /dev/null
+++ b/metricsd/uploader/proto/README
@@ -0,0 +1,37 @@
+Copyright (C) 2015 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 directory contains the protocol buffers used by the standalone metrics
+uploader. Those protobuffers are copied from the chromium protobuffers from
+https://chromium.googlesource.com/chromium/src/+/master/components/metrics/proto/
+at 3bfe5f2b4c03d2cac718d137ed14cd2c6354bfed.
+
+Any change to this protobuf must first be made to the backend's protobuf and be
+compatible with the chromium protobuffers.
+
+
+Q: Why fork the chromium protobuffers ?
+A: The standalone metrics uploader needs chromium os fields that are not defined
+by the chromium protobufs. Instead of pushing chromium os specific changes to
+chromium, we can add them only to chromium os (and to the backend of course).
+
+
+Q: What's the difference between those protobuffers and chromium's protobuffers?
+A: When the protobuffers were copied, some chromium specific protobuffers were
+not imported:
+* omnibox related protobuffers.
+* performance profiling protobuffers (not used in chromium os).
diff --git a/metricsd/uploader/proto/chrome_user_metrics_extension.proto b/metricsd/uploader/proto/chrome_user_metrics_extension.proto
new file mode 100644
index 0000000..a07830f
--- /dev/null
+++ b/metricsd/uploader/proto/chrome_user_metrics_extension.proto
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+//
+// Protocol buffer for Chrome UMA (User Metrics Analysis).
+//
+// Note: this protobuf must be compatible with the one in chromium.
+
+syntax = "proto2";
+
+option optimize_for = LITE_RUNTIME;
+option java_outer_classname = "ChromeUserMetricsExtensionProtos";
+option java_package = "org.chromium.components.metrics";
+
+package metrics;
+
+import "system/core/metricsd/uploader/proto/histogram_event.proto";
+import "system/core/metricsd/uploader/proto/system_profile.proto";
+import "system/core/metricsd/uploader/proto/user_action_event.proto";
+
+// Next tag: 13
+message ChromeUserMetricsExtension {
+  // The product (i.e. end user application) for a given UMA log.
+  enum Product {
+    // Google Chrome product family.
+    CHROME = 0;
+  }
+  // The product corresponding to this log. The field type is int32 instead of
+  // Product so that downstream users of the Chromium metrics component can
+  // introduce products without needing to make changes to the Chromium code
+  // (though they still need to add the new product to the server-side enum).
+  // Note: The default value is Chrome, so Chrome products will not transmit
+  // this field.
+  optional int32 product = 10 [default = 0];
+
+  // The id of the client install that generated these events.
+  //
+  // For Chrome clients, this id is unique to a top-level (one level above the
+  // "Default" directory) Chrome user data directory [1], and so is shared among
+  // all Chrome user profiles contained in this user data directory.
+  // An id of 0 is reserved for test data (monitoring and internal testing) and
+  // should normally be ignored in analysis of the data.
+  // [1] http://www.chromium.org/user-experience/user-data-directory
+  optional fixed64 client_id = 1;
+
+  // The session id for this user.
+  // Values such as tab ids are only meaningful within a particular session.
+  // The client keeps track of the session id and sends it with each event.
+  // The session id is simply an integer that is incremented each time the user
+  // relaunches Chrome.
+  optional int32 session_id = 2;
+
+  // Information about the user's browser and system configuration.
+  optional SystemProfileProto system_profile = 3;
+
+  // This message will log one or more of the following event types:
+  repeated UserActionEventProto user_action_event = 4;
+  repeated HistogramEventProto histogram_event = 6;
+
+}
diff --git a/metricsd/uploader/proto/histogram_event.proto b/metricsd/uploader/proto/histogram_event.proto
new file mode 100644
index 0000000..3825063
--- /dev/null
+++ b/metricsd/uploader/proto/histogram_event.proto
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+//
+// Histogram-collected metrics.
+
+syntax = "proto2";
+
+option optimize_for = LITE_RUNTIME;
+option java_outer_classname = "HistogramEventProtos";
+option java_package = "org.chromium.components.metrics";
+
+package metrics;
+
+// Next tag: 4
+message HistogramEventProto {
+  // The name of the histogram, hashed.
+  optional fixed64 name_hash = 1;
+
+  // The sum of all the sample values.
+  // Together with the total count of the sample values, this allows us to
+  // compute the average value.  The count of all sample values is just the sum
+  // of the counts of all the buckets.
+  optional int64 sum = 2;
+
+  // The per-bucket data.
+  message Bucket {
+    // Each bucket's range is bounded by min <= x < max.
+    // It is valid to omit one of these two fields in a bucket, but not both.
+    // If the min field is omitted, its value is assumed to be equal to max - 1.
+    // If the max field is omitted, its value is assumed to be equal to the next
+    // bucket's min value (possibly computed per above).  The last bucket in a
+    // histogram should always include the max field.
+    optional int64 min = 1;
+    optional int64 max = 2;
+
+    // The bucket's index in the list of buckets, sorted in ascending order.
+    // This field was intended to provide extra redundancy to detect corrupted
+    // records, but was never used.  As of M31, it is no longer sent by Chrome
+    // clients to reduce the UMA upload size.
+    optional int32 bucket_index = 3 [deprecated = true];
+
+    // The number of entries in this bucket.
+    optional int64 count = 4;
+  }
+  repeated Bucket bucket = 3;
+}
diff --git a/metricsd/uploader/proto/system_profile.proto b/metricsd/uploader/proto/system_profile.proto
new file mode 100644
index 0000000..4cab0d9
--- /dev/null
+++ b/metricsd/uploader/proto/system_profile.proto
@@ -0,0 +1,759 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+//
+// Stores information about the user's brower and system configuration.
+// The system configuration fields are recorded once per client session.
+
+syntax = "proto2";
+
+option optimize_for = LITE_RUNTIME;
+option java_outer_classname = "SystemProfileProtos";
+option java_package = "org.chromium.components.metrics";
+
+package metrics;
+
+// Next tag: 21
+message SystemProfileProto {
+  // The time when the client was compiled/linked, in seconds since the epoch.
+  optional int64 build_timestamp = 1;
+
+  // A version number string for the application.
+  // Most commonly this is the browser version number found in a user agent
+  // string, and is typically a 4-tuple of numbers separated by periods.  In
+  // cases where the user agent version might be ambiguous (example: Linux 64-
+  // bit build, rather than 32-bit build, or a Windows version used in some
+  // special context, such as ChromeFrame running in IE), then this may include
+  // some additional postfix to provide clarification not available in the UA
+  // string.
+  //
+  // An example of a browser version 4-tuple is "5.0.322.0".  Currently used
+  // postfixes are:
+  //
+  //   "-64": a 64-bit build
+  //   "-F": Chrome is running under control of ChromeFrame
+  //   "-devel": this is not an official build of Chrome
+  //
+  // A full version number string could look similar to:
+  // "5.0.322.0-F-devel".
+  //
+  // This value, when available, is more trustworthy than the UA string
+  // associated with the request; and including the postfix, may be more
+  // specific.
+  optional string app_version = 2;
+
+  // The brand code or distribution tag assigned to a partner, if available.
+  // Brand codes are only available on Windows.  Not every Windows install
+  // though will have a brand code.
+  optional string brand_code = 12;
+
+  // The possible channels for an installation, from least to most stable.
+  enum Channel {
+    CHANNEL_UNKNOWN = 0;  // Unknown channel -- perhaps an unofficial build?
+    CHANNEL_CANARY = 1;
+    CHANNEL_DEV = 2;
+    CHANNEL_BETA = 3;
+    CHANNEL_STABLE = 4;
+  }
+  optional Channel channel = 10;
+
+  // True if Chrome build is ASan-instrumented.
+  optional bool is_asan_build = 20 [default = false];
+
+  // The date the user enabled UMA, in seconds since the epoch.
+  // If the user has toggled the UMA enabled state multiple times, this will
+  // be the most recent date on which UMA was enabled.
+  // For privacy, this is rounded to the nearest hour.
+  optional int64 uma_enabled_date = 3;
+
+  // The time when the client was installed, in seconds since the epoch.
+  // For privacy, this is rounded to the nearest hour.
+  optional int64 install_date = 16;
+
+  // The user's selected application locale, i.e. the user interface language.
+  // The locale includes a language code and, possibly, also a country code,
+  // e.g. "en-US".
+  optional string application_locale = 4;
+
+  message BrilloDeviceData {
+    optional string build_target_id = 1;
+  }
+  optional BrilloDeviceData brillo = 21;
+
+  // Information on the user's operating system.
+  message OS {
+    // The user's operating system. This should be one of:
+    // - Android
+    // - Windows NT
+    // - Linux (includes ChromeOS)
+    // - iPhone OS
+    // - Mac OS X
+    optional string name = 1;
+
+    // The version of the OS.  The meaning of this field is OS-dependent.
+    optional string version = 2;
+
+    // The fingerprint of the build.  This field is used only on Android.
+    optional string fingerprint = 3;
+
+    // Whether the version of iOS appears to be "jailbroken". This field is
+    // used only on iOS. Chrome for iOS detects whether device contains a
+    // DynamicLibraries/ directory. It's a necessary but insufficient indicator
+    // of whether the operating system has been jailbroken.
+    optional bool is_jailbroken = 4;
+  }
+  optional OS os = 5;
+
+  // Next tag for Hardware: 18
+  // Information on the user's hardware.
+  message Hardware {
+    // The CPU architecture (x86, PowerPC, x86_64, ...)
+    optional string cpu_architecture = 1;
+
+    // The amount of RAM present on the system, in megabytes.
+    optional int64 system_ram_mb = 2;
+
+    // The base memory address that chrome.dll was loaded at.
+    // (Logged only on Windows.)
+    optional int64 dll_base = 3;
+
+    // The Chrome OS device hardware class ID is a unique string associated with
+    // each Chrome OS device product revision generally assigned at hardware
+    // qualification time.  The hardware class effectively identifies the
+    // configured system components such as CPU, WiFi adapter, etc.
+    //
+    // An example of such a hardware class is "IEC MARIO PONY 6101".  An
+    // internal database associates this hardware class with the qualified
+    // device specifications including OEM information, schematics, hardware
+    // qualification reports, test device tags, etc.
+    optional string hardware_class = 4;
+
+    // The number of physical screens.
+    optional int32 screen_count = 5;
+
+    // The screen dimensions of the primary screen, in pixels.
+    optional int32 primary_screen_width = 6;
+    optional int32 primary_screen_height = 7;
+
+    // The device scale factor of the primary screen.
+    optional float primary_screen_scale_factor = 12;
+
+    // Max DPI for any attached screen. (Windows only)
+    optional float max_dpi_x = 9;
+    optional float max_dpi_y = 10;
+
+    // Information on the CPU obtained by CPUID.
+    message CPU {
+      // A 12 character string naming the vendor, e.g. "GeniuneIntel".
+      optional string vendor_name = 1;
+
+      // The signature reported by CPUID (from EAX).
+      optional uint32 signature = 2;
+
+      // Number of logical processors/cores on the current machine.
+      optional uint32 num_cores = 3;
+    }
+    optional CPU cpu = 13;
+
+    // Information on the GPU
+    message Graphics {
+      // The GPU manufacturer's vendor id.
+      optional uint32 vendor_id = 1;
+
+      // The GPU manufacturer's device id for the chip set.
+      optional uint32 device_id = 2;
+
+      // The driver version on the GPU.
+      optional string driver_version = 3;
+
+      // The driver date on the GPU.
+      optional string driver_date = 4;
+
+      // The GL_VENDOR string. An example of a gl_vendor string is
+      // "Imagination Technologies". "" if we are not using OpenGL.
+      optional string gl_vendor = 6;
+
+      // The GL_RENDERER string. An example of a gl_renderer string is
+      // "PowerVR SGX 540". "" if we are not using OpenGL.
+      optional string gl_renderer = 7;
+    }
+    optional Graphics gpu = 8;
+
+    // Information about Bluetooth devices paired with the system.
+    message Bluetooth {
+      // Whether Bluetooth is present on this system.
+      optional bool is_present = 1;
+
+      // Whether Bluetooth is enabled on this system.
+      optional bool is_enabled = 2;
+
+      // Describes a paired device.
+      message PairedDevice {
+        // Assigned class of the device. This is a bitfield according to the
+        // Bluetooth specification available at the following URL:
+        // https://www.bluetooth.org/en-us/specification/assigned-numbers-overview/baseband
+        optional uint32 bluetooth_class = 1;
+
+        // Decoded device type.
+        enum Type {
+          DEVICE_UNKNOWN = 0;
+          DEVICE_COMPUTER = 1;
+          DEVICE_PHONE = 2;
+          DEVICE_MODEM = 3;
+          DEVICE_AUDIO = 4;
+          DEVICE_CAR_AUDIO = 5;
+          DEVICE_VIDEO = 6;
+          DEVICE_PERIPHERAL = 7;
+          DEVICE_JOYSTICK = 8;
+          DEVICE_GAMEPAD = 9;
+          DEVICE_KEYBOARD = 10;
+          DEVICE_MOUSE = 11;
+          DEVICE_TABLET = 12;
+          DEVICE_KEYBOARD_MOUSE_COMBO = 13;
+        }
+        optional Type type = 2;
+
+        // Vendor prefix of the Bluetooth address, these are OUI registered by
+        // the IEEE and are encoded with the first byte in bits 16-23, the
+        // second byte in bits 8-15 and the third byte in bits 0-7.
+        //
+        // ie. Google's OUI (00:1A:11) is encoded as 0x00001A11
+        optional uint32 vendor_prefix = 4;
+
+        // The Vendor ID of a device, returned in vendor_id below, can be
+        // either allocated by the Bluetooth SIG or USB IF, providing two
+        // completely overlapping namespaces for identifiers.
+        //
+        // This field should be read along with vendor_id to correctly
+        // identify the vendor. For example Google is identified by either
+        // vendor_id_source = VENDOR_ID_BLUETOOTH, vendor_id = 0x00E0 or
+        // vendor_id_source = VENDOR_ID_USB, vendor_id = 0x18D1.
+        //
+        // If the device does not support the Device ID specification the
+        // unknown value will be set.
+        enum VendorIDSource {
+          VENDOR_ID_UNKNOWN = 0;
+          VENDOR_ID_BLUETOOTH = 1;
+          VENDOR_ID_USB = 2;
+        }
+        optional VendorIDSource vendor_id_source = 8;
+
+        // Vendor ID of the device, where available.
+        optional uint32 vendor_id = 5;
+
+        // Product ID of the device, where available.
+        optional uint32 product_id = 6;
+
+        // Device ID of the device, generally the release or version number in
+        // BCD format, where available.
+        optional uint32 device_id = 7;
+      }
+      repeated PairedDevice paired_device = 3;
+    }
+    optional Bluetooth bluetooth = 11;
+
+    // Whether the internal display produces touch events. Omitted if unknown.
+    // Logged on ChromeOS only.
+    optional bool internal_display_supports_touch = 14;
+
+    // Vendor ids and product ids of external touchscreens.
+    message TouchScreen {
+      // Touch screen vendor id.
+      optional uint32 vendor_id = 1;
+      // Touch screen product id.
+      optional uint32 product_id = 2;
+    }
+    // Lists vendor and product ids of external touchscreens.
+    // Logged on ChromeOS only.
+    repeated TouchScreen external_touchscreen = 15;
+
+    // Drive messages are currently logged on Windows 7+, iOS, and Android.
+    message Drive {
+      // Whether this drive incurs a time penalty when randomly accessed. This
+      // should be true for spinning disks but false for SSDs or other
+      // flash-based drives.
+      optional bool has_seek_penalty = 1;
+    }
+    // The drive that the application executable was loaded from.
+    optional Drive app_drive = 16;
+    // The drive that the current user data directory was loaded from.
+    optional Drive user_data_drive = 17;
+  }
+  optional Hardware hardware = 6;
+
+  // Information about the network connection.
+  message Network {
+    // Set to true if connection_type changed during the lifetime of the log.
+    optional bool connection_type_is_ambiguous = 1;
+
+    // See net::NetworkChangeNotifier::ConnectionType.
+    enum ConnectionType {
+      CONNECTION_UNKNOWN = 0;
+      CONNECTION_ETHERNET = 1;
+      CONNECTION_WIFI = 2;
+      CONNECTION_2G = 3;
+      CONNECTION_3G = 4;
+      CONNECTION_4G = 5;
+      CONNECTION_BLUETOOTH = 6;
+    }
+    // The connection type according to NetworkChangeNotifier.
+    optional ConnectionType connection_type = 2;
+
+    // Set to true if wifi_phy_layer_protocol changed during the lifetime of the log.
+    optional bool wifi_phy_layer_protocol_is_ambiguous = 3;
+
+    // See net::WifiPHYLayerProtocol.
+    enum WifiPHYLayerProtocol {
+      WIFI_PHY_LAYER_PROTOCOL_NONE = 0;
+      WIFI_PHY_LAYER_PROTOCOL_ANCIENT = 1;
+      WIFI_PHY_LAYER_PROTOCOL_A = 2;
+      WIFI_PHY_LAYER_PROTOCOL_B = 3;
+      WIFI_PHY_LAYER_PROTOCOL_G = 4;
+      WIFI_PHY_LAYER_PROTOCOL_N = 5;
+      WIFI_PHY_LAYER_PROTOCOL_UNKNOWN = 6;
+    }
+    // The physical layer mode of the associated wifi access point, if any.
+    optional WifiPHYLayerProtocol wifi_phy_layer_protocol = 4;
+
+    // Describe wifi access point information.
+    message WifiAccessPoint {
+      // Vendor prefix of the access point's BSSID, these are OUIs
+      // (Organizationally Unique Identifiers) registered by
+      // the IEEE and are encoded with the first byte in bits 16-23, the
+      // second byte in bits 8-15 and the third byte in bits 0-7.
+      optional uint32 vendor_prefix = 1;
+
+      // Access point seurity mode definitions.
+      enum SecurityMode {
+        SECURITY_UNKNOWN = 0;
+        SECURITY_WPA = 1;
+        SECURITY_WEP = 2;
+        SECURITY_RSN = 3;
+        SECURITY_802_1X = 4;
+        SECURITY_PSK = 5;
+        SECURITY_NONE = 6;
+      }
+      // The security mode of the access point.
+      optional SecurityMode security_mode = 2;
+
+      // Vendor specific information.
+      message VendorInformation {
+        // The model number, for example "0".
+        optional string model_number = 1;
+
+        // The model name (sometimes the same as the model_number),
+        // for example "WZR-HP-AG300H".
+        optional string model_name = 2;
+
+        // The device name (sometimes the same as the model_number),
+        // for example "Dummynet"
+        optional string device_name = 3;
+
+        // The list of vendor-specific OUIs (Organziationally Unqiue
+        // Identifiers). These are provided by the vendor through WPS
+        // (Wireless Provisioning Service) information elements, which
+        // identifies the content of the element.
+        repeated uint32 element_identifier = 4;
+      }
+      // The wireless access point vendor information.
+      optional VendorInformation vendor_info = 3;
+    }
+    // Information of the wireless AP that device is connected to.
+    optional WifiAccessPoint access_point_info = 5;
+  }
+  optional Network network = 13;
+
+  // Information on the Google Update install that is managing this client.
+  message GoogleUpdate {
+    // Whether the Google Update install is system-level or user-level.
+    optional bool is_system_install = 1;
+
+    // The date at which Google Update last started performing an automatic
+    // update check, in seconds since the Unix epoch.
+    optional int64 last_automatic_start_timestamp = 2;
+
+    // The date at which Google Update last successfully sent an update check
+    // and recieved an intact response from the server, in seconds since the
+    // Unix epoch. (The updates don't need to be successfully installed.)
+    optional int64 last_update_check_timestamp = 3;
+
+    // Describes a product being managed by Google Update. (This can also
+    // describe Google Update itself.)
+    message ProductInfo {
+      // The current version of the product that is installed.
+      optional string version = 1;
+
+      // The date at which Google Update successfully updated this product,
+      // stored in seconds since the Unix epoch.  This is updated when an update
+      // is successfully applied, or if the server reports that no update
+      // is available.
+      optional int64 last_update_success_timestamp = 2;
+
+      // The result reported by the product updater on its last run.
+      enum InstallResult {
+        INSTALL_RESULT_SUCCESS = 0;
+        INSTALL_RESULT_FAILED_CUSTOM_ERROR = 1;
+        INSTALL_RESULT_FAILED_MSI_ERROR = 2;
+        INSTALL_RESULT_FAILED_SYSTEM_ERROR = 3;
+        INSTALL_RESULT_EXIT_CODE = 4;
+      }
+      optional InstallResult last_result = 3;
+
+      // The error code reported by the product updater on its last run.  This
+      // will typically be a error code specific to the product installer.
+      optional int32 last_error = 4;
+
+      // The extra error code reported by the product updater on its last run.
+      // This will typically be a Win32 error code.
+      optional int32 last_extra_error = 5;
+    }
+    optional ProductInfo google_update_status = 4;
+    optional ProductInfo client_status = 5;
+  }
+  optional GoogleUpdate google_update = 11;
+
+  // Information on all installed plugins.
+  message Plugin {
+    // The plugin's self-reported name and filename (without path).
+    optional string name = 1;
+    optional string filename = 2;
+
+    // The plugin's version.
+    optional string version = 3;
+
+    // True if the plugin is disabled.
+    // If a client has multiple local Chrome user accounts, this is logged based
+    // on the first user account launched during the current session.
+    optional bool is_disabled = 4;
+
+    // True if the plugin is PPAPI.
+    optional bool is_pepper = 5;
+  }
+  repeated Plugin plugin = 7;
+
+  // Figures that can be used to generate application stability metrics.
+  // All values are counts of events since the last time that these
+  // values were reported.
+  // Next tag: 24
+  message Stability {
+    // Total amount of time that the program was running, in seconds,
+    // since the last time a log was recorded, as measured using a client-side
+    // clock implemented via TimeTicks, which guarantees that it is monotonic
+    // and does not jump if the user changes his/her clock.  The TimeTicks
+    // implementation also makes the clock not count time the computer is
+    // suspended.
+    optional int64 incremental_uptime_sec = 1;
+
+    // Total amount of time that the program was running, in seconds,
+    // since startup, as measured using a client-side clock implemented
+    // via TimeTicks, which guarantees that it is monotonic and does not
+    // jump if the user changes his/her clock.  The TimeTicks implementation
+    // also makes the clock not count time the computer is suspended.
+    // This field was added for M-35.
+    optional int64 uptime_sec = 23;
+
+    // Page loads along with renderer crashes and hangs, since page load count
+    // roughly corresponds to usage.
+    optional int32 page_load_count = 2;
+    optional int32 renderer_crash_count = 3;
+    optional int32 renderer_hang_count = 4;
+
+    // Number of renderer crashes that were for extensions. These crashes are
+    // not counted in renderer_crash_count.
+    optional int32 extension_renderer_crash_count = 5;
+
+    // Number of non-renderer child process crashes.
+    optional int32 child_process_crash_count = 6;
+
+    // Number of times the browser has crashed while logged in as the "other
+    // user" (guest) account.
+    // Logged on ChromeOS only.
+    optional int32 other_user_crash_count = 7;
+
+    // Number of times the kernel has crashed.
+    // Logged on ChromeOS only.
+    optional int32 kernel_crash_count = 8;
+
+    // Number of times the system has shut down uncleanly.
+    // Logged on ChromeOS only.
+    optional int32 unclean_system_shutdown_count = 9;
+
+    //
+    // All the remaining fields in the Stability are recorded at most once per
+    // client session.
+    //
+
+    // The number of times the program was launched.
+    // This will typically be equal to 1.  However, it is possible that Chrome
+    // was unable to upload stability metrics for previous launches (e.g. due to
+    // crashing early during startup), and hence this value might be greater
+    // than 1.
+    optional int32 launch_count = 15;
+    // The number of times that it didn't exit cleanly (which we assume to be
+    // mostly crashes).
+    optional int32 crash_count = 16;
+
+    // The number of times the program began, but did not complete, the shutdown
+    // process.  (For example, this may occur when Windows is shutting down, and
+    // it only gives the process a few seconds to clean up.)
+    optional int32 incomplete_shutdown_count = 17;
+
+    // The number of times the program was able register with breakpad crash
+    // services.
+    optional int32 breakpad_registration_success_count = 18;
+
+    // The number of times the program failed to register with breakpad crash
+    // services.  If crash registration fails then when the program crashes no
+    // crash report will be generated.
+    optional int32 breakpad_registration_failure_count = 19;
+
+    // The number of times the program has run under a debugger.  This should
+    // be an exceptional condition.  Running under a debugger prevents crash
+    // dumps from being generated.
+    optional int32 debugger_present_count = 20;
+
+    // The number of times the program has run without a debugger attached.
+    // This should be most common scenario and should be very close to
+    // |launch_count|.
+    optional int32 debugger_not_present_count = 21;
+
+    // Stability information for all installed plugins.
+    message PluginStability {
+      // The relevant plugin's information (name, etc.)
+      optional Plugin plugin = 1;
+
+      // The number of times this plugin's process was launched.
+      optional int32 launch_count = 2;
+
+      // The number of times this plugin was instantiated on a web page.
+      // This will be >= |launch_count|.
+      // (A page load with multiple sections drawn by this plugin will
+      // increase this count multiple times.)
+      optional int32 instance_count = 3;
+
+      // The number of times this plugin process crashed.
+      // This value will be <= |launch_count|.
+      optional int32 crash_count = 4;
+
+      // The number of times this plugin could not be loaded.
+      optional int32 loading_error_count = 5;
+    }
+    repeated PluginStability plugin_stability = 22;
+  }
+  optional Stability stability = 8;
+
+  // Description of a field trial or experiment that the user is currently
+  // enrolled in.
+  // All metrics reported in this upload can potentially be influenced by the
+  // field trial.
+  message FieldTrial {
+    // The name of the field trial, as a 32-bit identifier.
+    // Currently, the identifier is a hash of the field trial's name.
+    optional fixed32 name_id = 1;
+
+    // The user's group within the field trial, as a 32-bit identifier.
+    // Currently, the identifier is a hash of the group's name.
+    optional fixed32 group_id = 2;
+  }
+  repeated FieldTrial field_trial = 9;
+
+  // Information about the A/V output device(s) (typically just a TV).
+  // However, a configuration may have one or more intermediate A/V devices
+  // between the source device and the TV (e.g. an A/V receiver, video
+  // processor, etc.).
+  message ExternalAudioVideoDevice {
+    // The manufacturer name (possibly encoded as a 3-letter code, e.g. "YMH"
+    // for Yamaha).
+    optional string manufacturer_name = 1;
+
+    // The model name (e.g. "RX-V1900"). Some devices may report generic names
+    // like "receiver" or use the full manufacturer name (e.g "PHILIPS").
+    optional string model_name = 2;
+
+    // The product code (e.g. "0218").
+    optional string product_code = 3;
+
+    // The device types. A single device can have multiple types (e.g. a set-top
+    // box could be both a tuner and a player).  The same type may even be
+    // repeated (e.g a device that reports two tuners).
+    enum AVDeviceType {
+      AV_DEVICE_TYPE_UNKNOWN = 0;
+      AV_DEVICE_TYPE_TV = 1;
+      AV_DEVICE_TYPE_RECORDER = 2;
+      AV_DEVICE_TYPE_TUNER = 3;
+      AV_DEVICE_TYPE_PLAYER = 4;
+      AV_DEVICE_TYPE_AUDIO_SYSTEM = 5;
+    }
+    repeated AVDeviceType av_device_type = 4;
+
+    // The year of manufacture.
+    optional int32 manufacture_year = 5;
+
+    // The week of manufacture.
+    // Note: per the Wikipedia EDID article, numbering for this field may not
+    // be consistent between manufacturers.
+    optional int32 manufacture_week = 6;
+
+    // Max horizontal resolution in pixels.
+    optional int32 horizontal_resolution = 7;
+
+    // Max vertical resolution in pixels.
+    optional int32 vertical_resolution = 8;
+
+    // Audio capabilities of the device.
+    // Ref: http://en.wikipedia.org/wiki/Extended_display_identification_data
+    message AudioDescription {
+      // Audio format
+      enum AudioFormat {
+        AUDIO_FORMAT_UNKNOWN = 0;
+        AUDIO_FORMAT_LPCM = 1;
+        AUDIO_FORMAT_AC_3 = 2;
+        AUDIO_FORMAT_MPEG1 = 3;
+        AUDIO_FORMAT_MP3 = 4;
+        AUDIO_FORMAT_MPEG2 = 5;
+        AUDIO_FORMAT_AAC = 6;
+        AUDIO_FORMAT_DTS = 7;
+        AUDIO_FORMAT_ATRAC = 8;
+        AUDIO_FORMAT_ONE_BIT = 9;
+        AUDIO_FORMAT_DD_PLUS = 10;
+        AUDIO_FORMAT_DTS_HD = 11;
+        AUDIO_FORMAT_MLP_DOLBY_TRUEHD = 12;
+        AUDIO_FORMAT_DST_AUDIO = 13;
+        AUDIO_FORMAT_MICROSOFT_WMA_PRO = 14;
+      }
+      optional AudioFormat audio_format = 1;
+
+      // Number of channels (e.g. 1, 2, 8, etc.).
+      optional int32 num_channels = 2;
+
+      // Supported sample frequencies in Hz (e.g. 32000, 44100, etc.).
+      // Multiple frequencies may be specified.
+      repeated int32 sample_frequency_hz = 3;
+
+      // Maximum bit rate in bits/s.
+      optional int32 max_bit_rate_per_second = 4;
+
+      // Bit depth (e.g. 16, 20, 24, etc.).
+      optional int32 bit_depth = 5;
+    }
+    repeated AudioDescription audio_description = 9;
+
+    // The position in AV setup.
+    // A value of 0 means this device is the TV.
+    // A value of 1 means this device is directly connected to one of
+    // the TV's inputs.
+    // Values > 1 indicate there are 1 or more devices between this device
+    // and the TV.
+    optional int32 position_in_setup = 10;
+
+    // Whether this device is in the path to the TV.
+    optional bool is_in_path_to_tv = 11;
+
+    // The CEC version the device supports.
+    // CEC stands for Consumer Electronics Control, a part of the HDMI
+    // specification.  Not all HDMI devices support CEC.
+    // Only devices that support CEC will report a value here.
+    optional int32 cec_version = 12;
+
+    // This message reports CEC commands seen by a device.
+    // After each log is sent, this information is cleared and gathered again.
+    // By collecting CEC status information by opcode we can determine
+    // which CEC features can be supported.
+    message CECCommand {
+      // The CEC command opcode.  CEC supports up to 256 opcodes.
+      // We add only one CECCommand message per unique opcode.  Only opcodes
+      // seen by the device will be reported. The remainder of the message
+      // accumulates status for this opcode (and device).
+      optional int32 opcode = 1;
+
+      // The total number of commands received from the external device.
+      optional int32 num_received_direct = 2;
+
+      // The number of commands received from the external device as part of a
+      // broadcast message.
+      optional int32 num_received_broadcast = 3;
+
+      // The total number of commands sent to the external device.
+      optional int32 num_sent_direct = 4;
+
+      // The number of commands sent to the external device as part of a
+      // broadcast message.
+      optional int32 num_sent_broadcast = 5;
+
+      // The number of aborted commands for unknown reasons.
+      optional int32 num_aborted_unknown_reason = 6;
+
+      // The number of aborted commands because of an unrecognized opcode.
+      optional int32 num_aborted_unrecognized = 7;
+    }
+    repeated CECCommand cec_command = 13;
+  }
+  repeated ExternalAudioVideoDevice external_audio_video_device = 14;
+
+  // Information about the current wireless access point. Collected directly
+  // from the wireless access point via standard apis if the device is
+  // connected to the Internet wirelessly. Introduced for Chrome on TV devices
+  // but also can be collected by ChromeOS, Android or other clients.
+  message ExternalAccessPoint {
+    // The manufacturer name, for example "ASUSTeK Computer Inc.".
+    optional string manufacturer = 1;
+
+    // The model name, for example "Wi-Fi Protected Setup Router".
+    optional string model_name = 2;
+
+    // The model number, for example "RT-N16".
+    optional string model_number = 3;
+
+    // The device name (sometime same as model_number), for example "RT-N16".
+    optional string device_name = 4;
+  }
+  optional ExternalAccessPoint external_access_point = 15;
+
+  // Number of users currently signed into a multiprofile session.
+  // A zero value indicates that the user count changed while the log is open.
+  // Logged only on ChromeOS.
+  optional uint32 multi_profile_user_count = 17;
+
+  // Information about extensions that are installed, masked to provide better
+  // privacy.  Only extensions from a single profile are reported; this will
+  // generally be the profile used when the browser is started.  The profile
+  // reported on will remain consistent at least until the browser is
+  // relaunched (or the profile is deleted by the user).
+  //
+  // Each client first picks a value for client_key derived from its UMA
+  // client_id:
+  //   client_key = client_id % 4096
+  // Then, each installed extension is mapped into a hash bucket according to
+  //   bucket = CityHash64(StringPrintf("%d:%s",
+  //                                    client_key, extension_id)) % 1024
+  // The client reports the set of hash buckets occupied by all installed
+  // extensions.  If multiple extensions map to the same bucket, that bucket is
+  // still only reported once.
+  repeated int32 occupied_extension_bucket = 18;
+
+  // The state of loaded extensions for this system. The system can have either
+  // no applicable extensions, extensions only from the webstore and verified by
+  // the webstore, extensions only from the webstore but not verified, or
+  // extensions not from the store. If there is a single off-store extension,
+  // then HAS_OFFSTORE is reported. This should be kept in sync with the
+  // corresponding enum in chrome/browser/metrics/extensions_metrics_provider.cc
+  enum ExtensionsState {
+    NO_EXTENSIONS = 0;
+    NO_OFFSTORE_VERIFIED = 1;
+    NO_OFFSTORE_UNVERIFIED = 2;
+    HAS_OFFSTORE = 3;
+  }
+  optional ExtensionsState offstore_extensions_state = 19;
+}
diff --git a/metricsd/uploader/proto/user_action_event.proto b/metricsd/uploader/proto/user_action_event.proto
new file mode 100644
index 0000000..464f3c8
--- /dev/null
+++ b/metricsd/uploader/proto/user_action_event.proto
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+//
+// Stores information about an event that occurs in response to a user action,
+// e.g. an interaction with a browser UI element.
+
+syntax = "proto2";
+
+option optimize_for = LITE_RUNTIME;
+option java_outer_classname = "UserActionEventProtos";
+option java_package = "org.chromium.components.metrics";
+
+package metrics;
+
+// Next tag: 3
+message UserActionEventProto {
+  // The name of the action, hashed.
+  optional fixed64 name_hash = 1;
+
+  // The timestamp for the event, in seconds since the epoch.
+  optional int64 time = 2;
+}
diff --git a/base/test_utils.h b/metricsd/uploader/sender.h
similarity index 63%
copy from base/test_utils.h
copy to metricsd/uploader/sender.h
index 132d3a7..369c9c2 100644
--- a/base/test_utils.h
+++ b/metricsd/uploader/sender.h
@@ -14,19 +14,17 @@
  * limitations under the License.
  */
 
-#ifndef TEST_UTILS_H
-#define TEST_UTILS_H
+#ifndef METRICS_UPLOADER_SENDER_H_
+#define METRICS_UPLOADER_SENDER_H_
 
-class TemporaryFile {
+#include <string>
+
+// Abstract class for a Sender that uploads a metrics message.
+class Sender {
  public:
-  TemporaryFile();
-  ~TemporaryFile();
-
-  int fd;
-  char filename[1024];
-
- private:
-  void init(const char* tmp_dir);
+  virtual ~Sender() {}
+  // Sends a message |content| with its sha1 hash |hash|
+  virtual bool Send(const std::string& content, const std::string& hash) = 0;
 };
 
-#endif // TEST_UTILS_H
+#endif  // METRICS_UPLOADER_SENDER_H_
diff --git a/metricsd/uploader/sender_http.cc b/metricsd/uploader/sender_http.cc
new file mode 100644
index 0000000..953afc1
--- /dev/null
+++ b/metricsd/uploader/sender_http.cc
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2015 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 "uploader/sender_http.h"
+
+#include <string>
+
+#include <base/logging.h>
+#include <base/strings/string_number_conversions.h>
+#include <chromeos/http/http_utils.h>
+#include <chromeos/mime_utils.h>
+
+HttpSender::HttpSender(const std::string server_url)
+    : server_url_(server_url) {}
+
+bool HttpSender::Send(const std::string& content,
+                      const std::string& content_hash) {
+  const std::string hash =
+      base::HexEncode(content_hash.data(), content_hash.size());
+
+  chromeos::http::HeaderList headers = {{"X-Chrome-UMA-Log-SHA1", hash}};
+  chromeos::ErrorPtr error;
+  auto response = chromeos::http::PostTextAndBlock(
+      server_url_,
+      content,
+      chromeos::mime::application::kWwwFormUrlEncoded,
+      headers,
+      chromeos::http::Transport::CreateDefault(),
+      &error);
+  if (!response || response->ExtractDataAsString() != "OK") {
+    if (error) {
+      DLOG(ERROR) << "Failed to send data: " << error->GetMessage();
+    }
+    return false;
+  }
+  return true;
+}
diff --git a/metricsd/uploader/sender_http.h b/metricsd/uploader/sender_http.h
new file mode 100644
index 0000000..6249d90
--- /dev/null
+++ b/metricsd/uploader/sender_http.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2015 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 METRICS_UPLOADER_SENDER_HTTP_H_
+#define METRICS_UPLOADER_SENDER_HTTP_H_
+
+#include <string>
+
+#include <base/macros.h>
+
+#include "uploader/sender.h"
+
+// Sender implemented using http_utils from libchromeos
+class HttpSender : public Sender {
+ public:
+  explicit HttpSender(std::string server_url);
+  ~HttpSender() override = default;
+  // Sends |content| whose SHA1 hash is |hash| to server_url with a synchronous
+  // POST request to server_url.
+  bool Send(const std::string& content, const std::string& hash) override;
+
+ private:
+  const std::string server_url_;
+
+  DISALLOW_COPY_AND_ASSIGN(HttpSender);
+};
+
+#endif  // METRICS_UPLOADER_SENDER_HTTP_H_
diff --git a/metricsd/uploader/system_profile_cache.cc b/metricsd/uploader/system_profile_cache.cc
new file mode 100644
index 0000000..8635fb0
--- /dev/null
+++ b/metricsd/uploader/system_profile_cache.cc
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2015 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 "uploader/system_profile_cache.h"
+
+#include <base/files/file_util.h>
+#include <base/guid.h>
+#include <base/logging.h>
+#include <base/strings/string_number_conversions.h>
+#include <base/strings/string_util.h>
+#include <cutils/properties.h>
+#include <string>
+#include <vector>
+
+#include "constants.h"
+#include "persistent_integer.h"
+#include "uploader/metrics_log_base.h"
+#include "uploader/proto/chrome_user_metrics_extension.pb.h"
+
+namespace {
+
+const char kPersistentSessionIdFilename[] = "Sysinfo.SessionId";
+
+}  // namespace
+
+std::string ChannelToString(
+    const metrics::SystemProfileProto_Channel& channel) {
+  switch (channel) {
+    case metrics::SystemProfileProto::CHANNEL_STABLE:
+    return "STABLE";
+  case metrics::SystemProfileProto::CHANNEL_DEV:
+    return "DEV";
+  case metrics::SystemProfileProto::CHANNEL_BETA:
+    return "BETA";
+  case metrics::SystemProfileProto::CHANNEL_CANARY:
+    return "CANARY";
+  default:
+    return "UNKNOWN";
+  }
+}
+
+SystemProfileCache::SystemProfileCache()
+    : initialized_(false),
+    testing_(false),
+    config_root_("/"),
+    session_id_(new chromeos_metrics::PersistentInteger(
+        kPersistentSessionIdFilename)) {
+}
+
+SystemProfileCache::SystemProfileCache(bool testing,
+                                       const base::FilePath& config_root)
+    : initialized_(false),
+      testing_(testing),
+      config_root_(config_root),
+      session_id_(new chromeos_metrics::PersistentInteger(
+          kPersistentSessionIdFilename)) {
+}
+
+bool SystemProfileCache::Initialize() {
+  CHECK(!initialized_)
+      << "this should be called only once in the metrics_daemon lifetime.";
+
+  profile_.build_target_id = GetProperty(metrics::kBuildTargetIdProperty);
+
+  if (profile_.build_target_id.empty()) {
+    LOG(ERROR) << "System property " << metrics::kBuildTargetIdProperty
+               << " is not set.";
+    return false;
+  }
+
+  std::string channel = GetProperty(metrics::kChannelProperty);
+  profile_.version = GetProperty(metrics::kProductVersionProperty);
+
+  if (channel.empty() || profile_.version.empty()) {
+    // If the channel or version is missing, the image is not official.
+    // In this case, set the channel to unknown and the version to 0.0.0.0 to
+    // avoid polluting the production data.
+    channel = "";
+    profile_.version = metrics::kDefaultVersion;
+  }
+  profile_.client_id =
+      testing_ ? "client_id_test" :
+      GetPersistentGUID(metrics::kMetricsGUIDFilePath);
+  profile_.hardware_class = "unknown";
+  profile_.channel = ProtoChannelFromString(channel);
+
+  // Increment the session_id everytime we initialize this. If metrics_daemon
+  // does not crash, this should correspond to the number of reboots of the
+  // system.
+  session_id_->Add(1);
+  profile_.session_id = static_cast<int32_t>(session_id_->Get());
+
+  initialized_ = true;
+  return initialized_;
+}
+
+bool SystemProfileCache::InitializeOrCheck() {
+  return initialized_ || Initialize();
+}
+
+bool SystemProfileCache::Populate(
+    metrics::ChromeUserMetricsExtension* metrics_proto) {
+  CHECK(metrics_proto);
+  if (not InitializeOrCheck()) {
+    return false;
+  }
+
+  // The client id is hashed before being sent.
+  metrics_proto->set_client_id(
+      metrics::MetricsLogBase::Hash(profile_.client_id));
+  metrics_proto->set_session_id(profile_.session_id);
+
+  // Sets the product id.
+  metrics_proto->set_product(9);
+
+  metrics::SystemProfileProto* profile_proto =
+      metrics_proto->mutable_system_profile();
+  profile_proto->mutable_hardware()->set_hardware_class(
+      profile_.hardware_class);
+  profile_proto->set_app_version(profile_.version);
+  profile_proto->set_channel(profile_.channel);
+  metrics::SystemProfileProto_BrilloDeviceData* device_data =
+      profile_proto->mutable_brillo();
+  device_data->set_build_target_id(profile_.build_target_id);
+
+  return true;
+}
+
+std::string SystemProfileCache::GetPersistentGUID(
+    const std::string& filename) {
+  std::string guid;
+  base::FilePath filepath(filename);
+  if (!base::ReadFileToString(filepath, &guid)) {
+    guid = base::GenerateGUID();
+    // If we can't read or write the file, the guid will not be preserved during
+    // the next reboot. Crash.
+    CHECK(base::WriteFile(filepath, guid.c_str(), guid.size()));
+  }
+  return guid;
+}
+
+std::string SystemProfileCache::GetProperty(const std::string& name) {
+  if (testing_) {
+    std::string content;
+    base::ReadFileToString(config_root_.Append(name), &content);
+    return content;
+  } else {
+    char value[PROPERTY_VALUE_MAX];
+    property_get(name.data(), value, "");
+    return std::string(value);
+  }
+}
+
+metrics::SystemProfileProto_Channel SystemProfileCache::ProtoChannelFromString(
+    const std::string& channel) {
+  if (channel == "stable") {
+    return metrics::SystemProfileProto::CHANNEL_STABLE;
+  } else if (channel == "dev") {
+    return metrics::SystemProfileProto::CHANNEL_DEV;
+  } else if (channel == "beta") {
+    return metrics::SystemProfileProto::CHANNEL_BETA;
+  } else if (channel == "canary") {
+    return metrics::SystemProfileProto::CHANNEL_CANARY;
+  }
+
+  DLOG(INFO) << "unknown channel: " << channel;
+  return metrics::SystemProfileProto::CHANNEL_UNKNOWN;
+}
diff --git a/metricsd/uploader/system_profile_cache.h b/metricsd/uploader/system_profile_cache.h
new file mode 100644
index 0000000..7157810
--- /dev/null
+++ b/metricsd/uploader/system_profile_cache.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2015 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 METRICS_UPLOADER_SYSTEM_PROFILE_CACHE_H_
+#define METRICS_UPLOADER_SYSTEM_PROFILE_CACHE_H_
+
+#include <stdint.h>
+
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "base/files/file_path.h"
+#include "base/gtest_prod_util.h"
+#include "base/memory/scoped_ptr.h"
+#include "persistent_integer.h"
+#include "uploader/proto/system_profile.pb.h"
+#include "uploader/system_profile_setter.h"
+
+namespace metrics {
+class ChromeUserMetricsExtension;
+}
+
+struct SystemProfile {
+  std::string version;
+  std::string hardware_class;
+  std::string client_id;
+  int session_id;
+  metrics::SystemProfileProto::Channel channel;
+  std::string build_target_id;
+};
+
+// Retrieves general system informations needed by the protobuf for context and
+// remembers them to avoid expensive calls.
+//
+// The cache is populated lazily. The only method needed is Populate.
+class SystemProfileCache : public SystemProfileSetter {
+ public:
+  SystemProfileCache();
+
+  SystemProfileCache(bool testing, const base::FilePath& config_root);
+
+  // Populates the ProfileSystem protobuf with system information.
+  bool Populate(metrics::ChromeUserMetricsExtension* metrics_proto) override;
+
+  // Converts a string representation of the channel to a
+  // SystemProfileProto_Channel
+  static metrics::SystemProfileProto_Channel ProtoChannelFromString(
+      const std::string& channel);
+
+  // Gets the persistent GUID and create it if it has not been created yet.
+  static std::string GetPersistentGUID(const std::string& filename);
+
+ private:
+  friend class UploadServiceTest;
+  FRIEND_TEST(UploadServiceTest, ExtractChannelFromDescription);
+  FRIEND_TEST(UploadServiceTest, ReadKeyValueFromFile);
+  FRIEND_TEST(UploadServiceTest, SessionIdIncrementedAtInitialization);
+  FRIEND_TEST(UploadServiceTest, ValuesInConfigFileAreSent);
+
+  // Fetches all informations and populates |profile_|
+  bool Initialize();
+
+  // Initializes |profile_| only if it has not been yet initialized.
+  bool InitializeOrCheck();
+
+  // Gets a system property as a string.
+  // When |testing_| is true, reads the value from |config_root_|/|name|
+  // instead.
+  std::string GetProperty(const std::string& name);
+
+  bool initialized_;
+  bool testing_;
+  base::FilePath config_root_;
+  scoped_ptr<chromeos_metrics::PersistentInteger> session_id_;
+  SystemProfile profile_;
+};
+
+#endif  // METRICS_UPLOADER_SYSTEM_PROFILE_CACHE_H_
diff --git a/metricsd/uploader/system_profile_setter.h b/metricsd/uploader/system_profile_setter.h
new file mode 100644
index 0000000..bd3ff42
--- /dev/null
+++ b/metricsd/uploader/system_profile_setter.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2015 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 METRICS_UPLOADER_SYSTEM_PROFILE_SETTER_H_
+#define METRICS_UPLOADER_SYSTEM_PROFILE_SETTER_H_
+
+namespace metrics {
+class ChromeUserMetricsExtension;
+}
+
+// Abstract class used to delegate populating SystemProfileProto with system
+// information to simplify testing.
+class SystemProfileSetter {
+ public:
+  virtual ~SystemProfileSetter() {}
+  // Populates the protobuf with system informations.
+  virtual bool Populate(metrics::ChromeUserMetricsExtension* profile_proto) = 0;
+};
+
+#endif  // METRICS_UPLOADER_SYSTEM_PROFILE_SETTER_H_
diff --git a/metricsd/uploader/upload_service.cc b/metricsd/uploader/upload_service.cc
new file mode 100644
index 0000000..2335630
--- /dev/null
+++ b/metricsd/uploader/upload_service.cc
@@ -0,0 +1,239 @@
+/*
+ * Copyright (C) 2015 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 "uploader/upload_service.h"
+
+#include <string>
+
+#include <base/bind.h>
+#include <base/logging.h>
+#include <base/memory/scoped_vector.h>
+#include <base/message_loop/message_loop.h>
+#include <base/metrics/histogram.h>
+#include <base/metrics/histogram_base.h>
+#include <base/metrics/histogram_snapshot_manager.h>
+#include <base/metrics/sparse_histogram.h>
+#include <base/metrics/statistics_recorder.h>
+#include <base/sha1.h>
+
+#include "serialization/metric_sample.h"
+#include "serialization/serialization_utils.h"
+#include "uploader/metrics_log.h"
+#include "uploader/sender_http.h"
+#include "uploader/system_profile_setter.h"
+
+const int UploadService::kMaxFailedUpload = 10;
+
+UploadService::UploadService(SystemProfileSetter* setter,
+                             MetricsLibraryInterface* metrics_lib,
+                             const std::string& server)
+    : system_profile_setter_(setter),
+      metrics_lib_(metrics_lib),
+      histogram_snapshot_manager_(this),
+      sender_(new HttpSender(server)),
+      testing_(false) {
+}
+
+UploadService::UploadService(SystemProfileSetter* setter,
+                             MetricsLibraryInterface* metrics_lib,
+                             const std::string& server,
+                             bool testing)
+    : UploadService(setter, metrics_lib, server) {
+  testing_ = testing;
+}
+
+void UploadService::Init(const base::TimeDelta& upload_interval,
+                         const std::string& metrics_file) {
+  base::StatisticsRecorder::Initialize();
+  metrics_file_ = metrics_file;
+
+  if (!testing_) {
+    base::MessageLoop::current()->PostDelayedTask(FROM_HERE,
+        base::Bind(&UploadService::UploadEventCallback,
+                   base::Unretained(this),
+                   upload_interval),
+        upload_interval);
+  }
+}
+
+void UploadService::StartNewLog() {
+  CHECK(!staged_log_) << "the staged log should be discarded before starting "
+                         "a new metrics log";
+  MetricsLog* log = new MetricsLog();
+  current_log_.reset(log);
+}
+
+void UploadService::UploadEventCallback(const base::TimeDelta& interval) {
+  UploadEvent();
+
+  base::MessageLoop::current()->PostDelayedTask(FROM_HERE,
+      base::Bind(&UploadService::UploadEventCallback,
+                 base::Unretained(this),
+                 interval),
+      interval);
+}
+
+void UploadService::UploadEvent() {
+  if (staged_log_) {
+    // Previous upload failed, retry sending the logs.
+    SendStagedLog();
+    return;
+  }
+
+  // Previous upload successful, reading metrics sample from the file.
+  ReadMetrics();
+  GatherHistograms();
+  StageCurrentLog();
+
+  // If a log is available for upload, upload it.
+  if (staged_log_) {
+    SendStagedLog();
+  }
+}
+
+void UploadService::SendStagedLog() {
+  CHECK(staged_log_) << "staged_log_ must exist to be sent";
+
+  // If metrics are not enabled, discard the log and exit.
+  if (!metrics_lib_->AreMetricsEnabled()) {
+    LOG(INFO) << "Metrics disabled. Don't upload metrics samples.";
+    staged_log_.reset();
+    return;
+  }
+
+  std::string log_text;
+  staged_log_->GetEncodedLog(&log_text);
+  if (!sender_->Send(log_text, base::SHA1HashString(log_text))) {
+    ++failed_upload_count_;
+    if (failed_upload_count_ <= kMaxFailedUpload) {
+      LOG(WARNING) << "log upload failed " << failed_upload_count_
+                   << " times. It will be retried later.";
+      return;
+    }
+    LOG(WARNING) << "log failed more than " << kMaxFailedUpload << " times.";
+  } else {
+    LOG(INFO) << "uploaded " << log_text.length() << " bytes";
+  }
+  // Discard staged log.
+  staged_log_.reset();
+}
+
+void UploadService::Reset() {
+  staged_log_.reset();
+  current_log_.reset();
+  failed_upload_count_ = 0;
+}
+
+void UploadService::ReadMetrics() {
+  CHECK(!staged_log_)
+      << "cannot read metrics until the old logs have been discarded";
+
+  ScopedVector<metrics::MetricSample> vector;
+  metrics::SerializationUtils::ReadAndTruncateMetricsFromFile(
+      metrics_file_, &vector);
+
+  int i = 0;
+  for (ScopedVector<metrics::MetricSample>::iterator it = vector.begin();
+       it != vector.end(); it++) {
+    metrics::MetricSample* sample = *it;
+    AddSample(*sample);
+    i++;
+  }
+  DLOG(INFO) << i << " samples read";
+}
+
+void UploadService::AddSample(const metrics::MetricSample& sample) {
+  base::HistogramBase* counter;
+  switch (sample.type()) {
+    case metrics::MetricSample::CRASH:
+      AddCrash(sample.name());
+      break;
+    case metrics::MetricSample::HISTOGRAM:
+      counter = base::Histogram::FactoryGet(
+          sample.name(), sample.min(), sample.max(), sample.bucket_count(),
+          base::Histogram::kUmaTargetedHistogramFlag);
+      counter->Add(sample.sample());
+      break;
+    case metrics::MetricSample::SPARSE_HISTOGRAM:
+      counter = base::SparseHistogram::FactoryGet(
+          sample.name(), base::HistogramBase::kUmaTargetedHistogramFlag);
+      counter->Add(sample.sample());
+      break;
+    case metrics::MetricSample::LINEAR_HISTOGRAM:
+      counter = base::LinearHistogram::FactoryGet(
+          sample.name(),
+          1,
+          sample.max(),
+          sample.max() + 1,
+          base::Histogram::kUmaTargetedHistogramFlag);
+      counter->Add(sample.sample());
+      break;
+    case metrics::MetricSample::USER_ACTION:
+      GetOrCreateCurrentLog()->RecordUserAction(sample.name());
+      break;
+    default:
+      break;
+  }
+}
+
+void UploadService::AddCrash(const std::string& crash_name) {
+  if (crash_name == "user") {
+    GetOrCreateCurrentLog()->IncrementUserCrashCount();
+  } else if (crash_name == "kernel") {
+    GetOrCreateCurrentLog()->IncrementKernelCrashCount();
+  } else if (crash_name == "uncleanshutdown") {
+    GetOrCreateCurrentLog()->IncrementUncleanShutdownCount();
+  } else {
+    DLOG(ERROR) << "crash name unknown" << crash_name;
+  }
+}
+
+void UploadService::GatherHistograms() {
+  base::StatisticsRecorder::Histograms histograms;
+  base::StatisticsRecorder::GetHistograms(&histograms);
+
+  histogram_snapshot_manager_.PrepareDeltas(
+      base::Histogram::kNoFlags, base::Histogram::kUmaTargetedHistogramFlag);
+}
+
+void UploadService::RecordDelta(const base::HistogramBase& histogram,
+                                const base::HistogramSamples& snapshot) {
+  GetOrCreateCurrentLog()->RecordHistogramDelta(histogram.histogram_name(),
+                                                snapshot);
+}
+
+void UploadService::StageCurrentLog() {
+  CHECK(!staged_log_)
+      << "staged logs must be discarded before another log can be staged";
+
+  if (!current_log_) return;
+
+  staged_log_.swap(current_log_);
+  staged_log_->CloseLog();
+  if (!staged_log_->PopulateSystemProfile(system_profile_setter_.get())) {
+    LOG(WARNING) << "Error while adding metadata to the log. Discarding the "
+                 << "log.";
+    staged_log_.reset();
+  }
+  failed_upload_count_ = 0;
+}
+
+MetricsLog* UploadService::GetOrCreateCurrentLog() {
+  if (!current_log_) {
+    StartNewLog();
+  }
+  return current_log_.get();
+}
diff --git a/metricsd/uploader/upload_service.h b/metricsd/uploader/upload_service.h
new file mode 100644
index 0000000..7f2f413
--- /dev/null
+++ b/metricsd/uploader/upload_service.h
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2015 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 METRICS_UPLOADER_UPLOAD_SERVICE_H_
+#define METRICS_UPLOADER_UPLOAD_SERVICE_H_
+
+#include <string>
+
+#include "base/metrics/histogram_base.h"
+#include "base/metrics/histogram_flattener.h"
+#include "base/metrics/histogram_snapshot_manager.h"
+
+#include "metrics/metrics_library.h"
+#include "uploader/metrics_log.h"
+#include "uploader/sender.h"
+#include "uploader/system_profile_cache.h"
+
+namespace metrics {
+class ChromeUserMetricsExtension;
+class CrashSample;
+class HistogramSample;
+class LinearHistogramSample;
+class MetricSample;
+class SparseHistogramSample;
+class UserActionSample;
+}
+
+class SystemProfileSetter;
+
+// Service responsible for uploading the metrics periodically to the server.
+// This service works as a simple 2-state state-machine.
+//
+// The two states are the presence or not of a staged log.
+// A staged log is a compressed protobuffer containing both the aggregated
+// metrics and event and information about the client. (product, hardware id,
+// etc...).
+//
+// At regular intervals, the upload event will be triggered and the following
+// will happen:
+// * if a staged log is present:
+//    The previous upload may have failed for various reason. We then retry to
+//    upload the same log.
+//    - if the upload is successful, we discard the log (therefore
+//      transitioning back to no staged log)
+//    - if the upload fails, we keep the log to try again later.
+//    We do not try to read the metrics that are stored on
+//    the disk as we want to avoid storing the metrics in memory.
+//
+// * if no staged logs are present:
+//    Read all metrics from the disk, aggregate them and try to send them.
+//    - if the upload succeeds, we discard the staged log (transitioning back
+//      to the no staged log state)
+//    - if the upload fails, we keep the staged log in memory to retry
+//      uploading later.
+//
+class UploadService : public base::HistogramFlattener {
+ public:
+  explicit UploadService(SystemProfileSetter* setter,
+                         MetricsLibraryInterface* metrics_lib,
+                         const std::string& server);
+
+  void Init(const base::TimeDelta& upload_interval,
+            const std::string& metrics_file);
+
+  // Starts a new log. The log needs to be regenerated after each successful
+  // launch as it is destroyed when staging the log.
+  void StartNewLog();
+
+  // Event callback for handling MessageLoop events.
+  void UploadEventCallback(const base::TimeDelta& interval);
+
+  // Triggers an upload event.
+  void UploadEvent();
+
+  // Sends the staged log.
+  void SendStagedLog();
+
+  // Implements inconsistency detection to match HistogramFlattener's
+  // interface.
+  void InconsistencyDetected(
+      base::HistogramBase::Inconsistency problem) override {}
+  void UniqueInconsistencyDetected(
+      base::HistogramBase::Inconsistency problem) override {}
+  void InconsistencyDetectedInLoggedCount(int amount) override {}
+
+ private:
+  friend class UploadServiceTest;
+
+  FRIEND_TEST(UploadServiceTest, CanSendMultipleTimes);
+  FRIEND_TEST(UploadServiceTest, DiscardLogsAfterTooManyFailedUpload);
+  FRIEND_TEST(UploadServiceTest, EmptyLogsAreNotSent);
+  FRIEND_TEST(UploadServiceTest, FailedSendAreRetried);
+  FRIEND_TEST(UploadServiceTest, LogContainsAggregatedValues);
+  FRIEND_TEST(UploadServiceTest, LogEmptyAfterUpload);
+  FRIEND_TEST(UploadServiceTest, LogEmptyByDefault);
+  FRIEND_TEST(UploadServiceTest, LogKernelCrash);
+  FRIEND_TEST(UploadServiceTest, LogUncleanShutdown);
+  FRIEND_TEST(UploadServiceTest, LogUserCrash);
+  FRIEND_TEST(UploadServiceTest, UnknownCrashIgnored);
+  FRIEND_TEST(UploadServiceTest, ValuesInConfigFileAreSent);
+
+  // Private constructor for use in unit testing.
+  UploadService(SystemProfileSetter* setter,
+                MetricsLibraryInterface* metrics_lib,
+                const std::string& server,
+                bool testing);
+
+  // If a staged log fails to upload more than kMaxFailedUpload times, it
+  // will be discarded.
+  static const int kMaxFailedUpload;
+
+  // Resets the internal state.
+  void Reset();
+
+  // Reads all the metrics from the disk.
+  void ReadMetrics();
+
+  // Adds a generic sample to the current log.
+  void AddSample(const metrics::MetricSample& sample);
+
+  // Adds a crash to the current log.
+  void AddCrash(const std::string& crash_name);
+
+  // Aggregates all histogram available in memory and store them in the current
+  // log.
+  void GatherHistograms();
+
+  // Callback for HistogramSnapshotManager to store the histograms.
+  void RecordDelta(const base::HistogramBase& histogram,
+                   const base::HistogramSamples& snapshot) override;
+
+  // Compiles all the samples received into a single protobuf and adds all
+  // system information.
+  void StageCurrentLog();
+
+  // Returns the current log. If there is no current log, creates it first.
+  MetricsLog* GetOrCreateCurrentLog();
+
+  scoped_ptr<SystemProfileSetter> system_profile_setter_;
+  MetricsLibraryInterface* metrics_lib_;
+  base::HistogramSnapshotManager histogram_snapshot_manager_;
+  scoped_ptr<Sender> sender_;
+  int failed_upload_count_;
+  scoped_ptr<MetricsLog> current_log_;
+  scoped_ptr<MetricsLog> staged_log_;
+
+  std::string metrics_file_;
+
+  bool testing_;
+};
+
+#endif  // METRICS_UPLOADER_UPLOAD_SERVICE_H_
diff --git a/metricsd/uploader/upload_service_test.cc b/metricsd/uploader/upload_service_test.cc
new file mode 100644
index 0000000..40c235d
--- /dev/null
+++ b/metricsd/uploader/upload_service_test.cc
@@ -0,0 +1,271 @@
+/*
+ * Copyright (C) 2015 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 <gtest/gtest.h>
+
+#include <base/at_exit.h>
+#include <base/files/file_util.h>
+#include <base/files/scoped_temp_dir.h>
+#include <base/logging.h>
+#include <base/sys_info.h>
+
+#include "constants.h"
+#include "metrics_library_mock.h"
+#include "serialization/metric_sample.h"
+#include "uploader/metrics_log.h"
+#include "uploader/mock/mock_system_profile_setter.h"
+#include "uploader/mock/sender_mock.h"
+#include "uploader/proto/chrome_user_metrics_extension.pb.h"
+#include "uploader/proto/histogram_event.pb.h"
+#include "uploader/proto/system_profile.pb.h"
+#include "uploader/system_profile_cache.h"
+#include "uploader/upload_service.h"
+
+class UploadServiceTest : public testing::Test {
+ protected:
+  virtual void SetUp() {
+    CHECK(dir_.CreateUniqueTempDir());
+    upload_service_.reset(new UploadService(new MockSystemProfileSetter(),
+                                            &metrics_lib_, "", true));
+
+    upload_service_->sender_.reset(new SenderMock);
+    event_file_ = dir_.path().Append("event");
+    upload_service_->Init(base::TimeDelta::FromMinutes(30), event_file_.value());
+    upload_service_->GatherHistograms();
+    upload_service_->Reset();
+
+    chromeos_metrics::PersistentInteger::SetMetricsDirectory(
+        dir_.path().value());
+  }
+
+  scoped_ptr<metrics::MetricSample> Crash(const std::string& name) {
+    return metrics::MetricSample::CrashSample(name);
+  }
+
+  void SetTestingProperty(const std::string& name, const std::string& value) {
+    ASSERT_EQ(
+        value.size(),
+        base::WriteFile(dir_.path().Append(name), value.data(), value.size()));
+  }
+
+  base::FilePath event_file_;
+
+  base::ScopedTempDir dir_;
+  scoped_ptr<UploadService> upload_service_;
+  MetricsLibraryMock metrics_lib_;
+
+  scoped_ptr<base::AtExitManager> exit_manager_;
+};
+
+// Tests that the right crash increments a values.
+TEST_F(UploadServiceTest, LogUserCrash) {
+  upload_service_->AddSample(*Crash("user").get());
+
+  MetricsLog* log = upload_service_->current_log_.get();
+  metrics::ChromeUserMetricsExtension* proto = log->uma_proto();
+
+  EXPECT_EQ(1, proto->system_profile().stability().other_user_crash_count());
+}
+
+TEST_F(UploadServiceTest, LogUncleanShutdown) {
+  upload_service_->AddSample(*Crash("uncleanshutdown"));
+
+  EXPECT_EQ(1, upload_service_->current_log_
+                   ->uma_proto()
+                   ->system_profile()
+                   .stability()
+                   .unclean_system_shutdown_count());
+}
+
+TEST_F(UploadServiceTest, LogKernelCrash) {
+  upload_service_->AddSample(*Crash("kernel"));
+
+  EXPECT_EQ(1, upload_service_->current_log_
+                   ->uma_proto()
+                   ->system_profile()
+                   .stability()
+                   .kernel_crash_count());
+}
+
+TEST_F(UploadServiceTest, UnknownCrashIgnored) {
+  upload_service_->AddSample(*Crash("foo"));
+
+  // The log should be empty.
+  EXPECT_FALSE(upload_service_->current_log_);
+}
+
+TEST_F(UploadServiceTest, FailedSendAreRetried) {
+  SenderMock* sender = new SenderMock();
+  upload_service_->sender_.reset(sender);
+
+  sender->set_should_succeed(false);
+
+  upload_service_->AddSample(*Crash("user"));
+  upload_service_->UploadEvent();
+  EXPECT_EQ(1, sender->send_call_count());
+  std::string sent_string = sender->last_message();
+
+  upload_service_->UploadEvent();
+  EXPECT_EQ(2, sender->send_call_count());
+  EXPECT_EQ(sent_string, sender->last_message());
+}
+
+TEST_F(UploadServiceTest, DiscardLogsAfterTooManyFailedUpload) {
+  SenderMock* sender = new SenderMock();
+  upload_service_->sender_.reset(sender);
+
+  sender->set_should_succeed(false);
+
+  upload_service_->AddSample(*Crash("user"));
+
+  for (int i = 0; i < UploadService::kMaxFailedUpload; i++) {
+    upload_service_->UploadEvent();
+  }
+
+  EXPECT_TRUE(upload_service_->staged_log_);
+  upload_service_->UploadEvent();
+  EXPECT_FALSE(upload_service_->staged_log_);
+}
+
+TEST_F(UploadServiceTest, EmptyLogsAreNotSent) {
+  SenderMock* sender = new SenderMock();
+  upload_service_->sender_.reset(sender);
+  upload_service_->UploadEvent();
+  EXPECT_FALSE(upload_service_->current_log_);
+  EXPECT_EQ(0, sender->send_call_count());
+}
+
+TEST_F(UploadServiceTest, LogEmptyByDefault) {
+  UploadService upload_service(new MockSystemProfileSetter(), &metrics_lib_,
+                               "");
+
+  // current_log_ should be initialized later as it needs AtExitManager to exit
+  // in order to gather system information from SysInfo.
+  EXPECT_FALSE(upload_service.current_log_);
+}
+
+TEST_F(UploadServiceTest, CanSendMultipleTimes) {
+  SenderMock* sender = new SenderMock();
+  upload_service_->sender_.reset(sender);
+
+  upload_service_->AddSample(*Crash("user"));
+  upload_service_->UploadEvent();
+
+  std::string first_message = sender->last_message();
+
+  upload_service_->AddSample(*Crash("kernel"));
+  upload_service_->UploadEvent();
+
+  EXPECT_NE(first_message, sender->last_message());
+}
+
+TEST_F(UploadServiceTest, LogEmptyAfterUpload) {
+  upload_service_->AddSample(*Crash("user"));
+
+  EXPECT_TRUE(upload_service_->current_log_);
+
+  upload_service_->UploadEvent();
+  EXPECT_FALSE(upload_service_->current_log_);
+}
+
+TEST_F(UploadServiceTest, LogContainsAggregatedValues) {
+  scoped_ptr<metrics::MetricSample> histogram =
+      metrics::MetricSample::HistogramSample("foo", 10, 0, 42, 10);
+  upload_service_->AddSample(*histogram.get());
+
+
+  scoped_ptr<metrics::MetricSample> histogram2 =
+      metrics::MetricSample::HistogramSample("foo", 11, 0, 42, 10);
+  upload_service_->AddSample(*histogram2.get());
+
+  upload_service_->GatherHistograms();
+  metrics::ChromeUserMetricsExtension* proto =
+      upload_service_->current_log_->uma_proto();
+  EXPECT_EQ(1, proto->histogram_event().size());
+}
+
+TEST_F(UploadServiceTest, ExtractChannelFromString) {
+  EXPECT_EQ(
+      SystemProfileCache::ProtoChannelFromString(
+          "developer-build"),
+      metrics::SystemProfileProto::CHANNEL_UNKNOWN);
+
+  EXPECT_EQ(metrics::SystemProfileProto::CHANNEL_DEV,
+            SystemProfileCache::ProtoChannelFromString("dev"));
+
+  EXPECT_EQ(metrics::SystemProfileProto::CHANNEL_STABLE,
+            SystemProfileCache::ProtoChannelFromString("stable"));
+
+  EXPECT_EQ(metrics::SystemProfileProto::CHANNEL_UNKNOWN,
+            SystemProfileCache::ProtoChannelFromString("this is a test"));
+}
+
+TEST_F(UploadServiceTest, ValuesInConfigFileAreSent) {
+  SenderMock* sender = new SenderMock();
+  upload_service_->sender_.reset(sender);
+
+  SetTestingProperty(metrics::kChannelProperty, "beta");
+  SetTestingProperty(metrics::kBuildTargetIdProperty, "hello");
+  SetTestingProperty(metrics::kProductVersionProperty, "1.2.3.4");
+
+  scoped_ptr<metrics::MetricSample> histogram =
+      metrics::MetricSample::SparseHistogramSample("myhistogram", 1);
+  // Reset to create the new log with the profile setter.
+  upload_service_->system_profile_setter_.reset(
+      new SystemProfileCache(true, dir_.path()));
+  upload_service_->Reset();
+  upload_service_->AddSample(*histogram.get());
+  upload_service_->UploadEvent();
+
+  EXPECT_EQ(1, sender->send_call_count());
+  EXPECT_TRUE(sender->is_good_proto());
+  EXPECT_EQ(1, sender->last_message_proto().histogram_event().size());
+
+  EXPECT_EQ(metrics::SystemProfileProto::CHANNEL_BETA,
+            sender->last_message_proto().system_profile().channel());
+  EXPECT_NE(0, sender->last_message_proto().client_id());
+  EXPECT_NE(0, sender->last_message_proto().system_profile().build_timestamp());
+  EXPECT_NE(0, sender->last_message_proto().session_id());
+}
+
+TEST_F(UploadServiceTest, PersistentGUID) {
+  std::string tmp_file = dir_.path().Append("tmpfile").value();
+
+  std::string first_guid = SystemProfileCache::GetPersistentGUID(tmp_file);
+  std::string second_guid = SystemProfileCache::GetPersistentGUID(tmp_file);
+
+  // The GUID are cached.
+  EXPECT_EQ(first_guid, second_guid);
+
+  base::DeleteFile(base::FilePath(tmp_file), false);
+
+  first_guid = SystemProfileCache::GetPersistentGUID(tmp_file);
+  base::DeleteFile(base::FilePath(tmp_file), false);
+  second_guid = SystemProfileCache::GetPersistentGUID(tmp_file);
+
+  // Random GUIDs are generated (not all the same).
+  EXPECT_NE(first_guid, second_guid);
+}
+
+TEST_F(UploadServiceTest, SessionIdIncrementedAtInitialization) {
+  SetTestingProperty(metrics::kBuildTargetIdProperty, "hello");
+  SystemProfileCache cache(true, dir_.path());
+  cache.Initialize();
+  int session_id = cache.profile_.session_id;
+  cache.initialized_ = false;
+  cache.Initialize();
+  EXPECT_EQ(cache.profile_.session_id, session_id + 1);
+}
diff --git a/mkbootimg/Android.mk b/mkbootimg/Android.mk
index 0c9b0c6..8661d7d 100644
--- a/mkbootimg/Android.mk
+++ b/mkbootimg/Android.mk
@@ -2,12 +2,10 @@
 LOCAL_PATH:= $(call my-dir)
 include $(CLEAR_VARS)
 
-LOCAL_SRC_FILES := mkbootimg.c
-LOCAL_STATIC_LIBRARIES := libmincrypt
-LOCAL_CFLAGS := -Werror
+LOCAL_SRC_FILES := mkbootimg
+LOCAL_MODULE_CLASS := EXECUTABLES
+LOCAL_IS_HOST_MODULE := true
 
 LOCAL_MODULE := mkbootimg
 
-include $(BUILD_HOST_EXECUTABLE)
-
-$(call dist-for-goals,dist_files,$(LOCAL_BUILT_MODULE))
+include $(BUILD_PREBUILT)
diff --git a/mkbootimg/mkbootimg b/mkbootimg/mkbootimg
new file mode 100755
index 0000000..aea2585
--- /dev/null
+++ b/mkbootimg/mkbootimg
@@ -0,0 +1,140 @@
+#!/usr/bin/env python
+# Copyright 2015, 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.
+
+from __future__ import print_function
+from sys import argv, exit, stderr
+from argparse import ArgumentParser, FileType, Action
+from os import fstat
+from struct import pack
+from hashlib import sha1
+
+def filesize(f):
+    if f is None:
+        return 0
+    try:
+        return fstat(f.fileno()).st_size
+    except OSError:
+        return 0
+
+
+def update_sha(sha, f):
+    if f:
+        sha.update(f.read())
+        f.seek(0)
+        sha.update(pack('I', filesize(f)))
+    else:
+        sha.update(pack('I', 0))
+
+
+def pad_file(f, padding):
+    pad = (padding - (f.tell() & (padding - 1))) & (padding - 1)
+    f.write(pack(str(pad) + 'x'))
+
+
+def write_header(args):
+    BOOT_MAGIC = 'ANDROID!'.encode()
+    args.output.write(pack('8s', BOOT_MAGIC))
+    args.output.write(pack('8I',
+        filesize(args.kernel),                          # size in bytes
+        args.base + args.kernel_offset,                 # physical load addr
+        filesize(args.ramdisk),                         # size in bytes
+        args.base + args.ramdisk_offset,                # physical load addr
+        filesize(args.second),                          # size in bytes
+        args.base + args.second_offset,                 # physical load addr
+        args.base + args.tags_offset,                   # physical addr for kernel tags
+        args.pagesize))                                 # flash page size we assume
+    args.output.write(pack('8x'))                       # future expansion: should be 0
+    args.output.write(pack('16s', args.board.encode())) # asciiz product name
+    args.output.write(pack('512s', args.cmdline[:512].encode()))
+
+    sha = sha1()
+    update_sha(sha, args.kernel)
+    update_sha(sha, args.ramdisk)
+    update_sha(sha, args.second)
+    img_id = pack('32s', sha.digest())
+
+    args.output.write(img_id)
+    args.output.write(pack('1024s', args.cmdline[512:].encode()))
+    pad_file(args.output, args.pagesize)
+    return img_id
+
+
+class ValidateStrLenAction(Action):
+    def __init__(self, option_strings, dest, nargs=None, **kwargs):
+        if 'maxlen' not in kwargs:
+            raise ValueError('maxlen must be set')
+        self.maxlen = int(kwargs['maxlen'])
+        del kwargs['maxlen']
+        super(ValidateStrLenAction, self).__init__(option_strings, dest, **kwargs)
+
+    def __call__(self, parser, namespace, values, option_string=None):
+        if len(values) > self.maxlen:
+            raise ValueError('String argument too long: max {0:d}, got {1:d}'.
+                format(self.maxlen, len(values)))
+        setattr(namespace, self.dest, values)
+
+
+def write_padded_file(f_out, f_in, padding):
+    if f_in is None:
+        return
+    f_out.write(f_in.read())
+    pad_file(f_out, padding)
+
+
+def parse_int(x):
+    return int(x, 0)
+
+
+def parse_cmdline():
+    parser = ArgumentParser()
+    parser.add_argument('--kernel', help='path to the kernel', type=FileType('rb'),
+                        required=True)
+    parser.add_argument('--ramdisk', help='path to the ramdisk', type=FileType('rb'))
+    parser.add_argument('--second', help='path to the 2nd bootloader', type=FileType('rb'))
+    parser.add_argument('--cmdline', help='extra arguments to be passed on the '
+                        'kernel command line', default='', action=ValidateStrLenAction, maxlen=1536)
+    parser.add_argument('--base', help='base address', type=parse_int, default=0x10000000)
+    parser.add_argument('--kernel_offset', help='kernel offset', type=parse_int, default=0x00008000)
+    parser.add_argument('--ramdisk_offset', help='ramdisk offset', type=parse_int, default=0x01000000)
+    parser.add_argument('--second_offset', help='2nd bootloader offset', type=parse_int,
+                        default=0x00f00000)
+    parser.add_argument('--tags_offset', help='tags offset', type=parse_int, default=0x00000100)
+    parser.add_argument('--board', help='board name', default='', action=ValidateStrLenAction,
+                        maxlen=16)
+    parser.add_argument('--pagesize', help='page size', type=parse_int,
+                        choices=[2**i for i in range(11,15)], default=2048)
+    parser.add_argument('--id', help='print the image ID on standard output',
+                        action='store_true')
+    parser.add_argument('-o', '--output', help='output file name', type=FileType('wb'),
+                        required=True)
+    return parser.parse_args()
+
+
+def write_data(args):
+    write_padded_file(args.output, args.kernel, args.pagesize)
+    write_padded_file(args.output, args.ramdisk, args.pagesize)
+    write_padded_file(args.output, args.second, args.pagesize)
+
+
+def main():
+    args = parse_cmdline()
+    img_id = write_header(args)
+    write_data(args)
+    if args.id:
+        print('0x' + ''.join('{:02x}'.format(ord(c)) for c in img_id))
+
+
+if __name__ == '__main__':
+    main()
diff --git a/mkbootimg/mkbootimg.c b/mkbootimg/mkbootimg.c
deleted file mode 100644
index b6a2801..0000000
--- a/mkbootimg/mkbootimg.c
+++ /dev/null
@@ -1,292 +0,0 @@
-/* tools/mkbootimg/mkbootimg.c
-**
-** Copyright 2007, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <stdbool.h>
-
-#include "mincrypt/sha.h"
-#include "bootimg.h"
-
-static void *load_file(const char *fn, unsigned *_sz)
-{
-    char *data;
-    int sz;
-    int fd;
-
-    data = 0;
-    fd = open(fn, O_RDONLY);
-    if(fd < 0) return 0;
-
-    sz = lseek(fd, 0, SEEK_END);
-    if(sz < 0) goto oops;
-
-    if(lseek(fd, 0, SEEK_SET) != 0) goto oops;
-
-    data = (char*) malloc(sz);
-    if(data == 0) goto oops;
-
-    if(read(fd, data, sz) != sz) goto oops;
-    close(fd);
-
-    if(_sz) *_sz = sz;
-    return data;
-
-oops:
-    close(fd);
-    if(data != 0) free(data);
-    return 0;
-}
-
-int usage(void)
-{
-    fprintf(stderr,"usage: mkbootimg\n"
-            "       --kernel <filename>\n"
-            "       [ --ramdisk <filename> ]\n"
-            "       [ --second <2ndbootloader-filename> ]\n"
-            "       [ --cmdline <kernel-commandline> ]\n"
-            "       [ --board <boardname> ]\n"
-            "       [ --base <address> ]\n"
-            "       [ --pagesize <pagesize> ]\n"
-            "       [ --id ]\n"
-            "       -o|--output <filename>\n"
-            );
-    return 1;
-}
-
-
-
-static unsigned char padding[16384] = { 0, };
-
-static void print_id(const uint8_t *id, size_t id_len) {
-    printf("0x");
-    for (unsigned i = 0; i < id_len; i++) {
-        printf("%02x", id[i]);
-    }
-    printf("\n");
-}
-
-int write_padding(int fd, unsigned pagesize, unsigned itemsize)
-{
-    unsigned pagemask = pagesize - 1;
-    ssize_t count;
-
-    if((itemsize & pagemask) == 0) {
-        return 0;
-    }
-
-    count = pagesize - (itemsize & pagemask);
-
-    if(write(fd, padding, count) != count) {
-        return -1;
-    } else {
-        return 0;
-    }
-}
-
-int main(int argc, char **argv)
-{
-    boot_img_hdr hdr;
-
-    char *kernel_fn = NULL;
-    void *kernel_data = NULL;
-    char *ramdisk_fn = NULL;
-    void *ramdisk_data = NULL;
-    char *second_fn = NULL;
-    void *second_data = NULL;
-    char *cmdline = "";
-    char *bootimg = NULL;
-    char *board = "";
-    uint32_t pagesize = 2048;
-    int fd;
-    SHA_CTX ctx;
-    const uint8_t* sha;
-    uint32_t base           = 0x10000000U;
-    uint32_t kernel_offset  = 0x00008000U;
-    uint32_t ramdisk_offset = 0x01000000U;
-    uint32_t second_offset  = 0x00f00000U;
-    uint32_t tags_offset    = 0x00000100U;
-    size_t cmdlen;
-
-    argc--;
-    argv++;
-
-    memset(&hdr, 0, sizeof(hdr));
-
-    bool get_id = false;
-    while(argc > 0){
-        char *arg = argv[0];
-        if (!strcmp(arg, "--id")) {
-            get_id = true;
-            argc -= 1;
-            argv += 1;
-        } else if(argc >= 2) {
-            char *val = argv[1];
-            argc -= 2;
-            argv += 2;
-            if(!strcmp(arg, "--output") || !strcmp(arg, "-o")) {
-                bootimg = val;
-            } else if(!strcmp(arg, "--kernel")) {
-                kernel_fn = val;
-            } else if(!strcmp(arg, "--ramdisk")) {
-                ramdisk_fn = val;
-            } else if(!strcmp(arg, "--second")) {
-                second_fn = val;
-            } else if(!strcmp(arg, "--cmdline")) {
-                cmdline = val;
-            } else if(!strcmp(arg, "--base")) {
-                base = strtoul(val, 0, 16);
-            } else if(!strcmp(arg, "--kernel_offset")) {
-                kernel_offset = strtoul(val, 0, 16);
-            } else if(!strcmp(arg, "--ramdisk_offset")) {
-                ramdisk_offset = strtoul(val, 0, 16);
-            } else if(!strcmp(arg, "--second_offset")) {
-                second_offset = strtoul(val, 0, 16);
-            } else if(!strcmp(arg, "--tags_offset")) {
-                tags_offset = strtoul(val, 0, 16);
-            } else if(!strcmp(arg, "--board")) {
-                board = val;
-            } else if(!strcmp(arg,"--pagesize")) {
-                pagesize = strtoul(val, 0, 10);
-                if ((pagesize != 2048) && (pagesize != 4096)
-                    && (pagesize != 8192) && (pagesize != 16384)) {
-                    fprintf(stderr,"error: unsupported page size %d\n", pagesize);
-                    return -1;
-                }
-            } else {
-                return usage();
-            }
-        } else {
-            return usage();
-        }
-    }
-    hdr.page_size = pagesize;
-
-    hdr.kernel_addr =  base + kernel_offset;
-    hdr.ramdisk_addr = base + ramdisk_offset;
-    hdr.second_addr =  base + second_offset;
-    hdr.tags_addr =    base + tags_offset;
-
-    if(bootimg == 0) {
-        fprintf(stderr,"error: no output filename specified\n");
-        return usage();
-    }
-
-    if(kernel_fn == 0) {
-        fprintf(stderr,"error: no kernel image specified\n");
-        return usage();
-    }
-
-    if(strlen(board) >= BOOT_NAME_SIZE) {
-        fprintf(stderr,"error: board name too large\n");
-        return usage();
-    }
-
-    strcpy((char *) hdr.name, board);
-
-    memcpy(hdr.magic, BOOT_MAGIC, BOOT_MAGIC_SIZE);
-
-    cmdlen = strlen(cmdline);
-    if(cmdlen > (BOOT_ARGS_SIZE + BOOT_EXTRA_ARGS_SIZE - 2)) {
-        fprintf(stderr,"error: kernel commandline too large\n");
-        return 1;
-    }
-    /* Even if we need to use the supplemental field, ensure we
-     * are still NULL-terminated */
-    strncpy((char *)hdr.cmdline, cmdline, BOOT_ARGS_SIZE - 1);
-    hdr.cmdline[BOOT_ARGS_SIZE - 1] = '\0';
-    if (cmdlen >= (BOOT_ARGS_SIZE - 1)) {
-        cmdline += (BOOT_ARGS_SIZE - 1);
-        strncpy((char *)hdr.extra_cmdline, cmdline, BOOT_EXTRA_ARGS_SIZE);
-    }
-
-    kernel_data = load_file(kernel_fn, &hdr.kernel_size);
-    if(kernel_data == 0) {
-        fprintf(stderr,"error: could not load kernel '%s'\n", kernel_fn);
-        return 1;
-    }
-
-    if(ramdisk_fn == 0) {
-        ramdisk_data = 0;
-        hdr.ramdisk_size = 0;
-    } else {
-        ramdisk_data = load_file(ramdisk_fn, &hdr.ramdisk_size);
-        if(ramdisk_data == 0) {
-            fprintf(stderr,"error: could not load ramdisk '%s'\n", ramdisk_fn);
-            return 1;
-        }
-    }
-
-    if(second_fn) {
-        second_data = load_file(second_fn, &hdr.second_size);
-        if(second_data == 0) {
-            fprintf(stderr,"error: could not load secondstage '%s'\n", second_fn);
-            return 1;
-        }
-    }
-
-    /* put a hash of the contents in the header so boot images can be
-     * differentiated based on their first 2k.
-     */
-    SHA_init(&ctx);
-    SHA_update(&ctx, kernel_data, hdr.kernel_size);
-    SHA_update(&ctx, &hdr.kernel_size, sizeof(hdr.kernel_size));
-    SHA_update(&ctx, ramdisk_data, hdr.ramdisk_size);
-    SHA_update(&ctx, &hdr.ramdisk_size, sizeof(hdr.ramdisk_size));
-    SHA_update(&ctx, second_data, hdr.second_size);
-    SHA_update(&ctx, &hdr.second_size, sizeof(hdr.second_size));
-    sha = SHA_final(&ctx);
-    memcpy(hdr.id, sha,
-           SHA_DIGEST_SIZE > sizeof(hdr.id) ? sizeof(hdr.id) : SHA_DIGEST_SIZE);
-
-    fd = open(bootimg, O_CREAT | O_TRUNC | O_WRONLY, 0644);
-    if(fd < 0) {
-        fprintf(stderr,"error: could not create '%s'\n", bootimg);
-        return 1;
-    }
-
-    if(write(fd, &hdr, sizeof(hdr)) != sizeof(hdr)) goto fail;
-    if(write_padding(fd, pagesize, sizeof(hdr))) goto fail;
-
-    if(write(fd, kernel_data, hdr.kernel_size) != (ssize_t) hdr.kernel_size) goto fail;
-    if(write_padding(fd, pagesize, hdr.kernel_size)) goto fail;
-
-    if(write(fd, ramdisk_data, hdr.ramdisk_size) != (ssize_t) hdr.ramdisk_size) goto fail;
-    if(write_padding(fd, pagesize, hdr.ramdisk_size)) goto fail;
-
-    if(second_data) {
-        if(write(fd, second_data, hdr.second_size) != (ssize_t) hdr.second_size) goto fail;
-        if(write_padding(fd, pagesize, hdr.second_size)) goto fail;
-    }
-
-    if (get_id) {
-        print_id((uint8_t *) hdr.id, sizeof(hdr.id));
-    }
-
-    return 0;
-
-fail:
-    unlink(bootimg);
-    close(fd);
-    fprintf(stderr,"error: failed writing '%s': %s\n", bootimg,
-            strerror(errno));
-    return 1;
-}
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index 7ab76b8..f853fac 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -2,8 +2,6 @@
 
 #######################################
 # init.rc
-# Only copy init.rc if the target doesn't have its own.
-ifneq ($(TARGET_PROVIDES_INIT_RC),true)
 include $(CLEAR_VARS)
 
 LOCAL_MODULE := init.rc
@@ -12,7 +10,20 @@
 LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)
 
 include $(BUILD_PREBUILT)
+
+#######################################
+# asan.options
+ifeq (address,$(strip $(SANITIZE_TARGET)))
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := asan.options
+LOCAL_MODULE_CLASS := ETC
+LOCAL_SRC_FILES := $(LOCAL_MODULE)
+LOCAL_MODULE_PATH := $(TARGET_OUT)
+
+include $(BUILD_PREBUILT)
 endif
+
 #######################################
 # init.environ.rc
 
@@ -21,12 +32,26 @@
 LOCAL_MODULE := init.environ.rc
 LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)
 
+EXPORT_GLOBAL_ASAN_OPTIONS :=
+ifeq (address,$(strip $(SANITIZE_TARGET)))
+  EXPORT_GLOBAL_ASAN_OPTIONS := export ASAN_OPTIONS include=/system/asan.options
+  LOCAL_REQUIRED_MODULES := asan.options
+endif
 # Put it here instead of in init.rc module definition,
 # because init.rc is conditionally included.
 #
-# create some directories (some are mount points)
-LOCAL_POST_INSTALL_CMD := mkdir -p $(addprefix $(TARGET_ROOT_OUT)/, \
-    sbin dev proc sys system data oem)
+# create some directories (some are mount points) and symlinks
+local_post_install_cmd_base := mkdir -p $(addprefix $(TARGET_ROOT_OUT)/, \
+    sbin dev proc sys system data oem acct cache config storage mnt root); \
+    ln -sf /system/etc $(TARGET_ROOT_OUT)/etc; \
+    ln -sf /sys/kernel/debug $(TARGET_ROOT_OUT)/d; \
+    ln -sf /storage/self/primary $(TARGET_ROOT_OUT)/sdcard
+ifdef BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE
+  LOCAL_POST_INSTALL_CMD := $(local_post_install_cmd_base); mkdir -p $(TARGET_ROOT_OUT)/vendor
+else
+  LOCAL_POST_INSTALL_CMD := $(local_post_install_cmd_base)
+endif
+local_post_install_cmd_base :=
 
 include $(BUILD_SYSTEM)/base_rules.mk
 
@@ -41,6 +66,7 @@
 	@mkdir -p $(dir $@)
 	$(hide) sed -e 's?%BOOTCLASSPATH%?$(PRODUCT_BOOTCLASSPATH)?g' $< >$@
 	$(hide) sed -i -e 's?%SYSTEMSERVERCLASSPATH%?$(PRODUCT_SYSTEM_SERVER_CLASSPATH)?g' $@
+	$(hide) sed -i -e 's?%EXPORT_GLOBAL_ASAN_OPTIONS%?$(EXPORT_GLOBAL_ASAN_OPTIONS)?g' $@
 
 bcp_md5 :=
 bcp_dep :=
diff --git a/rootdir/asan.options b/rootdir/asan.options
new file mode 100644
index 0000000..2f12341
--- /dev/null
+++ b/rootdir/asan.options
@@ -0,0 +1 @@
+allow_user_segv_handler=1:detect_odr_violation=0:alloc_dealloc_mismatch=0:allocator_may_return_null=1
diff --git a/rootdir/init.environ.rc.in b/rootdir/init.environ.rc.in
index b34ea01..32817fa 100644
--- a/rootdir/init.environ.rc.in
+++ b/rootdir/init.environ.rc.in
@@ -9,3 +9,4 @@
     export ASEC_MOUNTPOINT /mnt/asec
     export BOOTCLASSPATH %BOOTCLASSPATH%
     export SYSTEMSERVERCLASSPATH %SYSTEMSERVERCLASSPATH%
+    %EXPORT_GLOBAL_ASAN_OPTIONS%
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 72f2144..cd9a0cc 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -17,6 +17,9 @@
     # Set the security context of /adb_keys if present.
     restorecon /adb_keys
 
+    # Shouldn't be necessary, but sdcard won't start without it. http://b/22568628.
+    mkdir /mnt 0775 root system
+
     start ueventd
 
 on init
@@ -29,8 +32,7 @@
     # Link /vendor to /system/vendor for devices without a vendor partition.
     symlink /system/vendor /vendor
 
-    # Create cgroup mount point for cpu accounting
-    mkdir /acct
+    # Mount cgroup mount point for cpu accounting
     mount cgroup none /acct cpuacct
     mkdir /acct/uid
 
@@ -47,14 +49,8 @@
     chown root system /sys/fs/cgroup/memory/sw/tasks
     chmod 0660 /sys/fs/cgroup/memory/sw/tasks
 
-    mkdir /system
-    mkdir /data 0771 system system
-    mkdir /cache 0770 system cache
-    mkdir /config 0500 root root
-
     # Mount staging areas for devices managed by vold
     # See storage config details at http://source.android.com/tech/storage/
-    mkdir /mnt 0755 root system
     mount tmpfs tmpfs /mnt mode=0755,uid=0,gid=1000
     restorecon_recursive /mnt
 
@@ -277,14 +273,13 @@
     # We restorecon /data in case the userdata partition has been reset.
     restorecon /data
 
-    # Emulated internal storage area
-    mkdir /data/media 0770 media_rw media_rw
-
     # Make sure we have the device encryption key
     start logd
     start vold
     installkey /data
 
+    # Emulated internal storage area
+    mkdir /data/media 0770 media_rw media_rw
     # Start bootcharting as soon as possible after the data partition is
     # mounted to collect more data.
     mkdir /data/bootchart 0755 shell shell
@@ -295,7 +290,6 @@
 
     # create basic filesystem structure
     mkdir /data/misc 01771 system misc
-    mkdir /data/misc/adb 02750 system shell
     mkdir /data/misc/bluedroid 02770 bluetooth net_bt_stack
     # Fix the access permissions and group ownership for 'bt_config.conf'
     chmod 0660 /data/misc/bluedroid/bt_config.conf
@@ -322,7 +316,7 @@
     chmod 0660 /data/misc/wifi/wpa_supplicant.conf
     mkdir /data/local 0751 root root
     mkdir /data/misc/media 0700 media media
-    mkdir /data/misc/vold 0700 root root
+    mkdir /data/misc/boottrace 0771 system shell
 
     # For security reasons, /data/local/tmp should always be empty.
     # Do not place files or directories in /data/local/tmp
@@ -355,7 +349,7 @@
     # the following directory.
     mkdir /data/mediadrm 0770 mediadrm mediadrm
 
-    mkdir /data/adb 0700 root root
+    mkdir /data/anr 0775 system system
 
     # symlink to bugreport storage location
     symlink /data/data/com.android.shell/files/bugreports /data/bugreports
@@ -541,17 +535,6 @@
     critical
     seclabel u:r:ueventd:s0
 
-service logd /system/bin/logd
-    class core
-    socket logd stream 0666 logd logd
-    socket logdr seqpacket 0666 logd logd
-    socket logdw dgram 0222 logd logd
-    group root system
-
-service logd-reinit /system/bin/logd --reinit
-    oneshot
-    disabled
-
 service healthd /sbin/healthd
     class core
     critical
@@ -569,147 +552,10 @@
 on property:ro.debuggable=1
     start console
 
-# adbd is controlled via property triggers in init.<platform>.usb.rc
-service adbd /sbin/adbd --root_seclabel=u:r:su:s0
-    class core
-    socket adbd stream 660 system system
-    disabled
-    seclabel u:r:adbd:s0
-
-# adbd on at boot in emulator
-on property:ro.kernel.qemu=1
-    start adbd
-
-service lmkd /system/bin/lmkd
-    class core
-    critical
-    socket lmkd seqpacket 0660 system system
-
-service servicemanager /system/bin/servicemanager
-    class core
-    user system
-    group system
-    critical
-    onrestart restart healthd
-    onrestart restart zygote
-    onrestart restart media
-    onrestart restart surfaceflinger
-    onrestart restart drm
-
-service vold /system/bin/vold \
-        --blkid_context=u:r:blkid:s0 --blkid_untrusted_context=u:r:blkid_untrusted:s0 \
-        --fsck_context=u:r:fsck:s0 --fsck_untrusted_context=u:r:fsck_untrusted:s0
-    class core
-    socket vold stream 0660 root mount
-    socket cryptd stream 0660 root mount
-    ioprio be 2
-
-service netd /system/bin/netd
-    class main
-    socket netd stream 0660 root system
-    socket dnsproxyd stream 0660 root inet
-    socket mdns stream 0660 root system
-    socket fwmarkd stream 0660 root inet
-
-service debuggerd /system/bin/debuggerd
-    class main
-
-service debuggerd64 /system/bin/debuggerd64
-    class main
-
-service ril-daemon /system/bin/rild
-    class main
-    socket rild stream 660 root radio
-    socket sap_uim_socket1 stream 660 bluetooth bluetooth
-    socket rild-debug stream 660 radio system
-    user root
-    group radio cache inet misc audio log
-
-service surfaceflinger /system/bin/surfaceflinger
-    class core
-    user system
-    group graphics drmrpc
-    onrestart restart zygote
-
-service drm /system/bin/drmserver
-    class main
-    user drm
-    group drm system inet drmrpc
-
-service media /system/bin/mediaserver
-    class main
-    user media
-    group audio camera inet net_bt net_bt_admin net_bw_acct drmrpc mediadrm
-    ioprio rt 4
-
-# One shot invocation to deal with encrypted volume.
-service defaultcrypto /system/bin/vdc --wait cryptfs mountdefaultencrypted
-    disabled
-    oneshot
-    # vold will set vold.decrypt to trigger_restart_framework (default
-    # encryption) or trigger_restart_min_framework (other encryption)
-
-# One shot invocation to encrypt unencrypted volumes
-service encrypt /system/bin/vdc --wait cryptfs enablecrypto inplace default
-    disabled
-    oneshot
-    # vold will set vold.decrypt to trigger_restart_framework (default
-    # encryption)
-
-service bootanim /system/bin/bootanimation
-    class core
-    user graphics
-    group graphics audio
-    disabled
-    oneshot
-
-service gatekeeperd /system/bin/gatekeeperd /data/misc/gatekeeper
-    class late_start
-    user system
-
-service installd /system/bin/installd
-    class main
-    socket installd stream 600 system system
-
 service flash_recovery /system/bin/install-recovery.sh
     class main
     oneshot
 
-service racoon /system/bin/racoon
-    class main
-    socket racoon stream 600 system system
-    # IKE uses UDP port 500. Racoon will setuid to vpn after binding the port.
-    group vpn net_admin inet
-    disabled
-    oneshot
-
-service mtpd /system/bin/mtpd
-    class main
-    socket mtpd stream 600 system system
-    user vpn
-    group vpn net_admin inet net_raw
-    disabled
-    oneshot
-
-service keystore /system/bin/keystore /data/misc/keystore
-    class main
-    user keystore
-    group keystore drmrpc
-
-service dumpstate /system/bin/dumpstate -s
-    class main
-    socket dumpstate stream 0660 shell log
-    disabled
-    oneshot
-
-service mdnsd /system/bin/mdnsd
-    class main
-    user mdnsr
-    group inet net_raw
-    socket mdnsd stream 0660 mdnsr inet
-    disabled
-    oneshot
-
 service uncrypt /system/bin/uncrypt
     class main
     disabled
@@ -719,22 +565,3 @@
     class main
     disabled
     oneshot
-
-service perfprofd /system/xbin/perfprofd
-    class late_start
-    user root
-    oneshot
-
-on property:persist.logd.logpersistd=logcatd
-    # all exec/services are called with umask(077), so no gain beyond 0700
-    mkdir /data/misc/logd 0700 logd log
-    # logd for write to /data/misc/logd, log group for read from pstore (-L)
-    exec - logd log -- /system/bin/logcat -L -b all -v threadtime -v usec -v printable -D -f /data/misc/logd/logcat -r 64 -n 256
-    start logcatd
-
-service logcatd /system/bin/logcat -b all -v threadtime -v usec -v printable -D -f /data/misc/logd/logcat -r 64 -n 256
-    class late_start
-    disabled
-    # logd for write to /data/misc/logd, log group for read from log daemon
-    user logd
-    group log
diff --git a/rootdir/init.trace.rc b/rootdir/init.trace.rc
index 50944e6..cde9c37 100644
--- a/rootdir/init.trace.rc
+++ b/rootdir/init.trace.rc
@@ -16,6 +16,16 @@
     chown root shell /sys/kernel/debug/tracing/events/power/cpu_idle/enable
     chown root shell /sys/kernel/debug/tracing/events/power/clock_set_rate/enable
     chown root shell /sys/kernel/debug/tracing/events/cpufreq_interactive/enable
+    chown root shell /sys/kernel/debug/tracing/events/vmscan/mm_vmscan_direct_reclaim_begin/enable
+    chown root shell /sys/kernel/debug/tracing/events/vmscan/mm_vmscan_direct_reclaim_end/enable
+    chown root shell /sys/kernel/debug/tracing/events/vmscan/mm_vmscan_kswapd_wake/enable
+    chown root shell /sys/kernel/debug/tracing/events/vmscan/mm_vmscan_kswapd_sleep/enable
+    chown root shell /sys/kernel/debug/tracing/events/binder/binder_transaction/enable
+    chown root shell /sys/kernel/debug/tracing/events/binder/binder_transaction_received/enable
+    chown root shell /sys/kernel/debug/tracing/events/binder/binder_lock/enable
+    chown root shell /sys/kernel/debug/tracing/events/binder/binder_locked/enable
+    chown root shell /sys/kernel/debug/tracing/events/binder/binder_unlock/enable
+
     chown root shell /sys/kernel/debug/tracing/tracing_on
 
     chmod 0664 /sys/kernel/debug/tracing/trace_clock
@@ -28,8 +38,25 @@
     chmod 0664 /sys/kernel/debug/tracing/events/power/cpu_idle/enable
     chmod 0664 /sys/kernel/debug/tracing/events/power/clock_set_rate/enable
     chmod 0664 /sys/kernel/debug/tracing/events/cpufreq_interactive/enable
+    chmod 0664 /sys/kernel/debug/tracing/events/vmscan/mm_vmscan_direct_reclaim_begin/enable
+    chmod 0664 /sys/kernel/debug/tracing/events/vmscan/mm_vmscan_direct_reclaim_end/enable
+    chmod 0664 /sys/kernel/debug/tracing/events/vmscan/mm_vmscan_kswapd_wake/enable
+    chmod 0664 /sys/kernel/debug/tracing/events/vmscan/mm_vmscan_kswapd_sleep/enable
     chmod 0664 /sys/kernel/debug/tracing/tracing_on
+    chmod 0664 /sys/kernel/debug/tracing/events/binder/binder_transaction/enable
+    chmod 0664 /sys/kernel/debug/tracing/events/binder/binder_transaction_received/enable
+    chmod 0664 /sys/kernel/debug/tracing/events/binder/binder_lock/enable
+    chmod 0664 /sys/kernel/debug/tracing/events/binder/binder_locked/enable
+    chmod 0664 /sys/kernel/debug/tracing/events/binder/binder_unlock/enable
 
 # Allow only the shell group to read and truncate the kernel trace.
     chown root shell /sys/kernel/debug/tracing/trace
     chmod 0660 /sys/kernel/debug/tracing/trace
+
+on property:persist.debug.atrace.boottrace=1
+    start boottrace
+
+# Run atrace with the categories written in a file
+service boottrace /system/bin/atrace --async_start -f /data/misc/boottrace/categories
+    disabled
+    oneshot
diff --git a/sdcard/sdcard.c b/sdcard/sdcard.c
index a79e2dd..d2d2315 100644
--- a/sdcard/sdcard.c
+++ b/sdcard/sdcard.c
@@ -244,7 +244,7 @@
      * buffer at the same time.  This allows us to share the underlying storage. */
     union {
         __u8 request_buffer[MAX_REQUEST_SIZE];
-        __u8 read_buffer[MAX_READ + PAGESIZE];
+        __u8 read_buffer[MAX_READ + PAGE_SIZE];
     };
 };
 
@@ -1215,7 +1215,7 @@
     __u32 size = req->size;
     __u64 offset = req->offset;
     int res;
-    __u8 *read_buffer = (__u8 *) ((uintptr_t)(handler->read_buffer + PAGESIZE) & ~((uintptr_t)PAGESIZE-1));
+    __u8 *read_buffer = (__u8 *) ((uintptr_t)(handler->read_buffer + PAGE_SIZE) & ~((uintptr_t)PAGE_SIZE-1));
 
     /* Don't access any other fields of hdr or req beyond this point, the read buffer
      * overlaps the request buffer and will clobber data in the request.  This
@@ -1241,7 +1241,7 @@
     struct fuse_write_out out;
     struct handle *h = id_to_ptr(req->fh);
     int res;
-    __u8 aligned_buffer[req->size] __attribute__((__aligned__(PAGESIZE)));
+    __u8 aligned_buffer[req->size] __attribute__((__aligned__(PAGE_SIZE)));
 
     if (req->flags & O_DIRECT) {
         memcpy(aligned_buffer, buffer, req->size);
diff --git a/toolbox/Android.mk b/toolbox/Android.mk
index ad99a39..24f702f 100644
--- a/toolbox/Android.mk
+++ b/toolbox/Android.mk
@@ -2,8 +2,7 @@
 
 
 common_cflags := \
-    -std=gnu99 \
-    -Werror -Wno-unused-parameter \
+    -Werror -Wno-unused-parameter -Wno-unused-const-variable \
     -I$(LOCAL_PATH)/upstream-netbsd/include/ \
     -include bsd-compatibility.h \
 
@@ -43,30 +42,27 @@
     getevent \
     iftop \
     ioctl \
-    ionice \
     log \
     ls \
     lsof \
-    mount \
     nandread \
     newfs_msdos \
     ps \
     prlimit \
-    renice \
     sendevent \
     start \
     stop \
     top \
-    uptime \
-    watchprops \
 
 ALL_TOOLS = $(BSD_TOOLS) $(OUR_TOOLS)
 
 LOCAL_SRC_FILES := \
+    start_stop.cpp \
     toolbox.c \
     $(patsubst %,%.c,$(OUR_TOOLS)) \
 
 LOCAL_CFLAGS += $(common_cflags)
+LOCAL_CONLYFLAGS += -std=gnu99
 
 LOCAL_SHARED_LIBRARIES := \
     libcutils \
diff --git a/toolbox/ionice.c b/toolbox/ionice.c
deleted file mode 100644
index 7abc261..0000000
--- a/toolbox/ionice.c
+++ /dev/null
@@ -1,58 +0,0 @@
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <errno.h>
-
-#include <cutils/iosched_policy.h>
-
-static char *classes[] = {"none", "rt", "be", "idle", NULL};
-
-int ionice_main(int argc, char *argv[])
-{
-    IoSchedClass clazz = IoSchedClass_NONE;
-    int ioprio = 0;
-    int pid;
-
-    if(argc != 2 && argc != 4) {
-        fprintf(stderr, "usage: ionice <pid> [none|rt|be|idle] [prio]\n");
-        return 1;
-    }
-
-    if (!(pid = atoi(argv[1]))) {
-        fprintf(stderr, "Invalid pid specified\n");
-        return 1;
-    }
-
-    if (argc == 2) {
-        if (android_get_ioprio(pid, &clazz, &ioprio)) {
-            fprintf(stderr, "Failed to read priority (%s)\n", strerror(errno));
-            return 1;
-        }
-        fprintf(stdout, "Pid %d, class %s (%d), prio %d\n", pid, classes[clazz], clazz, ioprio);
-        return 0;
-    }
-
-    if (!strcmp(argv[2], "none")) {
-        clazz = IoSchedClass_NONE;
-    } else if (!strcmp(argv[2], "rt")) {
-        clazz = IoSchedClass_RT;
-    } else if (!strcmp(argv[2], "be")) {
-        clazz = IoSchedClass_BE;
-    } else if (!strcmp(argv[2], "idle")) {
-        clazz = IoSchedClass_IDLE;
-    } else {
-        fprintf(stderr, "Unsupported class '%s'\n", argv[2]);
-        return 1;
-    }
-
-    ioprio = atoi(argv[3]);
-
-    printf("Setting pid %d i/o class to %d, prio %d\n", pid, clazz, ioprio);
-    if (android_set_ioprio(pid, clazz, ioprio)) {
-        fprintf(stderr, "Failed to set priority (%s)\n", strerror(errno));
-        return 1;
-    }
-
-    return 0;
-}
diff --git a/toolbox/lsof.c b/toolbox/lsof.c
index 982f5aa..198ca52 100644
--- a/toolbox/lsof.c
+++ b/toolbox/lsof.c
@@ -29,6 +29,7 @@
  * SUCH DAMAGE.
  */
 
+#include <ctype.h>
 #include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
@@ -57,7 +58,7 @@
 
 static void print_header()
 {
-    printf("%-9s %5s %10s %4s %9s %18s %9s %10s %s\n",
+    printf("%-9s %5s %10s %4s   %9s %18s %9s %10s %s\n",
             "COMMAND",
             "PID",
             "USER",
@@ -69,12 +70,12 @@
             "NAME");
 }
 
-static void print_type(char *type, struct pid_info_t* info)
+static void print_symlink(const char* name, const char* path, struct pid_info_t* info)
 {
     static ssize_t link_dest_size;
     static char link_dest[PATH_MAX];
 
-    strlcat(info->path, type, sizeof(info->path));
+    strlcat(info->path, path, sizeof(info->path));
     if ((link_dest_size = readlink(info->path, link_dest, sizeof(link_dest)-1)) < 0) {
         if (errno == ENOENT)
             goto out;
@@ -88,9 +89,53 @@
     if (!strcmp(link_dest, "/"))
         goto out;
 
-    printf("%-9s %5d %10s %4s %9s %18s %9s %10s %s\n",
-            info->cmdline, info->pid, info->user, type,
-            "???", "???", "???", "???", link_dest);
+    const char* fd = name;
+    char rw = ' ';
+    char locks = ' '; // TODO: read /proc/locks
+
+    const char* type = "unknown";
+    char device[32] = "?";
+    char size_off[32] = "?";
+    char node[32] = "?";
+
+    struct stat sb;
+    if (lstat(link_dest, &sb) != -1) {
+        switch ((sb.st_mode & S_IFMT)) {
+          case S_IFSOCK: type = "sock"; break; // TODO: what domain?
+          case S_IFLNK: type = "LINK"; break;
+          case S_IFREG: type = "REG"; break;
+          case S_IFBLK: type = "BLK"; break;
+          case S_IFDIR: type = "DIR"; break;
+          case S_IFCHR: type = "CHR"; break;
+          case S_IFIFO: type = "FIFO"; break;
+        }
+        snprintf(device, sizeof(device), "%d,%d", (int) sb.st_dev, (int) sb.st_rdev);
+        snprintf(node, sizeof(node), "%d", (int) sb.st_ino);
+        snprintf(size_off, sizeof(size_off), "%d", (int) sb.st_size);
+    }
+
+    if (!name) {
+        // We're looking at an fd, so read its flags.
+        fd = path;
+        char fdinfo_path[PATH_MAX];
+        snprintf(fdinfo_path, sizeof(fdinfo_path), "/proc/%d/fdinfo/%s", info->pid, path);
+        FILE* fp = fopen(fdinfo_path, "r");
+        if (fp != NULL) {
+            int pos;
+            unsigned flags;
+
+            if (fscanf(fp, "pos: %d flags: %o", &pos, &flags) == 2) {
+                flags &= O_ACCMODE;
+                if (flags == O_RDONLY) rw = 'r';
+                else if (flags == O_WRONLY) rw = 'w';
+                else rw = 'u';
+            }
+            fclose(fp);
+        }
+    }
+
+    printf("%-9s %5d %10s %4s%c%c %9s %18s %9s %10s %s\n",
+            info->cmdline, info->pid, info->user, fd, rw, locks, type, device, size_off, node, link_dest);
 
 out:
     info->path[info->parent_length] = '\0';
@@ -111,15 +156,14 @@
     if (!maps)
         goto out;
 
-    while (fscanf(maps, "%*x-%*x %*s %zx %s %ld %s\n", &offset, device, &inode,
-            file) == 4) {
+    while (fscanf(maps, "%*x-%*x %*s %zx %s %ld %s\n", &offset, device, &inode, file) == 4) {
         // We don't care about non-file maps
         if (inode == 0 || !strcmp(device, "00:00"))
             continue;
 
-        printf("%-9s %5d %10s %4s %9s %18s %9zd %10ld %s\n",
+        printf("%-9s %5d %10s %4s   %9s %18s %9zd %10ld %s\n",
                 info->cmdline, info->pid, info->user, "mem",
-                "???", device, offset, inode, file);
+                "REG", device, offset, inode, file);
     }
 
     fclose(maps);
@@ -141,7 +185,7 @@
     if (dir == NULL) {
         char msg[BUF_MAX];
         snprintf(msg, sizeof(msg), "%s (opendir: %s)", info->path, strerror(errno));
-        printf("%-9s %5d %10s %4s %9s %18s %9s %10s %s\n",
+        printf("%-9s %5d %10s %4s   %9s %18s %9s %10s %s\n",
                 info->cmdline, info->pid, info->user, "FDS",
                 "", "", "", "", msg);
         goto out;
@@ -152,7 +196,7 @@
         if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
             continue;
 
-        print_type(de->d_name, info);
+        print_symlink(NULL, de->d_name, info);
     }
     closedir(dir);
 
@@ -207,10 +251,9 @@
     strlcpy(info.cmdline, basename(cmdline), sizeof(info.cmdline));
 
     // Read each of these symlinks
-    print_type("cwd", &info);
-    print_type("exe", &info);
-    print_type("root", &info);
-
+    print_symlink("cwd", "cwd", &info);
+    print_symlink("txt", "exe", &info);
+    print_symlink("rtd", "root", &info);
     print_fds(&info);
     print_maps(&info);
 }
diff --git a/toolbox/mount.c b/toolbox/mount.c
deleted file mode 100644
index 66ae8b1..0000000
--- a/toolbox/mount.c
+++ /dev/null
@@ -1,360 +0,0 @@
-/*
- * mount.c, by rmk
- */
-
-#include <sys/mount.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <linux/loop.h>
-
-#define ARRAY_SIZE(x)	(sizeof(x) / sizeof(x[0]))
-
-#define DEFAULT_LOOP_DEVICE "/dev/block/loop0"
-#define LOOPDEV_MAXLEN 64
-
-struct mount_opts {
-	const char str[16];
-	unsigned long rwmask;
-	unsigned long rwset;
-	unsigned long rwnoset;
-};
-
-struct extra_opts {
-	char *str;
-	char *end;
-	int used_size;
-	int alloc_size;
-};
-
-/*
- * These options define the function of "mount(2)".
- */
-#define MS_TYPE	(MS_REMOUNT|MS_BIND|MS_MOVE)
-
-
-static const struct mount_opts options[] = {
-	/* name		mask		set		noset		*/
-	{ "async",	MS_SYNCHRONOUS,	0,		MS_SYNCHRONOUS	},
-	{ "atime",	MS_NOATIME,	0,		MS_NOATIME	},
-	{ "bind",	MS_TYPE,	MS_BIND,	0,		},
-	{ "dev",	MS_NODEV,	0,		MS_NODEV	},
-	{ "diratime",	MS_NODIRATIME,	0,		MS_NODIRATIME	},
-	{ "dirsync",	MS_DIRSYNC,	MS_DIRSYNC,	0		},
-	{ "exec",	MS_NOEXEC,	0,		MS_NOEXEC	},
-	{ "move",	MS_TYPE,	MS_MOVE,	0		},
-	{ "recurse",	MS_REC,		MS_REC,		0		},
-	{ "rec",	MS_REC,		MS_REC,		0		},
-	{ "remount",	MS_TYPE,	MS_REMOUNT,	0		},
-	{ "ro",		MS_RDONLY,	MS_RDONLY,	0		},
-	{ "rw",		MS_RDONLY,	0,		MS_RDONLY	},
-	{ "suid",	MS_NOSUID,	0,		MS_NOSUID	},
-	{ "sync",	MS_SYNCHRONOUS,	MS_SYNCHRONOUS,	0		},
-	{ "verbose",	MS_VERBOSE,	MS_VERBOSE,	0		},
-	{ "unbindable",	MS_UNBINDABLE,	MS_UNBINDABLE,	0		},
-	{ "private",	MS_PRIVATE,	MS_PRIVATE,	0		},
-	{ "slave",	MS_SLAVE,	MS_SLAVE,	0		},
-	{ "shared",	MS_SHARED,	MS_SHARED,	0		},
-};
-
-static void add_extra_option(struct extra_opts *extra, char *s)
-{
-	int len = strlen(s);
-	int newlen;
-
-	if (extra->str)
-	       len++;			/* +1 for ',' */
-	newlen = extra->used_size + len;
-
-	if (newlen >= extra->alloc_size) {
-		char *new;
-
-		new = realloc(extra->str, newlen + 1);	/* +1 for NUL */
-		if (!new)
-			return;
-
-		extra->str = new;
-		extra->end = extra->str + extra->used_size;
-		extra->alloc_size = newlen + 1;
-	}
-
-	if (extra->used_size) {
-		*extra->end = ',';
-		extra->end++;
-	}
-	strcpy(extra->end, s);
-	extra->used_size += len;
-
-}
-
-static unsigned long
-parse_mount_options(char *arg, unsigned long rwflag, struct extra_opts *extra, int* loop, char *loopdev)
-{
-	char *s;
-    
-    *loop = 0;
-	while ((s = strsep(&arg, ",")) != NULL) {
-		char *opt = s;
-		unsigned int i;
-		int res, no = s[0] == 'n' && s[1] == 'o';
-
-		if (no)
-			s += 2;
-
-        if (strncmp(s, "loop=", 5) == 0) {
-            *loop = 1;
-            strlcpy(loopdev, s + 5, LOOPDEV_MAXLEN);
-            continue;
-        }
-
-        if (strcmp(s, "loop") == 0) {
-            *loop = 1;
-            strlcpy(loopdev, DEFAULT_LOOP_DEVICE, LOOPDEV_MAXLEN);
-            continue;
-        }
-		for (i = 0, res = 1; i < ARRAY_SIZE(options); i++) {
-			res = strcmp(s, options[i].str);
-
-			if (res == 0) {
-				rwflag &= ~options[i].rwmask;
-				if (no)
-					rwflag |= options[i].rwnoset;
-				else
-					rwflag |= options[i].rwset;
-			}
-			if (res <= 0)
-				break;
-		}
-
-		if (res != 0 && s[0])
-			add_extra_option(extra, opt);
-	}
-
-	return rwflag;
-}
-
-/*
- * Mark the given block device as read-write, using the BLKROSET ioctl.
- */
-static void fs_set_blk_rw(const char *blockdev)
-{
-    int fd;
-    int OFF = 0;
-
-    fd = open(blockdev, O_RDONLY);
-    if (fd < 0) {
-        // should never happen
-        return;
-    }
-
-    ioctl(fd, BLKROSET, &OFF);
-    close(fd);
-}
-
-static char *progname;
-
-static struct extra_opts extra;
-static unsigned long rwflag;
-
-static int
-do_mount(char *dev, char *dir, char *type, unsigned long rwflag, void *data, int loop,
-         char *loopdev)
-{
-	char *s;
-	int error = 0;
-
-    if (loop) {
-        int file_fd, device_fd;
-        int flags;
-
-        flags = (rwflag & MS_RDONLY) ? O_RDONLY : O_RDWR;
-        
-        file_fd = open(dev, flags);
-        if (file_fd < 0) {
-            perror("open backing file failed");
-            return 1;
-        }
-        device_fd = open(loopdev, flags);
-        if (device_fd < 0) {
-            perror("open loop device failed");
-            close(file_fd);
-            return 1;
-        }
-        if (ioctl(device_fd, LOOP_SET_FD, file_fd) < 0) {
-            perror("ioctl LOOP_SET_FD failed");
-            close(file_fd);
-            close(device_fd);
-            return 1;
-        }
-
-        close(file_fd);
-        close(device_fd);
-        dev = loopdev;
-    }
-
-    if ((rwflag & MS_RDONLY) == 0) {
-        fs_set_blk_rw(dev);
-    }
-
-	while ((s = strsep(&type, ",")) != NULL) {
-retry:
-		if (mount(dev, dir, s, rwflag, data) == -1) {
-			error = errno;
-			/*
-			 * If the filesystem is not found, or the
-			 * superblock is invalid, try the next.
-			 */
-			if (error == ENODEV || error == EINVAL)
-				continue;
-
-			/*
-			 * If we get EACCESS, and we're trying to
-			 * mount readwrite and this isn't a remount,
-			 * try read only.
-			 */
-			if (error == EACCES &&
-			    (rwflag & (MS_REMOUNT|MS_RDONLY)) == 0) {
-				rwflag |= MS_RDONLY;
-				goto retry;
-			}
-			break;
-		}
-	}
-
-	if (error) {
-		errno = error;
-		perror("mount");
-		return 255;
-	}
-
-	return 0;
-}
-
-static int print_mounts()
-{
-    FILE* f;
-    int length;
-    char buffer[100];
-    
-    f = fopen("/proc/mounts", "r");
-    if (!f) {
-        fprintf(stdout, "could not open /proc/mounts\n");
-        return -1;
-    }
-
-    do {
-        length = fread(buffer, 1, 100, f);
-        if (length > 0)
-            fwrite(buffer, 1, length, stdout);
-    } while (length > 0);
-
-    fclose(f);
-    return 0;
-}
-
-static int get_mounts_dev_dir(const char *arg, char **dev, char **dir)
-{
-	FILE *f;
-	char mount_dev[256];
-	char mount_dir[256];
-	char mount_type[256];
-	char mount_opts[256];
-	int mount_freq;
-	int mount_passno;
-	int match;
-
-	f = fopen("/proc/mounts", "r");
-	if (!f) {
-		fprintf(stdout, "could not open /proc/mounts\n");
-		return -1;
-	}
-
-	do {
-		match = fscanf(f, "%255s %255s %255s %255s %d %d\n",
-					   mount_dev, mount_dir, mount_type,
-					   mount_opts, &mount_freq, &mount_passno);
-		mount_dev[255] = 0;
-		mount_dir[255] = 0;
-		mount_type[255] = 0;
-		mount_opts[255] = 0;
-		if (match == 6 &&
-			(strcmp(arg, mount_dev) == 0 ||
-			 strcmp(arg, mount_dir) == 0)) {
-			*dev = strdup(mount_dev);
-			*dir = strdup(mount_dir);
-			fclose(f);
-			return 0;
-		}
-	} while (match != EOF);
-
-	fclose(f);
-	return -1;
-}
-
-int mount_main(int argc, char *argv[])
-{
-	char *type = NULL;
-	char *dev = NULL;
-	char *dir = NULL;
-	int c;
-	int loop = 0;
-	char loopdev[LOOPDEV_MAXLEN];
-
-	progname = argv[0];
-	rwflag = MS_VERBOSE;
-	
-	// mount with no arguments is equivalent to "cat /proc/mounts"
-	if (argc == 1) return print_mounts();
-
-	do {
-		c = getopt(argc, argv, "o:rt:w");
-		if (c == EOF)
-			break;
-		switch (c) {
-		case 'o':
-			rwflag = parse_mount_options(optarg, rwflag, &extra, &loop, loopdev);
-			break;
-		case 'r':
-			rwflag |= MS_RDONLY;
-			break;
-		case 't':
-			type = optarg;
-			break;
-		case 'w':
-			rwflag &= ~MS_RDONLY;
-			break;
-		case '?':
-			fprintf(stderr, "%s: invalid option -%c\n",
-				progname, optopt);
-			exit(1);
-		}
-	} while (1);
-
-	/*
-	 * If remount, bind or move was specified, then we don't
-	 * have a "type" as such.  Use the dummy "none" type.
-	 */
-	if (rwflag & MS_TYPE)
-		type = "none";
-
-	if (optind + 2 == argc) {
-		dev = argv[optind];
-		dir = argv[optind + 1];
-	} else if (optind + 1 == argc && rwflag & MS_REMOUNT) {
-		get_mounts_dev_dir(argv[optind], &dev, &dir);
-	}
-
-	if (dev == NULL || dir == NULL || type == NULL) {
-		fprintf(stderr, "Usage: %s [-r] [-w] [-o options] [-t type] "
-			"device directory\n", progname);
-		exit(1);
-	}
-
-	return do_mount(dev, dir, type, rwflag, extra.str, loop, loopdev);
-	/* We leak dev and dir in some cases, but we're about to exit */
-}
diff --git a/toolbox/renice.c b/toolbox/renice.c
deleted file mode 100644
index 99a06f4..0000000
--- a/toolbox/renice.c
+++ /dev/null
@@ -1,161 +0,0 @@
-/*
- * Copyright (c) 2008, The Android Open Source Project
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *  * Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *  * Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in
- *    the documentation and/or other materials provided with the 
- *    distribution.
- *  * Neither the name of Google, Inc. nor the names of its contributors
- *    may be used to endorse or promote products derived from this
- *    software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#include <stdio.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/time.h>
-#include <sys/resource.h>
-#include <sched.h>
-#include <getopt.h>
-
-static void
-usage(const char *s)
-{
-    fprintf(stderr, "USAGE: %s [[-r] [-t TYPE] priority pids ...] [-g pid]\n", s);
-    exit(EXIT_FAILURE);
-}
-
-void print_prio(pid_t pid)
-{
-    int sched;
-    struct sched_param sp;
-
-    printf("pid %d's priority: %d\n", pid, getpriority(PRIO_PROCESS, pid));
-
-    printf("scheduling class: ");
-    sched = sched_getscheduler(pid);
-    switch (sched) {
-    case SCHED_FIFO:
-        printf("FIFO\n");
-        break;
-    case SCHED_RR:
-        printf("RR\n");
-        break;
-    case SCHED_OTHER:
-        printf("Normal\n");
-        break;
-    case -1:
-        perror("sched_getscheduler");
-        break;
-    default:
-        printf("Unknown\n");
-    }
-
-    sched_getparam(pid, &sp);
-    printf("RT prio: %d (of %d to %d)\n", sp.sched_priority,
-           sched_get_priority_min(sched), sched_get_priority_max(sched));
-}
-
-int get_sched(char *str)
-{
-    if (strcasecmp(str, "RR") == 0)
-        return SCHED_RR;
-    else if (strcasecmp(str, "FIFO") == 0)
-        return SCHED_FIFO;
-    else if (strcasecmp(str, "NORMAL") == 0)
-        return SCHED_OTHER;
-    else if (strcasecmp(str, "OTHER") == 0)
-        return SCHED_OTHER;
-    return SCHED_RR;
-}
-
-int renice_main(int argc, char *argv[])
-{
-    int prio;
-    int realtime = 0;
-    int opt;
-    int sched = SCHED_RR;
-    char *cmd = argv[0];
-
-    do {
-        opt = getopt(argc, argv, "rt:g:");
-        if (opt == -1)
-            break;
-        switch (opt) {
-        case 'r':
-            // do realtime priority adjustment
-            realtime = 1;
-            break;
-        case 't':
-            sched = get_sched(optarg);
-            break;
-        case 'g':
-            print_prio(atoi(optarg));
-            return 0;
-        default:
-            usage(cmd);
-        }
-    } while (1);
-
-    argc -= optind;
-    argv += optind;
-
-    if (argc < 1)
-        usage(cmd);
-
-    prio = atoi(argv[0]);
-    argc--;
-    argv++;
-
-    if (argc < 1)
-        usage(cmd);
-
-    while(argc) {
-        pid_t pid;
-
-        pid = atoi(argv[0]);
-        argc--;
-        argv++;
-
-        if (realtime) {
-            struct sched_param sp = { .sched_priority = prio };
-            int ret;
-
-            ret = sched_setscheduler(pid, sched, &sp);
-            if (ret) {
-                perror("sched_set_scheduler");
-                exit(EXIT_FAILURE);
-            }
-        } else {
-            int ret;
-
-            ret = setpriority(PRIO_PROCESS, pid, prio);
-            if (ret) {
-                perror("setpriority");
-                exit(EXIT_FAILURE);
-            }
-        }
-    }
-
-    return 0;
-}
diff --git a/toolbox/start.c b/toolbox/start.c
index 6c8a3f2..cca5fef 100644
--- a/toolbox/start.c
+++ b/toolbox/start.c
@@ -1,21 +1 @@
-
-#include <string.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-#include <cutils/properties.h>
-
-int start_main(int argc, char *argv[])
-{
-    if(argc > 1) {
-        property_set("ctl.start", argv[1]);
-    } else {
-        /* defaults to starting the common services stopped by stop.c */
-        property_set("ctl.start", "netd");
-        property_set("ctl.start", "surfaceflinger");
-        property_set("ctl.start", "zygote");
-        property_set("ctl.start", "zygote_secondary");
-    }
-
-    return 0;
-}
+/* Needed by Android.mk. Actual code in start_stop.cpp. */
diff --git a/toolbox/start_stop.cpp b/toolbox/start_stop.cpp
new file mode 100644
index 0000000..dc48c0c
--- /dev/null
+++ b/toolbox/start_stop.cpp
@@ -0,0 +1,43 @@
+#include <error.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <cutils/properties.h>
+
+static const char* services[] = {
+  "netd",
+  "surfaceflinger",
+  "zygote",
+  "zygote_secondary",
+};
+
+static int start_stop(bool start, int argc, char* argv[]) {
+  if (getuid() != 0) error(1, 0, "must be root");
+  const char* property = start ? "ctl.start" : "ctl.stop";
+  if (argc > 2) {
+    error(1, 0, "usage: %s [SERVICE]\n", argv[0]);
+  } else if (argc == 2) {
+    property_set(property, argv[1]);
+  } else {
+    if (start) {
+      for (size_t i = 0; i < sizeof(services)/sizeof(services[0]); ++i) {
+        property_set(property, services[i]);
+      }
+    } else {
+      for (int i = sizeof(services)/sizeof(services[0]) - 1; i >= 0; --i) {
+        property_set(property, services[i]);
+      }
+    }
+  }
+  return 0;
+}
+
+extern "C" int start_main(int argc, char* argv[]) {
+  return start_stop(true, argc, argv);
+}
+
+extern "C" int stop_main(int argc, char* argv[]) {
+  return start_stop(false, argc, argv);
+}
diff --git a/toolbox/stop.c b/toolbox/stop.c
index 5e3ce3c..cca5fef 100644
--- a/toolbox/stop.c
+++ b/toolbox/stop.c
@@ -1,19 +1 @@
-#include <stdio.h>
-#include <string.h>
-
-#include <cutils/properties.h>
-
-int stop_main(int argc, char *argv[])
-{
-    if(argc > 1) {
-        property_set("ctl.stop", argv[1]);
-    } else{
-        /* defaults to stopping the common services */
-        property_set("ctl.stop", "zygote_secondary");
-        property_set("ctl.stop", "zygote");
-        property_set("ctl.stop", "surfaceflinger");
-        property_set("ctl.stop", "netd");
-    }
-
-    return 0;
-}
+/* Needed by Android.mk. Actual code in start_stop.cpp. */
diff --git a/toolbox/uptime.c b/toolbox/uptime.c
deleted file mode 100644
index ebfb15e..0000000
--- a/toolbox/uptime.c
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright (c) 2010, The Android Open Source Project
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *  * Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *  * Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in
- *    the documentation and/or other materials provided with the
- *    distribution.
- *  * Neither the name of Google, Inc. nor the names of its contributors
- *    may be used to endorse or promote products derived from this
- *    software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#include <errno.h>
-#include <stdio.h>
-#include <string.h>
-#include <time.h>
-
-static void format_time(int time, char* buffer) {
-    int seconds = time % 60;
-    time /= 60;
-    int minutes = time % 60;
-    time /= 60;
-    int hours = time % 24;
-    int days = time / 24;
-
-    if (days > 0) {
-        sprintf(buffer, "%d day%s, %02d:%02d:%02d", days, (days == 1) ? "" : "s", hours, minutes, seconds);
-    } else {
-        sprintf(buffer, "%02d:%02d:%02d", hours, minutes, seconds);
-    }
-}
-
-int uptime_main(int argc __attribute__((unused)), char *argv[] __attribute__((unused))) {
-    FILE* file = fopen("/proc/uptime", "r");
-    if (!file) {
-        fprintf(stderr, "Could not open /proc/uptime\n");
-        return -1;
-    }
-    float idle_time;
-    if (fscanf(file, "%*f %f", &idle_time) != 1) {
-        fprintf(stderr, "Could not parse /proc/uptime\n");
-        fclose(file);
-        return -1;
-    }
-    fclose(file);
-
-    struct timespec up_timespec;
-    if (clock_gettime(CLOCK_MONOTONIC, &up_timespec) == -1) {
-        fprintf(stderr, "Could not get monotonic time: %s\n", strerror(errno));
-	return -1;
-    }
-    float up_time = up_timespec.tv_sec + up_timespec.tv_nsec / 1e9;
-
-    struct timespec elapsed_timespec;
-    if (clock_gettime(CLOCK_BOOTTIME, &elapsed_timespec) == -1) {
-        fprintf(stderr, "Could not get boot time: %s\n", strerror(errno));
-        return -1;
-    }
-    int elapsed = elapsed_timespec.tv_sec;
-
-    char up_string[100], idle_string[100], sleep_string[100];
-    format_time(elapsed, up_string);
-    format_time((int)idle_time, idle_string);
-    format_time((int)(elapsed - up_time), sleep_string);
-    printf("up time: %s, idle time: %s, sleep time: %s\n", up_string, idle_string, sleep_string);
-
-    return 0;
-}
diff --git a/toolbox/watchprops.c b/toolbox/watchprops.c
deleted file mode 100644
index cd62922..0000000
--- a/toolbox/watchprops.c
+++ /dev/null
@@ -1,92 +0,0 @@
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include <errno.h>
-
-#include <cutils/properties.h>
-#include <cutils/hashmap.h>
-
-#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
-#include <sys/_system_properties.h>
-
-static int str_hash(void *key)
-{
-    return hashmapHash(key, strlen(key));
-}
-
-static bool str_equals(void *keyA, void *keyB)
-{
-    return strcmp(keyA, keyB) == 0;
-}
-
-static void announce(char *name, char *value)
-{
-    unsigned char *x;
-    
-    for(x = (unsigned char *)value; *x; x++) {
-        if((*x < 32) || (*x > 127)) *x = '.';
-    }
-
-    fprintf(stderr,"%10d %s = '%s'\n", (int) time(0), name, value);
-}
-
-static void add_to_watchlist(Hashmap *watchlist, const char *name,
-        const prop_info *pi)
-{
-    char *key = strdup(name);
-    unsigned *value = malloc(sizeof(unsigned));
-    if (!key || !value)
-        exit(1);
-
-    *value = __system_property_serial(pi);
-    hashmapPut(watchlist, key, value);
-}
-
-static void populate_watchlist(const prop_info *pi, void *cookie)
-{
-    Hashmap *watchlist = cookie;
-    char name[PROP_NAME_MAX];
-    char value_unused[PROP_VALUE_MAX];
-
-    __system_property_read(pi, name, value_unused);
-    add_to_watchlist(watchlist, name, pi);
-}
-
-static void update_watchlist(const prop_info *pi, void *cookie)
-{
-    Hashmap *watchlist = cookie;
-    char name[PROP_NAME_MAX];
-    char value[PROP_VALUE_MAX];
-    unsigned *serial;
-
-    __system_property_read(pi, name, value);
-    serial = hashmapGet(watchlist, name);
-    if (!serial) {
-        add_to_watchlist(watchlist, name, pi);
-        announce(name, value);
-    } else {
-        unsigned tmp = __system_property_serial(pi);
-        if (*serial != tmp) {
-            *serial = tmp;
-            announce(name, value);
-        }
-    }
-}
-
-int watchprops_main(int argc, char *argv[])
-{
-    unsigned serial;
-    
-    Hashmap *watchlist = hashmapCreate(1024, str_hash, str_equals);
-    if (!watchlist)
-        exit(1);
-
-    __system_property_foreach(populate_watchlist, watchlist);
-
-    for(serial = 0;;) {
-        serial = __system_property_wait_any(serial);
-        __system_property_foreach(update_watchlist, watchlist);
-    }
-    return 0;
-}