am 4b70dd4c: am 26f0f657: Merge "init.rc: add healthd to system group to allow write to /dev/cpuset" into mnc-dev

* commit '4b70dd4c8f5c5c2baed8cb1f8436bf74971e20c6':
  init.rc: add healthd to system group to allow write to /dev/cpuset
diff --git a/adb/Android.mk b/adb/Android.mk
index 425bf9b..355bb7f 100644
--- a/adb/Android.mk
+++ b/adb/Android.mk
@@ -6,16 +6,17 @@
 LOCAL_PATH:= $(call my-dir)
 
 ifeq ($(HOST_OS),windows)
-  adb_host_clang := false  # libc++ for mingw not ready yet.
+    adb_host_clang := false  # libc++ for mingw not ready yet.
 else
-  adb_host_clang := true
+    adb_host_clang := true
 endif
 
 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)"' \
 
 # libadb
@@ -45,9 +46,13 @@
 
 LIBADB_CFLAGS := \
     $(ADB_COMMON_CFLAGS) \
-    -Wno-missing-field-initializers \
     -fvisibility=hidden \
 
+LIBADB_linux_CFLAGS := \
+    -std=c++14 \
+
+LIBADB_CFLAGS += $(LIBADB_$(HOST_OS)_CFLAGS)
+
 LIBADB_darwin_SRC_FILES := \
     fdevent.cpp \
     get_my_path_darwin.cpp \
@@ -77,6 +82,10 @@
 
 LOCAL_SHARED_LIBRARIES := libbase
 
+# 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)
@@ -91,8 +100,8 @@
 LOCAL_SHARED_LIBRARIES := libbase
 
 # 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/
@@ -109,6 +118,7 @@
 LOCAL_SHARED_LIBRARIES := liblog libbase libcutils
 include $(BUILD_NATIVE_TEST)
 
+ifneq ($(HOST_OS),windows)
 include $(CLEAR_VARS)
 LOCAL_CLANG := $(adb_host_clang)
 LOCAL_MODULE := adb_test
@@ -121,14 +131,15 @@
     libcutils \
 
 ifeq ($(HOST_OS),linux)
-  LOCAL_LDLIBS += -lrt -ldl -lpthread
+    LOCAL_LDLIBS += -lrt -ldl -lpthread
 endif
 
 ifeq ($(HOST_OS),darwin)
-  LOCAL_LDLIBS += -framework CoreFoundation -framework IOKit
+    LOCAL_LDLIBS += -framework CoreFoundation -framework IOKit
 endif
 
 include $(BUILD_HOST_NATIVE_TEST)
+endif
 
 # adb device tracker (used by ddms) test tool
 # =========================================================
@@ -150,24 +161,24 @@
 include $(CLEAR_VARS)
 
 ifeq ($(HOST_OS),linux)
-  LOCAL_LDLIBS += -lrt -ldl -lpthread
-  LOCAL_CFLAGS += -DWORKAROUND_BUG6558362
+    LOCAL_LDLIBS += -lrt -ldl -lpthread
+    LOCAL_CFLAGS += -DWORKAROUND_BUG6558362
 endif
 
 ifeq ($(HOST_OS),darwin)
-  LOCAL_LDLIBS += -lpthread -framework CoreFoundation -framework IOKit -framework Carbon
-  LOCAL_CFLAGS += -Wno-sizeof-pointer-memaccess -Wno-unused-parameter
+    LOCAL_LDLIBS += -lpthread -framework CoreFoundation -framework IOKit -framework Carbon
+    LOCAL_CFLAGS += -Wno-sizeof-pointer-memaccess -Wno-unused-parameter
 endif
 
 ifeq ($(HOST_OS),windows)
-  LOCAL_LDLIBS += -lws2_32 -lgdi32
-  EXTRA_STATIC_LIBS := AdbWinApi
+    LOCAL_LDLIBS += -lws2_32 -lgdi32
+    EXTRA_STATIC_LIBS := AdbWinApi
 endif
 
 LOCAL_CLANG := $(adb_host_clang)
 
 LOCAL_SRC_FILES := \
-    adb_main.cpp \
+    client/main.cpp \
     console.cpp \
     commandline.cpp \
     adb_client.cpp \
@@ -219,7 +230,7 @@
 LOCAL_CLANG := true
 
 LOCAL_SRC_FILES := \
-    adb_main.cpp \
+    daemon/main.cpp \
     services.cpp \
     file_sync_service.cpp \
     framebuffer_service.cpp \
@@ -251,10 +262,10 @@
     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/__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 f64b19f..97ce125 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -31,13 +31,17 @@
 #include <time.h>
 
 #include <string>
+#include <vector>
+#include <unordered_map>
 
+#include <base/logging.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 +52,25 @@
 #include <sys/mount.h>
 #endif
 
-ADB_MUTEX_DEFINE( D_lock );
+ADB_MUTEX_DEFINE(D_lock);
 
 int HOST = 0;
 
 #if !ADB_HOST
-const char *adb_device_banner = "device";
+const char* adb_device_banner = "device";
+static android::base::LogdLogger gLogdLogger;
 #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
+}
+
+void fatal(const char *fmt, ...) {
     va_list ap;
     va_start(ap, fmt);
     fprintf(stderr, "error: ");
@@ -67,8 +80,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 +91,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 +101,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 +147,65 @@
 #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}};
 
+    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;
+        }
+
+        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;
+        }
+    }
+}
+
+void adb_trace_init(char** argv) {
+    // Don't open log file if no tracing, since this will block
+    // the crypto unmount of /data
+    const std::string trace_setting = get_trace_setting();
     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);
-        }
-        len = q - p;
-
-        for (tagn = 0; tags[tagn].tag != NULL; tagn++) {
-            int  taglen = strlen(tags[tagn].tag);
-
-            if (len == taglen && !memcmp(tags[tagn].tag, p, len)) {
-                int  flag = tags[tagn].flag;
-                if (flag == 0) {
-                    adb_trace_mask = ~0;
-                    return;
-                }
-                adb_trace_mask |= (1 << flag);
-                break;
-            }
-        }
-        p = q;
-        if (*p)
-            p++;
-    }
-
 #if !ADB_HOST
-    start_device_log();
+    if (isatty(STDOUT_FILENO) == 0) {
+        start_device_log();
+    }
 #endif
+
+    setup_trace_mask();
+    android::base::InitLogging(argv, AdbLogger);
 }
 
 apacket* get_apacket(void)
@@ -323,10 +334,10 @@
     D("Calling send_connect \n");
     apacket *cp = get_apacket();
     cp->msg.command = A_CNXN;
-    cp->msg.arg0 = A_VERSION;
-    cp->msg.arg1 = MAX_PAYLOAD;
+    cp->msg.arg0 = t->get_protocol_version();
+    cp->msg.arg1 = t->get_max_payload();
     cp->msg.data_length = fill_connect_data((char *)cp->data,
-                                            sizeof(cp->data));
+                                            MAX_PAYLOAD_V1);
     send_packet(cp, t);
 }
 
@@ -369,24 +380,24 @@
 
     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;
     }
 }
 
@@ -406,19 +417,19 @@
             send_packet(p, t);
             if(HOST) send_connect(t);
         } 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;
+        if(t->connection_state != kCsOffline) {
+            t->connection_state = kCsOffline;
             handle_offline(t);
         }
 
+        t->update_version(p->msg.arg0, p->msg.arg1);
         parse_banner(reinterpret_cast<const char*>(p->data), t);
 
         if (HOST || !auth_required) {
@@ -431,7 +442,7 @@
 
     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);
@@ -542,6 +553,7 @@
     /* 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                nul_read, nul_write;
     HANDLE                pipe_read, pipe_write;
     HANDLE                stdout_handle, stderr_handle;
     SECURITY_ATTRIBUTES   sa;
@@ -554,10 +566,40 @@
     sa.lpSecurityDescriptor = NULL;
     sa.bInheritHandle = TRUE;
 
+    /* Redirect stdin and stderr to Windows /dev/null. If we instead pass our
+     * stdin/stderr handles and they are console handles, when the adb server
+     * starts up, the C Runtime will see console handles for a process that
+     * isn't connected to a console and it will configure stderr to be closed.
+     * At that point, freopen() could be used to reopen stderr, 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/stderr to `nul' and then the C Runtime acts as expected.
+     */
+    nul_read = CreateFileW(L"nul", GENERIC_READ,
+                           FILE_SHARE_READ | FILE_SHARE_WRITE, &sa,
+                           OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+    if (nul_read == INVALID_HANDLE_VALUE) {
+        fprintf(stderr, "CreateFileW(nul, GENERIC_READ) failure, error %ld\n",
+                GetLastError());
+        return -1;
+    }
+
+    nul_write = CreateFileW(L"nul", GENERIC_WRITE,
+                            FILE_SHARE_READ | FILE_SHARE_WRITE, &sa,
+                            OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+    if (nul_write == INVALID_HANDLE_VALUE) {
+        fprintf(stderr, "CreateFileW(nul, GENERIC_WRITE) failure, error %ld\n",
+                GetLastError());
+        CloseHandle(nul_read);
+        return -1;
+    }
+
     /* 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() );
+        CloseHandle(nul_read);
+        CloseHandle(nul_write);
         return -1;
     }
 
@@ -585,9 +627,9 @@
 
     ZeroMemory( &startup, sizeof(startup) );
     startup.cb = sizeof(startup);
-    startup.hStdInput  = GetStdHandle( STD_INPUT_HANDLE );
+    startup.hStdInput  = nul_read;
     startup.hStdOutput = pipe_write;
-    startup.hStdError  = GetStdHandle( STD_ERROR_HANDLE );
+    startup.hStdError  = nul_write;
     startup.dwFlags    = STARTF_USESTDHANDLES;
 
     ZeroMemory( &pinfo, sizeof(pinfo) );
@@ -610,6 +652,8 @@
             &startup,                 /* startup info, i.e. std handles */
             &pinfo );
 
+    CloseHandle( nul_read );
+    CloseHandle( nul_write );
     CloseHandle( pipe_write );
 
     if (!ret) {
@@ -696,7 +740,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 +762,50 @@
         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;
+            if (android::base::StartsWith(service, "norebind:")) {
+                no_rebind = true;
+                service += 9;
+            }
+        } else {
+            service += 8;
         }
 
-        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);
+        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);
         }
         if (r == INSTALL_STATUS_OK) {
 #if ADB_HOST
@@ -780,7 +818,7 @@
 
         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));
@@ -788,7 +826,9 @@
           case INSTALL_STATUS_CANNOT_REBIND:
             message = android::base::StringPrintf("cannot rebind existing socket: %s", strerror(errno));
             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 +836,30 @@
     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();
         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 +872,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 +890,69 @@
             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;
     }
 
     // remove TCP transport
     if (!strncmp(service, "disconnect:", 11)) {
-        char buffer[4096];
-        memset(buffer, 0, sizeof(buffer));
-        char* serial = service + 11;
-        if (serial[0] == 0) {
+        const std::string address(service + 11);
+        if (address.empty()) {
             // 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);
-            }
+            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()));
+        }
+        unregister_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->connection_state_name());
-        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..309b0e9 100644
--- a/adb/adb.h
+++ b/adb/adb.h
@@ -20,10 +20,16 @@
 #include <limits.h>
 #include <sys/types.h>
 
+#include <base/macros.h>
+
+#include <string>
+
 #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
@@ -43,7 +49,7 @@
 // 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 +141,8 @@
 
         /* A socket is bound to atransport */
     atransport *transport;
+
+    size_t get_max_payload() const;
 };
 
 
@@ -152,65 +160,97 @@
 };
 
 
-/* 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;
+enum ConnectionState {
+    kCsAny = -1,
+    kCsOffline = 0,
+    kCsBootloader,
+    kCsDevice,
+    kCsHost,
+    kCsRecovery,
+    kCsNoPerm,  // Insufficient permissions to communicate with the device.
+    kCsSideload,
+    kCsUnauthorized,
+};
 
-    int (*read_from_remote)(apacket *p, atransport *t);
-    int (*write_to_remote)(apacket *p, atransport *t);
-    void (*close)(atransport *t);
-    void (*kick)(atransport *t);
+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.
 
-    int fd;
-    int transport_socket;
+    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;
-    int ref_count;
-    unsigned sync_token;
-    int connection_state;
-    int online;
-    transport_type type;
+    int 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;
-    int sfd;
+    // USB handle or socket fd as needed.
+    usb_handle* usb = nullptr;
+    int sfd = -1;
 
-        /* used to identify transports for clients */
-    char *serial;
-    char *product;
-    char *model;
-    char *device;
-    char *devpath;
-    int adb_port; // Use for emulators (local transport)
+    // 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;
 
-        /* a list of adisconnect callbacks called when the transport is kicked */
-    int          kicked;
-    adisconnect  disconnects;
+    // A list of adisconnect callbacks called when the transport is kicked.
+    adisconnect disconnects = {};
 
-    void *key;
-    unsigned char token[TOKEN_SIZE];
+    void* key = nullptr;
+    unsigned char token[TOKEN_SIZE] = {};
     fdevent auth_fde;
-    unsigned failed_auth_attempts;
+    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;
+
+private:
+    int protocol_version;
+    size_t max_payload;
+
+    DISALLOW_COPY_AND_ASSIGN(atransport);
 };
 
 
@@ -266,7 +306,7 @@
 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 +324,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 +359,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 +376,13 @@
 
 int adb_commandline(int argc, const char **argv);
 
-int connection_state(atransport *t);
-
-#define CS_ANY       -1
-#define CS_OFFLINE    0
-#define CS_BOOTLOADER 1
-#define CS_DEVICE     2
-#define CS_HOST       3
-#define CS_RECOVERY   4
-#define CS_NOPERM     5 /* Insufficient permissions to communicate with the device */
-#define CS_SIDELOAD   6
-#define CS_UNAUTHORIZED 7
+ConnectionState connection_state(atransport *t);
 
 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,7 +397,7 @@
 #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);
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..be28202 100644
--- a/adb/adb_auth_client.cpp
+++ b/adb/adb_auth_client.cpp
@@ -54,7 +54,7 @@
 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;
 
@@ -191,7 +191,7 @@
 
 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) {
@@ -212,7 +212,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..b6bb00c 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;
     }
 
@@ -323,7 +323,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 +339,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 +414,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..418662c 100644
--- a/adb/adb_client.cpp
+++ b/adb/adb_client.cpp
@@ -33,10 +33,12 @@
 
 #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;
@@ -64,7 +66,7 @@
     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 +82,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,20 +147,27 @@
 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;
     if (__adb_server_name) {
-        fd = socket_network_client(__adb_server_name, __adb_server_port, SOCK_STREAM);
+        std::string reason;
+        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;
+        if (fd == -1) {
+            *error = perror_str("cannot connect to daemon");
+            return -2;
+        }
     }
 
     if (memcmp(&service[0],"host",4) != 0 && switch_socket_transport(fd, error)) {
@@ -273,7 +252,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 +265,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_listeners.cpp b/adb/adb_listeners.cpp
index 3fc4719..bb45022 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;
@@ -106,38 +106,27 @@
     free(l);
 }
 
-void listener_disconnect(void* listener, atransport*  t)
-{
+static void listener_disconnect(void* listener, atransport* t) {
     free_listener(reinterpret_cast<alistener*>(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) {
+    if (!strncmp("tcp:", name, 4)) {
+        int port = atoi(name + 4);
         if (gListenAll > 0) {
-            ret = socket_inaddr_any_server(port, SOCK_STREAM);
+            return socket_inaddr_any_server(port, SOCK_STREAM);
         } else {
-            ret = socket_loopback_server(port, SOCK_STREAM);
+            return socket_loopback_server(port, SOCK_STREAM);
         }
-
-        return ret;
     }
-#ifndef HAVE_WIN32_IPC  /* no Unix-domain sockets on Win32 */
-    // It's non-sensical to support the "reserved" space on the adb host side
-    if(!strncmp(name, "local:", 6)) {
-        return socket_local_server(name + 6,
-                ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM);
-    } else if(!strncmp(name, "localabstract:", 14)) {
-        return socket_local_server(name + 14,
-                ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM);
-    } else if(!strncmp(name, "localfilesystem:", 16)) {
-        return socket_local_server(name + 16,
-                ANDROID_SOCKET_NAMESPACE_FILESYSTEM, SOCK_STREAM);
+#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 socket_local_server(name + 6, ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM);
+    } else if (!strncmp(name, "localabstract:", 14)) {
+        return socket_local_server(name + 14, ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM);
+    } else if (!strncmp(name, "localfilesystem:", 16)) {
+        return socket_local_server(name + 16, ANDROID_SOCKET_NAMESPACE_FILESYSTEM, SOCK_STREAM);
     }
 
 #endif
@@ -154,14 +143,15 @@
             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) {
@@ -185,7 +175,7 @@
     }
 }
 
-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)
diff --git a/adb/adb_listeners.h b/adb/adb_listeners.h
index 67168ae..67deb21 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,14 @@
   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 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..dbc7ec8 100644
--- a/adb/adb_trace.h
+++ b/adb/adb_trace.h
@@ -57,9 +57,9 @@
 #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)
 
diff --git a/adb/adb_utils.cpp b/adb/adb_utils.cpp
index 604bd57..cd3c7bc 100644
--- a/adb/adb_utils.cpp
+++ b/adb/adb_utils.cpp
@@ -25,11 +25,20 @@
 
 #include <algorithm>
 
+#include <base/logging.h>
 #include <base/stringprintf.h>
+#include <base/strings.h>
+#include <cutils/sockets.h>
 
 #include "adb_trace.h"
 #include "sysdeps.h"
 
+#if defined(_WIN32)
+#include <ws2tcpip.h>
+#else
+#include <netdb.h>
+#endif
+
 bool getcwd(std::string* s) {
   char* cwd = getcwd(nullptr, 0);
   if (cwd != nullptr) *s = cwd;
@@ -63,6 +72,25 @@
   return result;
 }
 
+int mkdirs(const char *path)
+{
+    int ret;
+    char *x = (char *)path + 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;
+}
+
 void dump_hex(const void* data, size_t byte_count) {
     byte_count = std::min(byte_count, size_t(16));
 
@@ -84,3 +112,71 @@
 
     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;
+}
+
+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) {
+        // TODO: gai_strerror is not thread safe on Win32.
+        *error = gai_strerror(getaddrinfo_error);
+    } else {
+        *error = strerror(errno);
+    }
+    return -1;
+}
diff --git a/adb/adb_utils.h b/adb/adb_utils.h
index 84f7d0c..d1a3f5f 100644
--- a/adb/adb_utils.h
+++ b/adb/adb_utils.h
@@ -22,8 +22,23 @@
 bool getcwd(std::string* cwd);
 bool directory_exists(const std::string& path);
 
+int mkdirs(const char *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);
+
+int network_connect(const std::string& host, int port, int type, int timeout, std::string* error);
+
 #endif
diff --git a/adb/adb_utils_test.cpp b/adb/adb_utils_test.cpp
index 052aea5..7aa610a 100644
--- a/adb/adb_utils_test.cpp
+++ b/adb/adb_utils_test.cpp
@@ -50,3 +50,85 @@
   ASSERT_EQ(R"('abc(')", escape_arg("abc("));
   ASSERT_EQ(R"('abc)')", escape_arg("abc)"));
 }
+
+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));
+}
diff --git a/adb/client/main.cpp b/adb/client/main.cpp
new file mode 100644
index 0000000..c018b8a
--- /dev/null
+++ b/adb/client/main.cpp
@@ -0,0 +1,196 @@
+/*
+ * 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(WORKAROUND_BUG6558362) && defined(__linux__)
+static const bool kWorkaroundBug6558362 = true;
+#else
+static const bool kWorkaroundBug6558362 = false;
+#endif
+
+static void adb_workaround_affinity(void) {
+#if defined(__linux__)
+    const char affinity_env[] = "ADB_CPU_AFFINITY_BUG6558362";
+    const char* cpunum_str = getenv(affinity_env);
+    if (cpunum_str == nullptr || *cpunum_str == '\0') {
+        return;
+    }
+
+    char* strtol_res;
+    int 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_env);
+    }
+
+    cpu_set_t cpu_set;
+    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]);
+#else
+    // No workaround was ever implemented for the other platforms.
+#endif
+}
+
+#if defined(_WIN32)
+static const char kNullFileName[] = "NUL";
+
+static BOOL WINAPI ctrlc_handler(DWORD type) {
+    exit(STATUS_CONTROL_C_EXIT);
+    return TRUE;
+}
+
+static std::string GetLogFilePath() {
+    const char log_name[] = "adb.log";
+    char temp_path[MAX_PATH - sizeof(log_name) + 1];
+
+    // https://msdn.microsoft.com/en-us/library/windows/desktop/aa364992%28v=vs.85%29.aspx
+    DWORD nchars = GetTempPath(sizeof(temp_path), temp_path);
+    CHECK_LE(nchars, sizeof(temp_path));
+    if (nchars == 0) {
+        // TODO(danalbert): Log the error message from FormatError().
+        // Windows unfortunately has two errnos, errno and GetLastError(), so
+        // I'm not sure what to do about PLOG here. Probably better to just
+        // ignore it and add a simplified version of FormatError() for use in
+        // log messages.
+        LOG(ERROR) << "Error creating log file";
+    }
+
+    return std::string(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);
+    CHECK_NE(fd, -1);
+    dup2(fd, STDIN_FILENO);
+    unix_close(fd);
+}
+
+static void setup_daemon_logging(void) {
+    int fd = unix_open(GetLogFilePath().c_str(), O_WRONLY | O_CREAT | O_APPEND,
+                       0640);
+    if (fd == -1) {
+        fd = unix_open(kNullFileName, O_WRONLY);
+    }
+    dup2(fd, STDOUT_FILENO);
+    dup2(fd, STDERR_FILENO);
+    unix_close(fd);
+
+#ifdef _WIN32
+    // On Windows, stderr is buffered by default, so switch to non-buffered
+    // to match Linux.
+    setvbuf(stderr, NULL, _IONBF, 0);
+#endif
+    fprintf(stderr, "--- adb starting (pid %d) ---\n", getpid());
+}
+
+int adb_main(int is_daemon, int server_port) {
+    HOST = 1;
+
+#if defined(_WIN32)
+    SetConsoleCtrlHandler(ctrlc_handler, TRUE);
+#else
+    signal(SIGPIPE, SIG_IGN);
+#endif
+
+    init_transport_registration();
+
+    if (kWorkaroundBug6558362 && is_daemon) {
+        adb_workaround_affinity();
+    }
+
+    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*", nullptr, 0)) {
+        LOG(FATAL) << "Could not install *smartsocket* listener";
+    }
+
+    if (is_daemon) {
+        // Inform our parent that we are up and running.
+        // TODO(danalbert): Can't use SendOkay because we're sending "OK\n", not
+        // "OKAY".
+        // TODO(danalbert): Why do we use stdout for Windows? There is a
+        // comment in launch_server() that suggests that non-Windows uses
+        // stderr because it is non-buffered. So perhaps the history is that
+        // stdout was preferred for all platforms, but it was discovered that
+        // non-Windows needed a non-buffered fd, so stderr was used there.
+        // Note that using stderr on unix means that if you do
+        // `ADB_TRACE=all adb start-server`, it will say "ADB server didn't ACK"
+        // and "* failed to start daemon *" because the adb server will write
+        // logging to stderr, obscuring the OK\n output that is sent to stderr.
+#if defined(_WIN32)
+        int reply_fd = STDOUT_FILENO;
+        // Change stdout mode to binary so \n => \r\n translation does not
+        // occur. In a moment stdout will be reopened to the daemon log file
+        // anyway.
+        _setmode(reply_fd, _O_BINARY);
+#else
+        int reply_fd = STDERR_FILENO;
+#endif
+        android::base::WriteStringToFd("OK\n", reply_fd);
+        close_stdin();
+        setup_daemon_logging();
+    }
+
+    D("Event loop starting\n");
+    fdevent_loop();
+
+    return 0;
+}
+
+int main(int argc, char** argv) {
+    adb_sysdeps_init();
+    adb_trace_init(argv);
+    D("Handling commandline()\n");
+    return adb_commandline(argc - 1, const_cast<const char**>(argv + 1));
+}
diff --git a/adb/commandline.cpp b/adb/commandline.cpp
index fd9953c..fa1feca 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;
@@ -110,7 +110,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 +209,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 +229,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"
@@ -310,13 +310,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);
@@ -346,8 +365,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);
 }
@@ -404,7 +436,6 @@
 }
 
 static int interactive_shell() {
-    adb_thread_t thr;
     int fdi;
 
     std::string error;
@@ -425,14 +456,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);
     }
@@ -674,7 +706,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 +720,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 +745,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,25 +764,6 @@
     return send_shell_command(transport, serial, cmd);
 }
 
-static int mkdirs(const char *path)
-{
-    int ret;
-    char *x = (char *)path + 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";
 
@@ -953,11 +959,8 @@
     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;
 
     // If defined, this should be an absolute path to
     // the directory containing all of the various system images
@@ -970,10 +973,10 @@
     }
     // 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);
@@ -994,8 +997,6 @@
         } else if (!strcmp(argv[0], "fork-server")) {
             /* this is a special flag used only when the ADB client launches the ADB Server */
             is_daemon = 1;
-        } else if (!strcmp(argv[0],"persist")) {
-            persist = 1;
         } else if (!strncmp(argv[0], "-p", 2)) {
             const char *product = NULL;
             if (argv[0][2] == '\0') {
@@ -1021,9 +1022,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)) {
@@ -1068,7 +1069,7 @@
         argv++;
     }
 
-    adb_set_transport(ttype, serial);
+    adb_set_transport(transport_type, serial);
     adb_set_tcp_specifics(server_port);
 
     if (is_server) {
@@ -1091,7 +1092,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;
         }
 
@@ -1142,7 +1143,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');
@@ -1187,18 +1188,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")) {
@@ -1245,10 +1240,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") ||
@@ -1266,92 +1263,52 @@
     }
     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")) {
@@ -1386,15 +1343,15 @@
     }
     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;
@@ -1446,11 +1403,11 @@
         !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);
@@ -1486,9 +1443,7 @@
     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) {
@@ -1498,9 +1453,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)
@@ -1517,8 +1470,7 @@
     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, char* filename) {
     std::string cmd = "shell:rm -f " + escape_arg(filename);
     return send_shell_command(transport, serial, cmd);
 }
@@ -1534,9 +1486,7 @@
     }
 }
 
-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;
@@ -1588,7 +1538,7 @@
     return err;
 }
 
-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;
@@ -1619,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]);
     }
@@ -1664,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
 
         int localFd = adb_open(file, O_RDONLY);
         if (localFd < 0) {
diff --git a/adb/console.cpp b/adb/console.cpp
index 452ee41..b7f5345 100644
--- a/adb/console.cpp
+++ b/adb/console.cpp
@@ -1,44 +1,116 @@
+/*
+ * 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) {
+
+    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 port;
+}
+
+static int connect_to_console(const char* serial) {
+    int port = adb_get_emulator_console_port(serial);
+    if (port == -1) {
+        return -1;
+    }
+
+    int fd = socket_loopback_client(port, SOCK_STREAM);
+    if (fd == -1) {
         fprintf(stderr, "error: could not connect to TCP port %d\n", port);
         return -1;
     }
-    return  fd;
+    return fd;
 }
 
-
-int  adb_send_emulator_command(int  argc, const char**  argv)
-{
-    int   fd, nn;
-
-    fd = connect_to_console();
-    if (fd < 0)
+int adb_send_emulator_command(int argc, const char** argv, const char* serial) {
+    int fd = connect_to_console(serial);
+    if (fd == -1) {
         return 1;
-
-#define  QUIT  "quit\n"
-
-    for (nn = 1; nn < argc; nn++) {
-        adb_write( fd, argv[nn], strlen(argv[nn]) );
-        adb_write( fd, (nn == argc-1) ? "\n" : " ", 1 );
     }
-    adb_write( fd, QUIT, sizeof(QUIT)-1 );
+
+    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..157c97b
--- /dev/null
+++ b/adb/daemon/main.cpp
@@ -0,0 +1,275 @@
+/*
+ * 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"
+#include "qemu_tracing.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 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
+}
+
+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 local_name =
+            android::base::StringPrintf("tcp:%d", server_port);
+        if (install_listener(local_name, "*smartsocket*", nullptr, 0)) {
+            LOG(FATAL) << "Could not install *smartsocket* listener";
+        }
+    }
+
+    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);
+
+    /* 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 adbd_main(DEFAULT_ADB_PORT);
+}
diff --git a/adb/device.py b/adb/device.py
new file mode 100644
index 0000000..601989b
--- /dev/null
+++ b/adb/device.py
@@ -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.
+#
+import os
+import re
+import subprocess
+
+
+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)
+
+
+class AndroidDevice(object):
+    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
+        self._shell_result_pattern = None
+
+    @property
+    def linesep(self):
+        if self._linesep is None:
+            self._linesep = subprocess.check_output(['adb', 'shell', 'echo'])
+        return self._linesep
+
+    def _make_shell_cmd(self, user_cmd):
+        # Follow any shell command with `; echo; echo $?` to get the exit
+        # status of a program since this isn't propagated by adb.
+        #
+        # The leading newline is needed because `printf 1; echo $?` would print
+        # "10", and we wouldn't be able to distinguish the exit code.
+        rc_probe = '; echo "\n$?"'
+        return self.adb_cmd + ['shell'] + user_cmd + [rc_probe]
+
+    def _parse_shell_output(self, out):  # pylint: disable=no-self-use
+        search_text = out
+        max_result_len = len('{0}255{0}'.format(self.linesep))
+        if len(search_text) > max_result_len:
+            # We don't want to regex match over massive amounts of data when we
+            # know the part we want is right at the end.
+            search_text = search_text[-max_result_len:]
+        if self._shell_result_pattern is None:
+            self._shell_result_pattern = re.compile(
+                r'({0}\d+{0})$'.format(self.linesep), re.MULTILINE)
+        m = self._shell_result_pattern.search(search_text)
+        if m is None:
+            raise RuntimeError('Could not find exit status in shell output.')
+
+        result_text = m.group(1)
+        result = int(result_text.strip())
+        out = out[:-len(result_text)]  # Trim the result text from the output.
+        return result, out
+
+    def _simple_call(self, cmd):
+        return subprocess.check_output(
+            self.adb_cmd + cmd, stderr=subprocess.STDOUT)
+
+    def shell(self, 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)
+        p = subprocess.Popen(
+            cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+        out, _ = p.communicate()
+        return self._parse_shell_output(out)
+
+    def install(self, filename):
+        return self._simple_call(['install', filename])
+
+    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 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])
+        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..5cd4988 100644
--- a/adb/fdevent.cpp
+++ b/adb/fdevent.cpp
@@ -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,
@@ -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));
@@ -661,27 +681,12 @@
         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(;;) {
         D("--- ---- waiting for events\n");
diff --git a/adb/file_sync_client.cpp b/adb/file_sync_client.cpp
index aded301..49d42a3 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>
@@ -31,6 +32,7 @@
 #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;
@@ -63,13 +65,12 @@
             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) {
+static void print_transfer_progress(uint64_t bytes_current,
+                                    uint64_t bytes_total) {
     if (bytes_total == 0) return;
 
-    fprintf(stderr, transfer_progress_format, bytes_current, bytes_total,
+    fprintf(stderr, "\rTransferring: %" PRIu64 "/%" PRIu64 " (%d%%)",
+            bytes_current, bytes_total,
             (int) (bytes_current * 100 / bytes_total));
 
     if (bytes_current == bytes_total) {
@@ -79,8 +80,7 @@
     fflush(stderr);
 }
 
-void sync_quit(int fd)
-{
+static void sync_quit(int fd) {
     syncmsg msg;
 
     msg.req.id = ID_QUIT;
@@ -91,8 +91,7 @@
 
 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)
-{
+static int sync_ls(int fd, const char* path, sync_ls_cb func, void* cookie) {
     syncmsg msg;
     char buf[257];
     int len;
@@ -138,9 +137,7 @@
 
 static syncsendbuf send_buffer;
 
-int sync_readtime(int fd, const char *path, unsigned int *timestamp,
-                  unsigned int *mode)
-{
+static int sync_readtime(int fd, const char* path, unsigned int* timestamp, unsigned int* mode) {
     syncmsg msg;
     int len = strlen(path);
 
@@ -199,8 +196,7 @@
     return 0;
 }
 
-int sync_readmode(int fd, const char *path, unsigned *mode)
-{
+static int sync_readmode(int fd, const char* path, unsigned* mode) {
     syncmsg msg;
     int len = strlen(path);
 
@@ -400,27 +396,7 @@
     return -1;
 }
 
-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(int fd, const char* rpath, const char* lpath, int show_progress) {
     syncmsg msg;
     int len;
     int lfd = -1;
@@ -566,17 +542,14 @@
     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");
         abort();
@@ -807,9 +780,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;
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/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/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..82efb1c 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"
@@ -200,7 +207,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 +217,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 +245,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 +319,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,12 +389,7 @@
     }
 }
 
-static int create_subproc_thread(const char *name, const subproc_mode mode)
-{
-    adb_thread_t t;
-    int ret_fd;
-    pid_t pid = -1;
-
+static int create_subproc_thread(const char *name, bool pty = false) {
     const char *arg0, *arg1;
     if (name == 0 || *name == 0) {
         arg0 = "-"; arg1 = 0;
@@ -391,16 +397,12 @@
         arg0 = "-c"; arg1 = name;
     }
 
-    switch (mode) {
-    case SUBPROC_PTY:
+    pid_t pid = -1;
+    int ret_fd;
+    if (pty) {
         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 +412,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");
@@ -435,7 +437,8 @@
                 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
@@ -462,9 +465,9 @@
     } 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);
+        ret = create_subproc_thread(name + 6, true);
     } else if(!HOST && !strncmp(name, "exec:", 5)) {
-        ret = create_subproc_thread(name + 5, SUBPROC_RAW);
+        ret = create_subproc_thread(name + 5);
     } else if(!strncmp(name, "sync:", 5)) {
         ret = create_service_thread(file_sync_service, NULL);
     } else if(!strncmp(name, "remount:", 8)) {
@@ -479,13 +482,13 @@
         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());
     } else if(!strncmp(name, "restore:", 8)) {
-        ret = create_subproc_thread("/system/bin/bu restore", SUBPROC_RAW);
+        ret = create_subproc_thread("/system/bin/bu restore");
     } 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 +517,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 +529,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 +544,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 +615,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);
@@ -664,15 +661,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 +680,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..d8ea2ee 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,10 +355,10 @@
         }
         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;
 
             r = s->peer->enqueue(s->peer, p);
             D("LS(%d): fd=%d post peer->enqueue(). r=%d\n", s->id, s->fd,
@@ -569,9 +570,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 +692,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 +701,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 +749,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 +769,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 +816,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 +826,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 +902,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..6160923 100644
--- a/adb/sysdeps.h
+++ b/adb/sysdeps.h
@@ -24,6 +24,8 @@
 #  undef _WIN32
 #endif
 
+#include <errno.h>
+
 /*
  * 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
@@ -79,19 +81,13 @@
     LeaveCriticalSection( lock );
 }
 
-typedef struct { unsigned  tid; }  adb_thread_t;
-
 typedef  void*  (*adb_thread_func_t)(void*  arg);
 
 typedef  void (*win_thread_func_t)(void*  arg);
 
-static __inline__ int  adb_thread_create( adb_thread_t  *thread, adb_thread_func_t  func, void*  arg)
-{
-    thread->tid = _beginthread( (win_thread_func_t)func, 0, arg );
-    if (thread->tid == (unsigned)-1L) {
-        return -1;
-    }
-    return 0;
+static __inline__ 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__  unsigned long adb_thread_id()
@@ -131,6 +127,7 @@
 #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 +136,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 +144,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,11 +158,13 @@
 #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);
 }
 
+// See the comments for the !defined(_WIN32) version of unix_open().
 static __inline__ int  unix_open(const char*  path, int options,...)
 {
     if ((options & O_CREAT) == 0)
@@ -185,14 +187,6 @@
 /* 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
@@ -274,8 +268,8 @@
 #else /* !_WIN32 a.k.a. Unix */
 
 #include "fdevent.h"
-#include <cutils/sockets.h>
 #include <cutils/misc.h>
+#include <cutils/threads.h>
 #include <signal.h>
 #include <sys/wait.h>
 #include <sys/stat.h>
@@ -320,6 +314,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 +340,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 +373,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);
@@ -425,22 +440,28 @@
 #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 )
@@ -517,7 +538,7 @@
 
 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..a274892 100644
--- a/adb/sysdeps_win32.cpp
+++ b/adb/sysdeps_win32.cpp
@@ -25,6 +25,8 @@
 #include <stdio.h>
 #include <stdlib.h>
 
+#include <cutils/sockets.h>
+
 #include "adb.h"
 
 extern void fatal(const char *fmt, ...);
@@ -172,14 +174,15 @@
 static  int          _win32_fh_count;
 
 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 );
+        D( "_fh_from_int: invalid fd %d passed to %s\n", fd + WIN32_FH_BASE,
+           func );
         errno = EBADF;
         return NULL;
     }
@@ -187,7 +190,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;
     }
@@ -359,9 +363,10 @@
                                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 +378,7 @@
                 return -1;
 
             default:
-                D( "unknown error\n" );
+                D( "unknown error: %ld\n", err );
                 errno = ENOENT;
                 return -1;
         }
@@ -400,9 +405,10 @@
                                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 +420,7 @@
                 return -1;
 
             default:
-                D( "unknown error\n" );
+                D( "unknown error: %ld\n", err );
                 errno = ENOENT;
                 return -1;
         }
@@ -427,7 +433,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 +445,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 +457,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;
@@ -463,7 +469,7 @@
 
 int  adb_shutdown(int  fd)
 {
-    FH   f = _fh_from_int(fd);
+    FH   f = _fh_from_int(fd, __func__);
 
     if (!f || f->clazz != &_fh_socket_class) {
         D("adb_shutdown: invalid fd %d\n", fd);
@@ -478,7 +484,7 @@
 
 int  adb_close(int  fd)
 {
-    FH   f = _fh_from_int(fd);
+    FH   f = _fh_from_int(fd, __func__);
 
     if (!f) {
         return -1;
@@ -664,55 +670,45 @@
 }
 
 
-int socket_network_client(const char *host, int port, int type)
-{
+int socket_network_client_timeout(const char *host, int port, int type, int timeout,
+                                  int* getaddrinfo_error) {
     FH  f = _fh_alloc( &_fh_socket_class );
-    struct hostent *hp;
-    struct sockaddr_in addr;
-    SOCKET s;
+    if (!f) return -1;
 
-    if (!f)
-        return -1;
+    if (!_winsock_init) _init_winsock();
 
-    if (!_winsock_init)
-        _init_winsock();
-
-    hp = gethostbyname(host);
+    hostent* hp = gethostbyname(host);
     if(hp == 0) {
         _fh_close(f);
         return -1;
     }
 
+    sockaddr_in addr;
     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);
+    SOCKET s = socket(hp->h_addrtype, type, 0);
     if(s == INVALID_SOCKET) {
         _fh_close(f);
         return -1;
     }
     f->fh_socket = s;
 
+    // TODO: implement timeouts for Windows.
+
     if(connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
         _fh_close(f);
         return -1;
     }
 
     snprintf( f->name, sizeof(f->name), "%d(net-client:%s%d)", _fh_to_int(f), type != SOCK_STREAM ? "udp:" : "", port );
-    D( "socket_network_client: host '%s' port %d type %s => fd %d\n", host, port, type != SOCK_STREAM ? "udp" : "tcp", _fh_to_int(f) );
+    D( "socket_network_client_timeout: 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 );
@@ -763,7 +759,7 @@
 #undef accept
 int  adb_socket_accept(int  serverfd, struct sockaddr*  addr, socklen_t  *addrlen)
 {
-    FH   serverfh = _fh_from_int(serverfd);
+    FH   serverfh = _fh_from_int(serverfd, __func__);
     FH   fh;
 
     if ( !serverfh || serverfh->clazz != &_fh_socket_class ) {
@@ -779,8 +775,9 @@
 
     fh->fh_socket = accept( serverfh->fh_socket, addr, addrlen );
     if (fh->fh_socket == INVALID_SOCKET) {
+        const DWORD err = WSAGetLastError();
         _fh_close( fh );
-        D( "adb_socket_accept: accept on fd %d return error %ld\n", serverfd, GetLastError() );
+        D( "adb_socket_accept: accept on fd %d return error %ld\n", serverfd, err );
         return -1;
     }
 
@@ -792,7 +789,7 @@
 
 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);
@@ -1386,7 +1383,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 +1415,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 +1541,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 +1549,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 +1582,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);
@@ -3036,7 +3033,7 @@
     }
 }
 
-// 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 +3043,12 @@
         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")
     }
 }
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..6c20b6e
--- /dev/null
+++ b/adb/test_device.py
@@ -0,0 +1,424 @@
+#!/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
+
+
+class GetDeviceTest(unittest.TestCase):
+    def setUp(self):
+        self.android_serial = os.getenv('ANDROID_SERIAL')
+        del os.environ['ANDROID_SERIAL']
+
+    def tearDown(self):
+        os.environ['ANDROID_SERIAL'] = self.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)
+
+
+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')
+        self.assertIn("-text;ls;1.apk", self.device.install(tf.name))
+
+        # http://b/3090932
+        tf = tempfile.NamedTemporaryFile('wb', suffix="-Live Hold'em.apk")
+        self.assertIn("-Live Hold'em.apk", self.device.install(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)."""
+        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')
+
+
+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 = os.path.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])
+        try:
+            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)
+        finally:
+            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)
+        try:
+            rand_str = os.urandom(1024 * kbytes)
+            tmp.write(rand_str)
+            tmp.close()
+            self._test_push(tmp.name, compute_md5(rand_str))
+        finally:
+            os.remove(tmp.name)
+
+    # TODO: write push directory test.
+
+    def _test_pull(self, remote_file, checksum):
+        tmp_write = tempfile.NamedTemporaryFile(mode='wb', delete=False)
+        try:
+            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)
+        finally:
+            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])
+        try:
+            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)
+        finally:
+            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()
+        try:
+            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)
+        finally:
+            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()
+        try:
+            # 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)
+        finally:
+            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'로보카 폴리'.encode('utf-8')
+
+        ## push.
+        tf = tempfile.NamedTemporaryFile('wb', suffix=name)
+        self.device.push(tf.name, '/data/local/tmp/adb-test-{}'.format(name))
+        self.device.shell(['rm', '-f', '/data/local/tmp/adb-test-*'])
+
+        # pull.
+        cmd = ['touch', '"/data/local/tmp/adb-test-{}"'.format(name)]
+        self.device.shell(cmd)
+
+        tf = tempfile.NamedTemporaryFile('wb', suffix=name)
+        self.device.pull('/data/local/tmp/adb-test-{}'.format(name), tf.name)
+
+
+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..f78daeb 100644
--- a/adb/test_track_devices.cpp
+++ b/adb/test_track_devices.cpp
@@ -62,7 +62,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..87aff88 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -26,6 +26,8 @@
 #include <string.h>
 #include <unistd.h>
 
+#include <list>
+
 #include <base/stringprintf.h>
 
 #include "adb.h"
@@ -33,15 +35,8 @@
 
 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 );
 
@@ -288,7 +283,7 @@
     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:
@@ -530,8 +525,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;
 
@@ -555,8 +548,7 @@
         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);
@@ -572,23 +564,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,24 +591,18 @@
 
         fdevent_set(&(t->transport_fde), FDE_READ);
 
-        if(adb_thread_create(&input_thread_ptr, input_thread, t)){
+        if (!adb_thread_create(input_thread, t)) {
             fatal_errno("cannot create input thread");
         }
 
-        if(adb_thread_create(&output_thread_ptr, output_thread, t)){
+        if (!adb_thread_create(output_thread, t)) {
             fatal_errno("cannot create output 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;
@@ -632,7 +617,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];
@@ -740,20 +725,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 +748,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 +756,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 +786,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 +823,31 @@
 
 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;
+}
+
 #if ADB_HOST
 
 static void append_transport_info(std::string* result, const char* key,
@@ -869,7 +864,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)";
@@ -893,7 +889,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 +897,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 +909,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 +950,99 @@
 }
 
 #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)
 {
     adb_mutex_lock(&transport_lock);
-    t->next->prev = t->prev;
-    t->prev->next = t->next;
+    transport_list.remove(t);
     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;
+// Unregisters all non-emulator TCP transports.
+void unregister_all_tcp_transports() {
     adb_mutex_lock(&transport_lock);
-    for (t = transport_list.next; t != &transport_list; t = next) {
-        next = t->next;
+    for (auto it = transport_list.begin(); it != transport_list.end(); ) {
+        atransport* t = *it;
         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)
-            {
+            // We cannot call kick_transport when holding transport_lock.
+            if (!t->kicked) {
                 t->kicked = 1;
                 t->kick(t);
             }
             transport_unref_locked(t);
+
+            it = transport_list.erase(it);
+        } else {
+            ++it;
         }
-     }
+    }
 
     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..edcc99d 100644
--- a/adb/transport.h
+++ b/adb/transport.h
@@ -25,11 +25,11 @@
 
 /*
  * 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);
@@ -50,14 +50,14 @@
 /* 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..0dc9581 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,19 +85,18 @@
     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
     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) {
@@ -126,7 +127,7 @@
     /* 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);
+        local_connect(port);
     }
 #endif
     return 0;
@@ -234,9 +235,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,7 +279,6 @@
 
 void local_init(int port)
 {
-    adb_thread_t thr;
     void* (*func)(void *);
 
     if(HOST) {
@@ -304,9 +303,8 @@
 
     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");
+    if (!adb_thread_create(func, (void *) (uintptr_t) port)) {
+        fatal_errno("cannot create local socket %s thread", HOST ? "client" : "server");
     }
 }
 
@@ -334,7 +332,11 @@
 
 static void remote_close(atransport *t)
 {
-    adb_close(t->fd);
+    int fd = t->sfd;
+    if (fd != -1) {
+        t->sfd = -1;
+        adb_close(fd);
+    }
 }
 
 
@@ -390,7 +392,7 @@
     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;
 
diff --git a/adb/transport_test.cpp b/adb/transport_test.cpp
index 2b3fe3c..4b74adf 100644
--- a/adb/transport_test.cpp
+++ b/adb/transport_test.cpp
@@ -20,34 +20,85 @@
 
 #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(
+            0, memcmp(&disconnects, &rhs.disconnects, sizeof(adisconnect)));
+
+        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);
+
+        return true;
+    }
+};
+
 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 = {};
+  atransport t;
   run_transport_disconnects(&t);
 }
diff --git a/adb/transport_usb.cpp b/adb/transport_usb.cpp
index cdabffe..2c975a9 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;
diff --git a/adb/usb_linux.cpp b/adb/usb_linux.cpp
index 71baaee..e570ef5 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;
@@ -293,7 +280,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 +290,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 +346,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 +429,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 +456,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 +476,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 +509,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,14 +565,12 @@
     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) {
@@ -663,22 +584,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)){
+    if (!adb_thread_create(device_poll_thread, nullptr)) {
         fatal_errno("cannot create input thread");
     }
 }
diff --git a/adb/usb_linux_client.cpp b/adb/usb_linux_client.cpp
index 18289e2..b1b3538 100644
--- a/adb/usb_linux_client.cpp
+++ b/adb/usb_linux_client.cpp
@@ -82,7 +82,7 @@
     struct func_desc fs_descs, hs_descs;
 } __attribute__((packed));
 
-struct func_desc fs_descriptors = {
+static struct func_desc fs_descriptors = {
     .intf = {
         .bLength = sizeof(fs_descriptors.intf),
         .bDescriptorType = USB_DT_INTERFACE,
@@ -109,7 +109,7 @@
     },
 };
 
-struct func_desc hs_descriptors = {
+static struct func_desc hs_descriptors = {
     .intf = {
         .bLength = sizeof(hs_descriptors.intf),
         .bDescriptorType = USB_DT_INTERFACE,
@@ -201,7 +201,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));
@@ -213,14 +213,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;
@@ -230,7 +236,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
@@ -264,8 +270,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");
     }
 }
@@ -445,11 +450,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);
 
@@ -483,8 +488,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");
     }
 }
@@ -497,10 +501,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..af65130 100644
--- a/adb/usb_osx.cpp
+++ b/adb/usb_osx.cpp
@@ -398,22 +398,27 @@
     IONotificationPortDestroy(notificationPort);
 
     DBG("RunLoopThread done\n");
-    return NULL;    
+    return NULL;
 }
 
+static void usb_cleanup() {
+    DBG("usb_cleanup\n");
+    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))
+        if (!adb_thread_create(RunLoopThread, nullptr)) {
             fatal_errno("cannot create input thread");
+        }
 
         // Wait for initialization to finish
         adb_mutex_lock(&start_lock);
@@ -423,18 +428,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;
diff --git a/adb/usb_windows.cpp b/adb/usb_windows.cpp
index d2bd58c..4c9a152 100644
--- a/adb/usb_windows.cpp
+++ b/adb/usb_windows.cpp
@@ -93,9 +93,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);
 
@@ -181,16 +178,11 @@
 }
 
 void usb_init() {
-  adb_thread_t tid;
-
-  if(adb_thread_create(&tid, device_poll_thread, NULL)) {
+  if (!adb_thread_create(device_poll_thread, nullptr)) {
     fatal_errno("cannot create input thread");
   }
 }
 
-void usb_cleanup() {
-}
-
 usb_handle* do_usb_open(const wchar_t* interface_name) {
   // Allocate our handle
   usb_handle* ret = (usb_handle*)malloc(sizeof(usb_handle));
@@ -306,20 +298,13 @@
 int usb_read(usb_handle *handle, void* data, int len) {
   unsigned long time_out = 0;
   unsigned long read = 0;
-  int ret;
 
   D("usb_read %d\n", len);
-  if (NULL != handle) {
+  if (handle != nullptr) {
     while (len > 0) {
-      int xfer = (len > 4096) ? 4096 : len;
-
-      ret = AdbReadEndpointSync(handle->adb_read_pipe,
-                                  data,
-                                  (unsigned long)xfer,
-                                  &read,
-                                  time_out);
+      int ret = AdbReadEndpointSync(handle->adb_read_pipe, data, len, &read, time_out);
       int saved_errno = GetLastError();
-      D("usb_write got: %ld, expected: %d, errno: %d\n", read, xfer, saved_errno);
+      D("usb_write got: %ld, expected: %d, errno: %d\n", read, len, saved_errno);
       if (ret) {
         data = (char *)data + read;
         len -= read;
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..7bd317b 100644
--- a/base/Android.mk
+++ b/base/Android.mk
@@ -18,11 +18,13 @@
 
 libbase_src_files := \
     file.cpp \
+    logging.cpp \
     stringprintf.cpp \
     strings.cpp \
 
 libbase_test_src_files := \
     file_test.cpp \
+    logging_test.cpp \
     stringprintf_test.cpp \
     strings_test.cpp \
     test_main.cpp \
@@ -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,9 +63,6 @@
 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
@@ -85,7 +84,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 +96,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_test.cpp b/base/file_test.cpp
index 5445a0d..b138094 100644
--- a/base/file_test.cpp
+++ b/base/file_test.cpp
@@ -36,7 +36,8 @@
 
 TEST(file, ReadFileToString_success) {
   std::string s("hello");
-  ASSERT_TRUE(android::base::ReadFileToString("/proc/version", &s)) << errno;
+  ASSERT_TRUE(android::base::ReadFileToString("/proc/version", &s))
+    << strerror(errno);
   EXPECT_GT(s.length(), 6U);
   EXPECT_EQ('\n', s[s.length() - 1]);
   s[5] = 0;
@@ -46,37 +47,44 @@
 TEST(file, WriteStringToFile) {
   TemporaryFile tf;
   ASSERT_TRUE(tf.fd != -1);
-  ASSERT_TRUE(android::base::WriteStringToFile("abc", tf.filename)) << errno;
+  ASSERT_TRUE(android::base::WriteStringToFile("abc", tf.filename))
+    << strerror(errno);
   std::string s;
-  ASSERT_TRUE(android::base::ReadFileToString(tf.filename, &s)) << errno;
+  ASSERT_TRUE(android::base::ReadFileToString(tf.filename, &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,
                                                getuid(), getgid()))
-      << errno;
+      << strerror(errno);
   struct stat sb;
   ASSERT_EQ(0, stat(tf.filename, &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.filename, &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);
 }
 
@@ -101,6 +109,7 @@
   ASSERT_TRUE(tf.fd != -1);
   ASSERT_TRUE(android::base::WriteFully(tf.fd, "abc", 3));
   std::string s;
-  ASSERT_TRUE(android::base::ReadFileToString(tf.filename, &s)) << errno;
+  ASSERT_TRUE(android::base::ReadFileToString(tf.filename, &s))
+    << strerror(errno);
   EXPECT_EQ("abc", s);
 }
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/logging.cpp b/base/logging.cpp
index 0142b70..34229a2 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,79 @@
 #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) {
+    // TODO(danalbert): This is a full path on Windows. Just get the basename.
+    DWORD nchars = GetModuleFileName(nullptr, progname, sizeof(progname));
+    DCHECK_GT(nchars, 0U);
+    first = false;
+  }
+
+  return progname;
+}
+
+class mutex {
+ public:
+  mutex() {
+    semaphore_ = CreateSemaphore(nullptr, 1, 1, nullptr);
+    CHECK(semaphore_ != nullptr) << "Failed to create Mutex";
+  }
+  ~mutex() {
+    CloseHandle(semaphore_);
+  }
+
+  void lock() {
+    DWORD result = WaitForSingleObject(semaphore_, INFINITE);
+    CHECK_EQ(result, WAIT_OBJECT_0) << GetLastError();
+  }
+
+  void unlock() {
+    bool result = ReleaseSemaphore(semaphore_, 1, nullptr);
+    CHECK(result);
+  }
+
+ private:
+  HANDLE semaphore_;
+};
+
+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 +137,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()));
@@ -182,7 +253,7 @@
 }
 
 void SetLogger(LogFunction&& logger) {
-  std::lock_guard<std::mutex> lock(logging_lock);
+  lock_guard<mutex> lock(logging_lock);
   gLogger = std::move(logger);
 }
 
@@ -287,7 +358,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..c91857a 100644
--- a/base/logging_test.cpp
+++ b/base/logging_test.cpp
@@ -85,6 +85,9 @@
 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 +95,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));
+#endif
   }
 
   {
@@ -105,10 +111,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));
+#endif
   }
 
   {
@@ -129,10 +138,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));
+#endif
   }
 }
 
@@ -145,10 +157,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));
+#endif
   }
 }
 
@@ -161,11 +176,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));
+#endif
   }
 }
diff --git a/base/stringprintf_test.cpp b/base/stringprintf_test.cpp
index 5cc2086..54b2b6c 100644
--- a/base/stringprintf_test.cpp
+++ b/base/stringprintf_test.cpp
@@ -20,11 +20,14 @@
 
 #include <string>
 
+// The z size sepcifier isn't supported on Windows, so this test isn't useful.
+#if !defined(_WIN32)
 TEST(StringPrintfTest, HexSizeT) {
   size_t size = 0x00107e59;
   EXPECT_EQ("00107e59", android::base::StringPrintf("%08zx", size));
   EXPECT_EQ("0x00107e59", android::base::StringPrintf("0x%08zx", size));
 }
+#endif
 
 TEST(StringPrintfTest, StringAppendF) {
   std::string s("a");
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..0517bc7 100644
--- a/base/test_utils.cpp
+++ b/base/test_utils.cpp
@@ -16,15 +16,26 @@
 
 #include "test_utils.h"
 
+#include <fcntl.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <sys/stat.h>
 #include <unistd.h>
 
+#if defined(_WIN32)
+#include <windows.h>
+#endif
+
 TemporaryFile::TemporaryFile() {
+#if defined(__ANDROID__)
   init("/data/local/tmp");
-  if (fd == -1) {
-    init("/tmp");
-  }
+#elif defined(_WIN32)
+  char wd[MAX_PATH] = {};
+  _getcwd(wd, sizeof(wd));
+  init(wd);
+#else
+  init("/tmp");
+#endif
 }
 
 TemporaryFile::~TemporaryFile() {
@@ -34,5 +45,15 @@
 
 void TemporaryFile::init(const char* tmp_dir) {
   snprintf(filename, sizeof(filename), "%s/TemporaryFile-XXXXXX", tmp_dir);
+#if !defined(_WIN32)
   fd = mkstemp(filename);
+#else
+  // Windows doesn't have mkstemp, and tmpfile creates the file in the root
+  // directory, requiring root (?!) permissions. We have to settle for mktemp.
+  if (mktemp(filename) == nullptr) {
+    abort();
+  }
+
+  fd = open(filename, O_RDWR | O_NOINHERIT | O_CREAT, _S_IREAD | _S_IWRITE);
+#endif
 }
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/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/chrome_collector.cc b/crash_reporter/chrome_collector.cc
new file mode 100644
index 0000000..ec291c0
--- /dev/null
+++ b/crash_reporter/chrome_collector.cc
@@ -0,0 +1,335 @@
+// 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.
+
+#include "crash-reporter/chrome_collector.h"
+
+#include <pcrecpp.h>
+#include <stdint.h>
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include <base/files/file_util.h>
+#include <base/logging.h>
+#include <base/strings/string_number_conversions.h>
+#include <base/strings/string_util.h>
+#include <chromeos/data_encoding.h>
+#include <chromeos/dbus/service_constants.h>
+#include <chromeos/process.h>
+#include <chromeos/syslog_logging.h>
+
+using base::FilePath;
+using base::StringPrintf;
+
+namespace {
+
+const char kDefaultMinidumpName[] = "upload_file_minidump";
+
+// Path to the gzip binary.
+const char kGzipPath[] = "/bin/gzip";
+
+// Filenames for logs attached to crash reports. Also used as metadata keys.
+const char kChromeLogFilename[] = "chrome.txt";
+const char kGpuStateFilename[] = "i915_error_state.log.xz";
+
+// From //net/crash/collector/collector.h
+const int kDefaultMaxUploadBytes = 1024 * 1024;
+
+// Extract a string delimited by the given character, from the given offset
+// into a source string. Returns false if the string is zero-sized or no
+// delimiter was found.
+bool GetDelimitedString(const std::string &str, char ch, size_t offset,
+                        std::string *substr) {
+  size_t at = str.find_first_of(ch, offset);
+  if (at == std::string::npos || at == offset)
+    return false;
+  *substr = str.substr(offset, at - offset);
+  return true;
+}
+
+// Gets the GPU's error state from debugd and writes it to |error_state_path|.
+// Returns true on success.
+bool GetDriErrorState(const FilePath &error_state_path,
+                      org::chromium::debugdProxy *proxy) {
+  chromeos::ErrorPtr error;
+  std::string error_state_str;
+
+  proxy->GetLog("i915_error_state", &error_state_str, &error);
+
+  if (error) {
+    LOG(ERROR) << "Error calling D-Bus proxy call to interface "
+               << "'" << proxy->GetObjectPath().value() << "':"
+               << error->GetMessage();
+    return false;
+  }
+
+  if (error_state_str == "<empty>")
+    return false;
+
+  const char kBase64Header[] = "<base64>: ";
+  const size_t kBase64HeaderLength = sizeof(kBase64Header) - 1;
+  if (error_state_str.compare(0, kBase64HeaderLength, kBase64Header)) {
+    LOG(ERROR) << "i915_error_state is missing base64 header";
+    return false;
+  }
+
+  std::string decoded_error_state;
+
+  if (!chromeos::data_encoding::Base64Decode(
+      error_state_str.c_str() + kBase64HeaderLength,
+      &decoded_error_state)) {
+    LOG(ERROR) << "Could not decode i915_error_state";
+    return false;
+  }
+
+  int written = base::WriteFile(error_state_path,
+                                decoded_error_state.c_str(),
+                                decoded_error_state.length());
+  if (written < 0 ||
+      static_cast<size_t>(written) != decoded_error_state.length()) {
+    LOG(ERROR) << "Could not write file " << error_state_path.value()
+               << " Written: " << written << " Len: "
+               << decoded_error_state.length();
+    base::DeleteFile(error_state_path, false);
+    return false;
+  }
+
+  return true;
+}
+
+// Gzip-compresses |path|, removes the original file, and returns the path of
+// the new file. On failure, the original file is left alone and an empty path
+// is returned.
+FilePath GzipFile(const FilePath& path) {
+  chromeos::ProcessImpl proc;
+  proc.AddArg(kGzipPath);
+  proc.AddArg(path.value());
+  const int res = proc.Run();
+  if (res != 0) {
+    LOG(ERROR) << "Failed to gzip " << path.value();
+    return FilePath();
+  }
+  return path.AddExtension(".gz");
+}
+
+}  // namespace
+
+
+ChromeCollector::ChromeCollector() : output_file_ptr_(stdout) {}
+
+ChromeCollector::~ChromeCollector() {}
+
+bool ChromeCollector::HandleCrash(const FilePath &file_path,
+                                  const std::string &pid_string,
+                                  const std::string &uid_string,
+                                  const std::string &exe_name) {
+  if (!is_feedback_allowed_function_())
+    return true;
+
+  LOG(WARNING) << "Received crash notification for " << exe_name << "["
+               << pid_string << "] user " << uid_string << " (called directly)";
+
+  if (exe_name.find('/') != std::string::npos) {
+    LOG(ERROR) << "exe_name contains illegal characters: " << exe_name;
+    return false;
+  }
+
+  FilePath dir;
+  uid_t uid = atoi(uid_string.c_str());
+  pid_t pid = atoi(pid_string.c_str());
+  if (!GetCreatedCrashDirectoryByEuid(uid, &dir, nullptr)) {
+    LOG(ERROR) << "Can't create crash directory for uid " << uid;
+    return false;
+  }
+
+  std::string dump_basename = FormatDumpBasename(exe_name, time(nullptr), pid);
+  FilePath meta_path = GetCrashPath(dir, dump_basename, "meta");
+  FilePath minidump_path = GetCrashPath(dir, dump_basename, "dmp");
+
+  std::string data;
+  if (!base::ReadFileToString(file_path, &data)) {
+    LOG(ERROR) << "Can't read crash log: " << file_path.value();
+    return false;
+  }
+
+  if (!ParseCrashLog(data, dir, minidump_path, dump_basename)) {
+    LOG(ERROR) << "Failed to parse Chrome's crash log";
+    return false;
+  }
+
+
+  int64_t report_size = 0;
+  base::GetFileSize(minidump_path, &report_size);
+
+  // Keyed by crash metadata key name.
+  const std::map<std::string, base::FilePath> additional_logs =
+      GetAdditionalLogs(dir, dump_basename, exe_name);
+  for (auto it : additional_logs) {
+    int64_t file_size = 0;
+    if (!base::GetFileSize(it.second, &file_size)) {
+      PLOG(WARNING) << "Unable to get size of " << it.second.value();
+      continue;
+    }
+    if (report_size + file_size > kDefaultMaxUploadBytes) {
+      LOG(INFO) << "Skipping upload of " << it.second.value() << "("
+                << file_size << "B) because report size would exceed limit ("
+                << kDefaultMaxUploadBytes << "B)";
+      continue;
+    }
+    VLOG(1) << "Adding metadata: " << it.first << " -> " << it.second.value();
+    // Call AddCrashMetaUploadFile() rather than AddCrashMetaData() here. The
+    // former adds a prefix to the key name; without the prefix, only the key
+    // "logs" appears to be displayed on the crash server.
+    AddCrashMetaUploadFile(it.first, it.second.value());
+    report_size += file_size;
+  }
+
+  // We're done.
+  WriteCrashMetaData(meta_path, exe_name, minidump_path.value());
+
+  fprintf(output_file_ptr_, "%s", kSuccessMagic);
+  fflush(output_file_ptr_);
+
+  return true;
+}
+
+void ChromeCollector::SetUpDBus() {
+  CrashCollector::SetUpDBus();
+
+  debugd_proxy_.reset(
+      new org::chromium::debugdProxy(bus_, debugd::kDebugdServiceName));
+}
+
+bool ChromeCollector::ParseCrashLog(const std::string &data,
+                                    const FilePath &dir,
+                                    const FilePath &minidump,
+                                    const std::string &basename) {
+  size_t at = 0;
+  while (at < data.size()) {
+    // Look for a : followed by a decimal number, followed by another :
+    // followed by N bytes of data.
+    std::string name, size_string;
+    if (!GetDelimitedString(data, ':', at, &name)) {
+      LOG(ERROR) << "Can't find : after name @ offset " << at;
+      break;
+    }
+    at += name.size() + 1;  // Skip the name & : delimiter.
+
+    if (!GetDelimitedString(data, ':', at, &size_string)) {
+      LOG(ERROR) << "Can't find : after size @ offset " << at;
+      break;
+    }
+    at += size_string.size() + 1;  // Skip the size & : delimiter.
+
+    size_t size;
+    if (!base::StringToSizeT(size_string, &size)) {
+      LOG(ERROR) << "String not convertible to integer: " << size_string;
+      break;
+    }
+
+    // Data would run past the end, did we get a truncated file?
+    if (at + size > data.size()) {
+      LOG(ERROR) << "Overrun, expected " << size << " bytes of data, got "
+        << (data.size() - at);
+      break;
+    }
+
+    if (name.find("filename") != std::string::npos) {
+      // File.
+      // Name will be in a semi-MIME format of
+      // <descriptive name>"; filename="<name>"
+      // Descriptive name will be upload_file_minidump for the dump.
+      std::string desc, filename;
+      pcrecpp::RE re("(.*)\" *; *filename=\"(.*)\"");
+      if (!re.FullMatch(name.c_str(), &desc, &filename)) {
+        LOG(ERROR) << "Filename was not in expected format: " << name;
+        break;
+      }
+
+      if (desc.compare(kDefaultMinidumpName) == 0) {
+        // The minidump.
+        WriteNewFile(minidump, data.c_str() + at, size);
+      } else {
+        // Some other file.
+        FilePath path = GetCrashPath(dir, basename + "-" + filename, "other");
+        if (WriteNewFile(path, data.c_str() + at, size) >= 0) {
+          AddCrashMetaUploadFile(desc, path.value());
+        }
+      }
+    } else {
+      // Other attribute.
+      std::string value_str;
+      value_str.reserve(size);
+
+      // Since metadata is one line/value the values must be escaped properly.
+      for (size_t i = at; i < at + size; i++) {
+        switch (data[i]) {
+          case '"':
+          case '\\':
+            value_str.push_back('\\');
+            value_str.push_back(data[i]);
+            break;
+
+          case '\r':
+            value_str += "\\r";
+            break;
+
+          case '\n':
+            value_str += "\\n";
+           break;
+
+          case '\t':
+            value_str += "\\t";
+           break;
+
+          case '\0':
+            value_str += "\\0";
+           break;
+
+          default:
+           value_str.push_back(data[i]);
+           break;
+        }
+      }
+      AddCrashMetaUploadData(name, value_str);
+    }
+
+    at += size;
+  }
+
+  return at == data.size();
+}
+
+std::map<std::string, base::FilePath> ChromeCollector::GetAdditionalLogs(
+    const FilePath &dir,
+    const std::string &basename,
+    const std::string &exe_name) {
+  std::map<std::string, base::FilePath> logs;
+
+  // Run the command specified by the config file to gather logs.
+  const FilePath chrome_log_path =
+      GetCrashPath(dir, basename, kChromeLogFilename);
+  if (GetLogContents(log_config_path_, exe_name, chrome_log_path)) {
+    const FilePath compressed_path = GzipFile(chrome_log_path);
+    if (!compressed_path.empty())
+      logs[kChromeLogFilename] = compressed_path;
+    else
+      base::DeleteFile(chrome_log_path, false /* recursive */);
+  }
+
+  // For unit testing, debugd_proxy_ isn't initialized, so skip attempting to
+  // get the GPU error state from debugd.
+  if (debugd_proxy_) {
+    const FilePath dri_error_state_path =
+        GetCrashPath(dir, basename, kGpuStateFilename);
+    if (GetDriErrorState(dri_error_state_path, debugd_proxy_.get()))
+      logs[kGpuStateFilename] = dri_error_state_path;
+  }
+
+  return logs;
+}
+
+// static
+const char ChromeCollector::kSuccessMagic[] = "_sys_cr_finished";
diff --git a/crash_reporter/chrome_collector.h b/crash_reporter/chrome_collector.h
new file mode 100644
index 0000000..0b58c19
--- /dev/null
+++ b/crash_reporter/chrome_collector.h
@@ -0,0 +1,72 @@
+// 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.
+
+#ifndef CRASH_REPORTER_CHROME_COLLECTOR_H_
+#define CRASH_REPORTER_CHROME_COLLECTOR_H_
+
+#include <map>
+#include <string>
+
+#include <base/files/file_path.h>
+#include <base/macros.h>
+#include <gtest/gtest_prod.h>  // for FRIEND_TEST
+
+#include "crash-reporter/crash_collector.h"
+#include "debugd/dbus-proxies.h"
+
+class SystemLogging;
+
+// Chrome crash collector.
+class ChromeCollector : public CrashCollector {
+ public:
+  ChromeCollector();
+  ~ChromeCollector() override;
+
+  // Magic string to let Chrome know the crash report succeeded.
+  static const char kSuccessMagic[];
+
+  // Handle a specific chrome crash.  Returns true on success.
+  bool HandleCrash(const base::FilePath &file_path,
+                   const std::string &pid_string,
+                   const std::string &uid_string,
+                   const std::string &exe_name);
+
+ protected:
+  void SetUpDBus() override;
+
+ private:
+  friend class ChromeCollectorTest;
+  FRIEND_TEST(ChromeCollectorTest, GoodValues);
+  FRIEND_TEST(ChromeCollectorTest, BadValues);
+  FRIEND_TEST(ChromeCollectorTest, Newlines);
+  FRIEND_TEST(ChromeCollectorTest, File);
+  FRIEND_TEST(ChromeCollectorTest, HandleCrash);
+
+  // Crashes are expected to be in a TLV-style format of:
+  // <name>:<length>:<value>
+  // Length is encoded as a decimal number. It can be zero, but must consist of
+  // at least one character
+  // For file values, name actually contains both a description and a filename,
+  // in a fixed format of: <description>"; filename="<filename>"
+  bool ParseCrashLog(const std::string &data, const base::FilePath &dir,
+                     const base::FilePath &minidump,
+                     const std::string &basename);
+
+  // Writes additional logs for |exe_name| to files based on |basename| within
+  // |dir|. Crash report metadata key names and the corresponding file paths are
+  // returned.
+  std::map<std::string, base::FilePath> GetAdditionalLogs(
+      const base::FilePath &dir,
+      const std::string &basename,
+      const std::string &exe_name);
+
+  FILE *output_file_ptr_;
+
+  // D-Bus proxy for debugd interface.  Unset in unit tests.
+  std::unique_ptr<org::chromium::debugdProxy> debugd_proxy_;
+
+  DISALLOW_COPY_AND_ASSIGN(ChromeCollector);
+};
+
+#endif  // CRASH_REPORTER_CHROME_COLLECTOR_H_
diff --git a/crash_reporter/chrome_collector_test.cc b/crash_reporter/chrome_collector_test.cc
new file mode 100644
index 0000000..0d6a7ce
--- /dev/null
+++ b/crash_reporter/chrome_collector_test.cc
@@ -0,0 +1,150 @@
+// 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.
+
+#include "crash-reporter/chrome_collector.h"
+
+#include <stdio.h>
+
+#include <base/auto_reset.h>
+#include <base/files/file_util.h>
+#include <base/files/scoped_temp_dir.h>
+#include <chromeos/syslog_logging.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+using base::FilePath;
+
+namespace {
+
+const char kCrashFormatGood[] = "value1:10:abcdefghijvalue2:5:12345";
+const char kCrashFormatEmbeddedNewline[] =
+    "value1:10:abcd\r\nghijvalue2:5:12\n34";
+const char kCrashFormatBad1[] = "value1:10:abcdefghijvalue2:6=12345";
+const char kCrashFormatBad2[] = "value1:10:abcdefghijvalue2:512345";
+const char kCrashFormatBad3[] = "value1:10::abcdefghijvalue2:5=12345";
+const char kCrashFormatBad4[] = "value1:10:abcdefghijvalue2:4=12345";
+
+const char kCrashFormatWithFile[] =
+    "value1:10:abcdefghijvalue2:5:12345"
+    "some_file\"; filename=\"foo.txt\":15:12345\n789\n12345"
+    "value3:2:ok";
+
+void CountCrash() {
+}
+
+bool s_allow_crash = false;
+
+bool IsMetrics() {
+  return s_allow_crash;
+}
+
+}  // namespace
+
+class ChromeCollectorMock : public ChromeCollector {
+ public:
+  MOCK_METHOD0(SetUpDBus, void());
+};
+
+class ChromeCollectorTest : public ::testing::Test {
+ protected:
+  void ExpectFileEquals(const char *golden,
+                        const FilePath &file_path) {
+    std::string contents;
+    EXPECT_TRUE(base::ReadFileToString(file_path, &contents));
+    EXPECT_EQ(golden, contents);
+  }
+
+  ChromeCollectorMock collector_;
+
+ private:
+  void SetUp() override {
+    EXPECT_CALL(collector_, SetUpDBus()).WillRepeatedly(testing::Return());
+
+    collector_.Initialize(CountCrash, IsMetrics);
+    chromeos::ClearLog();
+  }
+};
+
+TEST_F(ChromeCollectorTest, GoodValues) {
+  FilePath dir(".");
+  EXPECT_TRUE(collector_.ParseCrashLog(kCrashFormatGood,
+                                       dir, dir.Append("minidump.dmp"),
+                                       "base"));
+
+  // Check to see if the values made it in properly.
+  std::string meta = collector_.extra_metadata_;
+  EXPECT_TRUE(meta.find("value1=abcdefghij") != std::string::npos);
+  EXPECT_TRUE(meta.find("value2=12345") != std::string::npos);
+}
+
+TEST_F(ChromeCollectorTest, Newlines) {
+  FilePath dir(".");
+  EXPECT_TRUE(collector_.ParseCrashLog(kCrashFormatEmbeddedNewline,
+                                       dir, dir.Append("minidump.dmp"),
+                                       "base"));
+
+  // Check to see if the values were escaped.
+  std::string meta = collector_.extra_metadata_;
+  EXPECT_TRUE(meta.find("value1=abcd\\r\\nghij") != std::string::npos);
+  EXPECT_TRUE(meta.find("value2=12\\n34") != std::string::npos);
+}
+
+TEST_F(ChromeCollectorTest, BadValues) {
+  FilePath dir(".");
+  const struct {
+    const char *data;
+  } list[] = {
+    {kCrashFormatBad1, },
+    {kCrashFormatBad2, },
+    {kCrashFormatBad3, },
+    {kCrashFormatBad4, },
+  };
+
+  for (size_t i = 0; i < sizeof(list) / sizeof(list[0]); i++) {
+    chromeos::ClearLog();
+    EXPECT_FALSE(collector_.ParseCrashLog(list[i].data,
+                                          dir, dir.Append("minidump.dmp"),
+                                          "base"));
+  }
+}
+
+TEST_F(ChromeCollectorTest, File) {
+  base::ScopedTempDir scoped_temp_dir;
+  ASSERT_TRUE(scoped_temp_dir.CreateUniqueTempDir());
+  const FilePath& dir = scoped_temp_dir.path();
+  EXPECT_TRUE(collector_.ParseCrashLog(kCrashFormatWithFile,
+                                       dir, dir.Append("minidump.dmp"),
+                                       "base"));
+
+  // Check to see if the values are still correct and that the file was
+  // written with the right data.
+  std::string meta = collector_.extra_metadata_;
+  EXPECT_TRUE(meta.find("value1=abcdefghij") != std::string::npos);
+  EXPECT_TRUE(meta.find("value2=12345") != std::string::npos);
+  EXPECT_TRUE(meta.find("value3=ok") != std::string::npos);
+  ExpectFileEquals("12345\n789\n12345", dir.Append("base-foo.txt.other"));
+}
+
+TEST_F(ChromeCollectorTest, HandleCrash) {
+  base::AutoReset<bool> auto_reset(&s_allow_crash, true);
+  base::ScopedTempDir scoped_temp_dir;
+  ASSERT_TRUE(scoped_temp_dir.CreateUniqueTempDir());
+  const FilePath& dir = scoped_temp_dir.path();
+  FilePath dump_file = dir.Append("test.dmp");
+  ASSERT_EQ(strlen(kCrashFormatWithFile),
+            base::WriteFile(dump_file, kCrashFormatWithFile,
+                            strlen(kCrashFormatWithFile)));
+  collector_.ForceCrashDirectory(dir);
+
+  FilePath log_file;
+  {
+    base::ScopedFILE output(
+        base::CreateAndOpenTemporaryFileInDir(dir, &log_file));
+    ASSERT_TRUE(output.get());
+    base::AutoReset<FILE*> auto_reset_file_ptr(&collector_.output_file_ptr_,
+                                               output.get());
+    EXPECT_TRUE(collector_.HandleCrash(dump_file, "123", "456", "chrome_test"));
+  }
+  ExpectFileEquals(ChromeCollector::kSuccessMagic, log_file);
+}
diff --git a/crash_reporter/crash-reporter.gyp b/crash_reporter/crash-reporter.gyp
new file mode 100644
index 0000000..a7f0e7e
--- /dev/null
+++ b/crash_reporter/crash-reporter.gyp
@@ -0,0 +1,147 @@
+{
+  # Shouldn't need this, but doesn't work otherwise.
+  # http://crbug.com/340086 and http://crbug.com/385186
+  # Note: the unused dependencies are optimized out by the compiler.
+  'target_defaults': {
+    'variables': {
+      'deps': [
+        'libchromeos-<(libbase_ver)',
+      ],
+    },
+  },
+  'targets': [
+    {
+      'target_name': 'libcrash',
+      'type': 'static_library',
+      'variables': {
+        'exported_deps': [
+          'libchrome-<(libbase_ver)',
+          'libpcrecpp',
+        ],
+        'deps': ['<@(exported_deps)'],
+      },
+      'all_dependent_settings': {
+        'variables': {
+          'deps': [
+            '<@(exported_deps)',
+          ],
+        },
+      },
+      'sources': [
+        'chrome_collector.cc',
+        'crash_collector.cc',
+        'kernel_collector.cc',
+        'kernel_warning_collector.cc',
+        'udev_collector.cc',
+        'unclean_shutdown_collector.cc',
+        'user_collector.cc',
+      ],
+      'actions': [
+        {
+          'action_name': 'generate-session-manager-proxies',
+          'variables': {
+            'proxy_output_file': 'include/session_manager/dbus-proxies.h'
+          },
+          'sources': [
+            '../login_manager/org.chromium.SessionManagerInterface.xml',
+          ],
+          'includes': ['../common-mk/generate-dbus-proxies.gypi'],
+        },
+        {
+          'action_name': 'generate-debugd-proxies',
+          'variables': {
+            'proxy_output_file': 'include/debugd/dbus-proxies.h'
+          },
+          'sources': [
+            '../debugd/share/org.chromium.debugd.xml',
+          ],
+          'includes': ['../common-mk/generate-dbus-proxies.gypi'],
+        },
+      ],
+    },
+    {
+      'target_name': 'crash_reporter',
+      'type': 'executable',
+      'variables': {
+        'deps': [
+          'dbus-1',
+          'libmetrics-<(libbase_ver)',
+        ],
+      },
+      'dependencies': [
+        'libcrash',
+      ],
+      'sources': [
+        'crash_reporter.cc',
+      ],
+    },
+    {
+      'target_name': 'list_proxies',
+      'type': 'executable',
+      'variables': {
+        'deps': [
+          'dbus-1',
+          'libchrome-<(libbase_ver)',
+        ],
+      },
+      'sources': [
+        'list_proxies.cc',
+      ],
+      'actions': [
+        {
+          'action_name': 'generate-lib-cros-service-proxies',
+          'variables': {
+            'proxy_output_file': 'include/libcrosservice/dbus-proxies.h'
+          },
+          'sources': [
+            './dbus_bindings/org.chromium.LibCrosService.xml',
+          ],
+          'includes': ['../common-mk/generate-dbus-proxies.gypi'],
+        },
+      ],
+    },
+    {
+      'target_name': 'warn_collector',
+      'type': 'executable',
+      'variables': {
+        'lexer_out_dir': 'crash-reporter',
+        'deps': [
+          'libmetrics-<(libbase_ver)',
+        ],
+      },
+      'link_settings': {
+        'libraries': [
+          '-lfl',
+        ],
+      },
+      'sources': [
+        'warn_collector.l',
+      ],
+      'includes': ['../common-mk/lex.gypi'],
+    },
+  ],
+  'conditions': [
+    ['USE_test == 1', {
+      'targets': [
+        {
+          'target_name': 'crash_reporter_test',
+          'type': 'executable',
+          'includes': ['../common-mk/common_test.gypi'],
+          'dependencies': ['libcrash'],
+          'sources': [
+            'chrome_collector_test.cc',
+            'crash_collector_test.cc',
+            'crash_collector_test.h',
+            'crash_reporter_logs_test.cc',
+            'kernel_collector_test.cc',
+            'kernel_collector_test.h',
+            'testrunner.cc',
+            'udev_collector_test.cc',
+            'unclean_shutdown_collector_test.cc',
+            'user_collector_test.cc',
+          ],
+        },
+      ],
+    }],
+  ],
+}
diff --git a/crash_reporter/crash_collector.cc b/crash_reporter/crash_collector.cc
new file mode 100644
index 0000000..04f3ba8
--- /dev/null
+++ b/crash_reporter/crash_collector.cc
@@ -0,0 +1,512 @@
+// Copyright (c) 2012 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.
+
+#include "crash-reporter/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/cryptohome.h>
+#include <chromeos/dbus/service_constants.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[] = "/var/spool/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() {
+  if (bus_)
+    bus_->ShutdownAndBlock();
+}
+
+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;
+
+  SetUpDBus();
+}
+
+void CrashCollector::SetUpDBus() {
+  dbus::Bus::Options options;
+  options.bus_type = dbus::Bus::SYSTEM;
+
+  bus_ = new dbus::Bus(options);
+  CHECK(bus_->Connect());
+
+  session_manager_proxy_.reset(
+      new org::chromium::SessionManagerInterfaceProxy(
+          bus_,
+          login_manager::kSessionManagerServiceName));
+}
+
+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()));
+}
+
+bool CrashCollector::GetActiveUserSessions(
+    std::map<std::string, std::string> *sessions) {
+  chromeos::ErrorPtr error;
+  session_manager_proxy_->RetrieveActiveSessions(sessions, &error);
+
+  if (error) {
+    LOG(ERROR) << "Error calling D-Bus proxy call to interface "
+               << "'" << session_manager_proxy_->GetObjectPath().value() << "':"
+               << error->GetMessage();
+    return false;
+  }
+
+  return true;
+}
+
+FilePath CrashCollector::GetUserCrashPath() {
+  // In this multiprofile world, there is no one-specific user dir anymore.
+  // Ask the session manager for the active ones, then just run with the
+  // first result we get back.
+  FilePath user_path = FilePath(kFallbackUserCrashPath);
+  std::map<std::string, std::string> active_sessions;
+  if (!GetActiveUserSessions(&active_sessions) || active_sessions.empty()) {
+    LOG(ERROR) << "Could not get active user sessions, using default.";
+    return user_path;
+  }
+
+  user_path = chromeos::cryptohome::home::GetHashedUserPath(
+      active_sessions.begin()->second).Append("crash");
+
+  return user_path;
+}
+
+FilePath CrashCollector::GetCrashDirectoryInfo(
+    uid_t process_euid,
+    uid_t default_user_id,
+    gid_t default_user_group,
+    mode_t *mode,
+    uid_t *directory_owner,
+    gid_t *directory_group) {
+  // TODO(mkrebs): This can go away once Chrome crashes are handled
+  // normally (see crosbug.com/5872).
+  // Check if the user crash directory should be used.  If we are
+  // collecting chrome crashes during autotesting, we want to put them in
+  // the system crash directory so they are outside the cryptohome -- in
+  // case we are being run during logout (see crosbug.com/18637).
+  if (process_euid == default_user_id && IsUserSpecificDirectoryEnabled()) {
+    *mode = kUserCrashPathMode;
+    *directory_owner = default_user_id;
+    *directory_group = default_user_group;
+    return GetUserCrashPath();
+  } else {
+    *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) {
+  uid_t default_user_id;
+  gid_t default_user_group;
+
+  if (out_of_capacity) *out_of_capacity = false;
+
+  // For testing.
+  if (!forced_crash_directory_.empty()) {
+    *crash_directory = forced_crash_directory_;
+    return true;
+  }
+
+  if (!GetUserInfoFromName(kDefaultUserName,
+                           &default_user_id,
+                           &default_user_group)) {
+    LOG(ERROR) << "Could not find default user info";
+    return false;
+  }
+  mode_t directory_mode;
+  uid_t directory_owner;
+  gid_t directory_group;
+  *crash_directory =
+      GetCrashDirectoryInfo(euid,
+                            default_user_id,
+                            default_user_group,
+                            &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;
+    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) {
+    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));
+}
+
+bool CrashCollector::ShouldHandleChromeCrashes() {
+  // If we're testing crash reporter itself, we don't want to allow an
+  // override for chrome crashes.  And, let's be conservative and only
+  // allow an override for developer images.
+  if (!IsCrashTestInProgress() && IsDeveloperImage()) {
+    // Check if there's an override to indicate we should indeed collect
+    // chrome crashes.  This allows the crashes to still be tracked when
+    // they occur in autotests.  See "crosbug.com/17987".
+    if (base::PathExists(FilePath(kCollectChromeFile)))
+      return true;
+  }
+  // We default to ignoring chrome crashes.
+  return false;
+}
+
+bool CrashCollector::IsUserSpecificDirectoryEnabled() {
+  return !ShouldHandleChromeCrashes();
+}
diff --git a/crash_reporter/crash_collector.h b/crash_reporter/crash_collector.h
new file mode 100644
index 0000000..ef443d3
--- /dev/null
+++ b/crash_reporter/crash_collector.h
@@ -0,0 +1,179 @@
+// Copyright (c) 2012 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.
+
+#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 <base/memory/scoped_ptr.h>
+#include <gtest/gtest_prod.h>  // for FRIEND_TEST
+
+#include "session_manager/dbus-proxies.h"
+
+// 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, IsUserSpecificDirectoryEnabled);
+  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;
+
+  // Set up D-Bus.
+  virtual void SetUpDBus();
+
+  // 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;
+  }
+
+  virtual bool GetActiveUserSessions(
+      std::map<std::string, std::string> *sessions);
+  base::FilePath GetUserCrashPath();
+  base::FilePath GetCrashDirectoryInfo(uid_t process_euid,
+                                 uid_t default_user_id,
+                                 gid_t default_user_group,
+                                 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();
+  // Returns true if chrome crashes should be handled.
+  bool ShouldHandleChromeCrashes();
+  // Returns true if user crash directory may be used.
+  bool IsUserSpecificDirectoryEnabled();
+
+  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_;
+
+  scoped_refptr<dbus::Bus> bus_;
+
+ private:
+  // D-Bus proxy for session manager interface.
+  std::unique_ptr<org::chromium::SessionManagerInterfaceProxy>
+      session_manager_proxy_;
+
+  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..ce9af2b
--- /dev/null
+++ b/crash_reporter/crash_collector_test.cc
@@ -0,0 +1,299 @@
+// Copyright (c) 2012 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.
+
+#include "crash-reporter/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-reporter/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;
+}
+
+bool GetActiveUserSessionsImpl(std::map<std::string, std::string> *sessions) {
+  char kUser[] = "chicken@butt.com";
+  char kHash[] = "hashcakes";
+  sessions->insert(std::pair<std::string, std::string>(kUser, kHash));
+  return true;
+}
+
+}  // 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, GetCrashDirectoryInfo) {
+  FilePath path;
+  const int kRootUid = 0;
+  const int kRootGid = 0;
+  const int kNtpUid = 5;
+  const int kChronosUid = 1000;
+  const int kChronosGid = 1001;
+  const mode_t kExpectedSystemMode = 01755;
+  const mode_t kExpectedUserMode = 0755;
+
+  mode_t directory_mode;
+  uid_t directory_owner;
+  gid_t directory_group;
+
+  path = collector_.GetCrashDirectoryInfo(kRootUid,
+                                          kChronosUid,
+                                          kChronosGid,
+                                          &directory_mode,
+                                          &directory_owner,
+                                          &directory_group);
+  EXPECT_EQ("/var/spool/crash", path.value());
+  EXPECT_EQ(kExpectedSystemMode, directory_mode);
+  EXPECT_EQ(kRootUid, directory_owner);
+  EXPECT_EQ(kRootGid, directory_group);
+
+  path = collector_.GetCrashDirectoryInfo(kNtpUid,
+                                          kChronosUid,
+                                          kChronosGid,
+                                          &directory_mode,
+                                          &directory_owner,
+                                          &directory_group);
+  EXPECT_EQ("/var/spool/crash", path.value());
+  EXPECT_EQ(kExpectedSystemMode, directory_mode);
+  EXPECT_EQ(kRootUid, directory_owner);
+  EXPECT_EQ(kRootGid, directory_group);
+
+  EXPECT_CALL(collector_, GetActiveUserSessions(testing::_))
+      .WillOnce(Invoke(&GetActiveUserSessionsImpl));
+
+  EXPECT_EQ(collector_.IsUserSpecificDirectoryEnabled(), true);
+
+  path = collector_.GetCrashDirectoryInfo(kChronosUid,
+                                          kChronosUid,
+                                          kChronosGid,
+                                          &directory_mode,
+                                          &directory_owner,
+                                          &directory_group);
+  EXPECT_EQ("/home/user/hashcakes/crash", path.value());
+  EXPECT_EQ(kExpectedUserMode, directory_mode);
+  EXPECT_EQ(kChronosUid, directory_owner);
+  EXPECT_EQ(kChronosGid, directory_group);
+}
+
+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..8339fa0
--- /dev/null
+++ b/crash_reporter/crash_collector_test.h
@@ -0,0 +1,23 @@
+// 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.
+
+#ifndef CRASH_REPORTER_CRASH_COLLECTOR_TEST_H_
+#define CRASH_REPORTER_CRASH_COLLECTOR_TEST_H_
+
+#include "crash-reporter/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..1528b3f
--- /dev/null
+++ b/crash_reporter/crash_reporter.cc
@@ -0,0 +1,334 @@
+// Copyright (c) 2012 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.
+
+#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/syslog_logging.h>
+#include <metrics/metrics_library.h>
+
+#include "crash-reporter/chrome_collector.h"
+#include "crash-reporter/kernel_collector.h"
+#include "crash-reporter/kernel_warning_collector.h"
+#include "crash-reporter/udev_collector.h"
+#include "crash-reporter/unclean_shutdown_collector.h"
+#include "crash-reporter/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");
+  std::string command = StringPrintf(
+      "/usr/bin/dbus-send --type=signal --system / \"%s\" &",
+      kUserCrashSignal);
+  // Announce through D-Bus whenever a user crash happens. This is
+  // used by the metrics daemon to log active use time between
+  // crashes.
+  //
+  // This could be done more efficiently by explicit fork/exec or
+  // using a dbus library directly. However, this should run
+  // relatively rarely and longer term we may need to implement a
+  // better way to do this that doesn't rely on D-Bus.
+  //
+  // 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.
+
+  int status = system(command.c_str());
+  LOG_IF(WARNING, status != 0) << "dbus-send running failed";
+}
+
+static void CountChromeCrash() {
+  // For now, consider chrome crashes the same as user crashes for reporting
+  // purposes.
+  CountUserCrash();
+}
+
+
+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 HandleChromeCrash(ChromeCollector *chrome_collector,
+                             const std::string& chrome_dump_file,
+                             const std::string& pid,
+                             const std::string& uid,
+                             const std::string& exe) {
+  CHECK(!chrome_dump_file.empty()) << "--chrome= must be set";
+  CHECK(!pid.empty()) << "--pid= must be set";
+  CHECK(!uid.empty()) << "--uid= must be set";
+  CHECK(!exe.empty()) << "--exe= must be set";
+
+  chromeos::LogToString(true);
+  bool handled = chrome_collector->HandleCrash(FilePath(chrome_dump_file),
+                                               pid, uid, exe);
+  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(chrome, "", "Chrome crash dump file");
+  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);
+  ChromeCollector chrome_collector;
+  chrome_collector.Initialize(CountChromeCrash, 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);
+  }
+
+  if (!FLAGS_chrome.empty()) {
+    return HandleChromeCrash(&chrome_collector,
+                             FLAGS_chrome,
+                             FLAGS_pid,
+                             FLAGS_uid,
+                             FLAGS_exe);
+  }
+
+  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..f5ca80c
--- /dev/null
+++ b/crash_reporter/crash_reporter_logs.conf
@@ -0,0 +1,112 @@
+# Copyright (c) 2012 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.
+
+# 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..9879470
--- /dev/null
+++ b/crash_reporter/crash_reporter_logs_test.cc
@@ -0,0 +1,28 @@
+// Copyright 2015 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.
+
+#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..641ae2d
--- /dev/null
+++ b/crash_reporter/crash_sender
@@ -0,0 +1,713 @@
+#!/bin/sh
+
+# Copyright (c) 2010 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.
+
+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.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..d86efbd
--- /dev/null
+++ b/crash_reporter/kernel_collector.cc
@@ -0,0 +1,591 @@
+// Copyright (c) 2012 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.
+
+#include "crash-reporter/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..c8aedfe
--- /dev/null
+++ b/crash_reporter/kernel_collector.h
@@ -0,0 +1,111 @@
+// Copyright (c) 2010 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.
+
+#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-reporter/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..48d94ea
--- /dev/null
+++ b/crash_reporter/kernel_collector_test.cc
@@ -0,0 +1,674 @@
+// Copyright (c) 2012 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.
+
+#include "crash-reporter/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..75ac01e
--- /dev/null
+++ b/crash_reporter/kernel_collector_test.h
@@ -0,0 +1,19 @@
+// Copyright 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.
+
+#ifndef CRASH_REPORTER_KERNEL_COLLECTOR_TEST_H_
+#define CRASH_REPORTER_KERNEL_COLLECTOR_TEST_H_
+
+#include "crash-reporter/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..d38479e
--- /dev/null
+++ b/crash_reporter/kernel_log_collector.sh
@@ -0,0 +1,49 @@
+#!/bin/sh
+
+# 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.
+
+# 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..5dcd1f6
--- /dev/null
+++ b/crash_reporter/kernel_warning_collector.cc
@@ -0,0 +1,101 @@
+// Copyright (c) 2012 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.
+
+#include "crash-reporter/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..f326b23
--- /dev/null
+++ b/crash_reporter/kernel_warning_collector.h
@@ -0,0 +1,35 @@
+// 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.
+
+#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-reporter/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..de6ef0a
--- /dev/null
+++ b/crash_reporter/list_proxies.cc
@@ -0,0 +1,291 @@
+// 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.
+
+#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/crash_reporter/testrunner.cc b/crash_reporter/testrunner.cc
new file mode 100644
index 0000000..d45bbf8
--- /dev/null
+++ b/crash_reporter/testrunner.cc
@@ -0,0 +1,11 @@
+// Copyright 2015 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.
+
+#include <chromeos/test_helpers.h>
+#include <gtest/gtest.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..908bbc9
--- /dev/null
+++ b/crash_reporter/udev_collector.cc
@@ -0,0 +1,232 @@
+// Copyright (c) 2012 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.
+
+#include "crash-reporter/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..1689dd3
--- /dev/null
+++ b/crash_reporter/udev_collector.h
@@ -0,0 +1,64 @@
+// Copyright (c) 2012 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.
+
+#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-reporter/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..08d9b2c
--- /dev/null
+++ b/crash_reporter/udev_collector_test.cc
@@ -0,0 +1,171 @@
+// Copyright (c) 2012 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.
+
+#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 "crash-reporter/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..e8273b5
--- /dev/null
+++ b/crash_reporter/unclean_shutdown_collector.cc
@@ -0,0 +1,81 @@
+// Copyright (c) 2010 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.
+
+#include "crash-reporter/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..d30a0b2
--- /dev/null
+++ b/crash_reporter/unclean_shutdown_collector.h
@@ -0,0 +1,50 @@
+// Copyright (c) 2010 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.
+
+#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-reporter/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..f5e1b32
--- /dev/null
+++ b/crash_reporter/unclean_shutdown_collector_test.cc
@@ -0,0 +1,135 @@
+// Copyright (c) 2010 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.
+
+#include "crash-reporter/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..302b130
--- /dev/null
+++ b/crash_reporter/user_collector.cc
@@ -0,0 +1,673 @@
+// Copyright (c) 2012 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.
+
+#include "crash-reporter/user_collector.h"
+
+#include <bits/wordsize.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/types.h>  // For getpwuid_r, getgrnam_r, WEXITSTATUS.
+
+#include <set>
+#include <string>
+#include <vector>
+
+#include <base/files/file_util.h>
+#include <base/logging.h>
+#include <base/posix/eintr_wrapper.h>
+#include <base/stl_util.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>
+
+static const char kCollectionErrorSignature[] =
+    "crash_reporter-user-collection";
+// This procfs file is used to cause kernel core file writing to
+// instead pipe the core file into a user space process.  See
+// core(5) man page.
+static const char kCorePatternFile[] = "/proc/sys/kernel/core_pattern";
+static const char kCorePipeLimitFile[] = "/proc/sys/kernel/core_pipe_limit";
+// Set core_pipe_limit to 4 so that we can catch a few unrelated concurrent
+// crashes, but finite to avoid infinitely recursing on crash handling.
+static const char kCorePipeLimit[] = "4";
+static const char kCoreToMinidumpConverterPath[] = "/usr/bin/core2md";
+
+static const char kStatePrefix[] = "State:\t";
+
+// 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),
+      core_pattern_file_(kCorePatternFile),
+      core_pipe_limit_file_(kCorePipeLimitFile),
+      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;
+}
+
+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";
+
+  if (base::WriteFile(FilePath(core_pipe_limit_file_), kCorePipeLimit,
+                      strlen(kCorePipeLimit)) !=
+      static_cast<int>(strlen(kCorePipeLimit))) {
+    PLOG(ERROR) << "Unable to write " << core_pipe_limit_file_;
+    return false;
+  }
+  std::string pattern = GetPattern(enabled);
+  if (base::WriteFile(FilePath(core_pattern_file_), pattern.c_str(),
+                      pattern.length()) != static_cast<int>(pattern.length())) {
+    PLOG(ERROR) << "Unable to write " << core_pattern_file_;
+    return false;
+  }
+  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("/dev/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("/tmp/crash_reporter/%d", 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);
+}
+
+// Returns true if the given executable name matches that of Chrome.  This
+// includes checks for threads that Chrome has renamed.
+static bool IsChromeExecName(const std::string &exec) {
+  static const char *kChromeNames[] = {
+    "chrome",
+    // These are additional thread names seen in http://crash/
+    "MediaPipeline",
+    // These come from the use of base::PlatformThread::SetName() directly
+    "CrBrowserMain", "CrRendererMain", "CrUtilityMain", "CrPPAPIMain",
+    "CrPPAPIBrokerMain", "CrPluginMain", "CrWorkerMain", "CrGpuMain",
+    "BrokerEvent", "CrVideoRenderer", "CrShutdownDetector",
+    "UsbEventHandler", "CrNaClMain", "CrServiceMain",
+    // These thread names come from the use of base::Thread
+    "Gamepad polling thread", "Chrome_InProcGpuThread",
+    "Chrome_DragDropThread", "Renderer::FILE", "VC manager",
+    "VideoCaptureModuleImpl", "JavaBridge", "VideoCaptureManagerThread",
+    "Geolocation", "Geolocation_wifi_provider",
+    "Device orientation polling thread", "Chrome_InProcRendererThread",
+    "NetworkChangeNotifier", "Watchdog", "inotify_reader",
+    "cf_iexplore_background_thread", "BrowserWatchdog",
+    "Chrome_HistoryThread", "Chrome_SyncThread", "Chrome_ShellDialogThread",
+    "Printing_Worker", "Chrome_SafeBrowsingThread", "SimpleDBThread",
+    "D-Bus thread", "AudioThread", "NullAudioThread", "V4L2Thread",
+    "ChromotingClientDecodeThread", "Profiling_Flush",
+    "worker_thread_ticker", "AudioMixerAlsa", "AudioMixerCras",
+    "FakeAudioRecordingThread", "CaptureThread",
+    "Chrome_WebSocketproxyThread", "ProcessWatcherThread",
+    "Chrome_CameraThread", "import_thread", "NaCl_IOThread",
+    "Chrome_CloudPrintJobPrintThread", "Chrome_CloudPrintProxyCoreThread",
+    "DaemonControllerFileIO", "ChromotingMainThread",
+    "ChromotingEncodeThread", "ChromotingDesktopThread",
+    "ChromotingIOThread", "ChromotingFileIOThread",
+    "Chrome_libJingle_WorkerThread", "Chrome_ChildIOThread",
+    "GLHelperThread", "RemotingHostPlugin",
+    // "PAC thread #%d",  // not easy to check because of "%d"
+    "Chrome_DBThread", "Chrome_WebKitThread", "Chrome_FileThread",
+    "Chrome_FileUserBlockingThread", "Chrome_ProcessLauncherThread",
+    "Chrome_CacheThread", "Chrome_IOThread", "Cache Thread", "File Thread",
+    "ServiceProcess_IO", "ServiceProcess_File",
+    "extension_crash_uploader", "gpu-process_crash_uploader",
+    "plugin_crash_uploader", "renderer_crash_uploader",
+    // These come from the use of webkit_glue::WebThreadImpl
+    "Compositor", "Browser Compositor",
+    // "WorkerPool/%d",  // not easy to check because of "%d"
+    // These come from the use of base::Watchdog
+    "Startup watchdog thread Watchdog", "Shutdown watchdog thread Watchdog",
+    // These come from the use of AudioDeviceThread::Start
+    "AudioDevice", "AudioInputDevice", "AudioOutputDevice",
+    // These come from the use of MessageLoopFactory::GetMessageLoop
+    "GpuVideoDecoder", "RtcVideoDecoderThread", "PipelineThread",
+    "AudioDecoderThread", "VideoDecoderThread",
+    // These come from the use of MessageLoopFactory::GetMessageLoopProxy
+    "CaptureVideoDecoderThread", "CaptureVideoDecoder",
+    // These come from the use of base::SimpleThread
+    "LocalInputMonitor/%d",  // "%d" gets lopped off for kernel-supplied
+    // These come from the use of base::DelegateSimpleThread
+    "ipc_channel_nacl reader thread/%d", "plugin_audio_input_thread/%d",
+    "plugin_audio_thread/%d",
+    // These come from the use of base::SequencedWorkerPool
+    "BrowserBlockingWorker%d/%d",  // "%d" gets lopped off for kernel-supplied
+  };
+  static std::set<std::string> chrome_names;
+
+  // Initialize a set of chrome names, for efficient lookup
+  if (chrome_names.empty()) {
+    for (size_t i = 0; i < arraysize(kChromeNames); i++) {
+      std::string check_name(kChromeNames[i]);
+      chrome_names.insert(check_name);
+      // When checking a kernel-supplied name, it should be truncated to 15
+      // chars.  See PR_SET_NAME in
+      // http://www.kernel.org/doc/man-pages/online/pages/man2/prctl.2.html,
+      // although that page misleads by saying "16 bytes".
+      chrome_names.insert("supplied_" + std::string(check_name, 0, 15));
+    }
+  }
+
+  return ContainsKey(chrome_names, exec);
+}
+
+bool UserCollector::ShouldDump(bool has_owner_consent,
+                               bool is_developer,
+                               bool handle_chrome_crashes,
+                               const std::string &exec,
+                               std::string *reason) {
+  reason->clear();
+
+  // Treat Chrome crashes as if the user opted-out.  We stop counting Chrome
+  // crashes towards user crashes, so user crashes really mean non-Chrome
+  // user-space crashes.
+  if (!handle_chrome_crashes && IsChromeExecName(exec)) {
+    *reason = "ignoring call by kernel - chrome crash; "
+              "waiting for chrome to call us directly";
+    return false;
+  }
+
+  // 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(),
+                         ShouldHandleChromeCrashes(),
+                         exec,
+                         &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..aac94cb
--- /dev/null
+++ b/crash_reporter/user_collector.h
@@ -0,0 +1,192 @@
+// Copyright (c) 2010 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.
+
+#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-reporter/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);
+
+  // Set (override the default) core file pattern.
+  void set_core_pattern_file(const std::string &pattern) {
+    core_pattern_file_ = pattern;
+  }
+
+  // Set (override the default) core pipe limit file.
+  void set_core_pipe_limit_file(const std::string &path) {
+    core_pipe_limit_file_ = path;
+  }
+
+ 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,
+                  bool handle_chrome_crashes,
+                  const std::string &exec,
+                  std::string *reason);
+
+  bool generate_diagnostics_;
+  std::string core_pattern_file_;
+  std::string core_pipe_limit_file_;
+  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..823d8b4
--- /dev/null
+++ b/crash_reporter/user_collector_test.cc
@@ -0,0 +1,553 @@
+// Copyright (c) 2012 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.
+
+#include "crash-reporter/user_collector.h"
+
+#include <bits/wordsize.h>
+#include <elf.h>
+#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);
+    collector_.set_core_pattern_file("test/core_pattern");
+    collector_.set_core_pipe_limit_file("test/core_pipe_limit");
+    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, EnableOK) {
+  ASSERT_TRUE(collector_.Enable());
+  ExpectFileEquals("|/my/path --user=%P:%s:%u:%e",
+                   FilePath("test/core_pattern"));
+  ExpectFileEquals("4", FilePath("test/core_pipe_limit"));
+  ASSERT_EQ(s_crashes, 0);
+  EXPECT_TRUE(FindLog("Enabling user crash handling"));
+}
+
+TEST_F(UserCollectorTest, EnableNoPatternFileAccess) {
+  collector_.set_core_pattern_file("/does_not_exist");
+  ASSERT_FALSE(collector_.Enable());
+  ASSERT_EQ(s_crashes, 0);
+  EXPECT_TRUE(FindLog("Enabling user crash handling"));
+  EXPECT_TRUE(FindLog("Unable to write /does_not_exist"));
+}
+
+TEST_F(UserCollectorTest, EnableNoPipeLimitFileAccess) {
+  collector_.set_core_pipe_limit_file("/does_not_exist");
+  ASSERT_FALSE(collector_.Enable());
+  ASSERT_EQ(s_crashes, 0);
+  // Core pattern should not be written if we cannot access the pipe limit
+  // or otherwise we may set a pattern that results in infinite recursion.
+  ASSERT_FALSE(base::PathExists(FilePath("test/core_pattern")));
+  EXPECT_TRUE(FindLog("Enabling user crash handling"));
+  EXPECT_TRUE(FindLog("Unable to write /does_not_exist"));
+}
+
+TEST_F(UserCollectorTest, DisableOK) {
+  ASSERT_TRUE(collector_.Disable());
+  ExpectFileEquals("core", FilePath("test/core_pattern"));
+  ASSERT_EQ(s_crashes, 0);
+  EXPECT_TRUE(FindLog("Disabling user crash handling"));
+}
+
+TEST_F(UserCollectorTest, DisableNoFileAccess) {
+  collector_.set_core_pattern_file("/does_not_exist");
+  ASSERT_FALSE(collector_.Disable());
+  ASSERT_EQ(s_crashes, 0);
+  EXPECT_TRUE(FindLog("Disabling user crash handling"));
+  EXPECT_TRUE(FindLog("Unable to write /does_not_exist"));
+}
+
+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, false,
+                                    "chrome-wm", &reason));
+  EXPECT_EQ("developer build - not testing - always dumping", reason);
+
+  // When running a crash test, behave as normal.
+  EXPECT_FALSE(collector_.ShouldDump(false, false, false,
+                                    "chrome-wm", &reason));
+  EXPECT_EQ("ignoring - no consent", reason);
+}
+
+TEST_F(UserCollectorTest, ShouldDumpChromeOverridesDeveloperImage) {
+  std::string reason;
+  // When running a crash test, behave as normal.
+  EXPECT_FALSE(collector_.ShouldDump(false, false, false,
+                                     "chrome", &reason));
+  EXPECT_EQ(kChromeIgnoreMsg, reason);
+  EXPECT_FALSE(collector_.ShouldDump(false, false, false,
+                                     "supplied_Compositor", &reason));
+  EXPECT_EQ(kChromeIgnoreMsg, reason);
+  EXPECT_FALSE(collector_.ShouldDump(false, false, false,
+                                     "supplied_PipelineThread", &reason));
+  EXPECT_EQ(kChromeIgnoreMsg, reason);
+  EXPECT_FALSE(collector_.ShouldDump(false, false, false,
+                                     "Chrome_ChildIOThread", &reason));
+  EXPECT_EQ(kChromeIgnoreMsg, reason);
+  EXPECT_FALSE(collector_.ShouldDump(false, false, false,
+                                     "supplied_Chrome_ChildIOT", &reason));
+  EXPECT_EQ(kChromeIgnoreMsg, reason);
+  EXPECT_FALSE(collector_.ShouldDump(false, false, false,
+                                     "supplied_ChromotingClien", &reason));
+  EXPECT_EQ(kChromeIgnoreMsg, reason);
+  EXPECT_FALSE(collector_.ShouldDump(false, false, false,
+                                     "supplied_LocalInputMonit", &reason));
+  EXPECT_EQ(kChromeIgnoreMsg, reason);
+
+  // When running a developer image, test that chrome crashes are handled
+  // when the "handle_chrome_crashes" flag is set.
+  EXPECT_TRUE(collector_.ShouldDump(false, true, true,
+                                    "chrome", &reason));
+  EXPECT_EQ("developer build - not testing - always dumping",
+            reason);
+  EXPECT_TRUE(collector_.ShouldDump(false, true, true,
+                                    "supplied_Compositor", &reason));
+  EXPECT_EQ("developer build - not testing - always dumping",
+            reason);
+  EXPECT_TRUE(collector_.ShouldDump(false, true, true,
+                                    "supplied_PipelineThread", &reason));
+  EXPECT_EQ("developer build - not testing - always dumping",
+            reason);
+  EXPECT_TRUE(collector_.ShouldDump(false, true, true,
+                                    "Chrome_ChildIOThread", &reason));
+  EXPECT_EQ("developer build - not testing - always dumping",
+            reason);
+  EXPECT_TRUE(collector_.ShouldDump(false, true, true,
+                                    "supplied_Chrome_ChildIOT", &reason));
+  EXPECT_EQ("developer build - not testing - always dumping",
+            reason);
+  EXPECT_TRUE(collector_.ShouldDump(false, true, true,
+                                    "supplied_ChromotingClien", &reason));
+  EXPECT_EQ("developer build - not testing - always dumping",
+            reason);
+  EXPECT_TRUE(collector_.ShouldDump(false, true, true,
+                                    "supplied_LocalInputMonit", &reason));
+  EXPECT_EQ("developer build - not testing - always dumping",
+            reason);
+}
+
+TEST_F(UserCollectorTest, ShouldDumpUseConsentProductionImage) {
+  std::string result;
+  EXPECT_FALSE(collector_.ShouldDump(false, false, false,
+                                     "chrome-wm", &result));
+  EXPECT_EQ("ignoring - no consent", result);
+
+  EXPECT_TRUE(collector_.ShouldDump(true, false, false,
+                                    "chrome-wm", &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..691ef99
--- /dev/null
+++ b/crash_reporter/warn_collector.l
@@ -0,0 +1,322 @@
+/* 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.
+ *
+ * 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.
+ */
+
+%{
+#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;
+}
+
+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 = index(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);
+  (void) input();
+}
diff --git a/crash_reporter/warn_collector_test.c b/crash_reporter/warn_collector_test.c
new file mode 100644
index 0000000..7e25d01
--- /dev/null
+++ b/crash_reporter/warn_collector_test.c
@@ -0,0 +1,14 @@
+/* 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.
+ */
+
+/*
+ * Test driver for the warn_collector daemon.
+ */
+#include <stdlib.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..d9bb6f9
--- /dev/null
+++ b/crash_reporter/warn_collector_test.sh
@@ -0,0 +1,79 @@
+#! /bin/bash
+# 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.
+
+# 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..d8f3fad
--- /dev/null
+++ b/crash_reporter/warn_collector_test_reporter.sh
@@ -0,0 +1,16 @@
+#! /bin/sh
+# 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.
+
+# 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..3fca709 100644
--- a/debuggerd/Android.mk
+++ b/debuggerd/Android.mk
@@ -77,12 +77,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/debuggerd.cpp b/debuggerd/debuggerd.cpp
index b84a4e5..787b7aa 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;
 }
@@ -589,7 +567,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/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..a771a39 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());
@@ -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..114c7e4 100644
--- a/debuggerd/tombstone.cpp
+++ b/debuggerd/tombstone.cpp
@@ -180,7 +180,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 +338,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 +448,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 +471,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 +517,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 +787,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 +799,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..ce8e15f 100644
--- a/fastboot/Android.mk
+++ b/fastboot/Android.mk
@@ -21,7 +21,7 @@
 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_CONLYFLAGS += -std=gnu99
@@ -30,17 +30,17 @@
 LOCAL_CFLAGS += -DFASTBOOT_REVISION='"$(fastboot_version)"'
 
 ifeq ($(HOST_OS),linux)
-  LOCAL_SRC_FILES += usb_linux.c util_linux.c
+  LOCAL_SRC_FILES += usb_linux.cpp util_linux.cpp
 endif
 
 ifeq ($(HOST_OS),darwin)
-  LOCAL_SRC_FILES += usb_osx.c util_osx.c
+  LOCAL_SRC_FILES += usb_osx.cpp util_osx.cpp
   LOCAL_LDLIBS += -lpthread -framework CoreFoundation -framework IOKit -framework Carbon
   LOCAL_CFLAGS += -Wno-unused-parameter
 endif
 
 ifeq ($(HOST_OS),windows)
-  LOCAL_SRC_FILES += usb_windows.c util_windows.c
+  LOCAL_SRC_FILES += usb_windows.cpp util_windows.cpp
   EXTRA_STATIC_LIBS := AdbWinApi
   ifneq ($(strip $(USE_CYGWIN)),)
     # Pure cygwin case
@@ -97,10 +97,9 @@
 $(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)
diff --git a/fastboot/engine.c b/fastboot/engine.cpp
similarity index 89%
rename from fastboot/engine.c
rename to fastboot/engine.cpp
index 2f90e41..66b8140 100644
--- a/fastboot/engine.c
+++ b/fastboot/engine.cpp
@@ -45,10 +45,6 @@
 #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
@@ -73,7 +69,7 @@
     unsigned 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 +117,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 +130,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 +192,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 +215,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 +258,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)
+                      int invert, unsigned nvalues, const char **value)
 {
     Action *a;
     a = queue_action(OP_QUERY, "getvar:%s", var);
@@ -285,11 +276,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 +293,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 +315,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 +386,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 +402,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 be80cce..b964a36 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -275,59 +275,61 @@
             "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"
-            "                                           partitions\n"
-            "  flashing unlock                          unlocks the device. Allows user to"
-            "                                           flash any partition except the ones"
-            "                                           that are related to bootloader\n"
-            "  flashing lock_critical                   Prevents flashing bootloader related"
-            "                                           partitions\n"
-            "  flashing unlock_critical                 Enables flashing bootloader related"
-            "                                           partitions\n"
-            "  flashing get_unlock_ability              Queries bootloader to see if the"
-            "                                           device is unlocked\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"
+            "  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"
+            "  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)
+                          const char *secondstage, unsigned *sz,
+                          const char *cmdline)
 {
-    void *kdata = 0, *rdata = 0;
-    unsigned ksize = 0, rsize = 0;
+    void *kdata = 0, *rdata = 0, *sdata = 0;
+    unsigned ksize = 0, rsize = 0, ssize = 0;
     void *bdata;
     unsigned bsize;
 
@@ -363,10 +365,18 @@
         }
     }
 
+    if (secondstage) {
+        sdata = load_file(secondstage, &ssize);
+        if(sdata == 0) {
+            fprintf(stderr,"cannot load '%s': %s\n", secondstage, strerror(errno));
+            return  0;
+        }
+    }
+
     fprintf(stderr,"creating boot image...\n");
     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) {
         fprintf(stderr,"failed to create boot.img\n");
@@ -381,7 +391,7 @@
 
 static void* unzip_file(ZipArchiveHandle zip, const char* entry_name, unsigned* 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);
@@ -443,7 +453,7 @@
         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);
@@ -1172,6 +1182,7 @@
         } else if(!strcmp(*argv, "boot")) {
             char *kname = 0;
             char *rname = 0;
+            char *sname = 0;
             skip(1);
             if (argc > 0) {
                 kname = argv[0];
@@ -1181,7 +1192,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");
@@ -1205,14 +1220,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")) {
diff --git a/fastboot/fs.c b/fastboot/fs.cpp
similarity index 93%
rename from fastboot/fs.c
rename to fastboot/fs.cpp
index 8a15e6f..d8f9e16 100644
--- a/fastboot/fs.c
+++ b/fastboot/fs.cpp
@@ -38,7 +38,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[] = {
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 97%
rename from fastboot/protocol.c
rename to fastboot/protocol.cpp
index 5b97600..00c8a03 100644
--- a/fastboot/protocol.c
+++ b/fastboot/protocol.cpp
@@ -223,9 +223,9 @@
 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);
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_linux.c b/fastboot/usb_linux.cpp
similarity index 98%
rename from fastboot/usb_linux.c
rename to fastboot/usb_linux.cpp
index 022f364..9078c8f 100644
--- a/fastboot/usb_linux.c
+++ b/fastboot/usb_linux.cpp
@@ -340,7 +340,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 98%
rename from fastboot/usb_osx.c
rename to fastboot/usb_osx.cpp
index 0b6c515..a959566 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>
@@ -121,7 +122,7 @@
         result = (*plugInInterface)->QueryInterface(
                 plugInInterface,
                 CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID),
-                (LPVOID) &interface);
+                (LPVOID*) &interface);
 
         // No longer need the intermediate plugin
         (*plugInInterface)->Release(plugInInterface);
@@ -279,7 +280,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;
@@ -432,7 +433,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..d8ca4fa 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));
@@ -161,10 +165,10 @@
     } 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,
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/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/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..07d1351 100644
--- a/include/cutils/sockets.h
+++ b/include/cutils/sockets.h
@@ -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/private/android_filesystem_config.h b/include/private/android_filesystem_config.h
index 2ed27dc..a49da8c 100644
--- a/include/private/android_filesystem_config.h
+++ b/include/private/android_filesystem_config.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 */
@@ -168,6 +169,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/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..5ef2ab0 100644
--- a/include/ziparchive/zip_archive.h
+++ b/include/ziparchive/zip_archive.h
@@ -33,17 +33,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 +152,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 +163,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 +178,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..6ef8dff 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,8 +18,6 @@
     -Wno-unused-parameter \
     -Werror \
 
-init_clang := true
-
 # --
 
 include $(CLEAR_VARS)
@@ -32,7 +30,7 @@
 
 LOCAL_STATIC_LIBRARIES := libbase
 LOCAL_MODULE := libinit
-LOCAL_CLANG := $(init_clang)
+LOCAL_CLANG := true
 include $(BUILD_STATIC_LIBRARY)
 
 include $(CLEAR_VARS)
@@ -81,7 +79,7 @@
     ln -sf ../init $(TARGET_ROOT_OUT)/sbin/ueventd; \
     ln -sf ../init $(TARGET_ROOT_OUT)/sbin/watchdogd
 
-LOCAL_CLANG := $(init_clang)
+LOCAL_CLANG := true
 include $(BUILD_EXECUTABLE)
 
 
@@ -98,5 +96,5 @@
     libbase \
 
 LOCAL_STATIC_LIBRARIES := libinit
-LOCAL_CLANG := $(init_clang)
+LOCAL_CLANG := true
 include $(BUILD_NATIVE_TEST)
diff --git a/init/bootchart.cpp b/init/bootchart.cpp
index 95687cb..e5b153a 100644
--- a/init/bootchart.cpp
+++ b/init/bootchart.cpp
@@ -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)
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 8eb5b5b..87e56bc 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -16,7 +16,9 @@
 
 #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,6 +40,7 @@
 #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"
@@ -49,6 +52,8 @@
 #include "log.h"
 
 #define chmod DO_NOT_USE_CHMOD_USE_FCHMODAT_SYMLINK_NOFOLLOW
+#define UNMOUNT_CHECK_MS 5000
+#define UNMOUNT_CHECK_TIMES 10
 
 int add_environment(const char *name, const char *value);
 
@@ -57,14 +62,8 @@
 
 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;
-    }
-
     std::string module;
-    if (!read_file(filename_val, &module)) {
+    if (!read_file(filename, &module)) {
         return -1;
     }
 
@@ -109,6 +108,67 @@
     }
 }
 
+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.
+     */
+    service_for_each(service_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);
+    } 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);
+    }
+}
+
 int do_class_start(int nargs, char **args)
 {
         /* Starting a class does not start services
@@ -406,7 +466,7 @@
     if (nargs != 2) {
         return -1;
     }
-
+    const char* fstabfile = args[1];
     /*
      * 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 +490,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) {
@@ -500,15 +560,7 @@
 {
     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);
+    property_set(name, value);
     return 0;
 }
 
@@ -554,21 +606,16 @@
 
 int do_powerctl(int nargs, char **args)
 {
-    char command[PROP_VALUE_MAX];
-    int res;
+    const char* command = args[1];
     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;
-    }
+    void (*callback_on_ro_remount)(const struct mntent*) = NULL;
 
     if (strncmp(command, "shutdown", 8) == 0) {
         cmd = ANDROID_RB_POWEROFF;
         len = 8;
+        callback_on_ro_remount = unmount_and_fsck;
     } else if (strncmp(command, "reboot", 6) == 0) {
         cmd = ANDROID_RB_RESTART2;
         len = 6;
@@ -586,7 +633,8 @@
         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)
@@ -646,13 +694,7 @@
 {
     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);
+    return write_file(path, value);
 }
 
 int do_copy(int nargs, char **args)
@@ -775,18 +817,12 @@
 }
 
 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);
+    int log_level = atoi(args[1]);
     if (log_level < KLOG_ERROR_LEVEL || log_level > KLOG_DEBUG_LEVEL) {
         ERROR("loglevel: invalid log level'%d'\n", log_level);
         return -EINVAL;
@@ -834,9 +870,8 @@
 }
 
 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)
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/init.cpp b/init/init.cpp
index 93fe944..4be16ea 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -43,6 +43,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>
@@ -75,7 +76,7 @@
 static struct command *cur_command = NULL;
 
 static int have_console;
-static char console_name[PROP_VALUE_MAX] = "/dev/console";
+static std::string console_name = "/dev/console";
 static time_t process_needs_restart;
 
 static const char *ENV[32];
@@ -159,7 +160,7 @@
 static void open_console()
 {
     int fd;
-    if ((fd = open(console_name, O_RDWR)) < 0) {
+    if ((fd = open(console_name.c_str(), O_RDWR)) < 0) {
         fd = open("/dev/null", O_RDWR);
     }
     ioctl(fd, TIOCSCTTY, 0);
@@ -205,55 +206,56 @@
         return;
     }
 
-    struct stat s;
-    if (stat(svc->args[0], &s) != 0) {
-        ERROR("cannot find '%s', disabling '%s'\n", svc->args[0], svc->name);
+    struct stat sb;
+    if (stat(svc->args[0], &sb) == -1) {
+        ERROR("cannot find '%s' (%s), disabling '%s'\n", svc->args[0], strerror(errno), 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]);
+        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;
+    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;
-            }
+        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 = getfilecon(svc->args[0], &fcon);
+        if (rc < 0) {
+            ERROR("could not get context while starting '%s'\n", svc->name);
+            free(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;
-            }
+        rc = security_compute_create(mycon, fcon, string_to_security_class("process"), &scon);
+        if (rc == 0 && !strcmp(scon, mycon)) {
+            ERROR("Service %s does not have a SELinux domain defined.\n", svc->name);
+            free(mycon);
+            free(fcon);
+            free(scon);
+            return;
+        }
+        free(mycon);
+        free(fcon);
+        if (rc < 0) {
+            ERROR("could not get context while starting '%s'\n", svc->name);
+            return;
         }
     }
 
@@ -287,7 +289,7 @@
             }
         }
 
-        freecon(scon);
+        free(scon);
         scon = NULL;
 
         if (svc->writepid_files_) {
@@ -345,7 +347,7 @@
             }
         }
         if (svc->seclabel) {
-            if (is_selinux_enabled() > 0 && setexeccon(svc->seclabel) < 0) {
+            if (setexeccon(svc->seclabel) < 0) {
                 ERROR("cannot setexeccon('%s'): %s\n", svc->seclabel, strerror(errno));
                 _exit(127);
             }
@@ -376,7 +378,7 @@
         _exit(127);
     }
 
-    freecon(scon);
+    free(scon);
 
     if (pid < 0) {
         ERROR("failed to start '%s'\n", svc->name);
@@ -568,25 +570,37 @@
 }
 
 
-void build_triggers_string(char *name_str, int length, struct action *cur_action) {
+std::string build_triggers_string(struct action *cur_action) {
+    std::string result;
     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);
+            result.push_back(' ');
         }
-        strlcat(name_str, cur_trigger->name , length);
+        result += cur_trigger->name;
     }
+    return result;
+}
+
+bool expand_command_arguments(int nargs, char** args, std::vector<std::string>* expanded_args) {
+    std::vector<std::string>& strs = *expanded_args;
+    strs.resize(nargs);
+    strs[0] = args[0];
+    for (int i = 1; i < nargs; ++i) {
+        if (expand_props(args[i], &strs[i]) == -1) {
+            ERROR("%s: cannot expand '%s'\n", args[0], args[i]);
+            return false;
+        }
+    }
+    return true;
 }
 
 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;
@@ -594,9 +608,8 @@
             return;
         }
 
-        build_triggers_string(name_str, sizeof(name_str), cur_action);
-
-        INFO("processing action %p (%s)\n", cur_action, name_str);
+        std::string trigger_name = build_triggers_string(cur_action);
+        INFO("processing action %p (%s)\n", cur_action, trigger_name.c_str());
         cur_command = get_first_command(cur_action);
     } else {
         cur_command = get_next_command(cur_action, cur_command);
@@ -605,23 +618,35 @@
     if (!cur_command) {
         return;
     }
-
-    int result = cur_command->func(cur_command->nargs, cur_command->args);
+    int result = 0;
+    std::vector<std::string> arg_strs;
+    if (!expand_command_arguments(cur_command->nargs, cur_command->args, &arg_strs)) {
+        result = -EINVAL;
+    }
+    if (result == 0) {
+        std::vector<char*> args;
+        for (auto& s : arg_strs) {
+            args.push_back(&s[0]);
+        }
+        result = cur_command->func(args.size(), &args[0]);
+    }
     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));
+        std::string cmd_str;
+        for (int i = 0; i < cur_command->nargs; ++i) {
+            if (i > 0) {
+                cmd_str.push_back(' ');
             }
+            cmd_str += cur_command->args[i];
         }
-        char source[256];
+        std::string trigger_name = build_triggers_string(cur_action);
+
+        std::string source;
         if (cur_command->filename) {
-            snprintf(source, sizeof(source), " (%s:%d)", cur_command->filename, cur_command->line);
-        } else {
-            *source = '\0';
+            source = android::base::StringPrintf(" (%s:%d)", cur_command->filename, cur_command->line);
         }
+
         INFO("Command '%s' action=%s%s returned %d took %.2fs\n",
-             cmd_str, cur_action ? name_str : "", source, result, t.duration());
+             cmd_str.c_str(), trigger_name.c_str(), source.c_str(), result, t.duration());
     }
 }
 
@@ -725,12 +750,12 @@
 
 static int console_init_action(int nargs, char **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 +785,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 +816,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 +834,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,18 +852,15 @@
     }
 }
 
-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)
@@ -874,46 +878,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 +902,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 +938,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 +945,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 +1001,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 +1014,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 +1040,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,7 +1057,7 @@
     property_load_boot_defaults();
     start_property_service();
 
-    init_parse_config_file("/init.rc");
+    init_parse_config("/init.rc");
 
     action_for_each_trigger("early-init", action_add_queue_tail);
 
@@ -1096,8 +1076,8 @@
     queue_builtin_action(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) {
+    std::string bootmode = property_get("ro.bootmode");
+    if (bootmode == "charger") {
         action_for_each_trigger("charger", action_add_queue_tail);
     } else {
         action_for_each_trigger("late-init", action_add_queue_tail);
diff --git a/init/init.h b/init/init.h
index c166969..d2b2dfb 100644
--- a/init/init.h
+++ b/init/init.h
@@ -138,7 +138,7 @@
 extern struct selabel_handle *sehandle;
 extern struct selabel_handle *sehandle_prop;
 
-void build_triggers_string(char *name_str, int length, struct action *cur_action);
+std::string build_triggers_string(struct action *cur_action);
 
 void handle_control_message(const char *msg, const char *arg);
 
@@ -161,5 +161,6 @@
 void zap_stdio(void);
 
 void register_epoll_handler(int fd, void (*fn)());
+bool expand_command_arguments(int nargs, char** args, std::vector<std::string>* expanded_args);
 
 #endif	/* _INIT_INIT_H */
diff --git a/init/init_parser.cpp b/init/init_parser.cpp
index 62e8b10..6df79f6 100644
--- a/init/init_parser.cpp
+++ b/init/init_parser.cpp
@@ -15,6 +15,7 @@
  */
 
 #include <ctype.h>
+#include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <inttypes.h>
@@ -32,6 +33,7 @@
 #include "property_service.h"
 #include "util.h"
 
+#include <base/stringprintf.h>
 #include <cutils/iosched_policy.h>
 #include <cutils/list.h>
 
@@ -95,9 +97,8 @@
         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);
+            std::string trigger_name = build_triggers_string(act);
+            INFO("%s", trigger_name.c_str());
             INFO("\n");
 
             struct listnode* node2;
@@ -217,27 +218,12 @@
 static void parse_line_no_op(struct parse_state*, int, char**) {
 }
 
-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;
+int expand_props(const char *src, std::string *dst) {
     const char *src_ptr = src;
-    int ret = 0;
-    int left = dst_size - 1;
 
-    if (!src || !dst || dst_size == 0)
+    if (!src || !dst) {
         return -1;
+    }
 
     /* - variables can either be $x.y or ${x.y}, in case they are only part
      *   of the string.
@@ -245,104 +231,75 @@
      * - 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;
+    while (*src_ptr) {
+        const char *c;
 
         c = strchr(src_ptr, '$');
         if (!c) {
-            while (left-- > 0 && *src_ptr)
-                *(dst_ptr++) = *(src_ptr++);
+            dst->append(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;
+        dst->append(src_ptr, c);
         c++;
 
         if (*c == '$') {
-            *(dst_ptr++) = *(c++);
+            dst->push_back(*(c++));
             src_ptr = c;
-            left--;
             continue;
         } else if (*c == '\0') {
             break;
         }
 
+        std::string prop_name;
         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);
+            const char* end = strchr(c, '}');
+            if (!end) {
+                // failed to find closing brace, abort.
+                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';
+            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",
-                  prop);
+                  c);
+            c += prop_name.size();
         }
 
-        if (prop_len == 0) {
+        if (prop_name.empty()) {
             ERROR("invalid zero-length prop name in '%s'\n", src);
             goto err;
         }
 
-        prop_val_len = property_get(prop, prop_val);
-        if (!prop_val_len) {
+        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, src);
+                  prop_name.c_str(), src);
             goto err;
         }
 
-        ret = push_chars(&dst_ptr, &left, prop_val, prop_val_len);
-        if (ret < 0)
-            goto err_nospace;
+        dst->append(prop_val);
         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));
+    std::string conf_file;
+    int ret = expand_props(args[1], &conf_file);
     if (ret) {
         ERROR("error while handling import on line '%d' in '%s'\n",
               state->line, state->filename);
@@ -350,7 +307,9 @@
     }
 
     struct import* import = (struct import*) calloc(1, sizeof(struct import));
-    import->filename = strdup(conf_file);
+    import->filename = strdup(conf_file.c_str());
+
+    struct listnode *import_list = (listnode*) state->priv;
     list_add_tail(import_list, &import->list);
     INFO("Added '%s' to import list\n", import->filename);
 }
@@ -428,22 +387,20 @@
 
 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);
+         struct import* import = node_to_item(node, struct import, list);
+         if (!init_parse_config(import->filename)) {
+             ERROR("could not import file '%s' from '%s': %s\n",
+                   import->filename, fn, strerror(errno));
+         }
     }
 }
 
-int init_parse_config_file(const char* path) {
-    INFO("Parsing %s...\n", path);
+static bool init_parse_config_file(const char* path) {
+    INFO("Parsing file %s...\n", path);
     Timer t;
     std::string data;
     if (!read_file(path, &data)) {
-        return -1;
+        return false;
     }
 
     data.push_back('\n'); // TODO: fix parse_config.
@@ -451,7 +408,35 @@
     dump_parser_state();
 
     NOTICE("(Parsing %s took %.2fs.)\n", path, t.duration());
-    return 0;
+    return true;
+}
+
+static bool init_parse_config_dir(const char* path) {
+    INFO("Parsing directory %s...\n", path);
+    std::unique_ptr<DIR, int(*)(DIR*)> config_dir(opendir(path), closedir);
+    if (!config_dir) {
+        ERROR("Could not import directory '%s'\n", path);
+        return false;
+    }
+    dirent* current_file;
+    while ((current_file = readdir(config_dir.get()))) {
+        std::string current_path =
+            android::base::StringPrintf("%s/%s", path, current_file->d_name);
+        // Ignore directories and only process regular files.
+        if (current_file->d_type == DT_REG) {
+            if (!init_parse_config_file(current_path.c_str())) {
+                ERROR("could not import file '%s'\n", current_path.c_str());
+            }
+        }
+    }
+    return true;
+}
+
+bool init_parse_config(const char* path) {
+    if (is_dir(path)) {
+        return init_parse_config_dir(path);
+    }
+    return init_parse_config_file(path);
 }
 
 static int valid_name(const char *name)
@@ -589,17 +574,13 @@
                 } 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;
+                             std::string prop_name(test, length);
+                             std::string value = property_get(prop_name.c_str());
 
                              /* does the property exist, and match the trigger value? */
-                             ret = property_get(prop_name, value);
-                             if (ret > 0 && (!strcmp(equals + 1, value) ||
+                             if (!value.empty() && (!strcmp(equals + 1, value.c_str()) ||
                                 !strcmp(equals + 1, "*"))) {
                                  continue;
                              }
diff --git a/init/init_parser.h b/init/init_parser.h
index 6348607..1ebb1ef 100644
--- a/init/init_parser.h
+++ b/init/init_parser.h
@@ -17,6 +17,8 @@
 #ifndef _INIT_INIT_PARSER_H_
 #define _INIT_INIT_PARSER_H_
 
+#include <string>
+
 #define INIT_PARSER_MAXARGS 64
 
 struct action;
@@ -31,8 +33,8 @@
 void queue_all_property_triggers();
 void queue_builtin_action(int (*func)(int nargs, char **args), const char *name);
 
-int init_parse_config_file(const char *fn);
-int expand_props(char *dst, const char *src, int len);
+bool init_parse_config(const char* path);
+int expand_props(const char *src, std::string *dst);
 
 service* make_exec_oneshot_service(int argc, char** argv);
 
diff --git a/init/keychords.cpp b/init/keychords.cpp
index 10d9573..c4ebdf9 100644
--- a/init/keychords.cpp
+++ b/init/keychords.cpp
@@ -64,19 +64,18 @@
 
 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")) {
+    // Only handle keychords if adb is enabled.
+    std::string adb_enabled = property_get("init.svc.adbd");
+    if (adb_enabled == "running") {
         svc = service_find_by_keychord(id);
         if (svc) {
             INFO("Starting service %s from keychord\n", svc->name);
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/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..5a758d7 100644
--- a/init/readme.txt
+++ b/init/readme.txt
@@ -197,8 +197,11 @@
 ifup <interface>
    Bring the network interface <interface> online.
 
-import <filename>
+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.
 
 insmod <path>
    Install the module at <path>
@@ -351,6 +354,29 @@
 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)
+
+
 Debugging init
 --------------
 By default, programs executed by init will drop stdout and stderr into
diff --git a/init/signal_handler.cpp b/init/signal_handler.cpp
index 39a466d..6893163 100644
--- a/init/signal_handler.cpp
+++ b/init/signal_handler.cpp
@@ -136,7 +136,14 @@
     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);
+        std::vector<std::string> arg_strs;
+        if (expand_command_arguments(cmd->nargs, cmd->args, &arg_strs)) {
+            std::vector<char*> args;
+            for (auto& s : arg_strs) {
+                args.push_back(&s[0]);
+            }
+            cmd->func(args.size(), &args[0]);
+        }
     }
     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/util.cpp b/init/util.cpp
index a5392c6..fdbdc1c 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>
@@ -46,7 +47,7 @@
 
 /*
  * 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 +59,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 +410,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 +465,14 @@
         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);
+}
diff --git a/init/util.h b/init/util.h
index 09d64cd..502b943 100644
--- a/init/util.h
+++ b/init/util.h
@@ -58,9 +58,11 @@
 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);
 #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 760f5cc..6bd7529 100644
--- a/libbacktrace/backtrace_test.cpp
+++ b/libbacktrace/backtrace_test.cpp
@@ -974,8 +974,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..6fb8c22 100644
--- a/libcutils/Android.mk
+++ b/libcutils/Android.mk
@@ -73,7 +73,7 @@
 LOCAL_SRC_FILES := $(commonSources) $(commonHostSources) dlmalloc_stubs.c
 LOCAL_STATIC_LIBRARIES := liblog
 ifneq ($(HOST_OS),windows)
-LOCAL_CFLAGS += -Werror
+LOCAL_CFLAGS += -Werror -Wall -Wextra
 endif
 LOCAL_MULTILIB := both
 include $(BUILD_HOST_STATIC_LIBRARY)
@@ -83,7 +83,7 @@
 LOCAL_SRC_FILES := $(commonSources) $(commonHostSources) dlmalloc_stubs.c
 LOCAL_SHARED_LIBRARIES := liblog
 ifneq ($(HOST_OS),windows)
-LOCAL_CFLAGS += -Werror
+LOCAL_CFLAGS += -Werror -Wall -Wextra
 endif
 LOCAL_MULTILIB := both
 include $(BUILD_HOST_SHARED_LIBRARY)
@@ -106,9 +106,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 +125,7 @@
 ifneq ($(ENABLE_CPUSETS),)
 LOCAL_CFLAGS += -DUSE_CPUSETS
 endif
-LOCAL_CFLAGS += -Werror -std=gnu90
+LOCAL_CFLAGS += -Werror -Wall -Wextra -std=gnu90
 include $(BUILD_STATIC_LIBRARY)
 
 include $(CLEAR_VARS)
@@ -140,7 +137,7 @@
 ifneq ($(ENABLE_CPUSETS),)
 LOCAL_CFLAGS += -DUSE_CPUSETS
 endif
-LOCAL_CFLAGS += -Werror
+LOCAL_CFLAGS += -Werror -Wall -Wextra
 LOCAL_C_INCLUDES := $(libcutils_c_includes)
 include $(BUILD_SHARED_LIBRARY)
 
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/fs_config.c b/libcutils/fs_config.c
index 9a1ad19..171d0f7 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" },
@@ -163,8 +167,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/sched_policy.c b/libcutils/sched_policy.c
index 83222f4..ab0331d 100644
--- a/libcutils/sched_policy.c
+++ b/libcutils/sched_policy.c
@@ -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)
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/liblog/Android.mk b/liblog/Android.mk
index d7766f5..930dcf7 100644
--- a/liblog/Android.mk
+++ b/liblog/Android.mk
@@ -24,11 +24,7 @@
 # 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
@@ -47,11 +43,7 @@
 ifeq ($(strip $(USE_MINGW)),)
 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
 # ========================================================
@@ -68,6 +60,7 @@
 LOCAL_LDLIBS := -lrt
 endif
 LOCAL_MULTILIB := both
+LOCAL_CXX_STL := none
 include $(BUILD_HOST_SHARED_LIBRARY)
 
 
@@ -77,6 +70,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 +82,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_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/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..a9671a9 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,
@@ -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/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 40cd31e..677d1e7 100644
--- a/libutils/Android.mk
+++ b/libutils/Android.mk
@@ -91,7 +91,7 @@
         liblog \
         libdl
 
-LOCAL_MODULE:= libutils
+LOCAL_MODULE := libutils
 include $(BUILD_STATIC_LIBRARY)
 
 # For the device, shared
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/SystemClock.cpp b/libutils/SystemClock.cpp
index ac3dd98..64204a8 100644
--- a/libutils/SystemClock.cpp
+++ b/libutils/SystemClock.cpp
@@ -29,7 +29,6 @@
 #include <sys/time.h>
 #include <limits.h>
 #include <fcntl.h>
-#include <errno.h>
 #include <string.h>
 
 #include <utils/SystemClock.h>
diff --git a/libutils/Threads.cpp b/libutils/Threads.cpp
index 1e014c6..c3666e4 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)
@@ -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;
     }
 
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/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/libziparchive/Android.mk b/libziparchive/Android.mk
index a3087ee..559c48b 100644
--- a/libziparchive/Android.mk
+++ b/libziparchive/Android.mk
@@ -42,8 +42,8 @@
 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/logd/CommandListener.cpp b/logd/CommandListener.cpp
index 5489cc9..489bea6 100644
--- a/logd/CommandListener.cpp
+++ b/logd/CommandListener.cpp
@@ -34,8 +34,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 +46,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) {
 }
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/LogKlog.cpp b/logd/LogKlog.cpp
index d578c04..eff26f5 100644
--- a/logd/LogKlog.cpp
+++ b/logd/LogKlog.cpp
@@ -43,8 +43,10 @@
     if (!isdigit(*s++)) {
         return NULL;
     }
+    static const size_t max_prio_len = 4;
+    size_t len = 0;
     char c;
-    while ((c = *s++)) {
+    while (((c = *s++)) && (++len <= max_prio_len)) {
         if (!isdigit(c)) {
             return (c == '>') ? s : NULL;
         }
@@ -73,7 +75,7 @@
 }
 
 // Like strtok_r with "\r\n" except that we look for log signatures (regex)
-// \(\(<[0-9]+>\)\([[] *[0-9]+[.][0-9]+[]] \)\{0,1\}\|[[] *[0-9]+[.][0-9]+[]] \)
+//  \(\(<[0-9]\{1,4\}>\)\([[] *[0-9]+[.][0-9]+[]] \)\{0,1\}\|[[] *[0-9]+[.][0-9]+[]] \)
 // and split if we see a second one without a newline.
 
 #define SIGNATURE_MASK     0xF0
@@ -165,7 +167,7 @@
             break;
         }
     }
-    /* NOTREACHED */
+    // NOTREACHED
 }
 
 log_time LogKlog::correction = log_time(CLOCK_REALTIME) - log_time(CLOCK_MONOTONIC);
@@ -175,8 +177,6 @@
         logbuf(buf),
         reader(reader),
         signature(CLOCK_MONOTONIC),
-        fdWrite(fdWrite),
-        fdRead(fdRead),
         initialized(false),
         enableLogging(true),
         auditd(auditd) {
@@ -465,7 +465,7 @@
             if (strncmp(bt, cp, size)) {
                 // <PRI>[<TIME>] <tag>_host '<tag>.<num>' : message
                 if (!strncmp(bt + size - 5, "_host", 5)
-                 && !strncmp(bt, cp, size - 5)) {
+                        && !strncmp(bt, cp, size - 5)) {
                     const char *b = cp;
                     cp += size - 5;
                     if (*cp == '.') {
@@ -490,7 +490,6 @@
                     }
                 }
             } else if (isspace(cp[size])) {
-                const char *b = cp;
                 cp += size;
                 while (isspace(*++cp));
                 // <PRI>[<TIME>] <tag> <tag> : message
@@ -535,11 +534,15 @@
         }
         size = etag - tag;
         if ((size <= 1)
-         || ((size == 2) && (isdigit(tag[0]) || isdigit(tag[1])))
-         || ((size == 3) && !strncmp(tag, "CPU", 3))
-         || ((size == 7) && !strncmp(tag, "WARNING", 7))
-         || ((size == 5) && !strncmp(tag, "ERROR", 5))
-         || ((size == 4) && !strncmp(tag, "INFO", 4))) {
+            // register names like x9
+                || ((size == 2) && (isdigit(tag[0]) || isdigit(tag[1])))
+            // register names like x18 but not driver names like en0
+                || ((size == 3) && (isdigit(tag[1]) && isdigit(tag[2])))
+            // blacklist
+                || ((size == 3) && !strncmp(tag, "CPU", 3))
+                || ((size == 7) && !strncmp(tag, "WARNING", 7))
+                || ((size == 5) && !strncmp(tag, "ERROR", 5))
+                || ((size == 4) && !strncmp(tag, "INFO", 4))) {
             buf = start;
             etag = tag = "";
         }
diff --git a/logd/LogKlog.h b/logd/LogKlog.h
index a898c63..24b2685 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;
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/logwrap.c b/logwrapper/logwrap.c
index 83576fb..44455d1 100644
--- a/logwrapper/logwrap.c
+++ b/logwrapper/logwrap.c
@@ -355,7 +355,8 @@
         }
 
         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));
 
             sz += b;
             // Log one line at a time
@@ -490,7 +491,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 +506,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;
diff --git a/metrics/OWNERS b/metrics/OWNERS
new file mode 100644
index 0000000..7f5e50d
--- /dev/null
+++ b/metrics/OWNERS
@@ -0,0 +1,3 @@
+semenzato@chromium.org
+derat@chromium.org
+bsimonnet@chromium.org
diff --git a/metrics/README b/metrics/README
new file mode 100644
index 0000000..4b92af3
--- /dev/null
+++ b/metrics/README
@@ -0,0 +1,138 @@
+Copyright (c) 2010 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.
+
+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/metrics/WATCHLISTS b/metrics/WATCHLISTS
new file mode 100644
index 0000000..a051f35
--- /dev/null
+++ b/metrics/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/metrics/c_metrics_library.cc b/metrics/c_metrics_library.cc
new file mode 100644
index 0000000..90a2d59
--- /dev/null
+++ b/metrics/c_metrics_library.cc
@@ -0,0 +1,78 @@
+// Copyright (c) 2010 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.
+
+//
+// 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/metrics/c_metrics_library.h b/metrics/c_metrics_library.h
new file mode 100644
index 0000000..7f78e43
--- /dev/null
+++ b/metrics/c_metrics_library.h
@@ -0,0 +1,49 @@
+// Copyright (c) 2010 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.
+
+#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/metrics/init/metrics_daemon.conf b/metrics/init/metrics_daemon.conf
new file mode 100644
index 0000000..e6932cf
--- /dev/null
+++ b/metrics/init/metrics_daemon.conf
@@ -0,0 +1,18 @@
+# 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     "Metrics collection daemon"
+author          "chromium-os-dev@chromium.org"
+
+# The metrics daemon is responsible for receiving and forwarding to
+# chrome UMA statistics not produced by chrome.
+start on starting system-services
+stop on stopping system-services
+respawn
+
+# metrics will update the next line to add -uploader for embedded builds.
+env DAEMON_FLAGS=""
+
+expect fork
+exec metrics_daemon ${DAEMON_FLAGS}
diff --git a/metrics/init/metrics_library.conf b/metrics/init/metrics_library.conf
new file mode 100644
index 0000000..03016d1
--- /dev/null
+++ b/metrics/init/metrics_library.conf
@@ -0,0 +1,25 @@
+# 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     "Metrics Library upstart file"
+author          "chromium-os-dev@chromium.org"
+
+# The metrics library is used by several programs (daemons and others)
+# to send UMA stats.
+start on starting boot-services
+
+pre-start script
+  # Create the file used as communication endpoint for metrics.
+  METRICS_DIR=/var/lib/metrics
+  EVENTS_FILE=${METRICS_DIR}/uma-events
+  mkdir -p ${METRICS_DIR}
+  touch ${EVENTS_FILE}
+  chown chronos.chronos ${EVENTS_FILE}
+  chmod 666 ${EVENTS_FILE}
+  # TRANSITION ONLY.
+  # TODO(semenzato) Remove after Chrome change, see issue 447256.
+  # Let Chrome read the metrics file from the old location.
+  mkdir -p /var/run/metrics
+  ln -sf ${EVENTS_FILE} /var/run/metrics
+end script
diff --git a/metrics/libmetrics-334380.gyp b/metrics/libmetrics-334380.gyp
new file mode 100644
index 0000000..9771821
--- /dev/null
+++ b/metrics/libmetrics-334380.gyp
@@ -0,0 +1,8 @@
+{
+  'variables': {
+    'libbase_ver': 334380,
+  },
+  'includes': [
+    'libmetrics.gypi',
+  ],
+}
diff --git a/metrics/libmetrics.gypi b/metrics/libmetrics.gypi
new file mode 100644
index 0000000..5b90a55
--- /dev/null
+++ b/metrics/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/metrics/libmetrics.pc.in b/metrics/libmetrics.pc.in
new file mode 100644
index 0000000..233f318
--- /dev/null
+++ b/metrics/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/metrics/make_tests.sh b/metrics/make_tests.sh
new file mode 100755
index 0000000..9dcc804
--- /dev/null
+++ b/metrics/make_tests.sh
@@ -0,0 +1,12 @@
+#!/bin/bash
+
+# Copyright (c) 2010 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.
+
+# Builds tests.
+
+set -e
+make tests
+mkdir -p "${OUT_DIR}"
+cp *_test "${OUT_DIR}"
diff --git a/metrics/metrics.gyp b/metrics/metrics.gyp
new file mode 100644
index 0000000..276ec78
--- /dev/null
+++ b/metrics/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/metrics/metrics_client.cc b/metrics/metrics_client.cc
new file mode 100644
index 0000000..bbe9dcd
--- /dev/null
+++ b/metrics/metrics_client.cc
@@ -0,0 +1,221 @@
+// 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.
+
+#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 [-ab] [-t] name sample min max nbuckets\n"
+          "        metrics_client [-ab] -e   name sample max\n"
+          "        metrics_client [-ab] -s   name sample\n"
+          "        metrics_client [-ab] -v   event\n"
+          "        metrics_client -u action\n"
+          "        metrics_client [-cg]\n"
+          "\n"
+          "  default: send metric with integer values to Chrome only\n"
+          "           |min| > 0, |min| <= sample < |max|\n"
+          "  -a: send metric (name/sample) to Autotest only\n"
+          "  -b: send metric to both Chrome and Autotest\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,
+                     bool send_to_autotest,
+                     bool send_to_chrome) {
+  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]);
+  }
+
+  // Send metrics
+  if (send_to_autotest) {
+    MetricsLibrary::SendToAutotest(name, sample);
+  }
+
+  if (send_to_chrome) {
+    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 send_to_autotest = false;
+  bool send_to_chrome = true;
+  bool secs_to_msecs = false;
+
+  // Parse arguments
+  int flag;
+  while ((flag = getopt(argc, argv, "abcegstuv")) != -1) {
+    switch (flag) {
+      case 'a':
+        send_to_autotest = true;
+        send_to_chrome = false;
+        break;
+      case 'b':
+        send_to_chrome = true;
+        send_to_autotest = true;
+        break;
+      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,
+                       send_to_autotest,
+                       send_to_chrome);
+    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/metrics/metrics_daemon.cc b/metrics/metrics_daemon.cc
new file mode 100644
index 0000000..880e90c
--- /dev/null
+++ b/metrics/metrics_daemon.cc
@@ -0,0 +1,1167 @@
+// 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.
+
+#include "metrics/metrics_daemon.h"
+
+#include <fcntl.h>
+#include <inttypes.h>
+#include <math.h>
+#include <string.h>
+#include <sysexits.h>
+#include <time.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 <base/sys_info.h>
+#include <chromeos/dbus/service_constants.h>
+#include <dbus/dbus.h>
+#include <dbus/message.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 {
+
+#define SAFE_MESSAGE(e) (e.message ? e.message : "unknown error")
+
+const char kCrashReporterInterface[] = "org.chromium.CrashReporter";
+const char kCrashReporterUserCrashSignal[] = "UserCrash";
+const char kCrashReporterMatchRule[] =
+    "type='signal',interface='%s',path='/',member='%s'";
+
+// Build type of an official build.
+// See src/third_party/chromiumos-overlay/chromeos/scripts/cros_set_lsb_release.
+const char kOfficialBuild[] = "Official Build";
+
+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";
+
+}  // namespace
+
+// disk stats metrics
+
+// The {Read,Write}Sectors numbers are in sectors/second.
+// A sector is usually 512 bytes.
+
+const char MetricsDaemon::kMetricReadSectorsLongName[] =
+    "Platform.ReadSectorsLong";
+const char MetricsDaemon::kMetricWriteSectorsLongName[] =
+    "Platform.WriteSectorsLong";
+const char MetricsDaemon::kMetricReadSectorsShortName[] =
+    "Platform.ReadSectorsShort";
+const char MetricsDaemon::kMetricWriteSectorsShortName[] =
+    "Platform.WriteSectorsShort";
+
+const int MetricsDaemon::kMetricStatsShortInterval = 1;  // seconds
+const int MetricsDaemon::kMetricStatsLongInterval = 30;  // seconds
+
+const int MetricsDaemon::kMetricMeminfoInterval = 30;        // seconds
+
+// Assume a max rate of 250Mb/s for reads (worse for writes) and 512 byte
+// sectors.
+const int MetricsDaemon::kMetricSectorsIOMax = 500000;  // sectors/second
+const int MetricsDaemon::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 MetricsDaemon::kMetricPageFaultsMax = kMetricSectorsIOMax / 8;
+const int MetricsDaemon::kMetricPageFaultsBuckets = 50;
+
+// Major page faults, i.e. the ones that require data to be read from disk.
+
+const char MetricsDaemon::kMetricPageFaultsLongName[] =
+    "Platform.PageFaultsLong";
+const char MetricsDaemon::kMetricPageFaultsShortName[] =
+    "Platform.PageFaultsShort";
+
+// Swap in and Swap out
+
+const char MetricsDaemon::kMetricSwapInLongName[] =
+    "Platform.SwapInLong";
+const char MetricsDaemon::kMetricSwapInShortName[] =
+    "Platform.SwapInShort";
+
+const char MetricsDaemon::kMetricSwapOutLongName[] =
+    "Platform.SwapOutLong";
+const char MetricsDaemon::kMetricSwapOutShortName[] =
+    "Platform.SwapOutShort";
+
+const char MetricsDaemon::kMetricsProcStatFileName[] = "/proc/stat";
+const int MetricsDaemon::kMetricsProcStatFirstLineItemsCount = 11;
+
+// Thermal CPU throttling.
+
+const char MetricsDaemon::kMetricScaledCpuFrequencyName[] =
+    "Platform.CpuFrequencyThermalScaling";
+
+// 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,
+                                                                 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;
+  std::string version;
+  if (base::SysInfo::GetLsbReleaseValue("CHROMEOS_RELEASE_VERSION", &version)) {
+    cached_version_hash = base::Hash(version);
+  } else if (testing_) {
+    cached_version_hash = 42;  // return any plausible value for the hash
+  } else {
+    LOG(FATAL) << "could not find CHROMEOS_RELEASE_VERSION";
+  }
+  return cached_version_hash;
+}
+
+bool MetricsDaemon::IsOnOfficialBuild() const {
+  std::string build_type;
+  return (base::SysInfo::GetLsbReleaseValue("CHROMEOS_RELEASE_BUILD_TYPE",
+                                            &build_type) &&
+          build_type == kOfficialBuild);
+}
+
+void MetricsDaemon::Init(bool testing,
+                         bool uploader_active,
+                         MetricsLibraryInterface* metrics_lib,
+                         const string& diskstats_path,
+                         const string& vmstats_path,
+                         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) {
+  testing_ = testing;
+  uploader_active_ = uploader_active;
+  config_root_ = config_root;
+  DCHECK(metrics_lib != nullptr);
+  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"));
+
+  diskstats_path_ = diskstats_path;
+  vmstats_path_ = vmstats_path;
+  scaling_max_freq_path_ = scaling_max_freq_path;
+  cpuinfo_max_freq_path_ = cpuinfo_max_freq_path;
+
+  // If testing, initialize Stats Reporter without connecting DBus
+  if (testing_)
+    StatsReporterInit();
+}
+
+int MetricsDaemon::OnInit() {
+  int return_code = chromeos::DBusDaemon::OnInit();
+  if (return_code != EX_OK)
+    return return_code;
+
+  StatsReporterInit();
+
+  // Start collecting meminfo stats.
+  ScheduleMeminfoCallback(kMetricMeminfoInterval);
+  memuse_final_time_ = GetActiveTime() + kMemuseIntervals[0];
+  ScheduleMemuseCallback(kMemuseIntervals[0]);
+
+  if (testing_)
+    return EX_OK;
+
+  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;
+  }
+
+  base::MessageLoop::current()->PostDelayedTask(FROM_HERE,
+      base::Bind(&MetricsDaemon::HandleUpdateStatsTimeout,
+                 base::Unretained(this)),
+      base::TimeDelta::FromMilliseconds(kUpdateStatsIntervalMs));
+
+  if (uploader_active_) {
+    if (IsOnOfficialBuild()) {
+      LOG(INFO) << "uploader enabled";
+      upload_service_.reset(
+          new UploadService(new SystemProfileCache(), metrics_lib_, server_));
+      upload_service_->Init(upload_interval_, metrics_file_);
+    } else {
+      LOG(INFO) << "uploader disabled on non-official build";
+    }
+  }
+
+  return EX_OK;
+}
+
+void MetricsDaemon::OnShutdown(int* return_code) {
+  if (!testing_ && 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::DiskStatsReadStats(uint64_t* read_sectors,
+                                       uint64_t* write_sectors) {
+  int nchars;
+  int nitems;
+  bool success = false;
+  char line[200];
+  if (diskstats_path_.empty()) {
+    return false;
+  }
+  int file = HANDLE_EINTR(open(diskstats_path_.c_str(), O_RDONLY));
+  if (file < 0) {
+    PLOG(WARNING) << "cannot open " << diskstats_path_;
+    return false;
+  }
+  nchars = HANDLE_EINTR(read(file, line, sizeof(line)));
+  if (nchars < 0) {
+    PLOG(WARNING) << "cannot read from " << diskstats_path_;
+    return false;
+  } else {
+    LOG_IF(WARNING, nchars == sizeof(line))
+        << "line too long in " << diskstats_path_;
+    line[nchars] = '\0';
+    nitems = sscanf(line, "%*d %*d %" PRIu64 " %*d %*d %*d %" PRIu64,
+                    read_sectors, write_sectors);
+    if (nitems == 2) {
+      success = true;
+    } else {
+      LOG(WARNING) << "found " << nitems << " items in "
+                   << diskstats_path_ << ", expected 2";
+    }
+  }
+  IGNORE_EINTR(close(file));
+  return success;
+}
+
+bool MetricsDaemon::VmStatsParseStats(const char* stats,
+                                      struct VmstatRecord* record) {
+  // a mapping of string name to field in VmstatRecord and whether we found it
+  struct mapping {
+    const string name;
+    uint64_t* value_p;
+    bool found;
+  } map[] =
+      { { .name = "pgmajfault",
+          .value_p = &record->page_faults_,
+          .found = false },
+        { .name = "pswpin",
+          .value_p = &record->swap_in_,
+          .found = false },
+        { .name = "pswpout",
+          .value_p = &record->swap_out_,
+          .found = false }, };
+
+  // Each line in the file has the form
+  // <ID> <VALUE>
+  // for instance:
+  // nr_free_pages 213427
+  vector<string> lines;
+  Tokenize(stats, "\n", &lines);
+  for (vector<string>::iterator it = lines.begin();
+       it != lines.end(); ++it) {
+    vector<string> tokens;
+    base::SplitString(*it, ' ', &tokens);
+    if (tokens.size() == 2) {
+      for (unsigned int i = 0; i < sizeof(map)/sizeof(struct mapping); i++) {
+        if (!tokens[0].compare(map[i].name)) {
+          if (!base::StringToUint64(tokens[1], map[i].value_p))
+            return false;
+          map[i].found = true;
+        }
+      }
+    } else {
+      LOG(WARNING) << "unexpected vmstat format";
+    }
+  }
+  // make sure we got all the stats
+  for (unsigned i = 0; i < sizeof(map)/sizeof(struct mapping); i++) {
+    if (map[i].found == false) {
+      LOG(WARNING) << "vmstat missing " << map[i].name;
+      return false;
+    }
+  }
+  return true;
+}
+
+bool MetricsDaemon::VmStatsReadStats(struct VmstatRecord* stats) {
+  string value_string;
+  FilePath* path = new FilePath(vmstats_path_);
+  if (!base::ReadFileToString(*path, &value_string)) {
+    delete path;
+    LOG(WARNING) << "cannot read " << vmstats_path_;
+    return false;
+  }
+  delete path;
+  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("/proc/meminfo");
+  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.
+      char* rest;
+      (*fields)[ifield].value =
+          static_cast<int>(strtol(tokens[1].c_str(), &rest, 10));
+      if (*rest != '\0') {
+        LOG(WARNING) << "missing meminfo value";
+        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("/proc/meminfo");
+  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/metrics/metrics_daemon.h b/metrics/metrics_daemon.h
new file mode 100644
index 0000000..b1b2d11
--- /dev/null
+++ b/metrics/metrics_daemon.h
@@ -0,0 +1,371 @@
+// Copyright (c) 2010 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.
+
+#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 "metrics/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,
+            MetricsLibraryInterface* metrics_lib,
+            const std::string& diskstats_path,
+            const std::string& vmstats_path,
+            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
+  };
+
+  // Data record for aggregating daily usage.
+  class UseRecord {
+   public:
+    UseRecord() : day_(0), seconds_(0) {}
+    int day_;
+    int seconds_;
+  };
+
+  // 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
+  };
+
+  // Metric parameters.
+  static const char kMetricReadSectorsLongName[];
+  static const char kMetricReadSectorsShortName[];
+  static const char kMetricWriteSectorsLongName[];
+  static const char kMetricWriteSectorsShortName[];
+  static const char kMetricPageFaultsShortName[];
+  static const char kMetricPageFaultsLongName[];
+  static const char kMetricSwapInLongName[];
+  static const char kMetricSwapInShortName[];
+  static const char kMetricSwapOutLongName[];
+  static const char kMetricSwapOutShortName[];
+  static const char kMetricScaledCpuFrequencyName[];
+  static const int kMetricStatsShortInterval;
+  static const int kMetricStatsLongInterval;
+  static const int kMetricMeminfoInterval;
+  static const int kMetricSectorsIOMax;
+  static const int kMetricSectorsBuckets;
+  static const int kMetricPageFaultsMax;
+  static const int kMetricPageFaultsBuckets;
+  static const char kMetricsDiskStatsPath[];
+  static const char kMetricsVmStatsPath[];
+  static const char kMetricsProcStatFileName[];
+  static const int kMetricsProcStatFirstLineItemsCount;
+
+  // 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 daily usage file, if necessary, by adding |seconds|
+  // of active use to the |day| since Epoch. If there's usage data for
+  // day in the past in the usage file, that data is sent to UMA and
+  // removed from the file. If there's already usage data for |day| in
+  // the usage file, the |seconds| are accumulated.
+  void LogDailyUseRecord(int day, int seconds);
+
+  // 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();
+
+  // Returns true if the system is using an official build.
+  bool IsOnOfficialBuild() const;
+
+  // 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_;
+
+  // Root of the configuration files to use.
+  std::string config_root_;
+
+  // The metrics library handle.
+  MetricsLibraryInterface* metrics_lib_;
+
+  // Timestamps last network state update.  This timestamp is used to
+  // sample the time from the network going online to going offline so
+  // TimeTicks ensures a monotonically increasing TimeDelta.
+  base::TimeTicks network_state_last_;
+
+  // 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 diskstats_path_;
+  std::string vmstats_path_;
+  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/metrics/metrics_daemon_main.cc b/metrics/metrics_daemon_main.cc
new file mode 100644
index 0000000..1f64ef3
--- /dev/null
+++ b/metrics/metrics_daemon_main.cc
@@ -0,0 +1,102 @@
+// Copyright (c) 2009 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.
+
+#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 <rootdev/rootdev.h>
+
+#include "metrics/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";
+
+// Returns the path to the disk stats in the sysfs.  Returns the null string if
+// it cannot find the disk stats file.
+static
+const std::string MetricsMainDiskStatsPath() {
+  char dev_path_cstr[PATH_MAX];
+  std::string dev_prefix = "/dev/";
+  std::string dev_path;
+  std::string dev_name;
+
+  int ret = rootdev(dev_path_cstr, sizeof(dev_path_cstr), true, true);
+  if (ret != 0) {
+    LOG(WARNING) << "error " << ret << " determining root device";
+    return "";
+  }
+  dev_path = dev_path_cstr;
+  // Check that rootdev begins with "/dev/".
+  if (!base::StartsWithASCII(dev_path, dev_prefix, false)) {
+    LOG(WARNING) << "unexpected root device " << dev_path;
+    return "";
+  }
+  // Get the device name, e.g. "sda" from "/dev/sda".
+  dev_name = dev_path.substr(dev_prefix.length());
+  return "/sys/class/block/" + dev_name + "/stat";
+}
+
+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");
+
+  // Upload Service flags.
+  DEFINE_int32(upload_interval_secs,
+               1800,
+               "Interval at which metrics_daemon sends the metrics. (needs "
+               "-uploader)");
+  DEFINE_string(server,
+                "https://clients4.google.com/uma/v2",
+                "Server to upload the metrics to. (needs -uploader)");
+  DEFINE_string(metrics_file,
+                "/var/lib/metrics/uma-events",
+                "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,
+              &metrics_lib,
+              MetricsMainDiskStatsPath(),
+              "/proc/vmstat",
+              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/metrics/metrics_daemon_test.cc b/metrics/metrics_daemon_test.cc
new file mode 100644
index 0000000..7dafbd6
--- /dev/null
+++ b/metrics/metrics_daemon_test.cc
@@ -0,0 +1,390 @@
+// Copyright (c) 2010 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.
+
+#include <inttypes.h>
+#include <utime.h>
+
+#include <string>
+#include <vector>
+
+#include <base/at_exit.h>
+#include <base/files/file_util.h>
+#include <base/strings/string_number_conversions.h>
+#include <base/strings/stringprintf.h>
+#include <chromeos/dbus/service_constants.h>
+#include <gtest/gtest.h>
+
+#include "metrics/metrics_daemon.h"
+#include "metrics/metrics_library_mock.h"
+#include "metrics/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 kFakeDiskStatsName[] = "fake-disk-stats";
+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};
+
+static const char kFakeVmStatsName[] = "fake-vm-stats";
+static const char kFakeScalingMaxFreqPath[] = "fake-scaling-max-freq";
+static const char kFakeCpuinfoMaxFreqPath[] = "fake-cpuinfo-max-freq";
+static const char kMetricsServer[] = "https://clients4.google.com/uma/v2";
+static const char kMetricsFilePath[] = "/var/lib/metrics/uma-events";
+
+class MetricsDaemonTest : public testing::Test {
+ protected:
+  std::string kFakeDiskStats0;
+  std::string kFakeDiskStats1;
+
+  virtual void SetUp() {
+    kFakeDiskStats0 = base::StringPrintf(kFakeDiskStatsFormat,
+                                           kFakeReadSectors[0],
+                                           kFakeWriteSectors[0]);
+    kFakeDiskStats1 = base::StringPrintf(kFakeDiskStatsFormat,
+                                           kFakeReadSectors[1],
+                                           kFakeWriteSectors[1]);
+    CreateFakeDiskStatsFile(kFakeDiskStats0.c_str());
+    CreateUint64ValueFile(base::FilePath(kFakeCpuinfoMaxFreqPath), 10000000);
+    CreateUint64ValueFile(base::FilePath(kFakeScalingMaxFreqPath), 10000000);
+
+    chromeos_metrics::PersistentInteger::SetTestingMode(true);
+    daemon_.Init(true,
+                 false,
+                 &metrics_lib_,
+                 kFakeDiskStatsName,
+                 kFakeVmStatsName,
+                 kFakeScalingMaxFreqPath,
+                 kFakeCpuinfoMaxFreqPath,
+                 base::TimeDelta::FromMinutes(30),
+                 kMetricsServer,
+                 kMetricsFilePath,
+                 "/");
+
+    // Replace original persistent values with mock ones.
+    daily_active_use_mock_ =
+        new StrictMock<PersistentIntegerMock>("1.mock");
+    daemon_.daily_active_use_.reset(daily_active_use_mock_);
+
+    kernel_crash_interval_mock_ =
+        new StrictMock<PersistentIntegerMock>("2.mock");
+    daemon_.kernel_crash_interval_.reset(kernel_crash_interval_mock_);
+
+    user_crash_interval_mock_ =
+        new StrictMock<PersistentIntegerMock>("3.mock");
+    daemon_.user_crash_interval_.reset(user_crash_interval_mock_);
+
+    unclean_shutdown_interval_mock_ =
+        new StrictMock<PersistentIntegerMock>("4.mock");
+    daemon_.unclean_shutdown_interval_.reset(unclean_shutdown_interval_mock_);
+  }
+
+  virtual void TearDown() {
+    EXPECT_EQ(0, unlink(kFakeDiskStatsName));
+    EXPECT_EQ(0, unlink(kFakeScalingMaxFreqPath));
+    EXPECT_EQ(0, unlink(kFakeCpuinfoMaxFreqPath));
+  }
+
+  // Adds active use aggregation counters update expectations that the
+  // specified count will be added.
+  void ExpectActiveUseUpdate(int count) {
+    EXPECT_CALL(*daily_active_use_mock_, Add(count))
+        .Times(1)
+        .RetiresOnSaturation();
+    EXPECT_CALL(*kernel_crash_interval_mock_, Add(count))
+        .Times(1)
+        .RetiresOnSaturation();
+    EXPECT_CALL(*user_crash_interval_mock_, Add(count))
+        .Times(1)
+        .RetiresOnSaturation();
+  }
+
+  // As above, but ignore values of counter updates.
+  void IgnoreActiveUseUpdate() {
+    EXPECT_CALL(*daily_active_use_mock_, Add(_))
+        .Times(1)
+        .RetiresOnSaturation();
+    EXPECT_CALL(*kernel_crash_interval_mock_, Add(_))
+        .Times(1)
+        .RetiresOnSaturation();
+    EXPECT_CALL(*user_crash_interval_mock_, Add(_))
+        .Times(1)
+        .RetiresOnSaturation();
+  }
+
+  // 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 char* fake_stats) {
+    if (unlink(kFakeDiskStatsName) < 0) {
+      EXPECT_EQ(errno, ENOENT);
+    }
+    FILE* f = fopen(kFakeDiskStatsName, "w");
+    EXPECT_EQ(1, fwrite(fake_stats, strlen(fake_stats), 1, f));
+    EXPECT_EQ(0, fclose(f));
+  }
+
+  // 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) {
+    base::DeleteFile(path, false);
+    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_;
+
+  // Mocks. They are strict mock so that all unexpected
+  // calls are marked as failures.
+  StrictMock<MetricsLibraryMock> metrics_lib_;
+  StrictMock<PersistentIntegerMock>* daily_active_use_mock_;
+  StrictMock<PersistentIntegerMock>* kernel_crash_interval_mock_;
+  StrictMock<PersistentIntegerMock>* user_crash_interval_mock_;
+  StrictMock<PersistentIntegerMock>* unclean_shutdown_interval_mock_;
+};
+
+TEST_F(MetricsDaemonTest, CheckSystemCrash) {
+  static const char kKernelCrashDetected[] = "test-kernel-crash-detected";
+  EXPECT_FALSE(daemon_.CheckSystemCrash(kKernelCrashDetected));
+
+  base::FilePath crash_detected(kKernelCrashDetected);
+  base::WriteFile(crash_detected, "", 0);
+  EXPECT_TRUE(base::PathExists(crash_detected));
+  EXPECT_TRUE(daemon_.CheckSystemCrash(kKernelCrashDetected));
+  EXPECT_FALSE(base::PathExists(crash_detected));
+  EXPECT_FALSE(daemon_.CheckSystemCrash(kKernelCrashDetected));
+  EXPECT_FALSE(base::PathExists(crash_detected));
+  base::DeleteFile(crash_detected, false);
+}
+
+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);
+
+  IgnoreActiveUseUpdate();
+  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, ReportDiskStats) {
+  uint64_t read_sectors_now, write_sectors_now;
+  CreateFakeDiskStatsFile(kFakeDiskStats1.c_str());
+  daemon_.DiskStatsReadStats(&read_sectors_now, &write_sectors_now);
+  EXPECT_EQ(read_sectors_now, kFakeReadSectors[1]);
+  EXPECT_EQ(write_sectors_now, kFakeWriteSectors[1]);
+
+  MetricsDaemon::StatsState s_state = daemon_.stats_state_;
+  EXPECT_CALL(metrics_lib_,
+              SendToUMA(_, (kFakeReadSectors[1] - kFakeReadSectors[0]) / 30,
+                        _, _, _));
+  EXPECT_CALL(metrics_lib_,
+              SendToUMA(_, (kFakeWriteSectors[1] - kFakeWriteSectors[0]) / 30,
+                        _, _, _));
+  EXPECT_CALL(metrics_lib_, SendEnumToUMA(_, _, _));  // SendCpuThrottleMetrics
+  daemon_.StatsCallback();
+  EXPECT_TRUE(s_state != daemon_.stats_state_);
+}
+
+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(base::FilePath(kFakeScalingMaxFreqPath),
+                        fake_scaled_freq);
+  CreateUint64ValueFile(base::FilePath(kFakeCpuinfoMaxFreqPath), fake_max_freq);
+  EXPECT_TRUE(daemon_.testing_);
+  EXPECT_TRUE(daemon_.ReadFreqToInt(kFakeScalingMaxFreqPath, &scaled_freq));
+  EXPECT_TRUE(daemon_.ReadFreqToInt(kFakeCpuinfoMaxFreqPath, &max_freq));
+  EXPECT_EQ(fake_scaled_freq, scaled_freq);
+  EXPECT_EQ(fake_max_freq, max_freq);
+}
+
+TEST_F(MetricsDaemonTest, SendCpuThrottleMetrics) {
+  CreateUint64ValueFile(base::FilePath(kFakeCpuinfoMaxFreqPath), 2001000);
+  // Test the 101% and 100% cases.
+  CreateUint64ValueFile(base::FilePath(kFakeScalingMaxFreqPath), 2001000);
+  EXPECT_TRUE(daemon_.testing_);
+  EXPECT_CALL(metrics_lib_, SendEnumToUMA(_, 101, 101));
+  daemon_.SendCpuThrottleMetrics();
+  CreateUint64ValueFile(base::FilePath(kFakeScalingMaxFreqPath), 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(base::FilePath(MetricsDaemon::kComprDataSizeName),
+                        compr_data_size);
+  CreateUint64ValueFile(base::FilePath(MetricsDaemon::kOrigDataSizeName),
+                        orig_data_size);
+  CreateUint64ValueFile(base::FilePath(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(base::FilePath(".")));
+}
+
+int main(int argc, char** argv) {
+  testing::InitGoogleTest(&argc, argv);
+
+  return RUN_ALL_TESTS();
+}
diff --git a/metrics/metrics_library.cc b/metrics/metrics_library.cc
new file mode 100644
index 0000000..70c8eac
--- /dev/null
+++ b/metrics/metrics_library.cc
@@ -0,0 +1,214 @@
+// Copyright (c) 2010 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.
+
+#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 "metrics/serialization/metric_sample.h"
+#include "metrics/serialization/serialization_utils.h"
+
+#include "policy/device_policy.h"
+
+static const char kAutotestPath[] = "/var/log/metrics/autotest-events";
+static const char kUMAEventsPath[] = "/var/lib/metrics/uma-events";
+static const char kConsentFile[] = "/home/chronos/Consent To Send Stats";
+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_(kConsentFile) {}
+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;
+
+    if (!policy_provider_.get())
+      policy_provider_.reset(new policy::PolicyProvider());
+    policy_provider_->Reload();
+    // We initialize with the default value which is false and will be preserved
+    // if the policy is not set.
+    bool enabled = false;
+    bool has_policy = false;
+    if (policy_provider_->device_policy_is_loaded()) {
+      has_policy =
+          policy_provider_->GetDevicePolicy().GetMetricsEnabled(&enabled);
+    }
+    // If policy couldn't be loaded or the metrics policy is not set we should
+    // still respect the consent file if it is present for migration purposes.
+    // TODO(pastarmovj)
+    if (!has_policy) {
+      enabled = stat(consent_file_.c_str(), &stat_buffer) >= 0;
+    }
+
+    if (enabled && !IsGuestMode())
+      cached_enabled_ = true;
+    else
+      cached_enabled_ = false;
+  }
+  return cached_enabled_;
+}
+
+void MetricsLibrary::Init() {
+  uma_events_file_ = kUMAEventsPath;
+}
+
+bool MetricsLibrary::SendToAutotest(const std::string& name, int value) {
+  FILE* autotest_file = fopen(kAutotestPath, "a+");
+  if (autotest_file == nullptr) {
+    PLOG(ERROR) << kAutotestPath << ": fopen";
+    return false;
+  }
+
+  fprintf(autotest_file, "%s=%d\n", name.c_str(), value);
+  fclose(autotest_file);
+  return true;
+}
+
+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(),
+      kUMAEventsPath);
+}
+
+bool MetricsLibrary::SendEnumToUMA(const std::string& name, int sample,
+                                   int max) {
+  return metrics::SerializationUtils::WriteMetricToFile(
+      *metrics::MetricSample::LinearHistogramSample(name, sample, max).get(),
+      kUMAEventsPath);
+}
+
+bool MetricsLibrary::SendSparseToUMA(const std::string& name, int sample) {
+  return metrics::SerializationUtils::WriteMetricToFile(
+      *metrics::MetricSample::SparseHistogramSample(name, sample).get(),
+      kUMAEventsPath);
+}
+
+bool MetricsLibrary::SendUserActionToUMA(const std::string& action) {
+  return metrics::SerializationUtils::WriteMetricToFile(
+      *metrics::MetricSample::UserActionSample(action).get(), kUMAEventsPath);
+}
+
+bool MetricsLibrary::SendCrashToUMA(const char *crash_kind) {
+  return metrics::SerializationUtils::WriteMetricToFile(
+      *metrics::MetricSample::CrashSample(crash_kind).get(), kUMAEventsPath);
+}
+
+void MetricsLibrary::SetPolicyProvider(policy::PolicyProvider* provider) {
+  policy_provider_.reset(provider);
+}
+
+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/metrics/metrics_library.h b/metrics/metrics_library.h
new file mode 100644
index 0000000..a90f3e6
--- /dev/null
+++ b/metrics/metrics_library.h
@@ -0,0 +1,150 @@
+// Copyright (c) 2010 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.
+
+#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
+
+#include "policy/libpolicy.h"
+
+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 both Autotest and 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);
+
+  // Sends to Autotest and returns true on success.
+  static bool SendToAutotest(const std::string& name, int value);
+
+ 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);
+
+  // 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);
+
+  // This function is used by tests only to mock the device policies.
+  void SetPolicyProvider(policy::PolicyProvider* provider);
+
+  // 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_;
+
+  scoped_ptr<policy::PolicyProvider> policy_provider_;
+
+  DISALLOW_COPY_AND_ASSIGN(MetricsLibrary);
+};
+
+#endif  // METRICS_METRICS_LIBRARY_H_
diff --git a/metrics/metrics_library_mock.h b/metrics/metrics_library_mock.h
new file mode 100644
index 0000000..99892bf
--- /dev/null
+++ b/metrics/metrics_library_mock.h
@@ -0,0 +1,29 @@
+// Copyright (c) 2010 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.
+
+#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/metrics/metrics_library_test.cc b/metrics/metrics_library_test.cc
new file mode 100644
index 0000000..7ede303
--- /dev/null
+++ b/metrics/metrics_library_test.cc
@@ -0,0 +1,214 @@
+// Copyright (c) 2010 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.
+
+#include <cstring>
+
+#include <base/files/file_util.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <policy/mock_device_policy.h>
+#include <policy/libpolicy.h>
+
+#include "metrics/c_metrics_library.h"
+#include "metrics/metrics_library.h"
+
+using base::FilePath;
+using ::testing::_;
+using ::testing::Return;
+using ::testing::AnyNumber;
+
+static const FilePath kTestUMAEventsFile("test-uma-events");
+static const char kTestMounts[] = "test-mounts";
+
+ACTION_P(SetMetricsPolicy, enabled) {
+  *arg0 = enabled;
+  return true;
+}
+
+class MetricsLibraryTest : public testing::Test {
+ protected:
+  virtual void SetUp() {
+    EXPECT_TRUE(lib_.uma_events_file_.empty());
+    lib_.Init();
+    EXPECT_FALSE(lib_.uma_events_file_.empty());
+    lib_.uma_events_file_ = kTestUMAEventsFile.value();
+    EXPECT_EQ(0, WriteFile(kTestUMAEventsFile, "", 0));
+    device_policy_ = new policy::MockDevicePolicy();
+    EXPECT_CALL(*device_policy_, LoadPolicy())
+        .Times(AnyNumber())
+        .WillRepeatedly(Return(true));
+    EXPECT_CALL(*device_policy_, GetMetricsEnabled(_))
+        .Times(AnyNumber())
+        .WillRepeatedly(SetMetricsPolicy(true));
+    provider_ = new policy::PolicyProvider(device_policy_);
+    lib_.SetPolicyProvider(provider_);
+    // Defeat metrics enabled caching between tests.
+    lib_.cached_enabled_time_ = 0;
+  }
+
+  virtual void TearDown() {
+    base::DeleteFile(FilePath(kTestMounts), false);
+    base::DeleteFile(kTestUMAEventsFile, false);
+  }
+
+  void VerifyEnabledCacheHit(bool to_value);
+  void VerifyEnabledCacheEviction(bool to_value);
+
+  MetricsLibrary lib_;
+  policy::MockDevicePolicy* device_policy_;
+  policy::PolicyProvider* provider_;
+};
+
+TEST_F(MetricsLibraryTest, IsDeviceMounted) {
+  static const char kTestContents[] =
+      "0123456789abcde 0123456789abcde\nguestfs foo bar\n";
+  char buffer[1024];
+  int block_sizes[] = { 1, 2, 3, 4, 5, 6, 8, 12, 14, 16, 32, 1024 };
+  bool result;
+  EXPECT_FALSE(lib_.IsDeviceMounted("guestfs",
+                                    "nonexistent",
+                                    buffer,
+                                    1,
+                                    &result));
+  ASSERT_TRUE(base::WriteFile(base::FilePath(kTestMounts),
+                              kTestContents,
+                              strlen(kTestContents)));
+  EXPECT_FALSE(lib_.IsDeviceMounted("guestfs",
+                                    kTestMounts,
+                                    buffer,
+                                    0,
+                                    &result));
+  for (size_t i = 0; i < arraysize(block_sizes); ++i) {
+    EXPECT_TRUE(lib_.IsDeviceMounted("0123456789abcde",
+                                     kTestMounts,
+                                     buffer,
+                                     block_sizes[i],
+                                     &result));
+    EXPECT_TRUE(result);
+    EXPECT_TRUE(lib_.IsDeviceMounted("guestfs",
+                                     kTestMounts,
+                                     buffer,
+                                     block_sizes[i],
+                                     &result));
+    EXPECT_TRUE(result);
+    EXPECT_TRUE(lib_.IsDeviceMounted("0123456",
+                                     kTestMounts,
+                                     buffer,
+                                     block_sizes[i],
+                                     &result));
+    EXPECT_FALSE(result);
+    EXPECT_TRUE(lib_.IsDeviceMounted("9abcde",
+                                     kTestMounts,
+                                     buffer,
+                                     block_sizes[i],
+                                     &result));
+    EXPECT_FALSE(result);
+    EXPECT_TRUE(lib_.IsDeviceMounted("foo",
+                                     kTestMounts,
+                                     buffer,
+                                     block_sizes[i],
+                                     &result));
+    EXPECT_FALSE(result);
+    EXPECT_TRUE(lib_.IsDeviceMounted("bar",
+                                     kTestMounts,
+                                     buffer,
+                                     block_sizes[i],
+                                     &result));
+    EXPECT_FALSE(result);
+  }
+}
+
+TEST_F(MetricsLibraryTest, AreMetricsEnabledFalse) {
+  EXPECT_CALL(*device_policy_, GetMetricsEnabled(_))
+      .WillOnce(SetMetricsPolicy(false));
+  EXPECT_FALSE(lib_.AreMetricsEnabled());
+}
+
+TEST_F(MetricsLibraryTest, AreMetricsEnabledTrue) {
+  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;
+    EXPECT_CALL(*device_policy_, GetMetricsEnabled(_))
+        .WillOnce(SetMetricsPolicy(!to_value));
+    ASSERT_EQ(!to_value, lib_.AreMetricsEnabled());
+    ON_CALL(*device_policy_, GetMetricsEnabled(_))
+        .WillByDefault(SetMetricsPolicy(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;
+  EXPECT_CALL(*device_policy_, GetMetricsEnabled(_))
+      .WillOnce(SetMetricsPolicy(!to_value));
+  ASSERT_EQ(!to_value, lib_.AreMetricsEnabled());
+  EXPECT_CALL(*device_policy_, GetMetricsEnabled(_))
+      .WillOnce(SetMetricsPolicy(to_value));
+  ASSERT_LT(abs(static_cast<int>(time(nullptr) - lib_.cached_enabled_time_)),
+            5);
+  // Sleep one second (or cheat to be faster).
+  --lib_.cached_enabled_time_;
+  ASSERT_EQ(to_value, lib_.AreMetricsEnabled());
+}
+
+TEST_F(MetricsLibraryTest, AreMetricsEnabledCaching) {
+  VerifyEnabledCacheHit(false);
+  VerifyEnabledCacheHit(true);
+  VerifyEnabledCacheEviction(false);
+  VerifyEnabledCacheEviction(true);
+}
+
+class CMetricsLibraryTest : public testing::Test {
+ protected:
+  virtual void SetUp() {
+    lib_ = CMetricsLibraryNew();
+    MetricsLibrary& ml = *reinterpret_cast<MetricsLibrary*>(lib_);
+    EXPECT_TRUE(ml.uma_events_file_.empty());
+    CMetricsLibraryInit(lib_);
+    EXPECT_FALSE(ml.uma_events_file_.empty());
+    ml.uma_events_file_ = kTestUMAEventsFile.value();
+    EXPECT_EQ(0, WriteFile(kTestUMAEventsFile, "", 0));
+    device_policy_ = new policy::MockDevicePolicy();
+    EXPECT_CALL(*device_policy_, LoadPolicy())
+        .Times(AnyNumber())
+        .WillRepeatedly(Return(true));
+    EXPECT_CALL(*device_policy_, GetMetricsEnabled(_))
+        .Times(AnyNumber())
+        .WillRepeatedly(SetMetricsPolicy(true));
+    provider_ = new policy::PolicyProvider(device_policy_);
+    ml.SetPolicyProvider(provider_);
+    reinterpret_cast<MetricsLibrary*>(lib_)->cached_enabled_time_ = 0;
+  }
+
+  virtual void TearDown() {
+    CMetricsLibraryDelete(lib_);
+    base::DeleteFile(kTestUMAEventsFile, false);
+  }
+
+  CMetricsLibrary lib_;
+  policy::MockDevicePolicy* device_policy_;
+  policy::PolicyProvider* provider_;
+};
+
+TEST_F(CMetricsLibraryTest, AreMetricsEnabledFalse) {
+  EXPECT_CALL(*device_policy_, GetMetricsEnabled(_))
+      .WillOnce(SetMetricsPolicy(false));
+  EXPECT_FALSE(CMetricsLibraryAreMetricsEnabled(lib_));
+}
+
+TEST_F(CMetricsLibraryTest, AreMetricsEnabledTrue) {
+  EXPECT_TRUE(CMetricsLibraryAreMetricsEnabled(lib_));
+}
+
+int main(int argc, char** argv) {
+  testing::InitGoogleTest(&argc, argv);
+  return RUN_ALL_TESTS();
+}
diff --git a/metrics/persistent_integer.cc b/metrics/persistent_integer.cc
new file mode 100644
index 0000000..dd38f1e
--- /dev/null
+++ b/metrics/persistent_integer.cc
@@ -0,0 +1,101 @@
+// 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.
+
+#include "metrics/persistent_integer.h"
+
+#include <fcntl.h>
+
+#include <base/logging.h>
+#include <base/posix/eintr_wrapper.h>
+
+#include "metrics/metrics_library.h"
+
+namespace {
+
+// The directory for the persistent storage.
+const char kBackingFilesDirectory[] = "/var/lib/metrics/";
+
+}
+
+namespace chromeos_metrics {
+
+// Static class member instantiation.
+bool PersistentInteger::testing_ = false;
+
+PersistentInteger::PersistentInteger(const std::string& name) :
+      value_(0),
+      version_(kVersion),
+      name_(name),
+      synced_(false) {
+  if (testing_) {
+    backing_file_name_ = name_;
+  } else {
+    backing_file_name_ = kBackingFilesDirectory + 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::SetTestingMode(bool testing) {
+  testing_ = testing;
+}
+
+
+}  // namespace chromeos_metrics
diff --git a/metrics/persistent_integer.h b/metrics/persistent_integer.h
new file mode 100644
index 0000000..b1cfcf4
--- /dev/null
+++ b/metrics/persistent_integer.h
@@ -0,0 +1,67 @@
+// 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.
+
+#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);
+
+  // After calling with |testing| = true, changes some behavior for the purpose
+  // of testing.  For instance: instances created while testing use the current
+  // directory for the backing files.
+  static void SetTestingMode(bool testing);
+
+ 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_;
+  bool synced_;
+  static bool testing_;
+};
+
+}  // namespace chromeos_metrics
+
+#endif  // METRICS_PERSISTENT_INTEGER_H_
diff --git a/metrics/persistent_integer_mock.h b/metrics/persistent_integer_mock.h
new file mode 100644
index 0000000..2061e55
--- /dev/null
+++ b/metrics/persistent_integer_mock.h
@@ -0,0 +1,25 @@
+// Copyright (c) 2010 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.
+
+#ifndef METRICS_PERSISTENT_INTEGER_MOCK_H_
+#define METRICS_PERSISTENT_INTEGER_MOCK_H_
+
+#include <string>
+
+#include <gmock/gmock.h>
+
+#include "metrics/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/metrics/persistent_integer_test.cc b/metrics/persistent_integer_test.cc
new file mode 100644
index 0000000..a56aede
--- /dev/null
+++ b/metrics/persistent_integer_test.cc
@@ -0,0 +1,66 @@
+// 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.
+
+#include <gtest/gtest.h>
+
+#include <base/compiler_specific.h>
+#include <base/files/file_enumerator.h>
+#include <base/files/file_util.h>
+
+#include "metrics/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.
+    chromeos_metrics::PersistentInteger::SetTestingMode(true);
+  }
+
+  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);
+    }
+  }
+};
+
+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());
+}
+
+int main(int argc, char** argv) {
+  testing::InitGoogleTest(&argc, argv);
+  return RUN_ALL_TESTS();
+}
diff --git a/metrics/platform2_preinstall.sh b/metrics/platform2_preinstall.sh
new file mode 100755
index 0000000..ccf353f
--- /dev/null
+++ b/metrics/platform2_preinstall.sh
@@ -0,0 +1,13 @@
+#!/bin/bash
+
+# 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.
+
+set -e
+
+OUT=$1
+shift
+for v; do
+  sed -e "s/@BSLOT@/${v}/g" libmetrics.pc.in > "${OUT}/lib/libmetrics-${v}.pc"
+done
diff --git a/metrics/serialization/metric_sample.cc b/metrics/serialization/metric_sample.cc
new file mode 100644
index 0000000..5447497
--- /dev/null
+++ b/metrics/serialization/metric_sample.cc
@@ -0,0 +1,197 @@
+// Copyright 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.
+
+#include "metrics/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/metrics/serialization/metric_sample.h b/metrics/serialization/metric_sample.h
new file mode 100644
index 0000000..877114d
--- /dev/null
+++ b/metrics/serialization/metric_sample.h
@@ -0,0 +1,119 @@
+// Copyright 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.
+
+#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/metrics/serialization/serialization_utils.cc b/metrics/serialization/serialization_utils.cc
new file mode 100644
index 0000000..9aa076a
--- /dev/null
+++ b/metrics/serialization/serialization_utils.cc
@@ -0,0 +1,221 @@
+// Copyright 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.
+
+#include "metrics/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 "metrics/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/metrics/serialization/serialization_utils.h b/metrics/serialization/serialization_utils.h
new file mode 100644
index 0000000..5af6166
--- /dev/null
+++ b/metrics/serialization/serialization_utils.h
@@ -0,0 +1,48 @@
+// Copyright 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.
+
+#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/metrics/serialization/serialization_utils_unittest.cc b/metrics/serialization/serialization_utils_unittest.cc
new file mode 100644
index 0000000..34d76cf
--- /dev/null
+++ b/metrics/serialization/serialization_utils_unittest.cc
@@ -0,0 +1,169 @@
+// Copyright 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.
+
+#include "metrics/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 "metrics/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/metrics/syslog_parser.sh b/metrics/syslog_parser.sh
new file mode 100755
index 0000000..7d064be
--- /dev/null
+++ b/metrics/syslog_parser.sh
@@ -0,0 +1,69 @@
+#! /bin/sh
+
+# This script parses /var/log/syslog for messages from programs that log
+# uptime and disk stats (number of sectors read).  It then outputs
+# these stats in a format usable by the metrics collector, which forwards
+# them to autotest and UMA.
+
+# To add a new metric add a line below, as PROGRAM_NAME  METRIC_NAME.
+# PROGRAM_NAME is the name of the job whose start time we
+# are interested in.  METRIC_NAME is the prefix we want to use for
+# reporting to UMA and autotest.  The script prepends "Time" and
+# "Sectors" to METRIC_NAME for the two available measurements, uptime
+# and number of sectors read thus far.
+
+# You will need to emit messages similar to the following in order to add a
+# a metric using this process.  You will need to emit both a start and stop
+# time and the metric reported will be the difference in values
+
+# Nov 15 08:05 localhost PROGRAM_NAME[822]: start METRIC_NAME time 12 sectors 56
+# Nov 15 08:05 localhost PROGRAM_NAME[822]: stop METRIC_NAME time 24 sectors 68
+
+# If you add metrics without a start, it is assumed you are requesting the
+# time differece from system start
+
+# Metrics we are interested in measuring
+METRICS="
+upstart start_x
+"
+
+first=1
+program=""
+
+# Get the metrics for all things
+for m in $METRICS
+do
+  if [ $first -eq 1 ]
+  then
+    first=0
+    program_name=$m
+  else
+    first=1
+    metrics_name=$m
+
+    # Example of line from /var/log/messages:
+    # Nov 15 08:05:42 localhost connmand[822]: start metric time 12 sectors 56
+    # "upstart:" is $5, 1234 is $9, etc.
+    program="${program}/$program_name([[0-9]+]:|:) start $metrics_name/\
+    {
+      metrics_start[\"${metrics_name}Time\"] = \$9;
+      metrics_start[\"${metrics_name}Sectors\"] = \$11;
+    }"
+    program="${program}/$program_name([[0-9]+]:|:) stop $metrics_name/\
+    {
+        metrics_stop[\"${metrics_name}Time\"] = \$9;
+        metrics_stop[\"${metrics_name}Sectors\"] = \$11;
+    }"
+  fi
+done
+
+# Do all the differencing here
+program="${program}\
+END{
+  for (i in metrics_stop) {
+    value_time = metrics_stop[i] - metrics_start[i];
+    print i \"=\" value_time;
+  }
+}"
+
+exec awk "$program" /var/log/syslog
diff --git a/metrics/timer.cc b/metrics/timer.cc
new file mode 100644
index 0000000..99f68fe
--- /dev/null
+++ b/metrics/timer.cc
@@ -0,0 +1,108 @@
+// 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.
+
+#include "metrics/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/metrics/timer.h b/metrics/timer.h
new file mode 100644
index 0000000..52cc578
--- /dev/null
+++ b/metrics/timer.h
@@ -0,0 +1,158 @@
+// 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.
+
+// 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/metrics/timer_mock.h b/metrics/timer_mock.h
new file mode 100644
index 0000000..2f2d0f4
--- /dev/null
+++ b/metrics/timer_mock.h
@@ -0,0 +1,47 @@
+// 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.
+
+#ifndef METRICS_TIMER_MOCK_H_
+#define METRICS_TIMER_MOCK_H_
+
+#include <string>
+
+#include <gmock/gmock.h>
+
+#include "metrics/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/metrics/timer_test.cc b/metrics/timer_test.cc
new file mode 100644
index 0000000..ec6c6bd
--- /dev/null
+++ b/metrics/timer_test.cc
@@ -0,0 +1,452 @@
+// 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.
+
+#include <stdint.h>
+
+#include <base/memory/scoped_ptr.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "metrics/metrics_library_mock.h"
+#include "metrics/timer.h"
+#include "metrics/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/metrics/uploader/metrics_hashes.cc b/metrics/uploader/metrics_hashes.cc
new file mode 100644
index 0000000..87405a3
--- /dev/null
+++ b/metrics/uploader/metrics_hashes.cc
@@ -0,0 +1,39 @@
+// Copyright 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.
+
+#include "metrics/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/metrics/uploader/metrics_hashes.h b/metrics/uploader/metrics_hashes.h
new file mode 100644
index 0000000..8679077
--- /dev/null
+++ b/metrics/uploader/metrics_hashes.h
@@ -0,0 +1,18 @@
+// Copyright 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.
+
+#ifndef METRICS_UPLOADER_METRICS_HASHES_H_
+#define METRICS_UPLOADER_METRICS_HASHES_H_
+
+#include <string>
+
+namespace metrics {
+
+// Computes a uint64 hash of a given string based on its MD5 hash. Suitable for
+// metric names.
+uint64_t HashMetricName(const std::string& name);
+
+}  // namespace metrics
+
+#endif  // METRICS_UPLOADER_METRICS_HASHES_H_
diff --git a/metrics/uploader/metrics_hashes_unittest.cc b/metrics/uploader/metrics_hashes_unittest.cc
new file mode 100644
index 0000000..f7e390f
--- /dev/null
+++ b/metrics/uploader/metrics_hashes_unittest.cc
@@ -0,0 +1,32 @@
+// Copyright 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.
+
+#include "metrics/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/metrics/uploader/metrics_log.cc b/metrics/uploader/metrics_log.cc
new file mode 100644
index 0000000..4d493b8
--- /dev/null
+++ b/metrics/uploader/metrics_log.cc
@@ -0,0 +1,41 @@
+// Copyright 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.
+
+#include "uploader/metrics_log.h"
+
+#include <string>
+
+#include "metrics/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);
+}
+
+void MetricsLog::PopulateSystemProfile(SystemProfileSetter* profile_setter) {
+  profile_setter->Populate(uma_proto());
+}
diff --git a/metrics/uploader/metrics_log.h b/metrics/uploader/metrics_log.h
new file mode 100644
index 0000000..5796325
--- /dev/null
+++ b/metrics/uploader/metrics_log.h
@@ -0,0 +1,42 @@
+// Copyright 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.
+
+#ifndef METRICS_UPLOADER_METRICS_LOG_H_
+#define METRICS_UPLOADER_METRICS_LOG_H_
+
+#include <string>
+
+#include <base/macros.h>
+
+#include "metrics/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.
+  void 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/metrics/uploader/metrics_log_base.cc b/metrics/uploader/metrics_log_base.cc
new file mode 100644
index 0000000..7fe1ae1
--- /dev/null
+++ b/metrics/uploader/metrics_log_base.cc
@@ -0,0 +1,142 @@
+// Copyright 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.
+
+#include "metrics/uploader/metrics_log_base.h"
+
+#include "base/metrics/histogram_base.h"
+#include "base/metrics/histogram_samples.h"
+#include "metrics/uploader/metrics_hashes.h"
+#include "metrics/uploader/proto/histogram_event.pb.h"
+#include "metrics/uploader/proto/system_profile.pb.h"
+#include "metrics/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/metrics/uploader/metrics_log_base.h b/metrics/uploader/metrics_log_base.h
new file mode 100644
index 0000000..e871c0f
--- /dev/null
+++ b/metrics/uploader/metrics_log_base.h
@@ -0,0 +1,110 @@
+// Copyright 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.
+
+// 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 "metrics/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/metrics/uploader/metrics_log_base_unittest.cc b/metrics/uploader/metrics_log_base_unittest.cc
new file mode 100644
index 0000000..5da428a
--- /dev/null
+++ b/metrics/uploader/metrics_log_base_unittest.cc
@@ -0,0 +1,125 @@
+// Copyright 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.
+
+#include "metrics/uploader/metrics_log_base.h"
+
+#include <string>
+
+#include <base/metrics/bucket_ranges.h>
+#include <base/metrics/sample_vector.h>
+#include <gtest/gtest.h>
+
+#include "metrics/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/metrics/uploader/mock/mock_system_profile_setter.h b/metrics/uploader/mock/mock_system_profile_setter.h
new file mode 100644
index 0000000..c6e8f0d
--- /dev/null
+++ b/metrics/uploader/mock/mock_system_profile_setter.h
@@ -0,0 +1,20 @@
+// Copyright 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.
+
+#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:
+  void Populate(metrics::ChromeUserMetricsExtension* profile_proto) override {}
+};
+
+#endif  // METRICS_UPLOADER_MOCK_MOCK_SYSTEM_PROFILE_SETTER_H_
diff --git a/metrics/uploader/mock/sender_mock.cc b/metrics/uploader/mock/sender_mock.cc
new file mode 100644
index 0000000..064ec6d
--- /dev/null
+++ b/metrics/uploader/mock/sender_mock.cc
@@ -0,0 +1,24 @@
+// Copyright 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.
+
+#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/metrics/uploader/mock/sender_mock.h b/metrics/uploader/mock/sender_mock.h
new file mode 100644
index 0000000..159b645
--- /dev/null
+++ b/metrics/uploader/mock/sender_mock.h
@@ -0,0 +1,48 @@
+// Copyright 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.
+
+#ifndef METRICS_UPLOADER_MOCK_SENDER_MOCK_H_
+#define METRICS_UPLOADER_MOCK_SENDER_MOCK_H_
+
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "metrics/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/metrics/uploader/proto/README b/metrics/uploader/proto/README
new file mode 100644
index 0000000..9bd3249
--- /dev/null
+++ b/metrics/uploader/proto/README
@@ -0,0 +1,25 @@
+Copyright 2015 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.
+
+
+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/metrics/uploader/proto/chrome_user_metrics_extension.proto b/metrics/uploader/proto/chrome_user_metrics_extension.proto
new file mode 100644
index 0000000..f712fc9
--- /dev/null
+++ b/metrics/uploader/proto/chrome_user_metrics_extension.proto
@@ -0,0 +1,60 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// 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 "histogram_event.proto";
+import "system_profile.proto";
+import "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/metrics/uploader/proto/histogram_event.proto b/metrics/uploader/proto/histogram_event.proto
new file mode 100644
index 0000000..4b68094
--- /dev/null
+++ b/metrics/uploader/proto/histogram_event.proto
@@ -0,0 +1,47 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// 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/metrics/uploader/proto/system_profile.proto b/metrics/uploader/proto/system_profile.proto
new file mode 100644
index 0000000..d33ff60
--- /dev/null
+++ b/metrics/uploader/proto/system_profile.proto
@@ -0,0 +1,747 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// 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/metrics/uploader/proto/user_action_event.proto b/metrics/uploader/proto/user_action_event.proto
new file mode 100644
index 0000000..30a9318
--- /dev/null
+++ b/metrics/uploader/proto/user_action_event.proto
@@ -0,0 +1,23 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// 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/metrics/uploader/sender.h b/metrics/uploader/sender.h
new file mode 100644
index 0000000..5211834
--- /dev/null
+++ b/metrics/uploader/sender.h
@@ -0,0 +1,18 @@
+// Copyright 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.
+
+#ifndef METRICS_UPLOADER_SENDER_H_
+#define METRICS_UPLOADER_SENDER_H_
+
+#include <string>
+
+// Abstract class for a Sender that uploads a metrics message.
+class Sender {
+ public:
+  virtual ~Sender() {}
+  // Sends a message |content| with its sha1 hash |hash|
+  virtual bool Send(const std::string& content, const std::string& hash) = 0;
+};
+
+#endif  // METRICS_UPLOADER_SENDER_H_
diff --git a/metrics/uploader/sender_http.cc b/metrics/uploader/sender_http.cc
new file mode 100644
index 0000000..8488b66
--- /dev/null
+++ b/metrics/uploader/sender_http.cc
@@ -0,0 +1,38 @@
+// Copyright 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.
+
+#include "metrics/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/metrics/uploader/sender_http.h b/metrics/uploader/sender_http.h
new file mode 100644
index 0000000..4880b28
--- /dev/null
+++ b/metrics/uploader/sender_http.h
@@ -0,0 +1,29 @@
+// Copyright 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.
+
+#ifndef METRICS_UPLOADER_SENDER_HTTP_H_
+#define METRICS_UPLOADER_SENDER_HTTP_H_
+
+#include <string>
+
+#include <base/macros.h>
+
+#include "metrics/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/metrics/uploader/system_profile_cache.cc b/metrics/uploader/system_profile_cache.cc
new file mode 100644
index 0000000..ea4a38c
--- /dev/null
+++ b/metrics/uploader/system_profile_cache.cc
@@ -0,0 +1,238 @@
+// Copyright 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.
+
+#include "metrics/uploader/system_profile_cache.h"
+
+#include <string>
+#include <vector>
+
+#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 "base/sys_info.h"
+#include "metrics/persistent_integer.h"
+#include "metrics/uploader/metrics_log_base.h"
+#include "metrics/uploader/proto/chrome_user_metrics_extension.pb.h"
+#include "vboot/crossystem.h"
+
+namespace {
+
+const char kPersistentGUIDFile[] = "/var/lib/metrics/Sysinfo.GUID";
+const char kPersistentSessionIdFilename[] = "Sysinfo.SessionId";
+const char kProductIdFieldName[] = "GOOGLE_METRICS_PRODUCT_ID";
+
+}  // 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 std::string& 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.";
+
+  std::string chromeos_version;
+  std::string board;
+  std::string build_type;
+  if (!base::SysInfo::GetLsbReleaseValue("CHROMEOS_RELEASE_NAME",
+                                         &profile_.os_name) ||
+      !base::SysInfo::GetLsbReleaseValue("CHROMEOS_RELEASE_VERSION",
+                                         &profile_.os_version) ||
+      !base::SysInfo::GetLsbReleaseValue("CHROMEOS_RELEASE_BOARD", &board) ||
+      !base::SysInfo::GetLsbReleaseValue("CHROMEOS_RELEASE_BUILD_TYPE",
+                                         &build_type) ||
+      !GetChromeOSVersion(&chromeos_version) ||
+      !GetHardwareId(&profile_.hardware_class)) {
+    DLOG(ERROR) << "failing to initialize profile cache";
+    return false;
+  }
+
+  std::string channel_string;
+  base::SysInfo::GetLsbReleaseValue("CHROMEOS_RELEASE_TRACK", &channel_string);
+  profile_.channel = ProtoChannelFromString(channel_string);
+
+  profile_.app_version = chromeos_version + " (" + build_type + ")" +
+      ChannelToString(profile_.channel) + " " + board;
+
+  // If the product id is not defined, use the default one from the protobuf.
+  profile_.product_id = metrics::ChromeUserMetricsExtension::CHROME;
+  if (GetProductId(&profile_.product_id)) {
+    DLOG(INFO) << "Set the product id to " << profile_.product_id;
+  }
+
+  profile_.client_id =
+      testing_ ? "client_id_test" : GetPersistentGUID(kPersistentGUIDFile);
+
+  // 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.
+  // TODO(bsimonnet): Change this to map to the number of time system-services
+  // is started.
+  session_id_->Add(1);
+  profile_.session_id = static_cast<int32_t>(session_id_->Get());
+
+  initialized_ = true;
+  return initialized_;
+}
+
+bool SystemProfileCache::InitializeOrCheck() {
+  return initialized_ || Initialize();
+}
+
+void SystemProfileCache::Populate(
+    metrics::ChromeUserMetricsExtension* metrics_proto) {
+  CHECK(metrics_proto);
+  CHECK(InitializeOrCheck())
+      << "failed to initialize system information.";
+
+  // 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(profile_.product_id);
+
+  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_.app_version);
+  profile_proto->set_channel(profile_.channel);
+
+  metrics::SystemProfileProto_OS* os = profile_proto->mutable_os();
+  os->set_name(profile_.os_name);
+  os->set_version(profile_.os_version);
+}
+
+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;
+}
+
+bool SystemProfileCache::GetChromeOSVersion(std::string* version) {
+  if (testing_) {
+    *version = "0.0.0.0";
+    return true;
+  }
+
+  std::string milestone, build, branch, patch;
+  unsigned tmp;
+  if (base::SysInfo::GetLsbReleaseValue("CHROMEOS_RELEASE_CHROME_MILESTONE",
+                                        &milestone) &&
+      base::SysInfo::GetLsbReleaseValue("CHROMEOS_RELEASE_BUILD_NUMBER",
+                                        &build) &&
+      base::SysInfo::GetLsbReleaseValue("CHROMEOS_RELEASE_BRANCH_NUMBER",
+                                        &branch) &&
+      base::SysInfo::GetLsbReleaseValue("CHROMEOS_RELEASE_PATCH_NUMBER",
+                                        &patch)) {
+    // Convert to uint to ensure those fields are positive numbers.
+    if (base::StringToUint(milestone, &tmp) &&
+        base::StringToUint(build, &tmp) &&
+        base::StringToUint(branch, &tmp) &&
+        base::StringToUint(patch, &tmp)) {
+      std::vector<std::string> parts = {milestone, build, branch, patch};
+      *version = JoinString(parts, '.');
+      return true;
+    }
+    DLOG(INFO) << "The milestone, build, branch or patch is not a positive "
+               << "number.";
+    return false;
+  }
+  DLOG(INFO) << "Field missing from /etc/lsb-release";
+  return false;
+}
+
+bool SystemProfileCache::GetHardwareId(std::string* hwid) {
+  CHECK(hwid);
+
+  if (testing_) {
+    // if we are in test mode, we do not call crossystem directly.
+    DLOG(INFO) << "skipping hardware id";
+    *hwid = "";
+    return true;
+  }
+
+  char buffer[128];
+  if (buffer != VbGetSystemPropertyString("hwid", buffer, sizeof(buffer))) {
+    LOG(ERROR) << "error getting hwid";
+    return false;
+  }
+
+  *hwid = std::string(buffer);
+  return true;
+}
+
+bool SystemProfileCache::GetProductId(int* product_id) const {
+  chromeos::OsReleaseReader reader;
+  if (testing_) {
+    base::FilePath root(config_root_);
+    reader.LoadTestingOnly(root);
+  } else {
+    reader.Load();
+  }
+
+  std::string id;
+  if (reader.GetString(kProductIdFieldName, &id)) {
+    CHECK(base::StringToInt(id, product_id)) << "Failed to convert product_id "
+                                             << id << " to int.";
+    return true;
+  }
+  return false;
+}
+
+metrics::SystemProfileProto_Channel SystemProfileCache::ProtoChannelFromString(
+    const std::string& channel) {
+
+  if (channel == "stable-channel") {
+    return metrics::SystemProfileProto::CHANNEL_STABLE;
+  } else if (channel == "dev-channel") {
+    return metrics::SystemProfileProto::CHANNEL_DEV;
+  } else if (channel == "beta-channel") {
+    return metrics::SystemProfileProto::CHANNEL_BETA;
+  } else if (channel == "canary-channel") {
+    return metrics::SystemProfileProto::CHANNEL_CANARY;
+  }
+
+  DLOG(INFO) << "unknown channel: " << channel;
+  return metrics::SystemProfileProto::CHANNEL_UNKNOWN;
+}
diff --git a/metrics/uploader/system_profile_cache.h b/metrics/uploader/system_profile_cache.h
new file mode 100644
index 0000000..e7a7337
--- /dev/null
+++ b/metrics/uploader/system_profile_cache.h
@@ -0,0 +1,91 @@
+// Copyright 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.
+
+#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/gtest_prod_util.h"
+#include "base/memory/scoped_ptr.h"
+#include "chromeos/osrelease_reader.h"
+#include "metrics/persistent_integer.h"
+#include "metrics/uploader/proto/system_profile.pb.h"
+#include "metrics/uploader/system_profile_setter.h"
+
+namespace metrics {
+class ChromeUserMetricsExtension;
+}
+
+struct SystemProfile {
+  std::string os_name;
+  std::string os_version;
+  metrics::SystemProfileProto::Channel channel;
+  std::string app_version;
+  std::string hardware_class;
+  std::string client_id;
+  int32_t session_id;
+  int32_t product_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 std::string& config_root);
+
+  // Populates the ProfileSystem protobuf with system information.
+  void Populate(metrics::ChromeUserMetricsExtension* profile_proto) override;
+
+  // Converts a string representation of the channel (|channel|-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 the hardware ID using crossystem
+  bool GetHardwareId(std::string* hwid);
+
+  // Gets the product ID from the GOOGLE_METRICS_PRODUCT_ID field.
+  bool GetProductId(int* product_id) const;
+
+  // Generate the formatted chromeos version from the fields in
+  // /etc/lsb-release. The format is A.B.C.D where A, B, C and D are positive
+  // integer representing:
+  // * the chrome milestone
+  // * the build number
+  // * the branch number
+  // * the patch number
+  bool GetChromeOSVersion(std::string* version);
+
+  bool initialized_;
+  bool testing_;
+  std::string config_root_;
+  scoped_ptr<chromeos_metrics::PersistentInteger> session_id_;
+  SystemProfile profile_;
+};
+
+#endif  // METRICS_UPLOADER_SYSTEM_PROFILE_CACHE_H_
diff --git a/metrics/uploader/system_profile_setter.h b/metrics/uploader/system_profile_setter.h
new file mode 100644
index 0000000..c535664
--- /dev/null
+++ b/metrics/uploader/system_profile_setter.h
@@ -0,0 +1,21 @@
+// Copyright 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.
+
+#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 void Populate(metrics::ChromeUserMetricsExtension* profile_proto) = 0;
+};
+
+#endif  // METRICS_UPLOADER_SYSTEM_PROFILE_SETTER_H_
diff --git a/metrics/uploader/upload_service.cc b/metrics/uploader/upload_service.cc
new file mode 100644
index 0000000..92c9e10
--- /dev/null
+++ b/metrics/uploader/upload_service.cc
@@ -0,0 +1,224 @@
+// Copyright 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.
+
+#include "metrics/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 "metrics/serialization/metric_sample.h"
+#include "metrics/serialization/serialization_utils.h"
+#include "metrics/uploader/metrics_log.h"
+#include "metrics/uploader/sender_http.h"
+#include "metrics/uploader/system_profile_cache.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();
+  log->PopulateSystemProfile(system_profile_setter_.get());
+  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();
+
+  // No samples found. Exit to avoid sending an empty log.
+  if (!current_log_)
+    return;
+
+  StageCurrentLog();
+  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();
+  failed_upload_count_ = 0;
+}
+
+MetricsLog* UploadService::GetOrCreateCurrentLog() {
+  if (!current_log_) {
+    StartNewLog();
+  }
+  return current_log_.get();
+}
diff --git a/metrics/uploader/upload_service.h b/metrics/uploader/upload_service.h
new file mode 100644
index 0000000..ebbb54f
--- /dev/null
+++ b/metrics/uploader/upload_service.h
@@ -0,0 +1,153 @@
+// Copyright 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.
+
+#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 "metrics/uploader/metrics_log.h"
+#include "metrics/uploader/sender.h"
+#include "metrics/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/metrics/uploader/upload_service_test.cc b/metrics/uploader/upload_service_test.cc
new file mode 100644
index 0000000..ee17e15
--- /dev/null
+++ b/metrics/uploader/upload_service_test.cc
@@ -0,0 +1,257 @@
+// Copyright 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.
+
+#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 "metrics/metrics_library_mock.h"
+#include "metrics/serialization/metric_sample.h"
+#include "metrics/uploader/metrics_log.h"
+#include "metrics/uploader/mock/mock_system_profile_setter.h"
+#include "metrics/uploader/mock/sender_mock.h"
+#include "metrics/uploader/proto/chrome_user_metrics_extension.pb.h"
+#include "metrics/uploader/proto/histogram_event.pb.h"
+#include "metrics/uploader/proto/system_profile.pb.h"
+#include "metrics/uploader/system_profile_cache.h"
+#include "metrics/uploader/upload_service.h"
+
+static const char kMetricsServer[] = "https://clients4.google.com/uma/v2";
+static const char kMetricsFilePath[] = "/var/run/metrics/uma-events";
+
+class UploadServiceTest : public testing::Test {
+ protected:
+  UploadServiceTest()
+      : cache_(true, "/"),
+        upload_service_(new MockSystemProfileSetter(), &metrics_lib_,
+                        kMetricsServer, true),
+        exit_manager_(new base::AtExitManager()) {
+    sender_ = new SenderMock;
+    upload_service_.sender_.reset(sender_);
+    upload_service_.Init(base::TimeDelta::FromMinutes(30), kMetricsFilePath);
+  }
+
+  virtual void SetUp() {
+    CHECK(dir_.CreateUniqueTempDir());
+    upload_service_.GatherHistograms();
+    upload_service_.Reset();
+    sender_->Reset();
+
+    chromeos_metrics::PersistentInteger::SetTestingMode(true);
+    cache_.session_id_.reset(new chromeos_metrics::PersistentInteger(
+        dir_.path().Append("session_id").value()));
+  }
+
+  scoped_ptr<metrics::MetricSample> Crash(const std::string& name) {
+    return metrics::MetricSample::CrashSample(name);
+  }
+
+  base::ScopedTempDir dir_;
+  SenderMock* sender_;
+  SystemProfileCache cache_;
+  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) {
+  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) {
+  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) {
+  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_,
+                               kMetricsServer);
+
+  // 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) {
+  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-channel"));
+
+  EXPECT_EQ(metrics::SystemProfileProto::CHANNEL_UNKNOWN,
+            SystemProfileCache::ProtoChannelFromString("dev-channel test"));
+}
+
+TEST_F(UploadServiceTest, ValuesInConfigFileAreSent) {
+  std::string name("os name");
+  std::string content(
+      "CHROMEOS_RELEASE_NAME=" + name +
+      "\nCHROMEOS_RELEASE_VERSION=version\n"
+      "CHROMEOS_RELEASE_DESCRIPTION=description beta-channel test\n"
+      "CHROMEOS_RELEASE_TRACK=beta-channel\n"
+      "CHROMEOS_RELEASE_BUILD_TYPE=developer build\n"
+      "CHROMEOS_RELEASE_BOARD=myboard");
+
+  base::SysInfo::SetChromeOSVersionInfoForTest(content, base::Time());
+  scoped_ptr<metrics::MetricSample> histogram =
+      metrics::MetricSample::SparseHistogramSample("myhistogram", 1);
+  SystemProfileCache* local_cache_ = new SystemProfileCache(true, "/");
+  local_cache_->session_id_.reset(new chromeos_metrics::PersistentInteger(
+        dir_.path().Append("session_id").value()));
+
+  upload_service_.system_profile_setter_.reset(local_cache_);
+  // Reset to create the new log with the profile setter.
+  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(name, sender_->last_message_proto().system_profile().os().name());
+  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) {
+  cache_.Initialize();
+  int session_id = cache_.profile_.session_id;
+  cache_.initialized_ = false;
+  cache_.Initialize();
+  EXPECT_EQ(cache_.profile_.session_id, session_id + 1);
+}
+
+int main(int argc, char** argv) {
+  testing::InitGoogleTest(&argc, argv);
+
+  return RUN_ALL_TESTS();
+}
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..d6dad2d 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -26,10 +26,15 @@
 #
 # create some directories (some are mount points)
 LOCAL_POST_INSTALL_CMD := mkdir -p $(addprefix $(TARGET_ROOT_OUT)/, \
-    sbin dev proc sys system data oem)
+    sbin dev proc sys system data oem acct cache config storage mnt root)
 
 include $(BUILD_SYSTEM)/base_rules.mk
 
+EXPORT_GLOBAL_ASAN_OPTIONS :=
+ifeq (address,$(strip $(SANITIZE_TARGET)))
+  EXPORT_GLOBAL_ASAN_OPTIONS := export ASAN_OPTIONS allow_user_segv_handler=1:detect_odr_violation=0:alloc_dealloc_mismatch=0
+endif
+
 # Regenerate init.environ.rc if PRODUCT_BOOTCLASSPATH has changed.
 bcp_md5 := $(word 1, $(shell echo $(PRODUCT_BOOTCLASSPATH) $(PRODUCT_SYSTEM_SERVER_CLASSPATH) | $(MD5SUM)))
 bcp_dep := $(intermediates)/$(bcp_md5).bcp.dep
@@ -41,6 +46,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/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 a4e31a9..27a8fc5 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
 
@@ -68,7 +64,6 @@
     mkdir /mnt/expand 0771 system system
 
     # Storage views to support runtime permissions
-    mkdir /storage 0755 root root
     mkdir /mnt/runtime_default 0755 root root
     mkdir /mnt/runtime_default/self 0755 root root
     mkdir /mnt/runtime_read 0755 root root
@@ -270,14 +265,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
@@ -315,7 +309,6 @@
     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
 
     # For security reasons, /data/local/tmp should always be empty.
     # Do not place files or directories in /data/local/tmp
@@ -349,6 +342,7 @@
     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
diff --git a/toolbox/Android.mk b/toolbox/Android.mk
index ad99a39..273b263 100644
--- a/toolbox/Android.mk
+++ b/toolbox/Android.mk
@@ -2,7 +2,6 @@
 
 
 common_cflags := \
-    -std=gnu99 \
     -Werror -Wno-unused-parameter \
     -I$(LOCAL_PATH)/upstream-netbsd/include/ \
     -include bsd-compatibility.h \
@@ -47,7 +46,6 @@
     log \
     ls \
     lsof \
-    mount \
     nandread \
     newfs_msdos \
     ps \
@@ -63,10 +61,12 @@
 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/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/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. */