Merge "Name the pixel format and transform enums"
diff --git a/adb/Android.mk b/adb/Android.mk
index cb6da62..d629223 100644
--- a/adb/Android.mk
+++ b/adb/Android.mk
@@ -207,6 +207,8 @@
 LOCAL_LDLIBS_windows := -lws2_32 -luserenv
 LOCAL_STATIC_LIBRARIES_windows := AdbWinApi
 
+LOCAL_MULTILIB := first
+
 include $(BUILD_HOST_NATIVE_TEST)
 
 # adb device tracker (used by ddms) test tool
@@ -347,8 +349,6 @@
     libcutils \
     libbase \
     libcrypto_static \
-    libminijail \
-    libminijail_generated \
-    libcap
+    libminijail
 
 include $(BUILD_EXECUTABLE)
diff --git a/adb/adb.cpp b/adb/adb.cpp
index c03d7db..3005652 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -35,6 +35,7 @@
 
 #include <android-base/logging.h>
 #include <android-base/macros.h>
+#include <android-base/parsenetaddress.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 
@@ -857,8 +858,7 @@
 #if ADB_HOST
         SendOkay(reply_fd);
 #endif
-        SendProtocolString(reply_fd, listeners);
-        return 1;
+        return SendProtocolString(reply_fd, listeners);
     }
 
     if (!strcmp(service, "killforward-all")) {
@@ -1038,7 +1038,7 @@
         std::string host;
         int port = DEFAULT_ADB_LOCAL_TRANSPORT_PORT;
         std::string error;
-        if (!parse_host_and_port(address, &serial, &host, &port, &error)) {
+        if (!android::base::ParseNetAddress(address, &host, &port, &serial, &error)) {
             return SendFail(reply_fd, android::base::StringPrintf("couldn't parse '%s': %s",
                                                                   address.c_str(), error.c_str()));
         }
diff --git a/adb/adb.h b/adb/adb.h
index 9020fc3..59644d4 100644
--- a/adb/adb.h
+++ b/adb/adb.h
@@ -142,10 +142,10 @@
 
 void print_packet(const char *label, apacket *p);
 
-
-
-void fatal(const char *fmt, ...) __attribute__((noreturn));
-void fatal_errno(const char *fmt, ...) __attribute__((noreturn));
+// These use the system (v)fprintf, not the adb prefixed ones defined in sysdeps.h, so they
+// shouldn't be tagged with ADB_FORMAT_ARCHETYPE.
+void fatal(const char* fmt, ...) __attribute__((noreturn, format(__printf__, 1, 2)));
+void fatal_errno(const char* fmt, ...) __attribute__((noreturn, format(__printf__, 1, 2)));
 
 void handle_packet(apacket *p, atransport *t);
 
diff --git a/adb/adb_client.cpp b/adb/adb_client.cpp
index cb5e488..bbc4dc7 100644
--- a/adb/adb_client.cpp
+++ b/adb/adb_client.cpp
@@ -124,7 +124,7 @@
 
 int _adb_connect(const std::string& service, std::string* error) {
     D("_adb_connect: %s", service.c_str());
-    if (service.empty() || service.size() > 1024) {
+    if (service.empty() || service.size() > MAX_PAYLOAD_V1) {
         *error = android::base::StringPrintf("bad service name length (%zd)",
                                              service.size());
         return -1;
diff --git a/adb/adb_io.cpp b/adb/adb_io.cpp
index 176b7bd..ae16834 100644
--- a/adb/adb_io.cpp
+++ b/adb/adb_io.cpp
@@ -22,14 +22,16 @@
 
 #include <android-base/stringprintf.h>
 
+#include "adb.h"
 #include "adb_trace.h"
 #include "adb_utils.h"
 #include "sysdeps.h"
 
 bool SendProtocolString(int fd, const std::string& s) {
-    int length = s.size();
-    if (length > 0xffff) {
-        length = 0xffff;
+    unsigned int length = s.size();
+    if (length > MAX_PAYLOAD_V1 - 4) {
+        errno = EMSGSIZE;
+        return false;
     }
 
     // The cost of sending two strings outweighs the cost of formatting.
diff --git a/adb/adb_utils.cpp b/adb/adb_utils.cpp
index b132118..474d1b4 100644
--- a/adb/adb_utils.cpp
+++ b/adb/adb_utils.cpp
@@ -208,59 +208,6 @@
     return line;
 }
 
-bool parse_host_and_port(const std::string& address,
-                         std::string* canonical_address,
-                         std::string* host, int* port,
-                         std::string* error) {
-    host->clear();
-
-    bool ipv6 = true;
-    bool saw_port = false;
-    size_t colons = std::count(address.begin(), address.end(), ':');
-    size_t dots = std::count(address.begin(), address.end(), '.');
-    std::string port_str;
-    if (address[0] == '[') {
-      // [::1]:123
-      if (address.rfind("]:") == std::string::npos) {
-        *error = android::base::StringPrintf("bad IPv6 address '%s'", address.c_str());
-        return false;
-      }
-      *host = address.substr(1, (address.find("]:") - 1));
-      port_str = address.substr(address.rfind("]:") + 2);
-      saw_port = true;
-    } else if (dots == 0 && colons >= 2 && colons <= 7) {
-      // ::1
-      *host = address;
-    } else if (colons <= 1) {
-      // 1.2.3.4 or some.accidental.domain.com
-      ipv6 = false;
-      std::vector<std::string> pieces = android::base::Split(address, ":");
-      *host = pieces[0];
-      if (pieces.size() > 1) {
-        port_str = pieces[1];
-        saw_port = true;
-      }
-    }
-
-    if (host->empty()) {
-      *error = android::base::StringPrintf("no host in '%s'", address.c_str());
-      return false;
-    }
-
-    if (saw_port) {
-      if (sscanf(port_str.c_str(), "%d", port) != 1 || *port <= 0 || *port > 65535) {
-        *error = android::base::StringPrintf("bad port number '%s' in '%s'",
-                                             port_str.c_str(), address.c_str());
-        return false;
-      }
-    }
-
-    *canonical_address = android::base::StringPrintf(ipv6 ? "[%s]:%d" : "%s:%d", host->c_str(), *port);
-    LOG(DEBUG) << "parsed " << address << " as " << *host << " and " << *port
-               << " (" << *canonical_address << ")";
-    return true;
-}
-
 std::string perror_str(const char* msg) {
     return android::base::StringPrintf("%s: %s", msg, strerror(errno));
 }
diff --git a/adb/adb_utils.h b/adb/adb_utils.h
index 388d7dd..f1149b3 100644
--- a/adb/adb_utils.h
+++ b/adb/adb_utils.h
@@ -35,17 +35,6 @@
 
 std::string dump_hex(const void* ptr, size_t byte_count);
 
-// Parses 'address' into 'host' and 'port'.
-// If no port is given, takes the default from *port.
-// 'canonical_address' then becomes "host:port" or "[host]:port" as appropriate.
-// Note that no real checking is done that 'host' or 'port' is valid; that's
-// left to getaddrinfo(3).
-// Returns false on failure and sets *error to an appropriate message.
-bool parse_host_and_port(const std::string& address,
-                         std::string* canonical_address,
-                         std::string* host, int* port,
-                         std::string* error);
-
 std::string perror_str(const char* msg);
 
 bool set_file_block_mode(int fd, bool block);
diff --git a/adb/adb_utils_test.cpp b/adb/adb_utils_test.cpp
index 4508bca..794dce6 100644
--- a/adb/adb_utils_test.cpp
+++ b/adb/adb_utils_test.cpp
@@ -111,88 +111,6 @@
   EXPECT_EQ("/system/bin", adb_dirname("/system/bin/sh/"));
 }
 
-TEST(adb_utils, parse_host_and_port) {
-  std::string canonical_address;
-  std::string host;
-  int port;
-  std::string error;
-
-  // Name, default port.
-  port = 123;
-  ASSERT_TRUE(parse_host_and_port("www.google.com", &canonical_address, &host, &port, &error));
-  ASSERT_EQ("www.google.com:123", canonical_address);
-  ASSERT_EQ("www.google.com", host);
-  ASSERT_EQ(123, port);
-
-  // Name, explicit port.
-  ASSERT_TRUE(parse_host_and_port("www.google.com:666", &canonical_address, &host, &port, &error));
-  ASSERT_EQ("www.google.com:666", canonical_address);
-  ASSERT_EQ("www.google.com", host);
-  ASSERT_EQ(666, port);
-
-  // IPv4, default port.
-  port = 123;
-  ASSERT_TRUE(parse_host_and_port("1.2.3.4", &canonical_address, &host, &port, &error));
-  ASSERT_EQ("1.2.3.4:123", canonical_address);
-  ASSERT_EQ("1.2.3.4", host);
-  ASSERT_EQ(123, port);
-
-  // IPv4, explicit port.
-  ASSERT_TRUE(parse_host_and_port("1.2.3.4:666", &canonical_address, &host, &port, &error));
-  ASSERT_EQ("1.2.3.4:666", canonical_address);
-  ASSERT_EQ("1.2.3.4", host);
-  ASSERT_EQ(666, port);
-
-  // Simple IPv6, default port.
-  port = 123;
-  ASSERT_TRUE(parse_host_and_port("::1", &canonical_address, &host, &port, &error));
-  ASSERT_EQ("[::1]:123", canonical_address);
-  ASSERT_EQ("::1", host);
-  ASSERT_EQ(123, port);
-
-  // Simple IPv6, explicit port.
-  ASSERT_TRUE(parse_host_and_port("[::1]:666", &canonical_address, &host, &port, &error));
-  ASSERT_EQ("[::1]:666", canonical_address);
-  ASSERT_EQ("::1", host);
-  ASSERT_EQ(666, port);
-
-  // Hairy IPv6, default port.
-  port = 123;
-  ASSERT_TRUE(parse_host_and_port("fe80::200:5aee:feaa:20a2", &canonical_address, &host, &port, &error));
-  ASSERT_EQ("[fe80::200:5aee:feaa:20a2]:123", canonical_address);
-  ASSERT_EQ("fe80::200:5aee:feaa:20a2", host);
-  ASSERT_EQ(123, port);
-
-  // Simple IPv6, explicit port.
-  ASSERT_TRUE(parse_host_and_port("[fe80::200:5aee:feaa:20a2]:666", &canonical_address, &host, &port, &error));
-  ASSERT_EQ("[fe80::200:5aee:feaa:20a2]:666", canonical_address);
-  ASSERT_EQ("fe80::200:5aee:feaa:20a2", host);
-  ASSERT_EQ(666, port);
-
-  // Invalid IPv4.
-  EXPECT_FALSE(parse_host_and_port("1.2.3.4:", &canonical_address, &host, &port, &error));
-  EXPECT_FALSE(parse_host_and_port("1.2.3.4::", &canonical_address, &host, &port, &error));
-  EXPECT_FALSE(parse_host_and_port("1.2.3.4:hello", &canonical_address, &host, &port, &error));
-  EXPECT_FALSE(parse_host_and_port(":123", &canonical_address, &host, &port, &error));
-
-  // Invalid IPv6.
-  EXPECT_FALSE(parse_host_and_port(":1", &canonical_address, &host, &port, &error));
-  EXPECT_FALSE(parse_host_and_port("::::::::1", &canonical_address, &host, &port, &error));
-  EXPECT_FALSE(parse_host_and_port("[::1", &canonical_address, &host, &port, &error));
-  EXPECT_FALSE(parse_host_and_port("[::1]", &canonical_address, &host, &port, &error));
-  EXPECT_FALSE(parse_host_and_port("[::1]:", &canonical_address, &host, &port, &error));
-  EXPECT_FALSE(parse_host_and_port("[::1]::", &canonical_address, &host, &port, &error));
-  EXPECT_FALSE(parse_host_and_port("[::1]:hello", &canonical_address, &host, &port, &error));
-
-  // Invalid ports.
-  EXPECT_FALSE(parse_host_and_port("[::1]:-1", &canonical_address, &host, &port, &error));
-  EXPECT_FALSE(parse_host_and_port("[::1]:0", &canonical_address, &host, &port, &error));
-  EXPECT_FALSE(parse_host_and_port("[::1]:65536", &canonical_address, &host, &port, &error));
-  EXPECT_FALSE(parse_host_and_port("1.2.3.4:-1", &canonical_address, &host, &port, &error));
-  EXPECT_FALSE(parse_host_and_port("1.2.3.4:0", &canonical_address, &host, &port, &error));
-  EXPECT_FALSE(parse_host_and_port("1.2.3.4:65536", &canonical_address, &host, &port, &error));
-}
-
 void test_mkdirs(const std::string basepath) {
   EXPECT_TRUE(mkdirs(basepath));
   EXPECT_NE(-1, adb_creat(basepath.c_str(), 0600));
diff --git a/adb/commandline.cpp b/adb/commandline.cpp
index 8575c86..f886698 100644
--- a/adb/commandline.cpp
+++ b/adb/commandline.cpp
@@ -204,7 +204,10 @@
         "  adb version                  - show version num\n"
         "\n"
         "scripting:\n"
-        "  adb wait-for-device          - block until device is online\n"
+        "  adb wait-for[-<transport>]-<state>\n"
+        "                               - wait for device to be in the given state:\n"
+        "                                 device, recovery, sideload, or bootloader\n"
+        "                                 Transport is: usb, local or any [default=any]\n"
         "  adb start-server             - ensure that there is a server running\n"
         "  adb kill-server              - kill the server if it is running\n"
         "  adb get-state                - prints: offline | bootloader | device\n"
@@ -440,13 +443,32 @@
     return android::base::StringPrintf("%s:%s", prefix, command);
 }
 
-// Returns the FeatureSet for the indicated transport.
-static FeatureSet GetFeatureSet(TransportType transport_type, const char* serial) {
+namespace {
+
+enum class ErrorAction {
+    kPrintToStderr,
+    kDoNotPrint
+};
+
+}  // namespace
+
+// Fills |feature_set| using the target indicated by |transport_type| and |serial|. Returns false
+// and clears |feature_set| on failure. |error_action| selects whether to also print error messages
+// on failure.
+static bool GetFeatureSet(TransportType transport_type, const char* serial, FeatureSet* feature_set,
+                          ErrorAction error_action) {
     std::string result, error;
+
     if (adb_query(format_host_command("features", transport_type, serial), &result, &error)) {
-        return StringToFeatureSet(result);
+        *feature_set = StringToFeatureSet(result);
+        return true;
     }
-    return FeatureSet();
+
+    if (error_action == ErrorAction::kPrintToStderr) {
+        fprintf(stderr, "error: %s\n", error.c_str());
+    }
+    feature_set->clear();
+    return false;
 }
 
 static void send_window_size_change(int fd, std::unique_ptr<ShellProtocol>& shell) {
@@ -692,7 +714,10 @@
 
 static int adb_shell(int argc, const char** argv,
                      TransportType transport_type, const char* serial) {
-    FeatureSet features = GetFeatureSet(transport_type, serial);
+    FeatureSet features;
+    if (!GetFeatureSet(transport_type, serial, &features, ErrorAction::kPrintToStderr)) {
+        return 1;
+    }
 
     bool use_shell_protocol = CanUseFeature(features, kFeatureShell2);
     if (!use_shell_protocol) {
@@ -1010,19 +1035,49 @@
 #endif /* !defined(_WIN32) */
 }
 
+static bool check_wait_for_device_syntax(const char* service) {
+    // TODO: when we have libc++ for Windows, use a regular expression instead.
+    // wait-for-((any|local|usb)-)?(bootloader|device|recovery|sideload)
+
+    char type[20];
+    char state[20];
+    int length = 0;
+    if (sscanf(service, "wait-for-%20[a-z]-%20[a-z]%n", type, state, &length) < 2 ||
+        length != static_cast<int>(strlen(service))) {
+        fprintf(stderr, "adb: couldn't parse 'wait-for' command: %s\n", service);
+        return false;
+    }
+
+    if (strcmp(type, "any") != 0 && strcmp(type, "local") != 0 && strcmp(type, "usb") != 0) {
+        fprintf(stderr, "adb: unknown type %s; expected 'any', 'local', or 'usb'\n", type);
+        return false;
+    }
+    if (strcmp(state, "bootloader") != 0 && strcmp(state, "device") != 0 &&
+        strcmp(state, "recovery") != 0 && strcmp(state, "sideload") != 0) {
+        fprintf(stderr, "adb: unknown state %s; "
+                        "expected 'bootloader', 'device', 'recovery', or 'sideload'\n", state);
+        return false;
+    }
+    return true;
+}
+
 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) {
         if (t == kTransportUsb) {
-            service = "wait-for-usb";
+            service = "wait-for-usb-device";
         } else if (t == kTransportLocal) {
-            service = "wait-for-local";
+            service = "wait-for-local-device";
         } else {
-            service = "wait-for-any";
+            service = "wait-for-any-device";
         }
     }
 
+    if (!check_wait_for_device_syntax(service)) {
+        return false;
+    }
+
     std::string cmd = format_host_command(service, t, serial);
     return adb_command(cmd);
 }
@@ -1032,23 +1087,33 @@
 static int send_shell_command(TransportType transport_type, const char* serial,
                               const std::string& command,
                               bool disable_shell_protocol) {
-    // Only use shell protocol if it's supported and the caller doesn't want
-    // to explicitly disable it.
-    bool use_shell_protocol = false;
-    if (!disable_shell_protocol) {
-        FeatureSet features = GetFeatureSet(transport_type, serial);
-        use_shell_protocol = CanUseFeature(features, kFeatureShell2);
-    }
-
-    std::string service_string = ShellServiceString(use_shell_protocol, "", command);
-
     int fd;
+    bool use_shell_protocol = false;
+
     while (true) {
-        std::string error;
-        fd = adb_connect(service_string, &error);
-        if (fd >= 0) {
-            break;
+        bool attempt_connection = true;
+
+        // Use shell protocol if it's supported and the caller doesn't explicitly disable it.
+        if (!disable_shell_protocol) {
+            FeatureSet features;
+            if (GetFeatureSet(transport_type, serial, &features, ErrorAction::kDoNotPrint)) {
+                use_shell_protocol = CanUseFeature(features, kFeatureShell2);
+            } else {
+                // Device was unreachable.
+                attempt_connection = false;
+            }
         }
+
+        if (attempt_connection) {
+            std::string error;
+            std::string service_string = ShellServiceString(use_shell_protocol, "", command);
+
+            fd = adb_connect(service_string, &error);
+            if (fd >= 0) {
+                break;
+            }
+        }
+
         fprintf(stderr,"- waiting for device -\n");
         adb_sleep_ms(1000);
         wait_for_device("wait-for-device", transport_type, serial);
@@ -1665,7 +1730,11 @@
     }
     else if (!strcmp(argv[0], "install")) {
         if (argc < 2) return usage();
-        FeatureSet features = GetFeatureSet(transport_type, serial);
+        FeatureSet features;
+        if (!GetFeatureSet(transport_type, serial, &features, ErrorAction::kPrintToStderr)) {
+            return 1;
+        }
+
         if (CanUseFeature(features, kFeatureCmd)) {
             return install_app(transport_type, serial, argc, argv);
         }
@@ -1677,7 +1746,11 @@
     }
     else if (!strcmp(argv[0], "uninstall")) {
         if (argc < 2) return usage();
-        FeatureSet features = GetFeatureSet(transport_type, serial);
+        FeatureSet features;
+        if (!GetFeatureSet(transport_type, serial, &features, ErrorAction::kPrintToStderr)) {
+            return 1;
+        }
+
         if (CanUseFeature(features, kFeatureCmd)) {
             return uninstall_app(transport_type, serial, argc, argv);
         }
@@ -1776,7 +1849,11 @@
     }
     else if (!strcmp(argv[0], "features")) {
         // Only list the features common to both the adb client and the device.
-        FeatureSet features = GetFeatureSet(transport_type, serial);
+        FeatureSet features;
+        if (!GetFeatureSet(transport_type, serial, &features, ErrorAction::kPrintToStderr)) {
+            return 1;
+        }
+
         for (const std::string& name : features) {
             if (CanUseFeature(features, name)) {
                 printf("%s\n", name.c_str());
@@ -1860,8 +1937,7 @@
     adb_close(remoteFd);
 
     if (strncmp("Success", buf, 7)) {
-        fprintf(stderr, "Failed to write %s\n", file);
-        fputs(buf, stderr);
+        fprintf(stderr, "Failed to install %s: %s", file, buf);
         return 1;
     }
     fputs(buf, stderr);
diff --git a/adb/daemon/main.cpp b/adb/daemon/main.cpp
index 78db69d..4721e2f 100644
--- a/adb/daemon/main.cpp
+++ b/adb/daemon/main.cpp
@@ -124,12 +124,9 @@
                       AID_INET,     AID_NET_BT,    AID_NET_BT_ADMIN,
                       AID_SDCARD_R, AID_SDCARD_RW, AID_NET_BW_STATS,
                       AID_READPROC};
-    if (minijail_set_supplementary_gids(
-            jail.get(),
-            sizeof(groups) / sizeof(groups[0]),
-            groups) != 0) {
-        LOG(FATAL) << "Could not configure supplementary groups";
-    }
+    minijail_set_supplementary_gids(jail.get(),
+                                    sizeof(groups) / sizeof(groups[0]),
+                                    groups);
 
     // Don't listen on a port (default 5037) if running in secure mode.
     // Don't run as root if running in secure mode.
diff --git a/adb/fdevent.cpp b/adb/fdevent.cpp
index 25e8376..386f221 100644
--- a/adb/fdevent.cpp
+++ b/adb/fdevent.cpp
@@ -306,12 +306,14 @@
         auto it = g_poll_node_map.find(subproc_fd);
         if (it == g_poll_node_map.end()) {
             D("subproc_fd %d cleared from fd_table", subproc_fd);
+            adb_close(subproc_fd);
             return;
         }
         fdevent* subproc_fde = it->second.fde;
         if(subproc_fde->fd != subproc_fd) {
             // Already reallocated?
-            D("subproc_fd(%d) != subproc_fde->fd(%d)", subproc_fd, subproc_fde->fd);
+            LOG(FATAL) << "subproc_fd(" << subproc_fd << ") != subproc_fde->fd(" << subproc_fde->fd
+                       << ")";
             return;
         }
 
diff --git a/adb/file_sync_client.cpp b/adb/file_sync_client.cpp
index c68f28b..0fa5917 100644
--- a/adb/file_sync_client.cpp
+++ b/adb/file_sync_client.cpp
@@ -51,9 +51,44 @@
     char data[SYNC_DATA_MAX];
 };
 
+static void ensure_trailing_separators(std::string& local_path, std::string& remote_path) {
+    if (!adb_is_separator(local_path.back())) {
+        local_path.push_back(OS_PATH_SEPARATOR);
+    }
+    if (remote_path.back() != '/') {
+        remote_path.push_back('/');
+    }
+}
+
+struct copyinfo {
+    std::string lpath;
+    std::string rpath;
+    unsigned int time = 0;
+    unsigned int mode;
+    uint64_t size = 0;
+    bool skip = false;
+
+    copyinfo(const std::string& local_path,
+             const std::string& remote_path,
+             const std::string& name,
+             unsigned int mode)
+            : lpath(local_path), rpath(remote_path), mode(mode) {
+        ensure_trailing_separators(lpath, rpath);
+        lpath.append(name);
+        rpath.append(name);
+        if (S_ISDIR(mode)) {
+            ensure_trailing_separators(lpath, rpath);
+        }
+    }
+};
+
 class SyncConnection {
   public:
-    SyncConnection() : total_bytes(0), start_time_ms_(CurrentTimeMs()) {
+    SyncConnection()
+            : total_bytes_(0),
+              start_time_ms_(CurrentTimeMs()),
+              expected_total_bytes_(0),
+              expect_multiple_files_(false) {
         max = SYNC_DATA_MAX; // TODO: decide at runtime.
 
         std::string error;
@@ -108,8 +143,6 @@
                        const char* lpath, const char* rpath,
                        unsigned mtime,
                        const char* data, size_t data_length) {
-        Print(rpath);
-
         size_t path_length = strlen(path_and_mode);
         if (path_length > 1024) {
             Error("SendSmallFile failed: path too long: %zu", path_length);
@@ -142,7 +175,7 @@
         p += sizeof(SyncRequest);
 
         WriteOrDie(lpath, rpath, &buf[0], (p - &buf[0]));
-        total_bytes += data_length;
+        total_bytes_ += data_length;
         return true;
     }
 
@@ -184,18 +217,10 @@
             sbuf.size = bytes_read;
             WriteOrDie(lpath, rpath, &sbuf, sizeof(SyncRequest) + bytes_read);
 
-            total_bytes += bytes_read;
+            total_bytes_ += bytes_read;
             bytes_copied += bytes_read;
 
-            if (total_size == 0) {
-                // This case can happen if we're racing against something that wrote to the file
-                // between our stat and our read, or if we're reading a magic file that lies about
-                // its size.
-                Printf("%s: ?%%", rpath);
-            } else {
-                int percentage = static_cast<int>(bytes_copied * 100 / total_size);
-                Printf("%s: %d%%", rpath, percentage);
-            }
+            ReportProgress(rpath, bytes_copied, total_size);
         }
 
         adb_close(lfd);
@@ -236,26 +261,52 @@
 
     std::string TransferRate() {
         uint64_t ms = CurrentTimeMs() - start_time_ms_;
-        if (total_bytes == 0 || ms == 0) return "";
+        if (total_bytes_ == 0 || ms == 0) return "";
 
         double s = static_cast<double>(ms) / 1000LL;
-        double rate = (static_cast<double>(total_bytes) / s) / (1024*1024);
+        double rate = (static_cast<double>(total_bytes_) / s) / (1024*1024);
         return android::base::StringPrintf(" %.1f MB/s (%" PRId64 " bytes in %.3fs)",
-                                           rate, total_bytes, s);
+                                           rate, total_bytes_, s);
     }
 
-    void Print(const std::string& s) {
-        line_printer_.Print(s, LinePrinter::INFO);
+    void ReportProgress(const char* file, uint64_t file_copied_bytes, uint64_t file_total_bytes) {
+        char overall_percentage_str[5] = "?";
+        if (expected_total_bytes_ != 0) {
+            int overall_percentage = static_cast<int>(total_bytes_ * 100 / expected_total_bytes_);
+            // If we're pulling symbolic links, we'll pull the target of the link rather than
+            // just create a local link, and that will cause us to go over 100%.
+            if (overall_percentage <= 100) {
+                snprintf(overall_percentage_str, sizeof(overall_percentage_str), "%d%%",
+                         overall_percentage);
+            }
+        }
+
+        if (file_copied_bytes > file_total_bytes || file_total_bytes == 0) {
+            // This case can happen if we're racing against something that wrote to the file
+            // between our stat and our read, or if we're reading a magic file that lies about
+            // its size. Just show how much we've copied.
+            Printf("[%4s] %s: %" PRId64 "/?", overall_percentage_str, file, file_copied_bytes);
+        } else {
+            // If we're transferring multiple files, we want to know how far through the current
+            // file we are, as well as the overall percentage.
+            if (expect_multiple_files_) {
+                int file_percentage = static_cast<int>(file_copied_bytes * 100 / file_total_bytes);
+                Printf("[%4s] %s: %d%%", overall_percentage_str, file, file_percentage);
+            } else {
+                Printf("[%4s] %s", overall_percentage_str, file);
+            }
+        }
     }
 
     void Printf(const char* fmt, ...) __attribute__((__format__(ADB_FORMAT_ARCHETYPE, 2, 3))) {
         std::string s;
+
         va_list ap;
         va_start(ap, fmt);
         android::base::StringAppendV(&s, fmt, ap);
         va_end(ap);
 
-        Print(s);
+        line_printer_.Print(s, LinePrinter::INFO);
     }
 
     void Error(const char* fmt, ...) __attribute__((__format__(ADB_FORMAT_ARCHETYPE, 2, 3))) {
@@ -280,7 +331,22 @@
         line_printer_.Print(s, LinePrinter::WARNING);
     }
 
-    uint64_t total_bytes;
+    void ComputeExpectedTotalBytes(const std::vector<copyinfo>& file_list) {
+        expected_total_bytes_ = 0;
+        for (const copyinfo& ci : file_list) {
+            // Unfortunately, this doesn't work for symbolic links, because we'll copy the
+            // target of the link rather than just creating a link. (But ci.size is the link size.)
+            if (!ci.skip) expected_total_bytes_ += ci.size;
+        }
+        expect_multiple_files_ = true;
+    }
+
+    void SetExpectedTotalBytes(uint64_t expected_total_bytes) {
+        expected_total_bytes_ = expected_total_bytes;
+        expect_multiple_files_ = false;
+    }
+
+    uint64_t total_bytes_;
 
     // TODO: add a char[max] buffer here, to replace syncsendbuf...
     int fd;
@@ -289,6 +355,9 @@
   private:
     uint64_t start_time_ms_;
 
+    uint64_t expected_total_bytes_;
+    bool expect_multiple_files_;
+
     LinePrinter line_printer_;
 
     bool SendQuit() {
@@ -417,8 +486,6 @@
 }
 
 static bool sync_recv(SyncConnection& sc, const char* rpath, const char* lpath) {
-    sc.Print(rpath);
-
     unsigned size = 0;
     if (!sync_stat(sc, rpath, nullptr, nullptr, &size)) return false;
 
@@ -476,18 +543,11 @@
             return false;
         }
 
-        sc.total_bytes += msg.data.size;
+        sc.total_bytes_ += msg.data.size;
 
         bytes_copied += msg.data.size;
 
-        if (size == 0) {
-            // This case can happen if we're racing against something that wrote to the file between
-            // our stat and our read, or if we're reading a magic file that lies about its size.
-            sc.Printf("%s: ?%%", rpath);
-        } else {
-            int percentage = static_cast<int>(bytes_copied * 100 / size);
-            sc.Printf("%s: %d%%", rpath, percentage);
-        }
+        sc.ReportProgress(rpath, bytes_copied, size);
     }
 
     adb_close(lfd);
@@ -504,41 +564,11 @@
     });
 }
 
-static void ensure_trailing_separator(std::string& lpath, std::string& rpath) {
-    if (!adb_is_separator(lpath.back())) {
-        lpath.push_back(OS_PATH_SEPARATOR);
-    }
-    if (rpath.back() != '/') {
-        rpath.push_back('/');
-    }
-}
-
-struct copyinfo {
-    std::string lpath;
-    std::string rpath;
-    unsigned int time = 0;
-    unsigned int mode;
-    uint64_t size = 0;
-    bool skip = false;
-
-    copyinfo(const std::string& lpath, const std::string& rpath, const std::string& name,
-             unsigned int mode)
-        : lpath(lpath), rpath(rpath), mode(mode) {
-        ensure_trailing_separator(this->lpath, this->rpath);
-        this->lpath.append(name);
-        this->rpath.append(name);
-
-        if (S_ISDIR(mode)) {
-            ensure_trailing_separator(this->lpath, this->rpath);
-        }
-    }
-};
-
 static bool IsDotOrDotDot(const char* name) {
     return name[0] == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0'));
 }
 
-static bool local_build_list(SyncConnection& sc, std::vector<copyinfo>* filelist,
+static bool local_build_list(SyncConnection& sc, std::vector<copyinfo>* file_list,
                              const std::string& lpath,
                              const std::string& rpath) {
     std::vector<copyinfo> dirlist;
@@ -576,7 +606,7 @@
                 ci.time = st.st_mtime;
                 ci.size = st.st_size;
             }
-            filelist->push_back(ci);
+            file_list->push_back(ci);
         }
     }
 
@@ -591,12 +621,12 @@
         sc.Warning("skipping empty directory '%s'", lpath.c_str());
         copyinfo ci(adb_dirname(lpath), adb_dirname(rpath), adb_basename(lpath), S_IFDIR);
         ci.skip = true;
-        filelist->push_back(ci);
+        file_list->push_back(ci);
         return true;
     }
 
     for (const copyinfo& ci : dirlist) {
-        local_build_list(sc, filelist, ci.lpath, ci.rpath);
+        local_build_list(sc, file_list, ci.lpath, ci.rpath);
     }
 
     return true;
@@ -607,29 +637,29 @@
                                   bool list_only) {
     // Make sure that both directory paths end in a slash.
     // Both paths are known to be nonempty, so we don't need to check.
-    ensure_trailing_separator(lpath, rpath);
+    ensure_trailing_separators(lpath, rpath);
 
     // Recursively build the list of files to copy.
-    std::vector<copyinfo> filelist;
+    std::vector<copyinfo> file_list;
     int pushed = 0;
     int skipped = 0;
-    if (!local_build_list(sc, &filelist, lpath, rpath)) {
+    if (!local_build_list(sc, &file_list, lpath, rpath)) {
         return false;
     }
 
     if (check_timestamps) {
-        for (const copyinfo& ci : filelist) {
+        for (const copyinfo& ci : file_list) {
             if (!sc.SendRequest(ID_STAT, ci.rpath.c_str())) {
                 return false;
             }
         }
-        for (copyinfo& ci : filelist) {
+        for (copyinfo& ci : file_list) {
             unsigned int timestamp, mode, size;
             if (!sync_finish_stat(sc, &timestamp, &mode, &size)) {
                 return false;
             }
             if (size == ci.size) {
-                /* for links, we cannot update the atime/mtime */
+                // For links, we cannot update the atime/mtime.
                 if ((S_ISREG(ci.mode & mode) && timestamp == ci.time) ||
                         (S_ISLNK(ci.mode & mode) && timestamp >= ci.time)) {
                     ci.skip = true;
@@ -638,14 +668,14 @@
         }
     }
 
-    for (const copyinfo& ci : filelist) {
+    sc.ComputeExpectedTotalBytes(file_list);
+
+    for (const copyinfo& ci : file_list) {
         if (!ci.skip) {
             if (list_only) {
-                sc.Error("would push: %s -> %s", ci.lpath.c_str(),
-                         ci.rpath.c_str());
+                sc.Error("would push: %s -> %s", ci.lpath.c_str(), ci.rpath.c_str());
             } else {
-                if (!sync_send(sc, ci.lpath.c_str(), ci.rpath.c_str(), ci.time,
-                               ci.mode)) {
+                if (!sync_send(sc, ci.lpath.c_str(), ci.rpath.c_str(), ci.time, ci.mode)) {
                     return false;
                 }
             }
@@ -727,6 +757,7 @@
                 "%s/%s", dst_path, adb_basename(src_path).c_str());
             dst_path = path_holder.c_str();
         }
+        sc.SetExpectedTotalBytes(st.st_size);
         success &= sync_send(sc, src_path, dst_path, st.st_mtime, st.st_mode);
     }
 
@@ -745,7 +776,7 @@
 }
 
 static bool remote_build_list(SyncConnection& sc,
-                              std::vector<copyinfo>* filelist,
+                              std::vector<copyinfo>* file_list,
                               const std::string& rpath,
                               const std::string& lpath) {
     std::vector<copyinfo> dirlist;
@@ -769,7 +800,7 @@
         } else {
             ci.time = time;
             ci.size = size;
-            filelist->push_back(ci);
+            file_list->push_back(ci);
         }
     };
 
@@ -780,7 +811,7 @@
     // Add the current directory to the list if it was empty, to ensure that it gets created.
     if (empty_dir) {
         copyinfo ci(adb_dirname(lpath), adb_dirname(rpath), adb_basename(rpath), S_IFDIR);
-        filelist->push_back(ci);
+        file_list->push_back(ci);
         return true;
     }
 
@@ -789,7 +820,7 @@
         if (remote_symlink_isdir(sc, link_ci.rpath)) {
             dirlist.emplace_back(std::move(link_ci));
         } else {
-            filelist->emplace_back(std::move(link_ci));
+            file_list->emplace_back(std::move(link_ci));
         }
     }
 
@@ -797,7 +828,7 @@
     while (!dirlist.empty()) {
         copyinfo current = dirlist.back();
         dirlist.pop_back();
-        if (!remote_build_list(sc, filelist, current.rpath, current.lpath)) {
+        if (!remote_build_list(sc, file_list, current.rpath, current.lpath)) {
             return false;
         }
     }
@@ -822,21 +853,21 @@
                                   std::string lpath, bool copy_attrs) {
     // Make sure that both directory paths end in a slash.
     // Both paths are known to be nonempty, so we don't need to check.
-    ensure_trailing_separator(lpath, rpath);
+    ensure_trailing_separators(lpath, rpath);
 
     // Recursively build the list of files to copy.
-    sc.Print("pull: building file list...");
-    std::vector<copyinfo> filelist;
-    if (!remote_build_list(sc, &filelist, rpath.c_str(), lpath.c_str())) {
+    sc.Printf("pull: building file list...");
+    std::vector<copyinfo> file_list;
+    if (!remote_build_list(sc, &file_list, rpath.c_str(), lpath.c_str())) {
         return false;
     }
 
+    sc.ComputeExpectedTotalBytes(file_list);
+
     int pulled = 0;
     int skipped = 0;
-    for (const copyinfo &ci : filelist) {
+    for (const copyinfo &ci : file_list) {
         if (!ci.skip) {
-            sc.Printf("pull: %s -> %s", ci.rpath.c_str(), ci.lpath.c_str());
-
             if (S_ISDIR(ci.mode)) {
                 // Entry is for an empty directory, create it and continue.
                 // TODO(b/25457350): We don't preserve permissions on directories.
@@ -914,8 +945,8 @@
 
     for (const char* src_path : srcs) {
         const char* dst_path = dst;
-        unsigned src_mode, src_time;
-        if (!sync_stat(sc, src_path, &src_time, &src_mode, nullptr)) {
+        unsigned src_mode, src_time, src_size;
+        if (!sync_stat(sc, src_path, &src_time, &src_mode, &src_size)) {
             sc.Error("failed to stat remote object '%s'", src_path);
             return false;
         }
@@ -964,6 +995,7 @@
                 dst_path = path_holder.c_str();
             }
 
+            sc.SetExpectedTotalBytes(src_size);
             if (!sync_recv(sc, src_path, dst_path)) {
                 success = false;
                 continue;
diff --git a/adb/file_sync_service.cpp b/adb/file_sync_service.cpp
index 781968b..ef0418e 100644
--- a/adb/file_sync_service.cpp
+++ b/adb/file_sync_service.cpp
@@ -21,6 +21,7 @@
 
 #include <dirent.h>
 #include <errno.h>
+#include <log/log.h>
 #include <selinux/android.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -34,6 +35,7 @@
 #include "adb_io.h"
 #include "adb_utils.h"
 #include "private/android_filesystem_config.h"
+#include "security_log_tags.h"
 
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
@@ -146,6 +148,8 @@
     syncmsg msg;
     unsigned int timestamp = 0;
 
+    __android_log_security_bswrite(SEC_TAG_ADB_SEND_FILE, path);
+
     int fd = adb_open_mode(path, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, mode);
     if (fd < 0 && errno == ENOENT) {
         if (!secure_mkdirs(adb_dirname(path))) {
@@ -314,6 +318,8 @@
 }
 
 static bool do_recv(int s, const char* path, std::vector<char>& buffer) {
+    __android_log_security_bswrite(SEC_TAG_ADB_RECV_FILE, path);
+
     int fd = adb_open(path, O_RDONLY | O_CLOEXEC);
     if (fd < 0) {
         SendSyncFailErrno(s, "open failed");
diff --git a/adb/line_printer.cpp b/adb/line_printer.cpp
index e8fe6c9..4ec8979 100644
--- a/adb/line_printer.cpp
+++ b/adb/line_printer.cpp
@@ -67,7 +67,7 @@
 
 void LinePrinter::Print(string to_print, LineType type) {
   if (!smart_terminal_) {
-    Out(to_print);
+    Out(to_print + "\n");
     return;
   }
 
diff --git a/adb/security_log_tags.h b/adb/security_log_tags.h
new file mode 100644
index 0000000..1d02744
--- /dev/null
+++ b/adb/security_log_tags.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 __SECURITY_LOG_TAGS_H
+#define __SECURITY_LOG_TAGS_H
+
+/* TODO: Automatically generate this file from the logtags file when build
+ * infrastructure is in place.
+ * Defined in frameworks/base/core/java/android/auditing/SecurityLog.logtags
+ */
+#define SEC_TAG_ADB_SHELL_INTERACTIVE 210001
+#define SEC_TAG_ADB_SHELL_CMD         210002
+#define SEC_TAG_ADB_RECV_FILE         210003
+#define SEC_TAG_ADB_SEND_FILE         210004
+
+#endif
diff --git a/adb/services.cpp b/adb/services.cpp
index 523353a..9cbf787 100644
--- a/adb/services.cpp
+++ b/adb/services.cpp
@@ -32,6 +32,7 @@
 #endif
 
 #include <android-base/file.h>
+#include <android-base/parsenetaddress.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <cutils/sockets.h>
@@ -356,20 +357,21 @@
 #if ADB_HOST
 struct state_info {
     TransportType transport_type;
-    char* serial;
+    std::string serial;
     ConnectionState state;
 };
 
-static void wait_for_state(int fd, void* cookie) {
-    state_info* sinfo = reinterpret_cast<state_info*>(cookie);
+static void wait_for_state(int fd, void* data) {
+    std::unique_ptr<state_info> sinfo(reinterpret_cast<state_info*>(data));
 
     D("wait_for_state %d", sinfo->state);
 
     while (true) {
         bool is_ambiguous = false;
         std::string error = "unknown error";
-        atransport* t = acquire_one_transport(sinfo->transport_type, sinfo->serial,
-                                              &is_ambiguous, &error);
+        const char* serial = sinfo->serial.length() ? sinfo->serial.c_str() : NULL;
+        atransport* t = acquire_one_transport(sinfo->transport_type, serial, &is_ambiguous, &error);
+
         if (t != nullptr && t->connection_state == sinfo->state) {
             SendOkay(fd);
             break;
@@ -382,10 +384,6 @@
         }
     }
 
-    if (sinfo->serial) {
-        free(sinfo->serial);
-    }
-    free(sinfo);
     adb_close(fd);
     D("wait_for_state is done");
 }
@@ -399,7 +397,7 @@
     std::string serial;
     std::string host;
     int port = DEFAULT_ADB_LOCAL_TRANSPORT_PORT;
-    if (!parse_host_and_port(address, &serial, &host, &port, response)) {
+    if (!android::base::ParseNetAddress(address, &host, &port, &serial, response)) {
         return;
     }
 
@@ -491,38 +489,43 @@
 asocket* host_service_to_socket(const char* name, const char* serial) {
     if (!strcmp(name,"track-devices")) {
         return create_device_tracker();
-    } else if (!strncmp(name, "wait-for-", strlen("wait-for-"))) {
-        auto sinfo = reinterpret_cast<state_info*>(malloc(sizeof(state_info)));
-        if (sinfo == nullptr) {
-            fprintf(stderr, "couldn't allocate state_info: %s", strerror(errno));
-            return NULL;
-        }
-
-        if (serial)
-            sinfo->serial = strdup(serial);
-        else
-            sinfo->serial = NULL;
-
+    } else if (android::base::StartsWith(name, "wait-for-")) {
         name += strlen("wait-for-");
 
-        if (!strncmp(name, "local", strlen("local"))) {
-            sinfo->transport_type = kTransportLocal;
-            sinfo->state = kCsDevice;
-        } else if (!strncmp(name, "usb", strlen("usb"))) {
-            sinfo->transport_type = kTransportUsb;
-            sinfo->state = kCsDevice;
-        } else if (!strncmp(name, "any", strlen("any"))) {
-            sinfo->transport_type = kTransportAny;
-            sinfo->state = kCsDevice;
-        } else {
-            if (sinfo->serial) {
-                free(sinfo->serial);
-            }
-            free(sinfo);
-            return NULL;
+        std::unique_ptr<state_info> sinfo(new state_info);
+        if (sinfo == nullptr) {
+            fprintf(stderr, "couldn't allocate state_info: %s", strerror(errno));
+            return nullptr;
         }
 
-        int fd = create_service_thread(wait_for_state, sinfo);
+        if (serial) sinfo->serial = serial;
+
+        if (android::base::StartsWith(name, "local")) {
+            name += strlen("local");
+            sinfo->transport_type = kTransportLocal;
+        } else if (android::base::StartsWith(name, "usb")) {
+            name += strlen("usb");
+            sinfo->transport_type = kTransportUsb;
+        } else if (android::base::StartsWith(name, "any")) {
+            name += strlen("any");
+            sinfo->transport_type = kTransportAny;
+        } else {
+            return nullptr;
+        }
+
+        if (!strcmp(name, "-device")) {
+            sinfo->state = kCsDevice;
+        } else if (!strcmp(name, "-recovery")) {
+            sinfo->state = kCsRecovery;
+        } else if (!strcmp(name, "-sideload")) {
+            sinfo->state = kCsSideload;
+        } else if (!strcmp(name, "-bootloader")) {
+            sinfo->state = kCsBootloader;
+        } else {
+            return nullptr;
+        }
+
+        int fd = create_service_thread(wait_for_state, sinfo.release());
         return create_local_socket(fd);
     } else if (!strncmp(name, "connect:", 8)) {
         char* host = strdup(name + 8);
diff --git a/adb/shell_service.cpp b/adb/shell_service.cpp
index 366ed07..c3b9044 100644
--- a/adb/shell_service.cpp
+++ b/adb/shell_service.cpp
@@ -95,11 +95,13 @@
 #include <android-base/logging.h>
 #include <android-base/stringprintf.h>
 #include <paths.h>
+#include <log/log.h>
 
 #include "adb.h"
 #include "adb_io.h"
 #include "adb_trace.h"
 #include "adb_utils.h"
+#include "security_log_tags.h"
 
 namespace {
 
@@ -183,7 +185,6 @@
     ~Subprocess();
 
     const std::string& command() const { return command_; }
-    bool is_interactive() const { return command_.empty(); }
 
     int local_socket_fd() const { return local_socket_sfd_.fd(); }
 
@@ -233,6 +234,7 @@
 }
 
 Subprocess::~Subprocess() {
+    WaitForExit();
 }
 
 bool Subprocess::ForkAndExec() {
@@ -240,6 +242,12 @@
     ScopedFd parent_error_sfd, child_error_sfd;
     char pts_name[PATH_MAX];
 
+    if (command_.empty()) {
+        __android_log_security_bswrite(SEC_TAG_ADB_SHELL_INTERACTIVE, "");
+    } else {
+        __android_log_security_bswrite(SEC_TAG_ADB_SHELL_CMD, command_.c_str());
+    }
+
     // Create a socketpair for the fork() child to report any errors back to the parent. Since we
     // use threads, logging directly from the child might deadlock due to locks held in another
     // thread during the fork.
@@ -331,7 +339,7 @@
         parent_error_sfd.Reset();
         close_on_exec(child_error_sfd.fd());
 
-        if (is_interactive()) {
+        if (command_.empty()) {
             execle(_PATH_BSHELL, _PATH_BSHELL, "-", nullptr, cenv.data());
         } else {
             execle(_PATH_BSHELL, _PATH_BSHELL, "-c", command_.c_str(), nullptr, cenv.data());
@@ -408,20 +416,6 @@
         exit(-1);
     }
 
-    if (!is_interactive()) {
-        termios tattr;
-        if (tcgetattr(child_fd, &tattr) == -1) {
-            WriteFdExactly(error_sfd->fd(), "tcgetattr failed");
-            exit(-1);
-        }
-
-        cfmakeraw(&tattr);
-        if (tcsetattr(child_fd, TCSADRAIN, &tattr) == -1) {
-            WriteFdExactly(error_sfd->fd(), "tcsetattr failed");
-            exit(-1);
-        }
-    }
-
     return child_fd;
 }
 
@@ -432,7 +426,6 @@
             "shell srvc %d", subprocess->local_socket_fd()));
 
     subprocess->PassDataStreams();
-    subprocess->WaitForExit();
 
     D("deleting Subprocess for PID %d", subprocess->pid());
     delete subprocess;
diff --git a/adb/sockets.cpp b/adb/sockets.cpp
index eb0ce85..d8e4e93 100644
--- a/adb/sockets.cpp
+++ b/adb/sockets.cpp
@@ -698,17 +698,17 @@
         p = s->pkt_first;
     }
 
-        /* don't bother if we can't decode the length */
+    /* don't bother if we can't decode the length */
     if(p->len < 4) return 0;
 
     len = unhex(p->data, 4);
-    if((len < 1) ||  (len > 1024)) {
+    if ((len < 1) || (len > MAX_PAYLOAD_V1)) {
         D("SS(%d): bad size (%d)", s->id, len);
         goto fail;
     }
 
     D("SS(%d): len is %d", s->id, len );
-        /* can't do anything until we have the full header */
+    /* can't do anything until we have the full header */
     if((len + 4) > p->len) {
         D("SS(%d): waiting for %d more bytes", s->id, len+4 - p->len);
         return 0;
diff --git a/adb/sysdeps_win32.cpp b/adb/sysdeps_win32.cpp
index 0a2a8f6..c3889b6 100644
--- a/adb/sysdeps_win32.cpp
+++ b/adb/sysdeps_win32.cpp
@@ -88,7 +88,10 @@
     _fh_socket_hook
 };
 
-#define assert(cond)  do { if (!(cond)) fatal( "assertion failed '%s' on %s:%ld\n", #cond, __FILE__, __LINE__ ); } while (0)
+#define assert(cond)                                                                       \
+    do {                                                                                   \
+        if (!(cond)) fatal("assertion failed '%s' on %s:%d\n", #cond, __FILE__, __LINE__); \
+    } while (0)
 
 std::string SystemErrorCodeToString(const DWORD error_code) {
   const int kErrorMessageBufferSize = 256;
@@ -1589,7 +1592,7 @@
 /**************************************************************************/
 /**************************************************************************/
 
-#define FATAL(x...) fatal(__FUNCTION__, x)
+#define FATAL(fmt, ...) fatal("%s: " fmt, __FUNCTION__, ##__VA_ARGS__)
 
 #if DEBUG
 static void dump_fde(fdevent *fde, const char *info)
diff --git a/adb/sysdeps_win32_test.cpp b/adb/sysdeps_win32_test.cpp
index 81923cb..1d40281 100755
--- a/adb/sysdeps_win32_test.cpp
+++ b/adb/sysdeps_win32_test.cpp
@@ -66,7 +66,7 @@
     const char* path_val = adb_getenv("PATH");
     EXPECT_NE(nullptr, path_val);
     if (path_val != nullptr) {
-        EXPECT_GT(strlen(path_val), 0);
+        EXPECT_GT(strlen(path_val), 0U);
     }
 }
 
diff --git a/adb/usb_linux_client.cpp b/adb/usb_linux_client.cpp
index 151c0aa..a4f1a70 100644
--- a/adb/usb_linux_client.cpp
+++ b/adb/usb_linux_client.cpp
@@ -30,6 +30,8 @@
 #include <sys/types.h>
 #include <unistd.h>
 
+#include <algorithm>
+
 #include "adb.h"
 #include "transport.h"
 
@@ -37,6 +39,13 @@
 #define MAX_PACKET_SIZE_HS	512
 #define MAX_PACKET_SIZE_SS	1024
 
+// Writes larger than 16k fail on some devices (seed with 3.10.49-g209ea2f in particular).
+#define USB_FFS_MAX_WRITE 16384
+
+// The kernel allocates a contiguous buffer for reads, which can fail for large ones due to
+// fragmentation. 16k chosen arbitrarily to match the write limit.
+#define USB_FFS_MAX_READ 16384
+
 #define cpu_to_le16(x)  htole16(x)
 #define cpu_to_le32(x)  htole32(x)
 
@@ -459,10 +468,9 @@
 static int usb_ffs_write(usb_handle* h, const void* data, int len) {
     D("about to write (fd=%d, len=%d)", h->bulk_in, len);
 
-    // Writes larger than 16k fail on some devices (seed with 3.10.49-g209ea2f in particular).
     const char* buf = static_cast<const char*>(data);
     while (len > 0) {
-        int write_len = (len > 16384) ? 16384 : len;
+        int write_len = std::min(USB_FFS_MAX_WRITE, len);
         int n = adb_write(h->bulk_in, buf, write_len);
         if (n < 0) {
             D("ERROR: fd = %d, n = %d: %s", h->bulk_in, n, strerror(errno));
@@ -481,7 +489,7 @@
 
     char* buf = static_cast<char*>(data);
     while (len > 0) {
-        int read_len = (len > 16384) ? 16384 : len;
+        int read_len = std::min(USB_FFS_MAX_READ, len);
         int n = adb_read(h->bulk_out, buf, read_len);
         if (n < 0) {
             D("ERROR: fd = %d, n = %d: %s", h->bulk_out, n, strerror(errno));
diff --git a/base/Android.mk b/base/Android.mk
index cba70d4..18f8686 100644
--- a/base/Android.mk
+++ b/base/Android.mk
@@ -19,6 +19,7 @@
 libbase_src_files := \
     file.cpp \
     logging.cpp \
+    parsenetaddress.cpp \
     stringprintf.cpp \
     strings.cpp \
     test_utils.cpp \
@@ -30,6 +31,7 @@
     file_test.cpp \
     logging_test.cpp \
     parseint_test.cpp \
+    parsenetaddress_test.cpp \
     stringprintf_test.cpp \
     strings_test.cpp \
     test_main.cpp \
diff --git a/base/include/android-base/parsenetaddress.h b/base/include/android-base/parsenetaddress.h
new file mode 100644
index 0000000..2de5ac9
--- /dev/null
+++ b/base/include/android-base/parsenetaddress.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 BASE_PARSENETADDRESS_H
+#define BASE_PARSENETADDRESS_H
+
+#include <string>
+
+namespace android {
+namespace base {
+
+// Parses |address| into |host| and |port|.
+//
+// If |address| doesn't contain a port number, the default value is taken from
+// |port|. If |canonical_address| is non-null it will be set to "host:port" or
+// "[host]:port" as appropriate.
+//
+// On failure, returns false and fills |error|.
+bool ParseNetAddress(const std::string& address, std::string* host, int* port,
+                     std::string* canonical_address, std::string* error);
+
+}  // namespace base
+}  // namespace android
+
+#endif  // BASE_PARSENETADDRESS_H
diff --git a/base/parsenetaddress.cpp b/base/parsenetaddress.cpp
new file mode 100644
index 0000000..dd80f6d
--- /dev/null
+++ b/base/parsenetaddress.cpp
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "android-base/parsenetaddress.h"
+
+#include <algorithm>
+
+#include "android-base/stringprintf.h"
+#include "android-base/strings.h"
+
+namespace android {
+namespace base {
+
+bool ParseNetAddress(const std::string& address, std::string* host, int* port,
+                     std::string* canonical_address, 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 = 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 = Split(address, ":");
+    *host = pieces[0];
+    if (pieces.size() > 1) {
+      port_str = pieces[1];
+      saw_port = true;
+    }
+  }
+
+  if (host->empty()) {
+    *error = 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 = StringPrintf("bad port number '%s' in '%s'", port_str.c_str(),
+                            address.c_str());
+      return false;
+    }
+  }
+
+  if (canonical_address != nullptr) {
+    *canonical_address =
+        StringPrintf(ipv6 ? "[%s]:%d" : "%s:%d", host->c_str(), *port);
+  }
+
+  return true;
+}
+
+}  // namespace base
+}  // namespace android
diff --git a/base/parsenetaddress_test.cpp b/base/parsenetaddress_test.cpp
new file mode 100644
index 0000000..a3bfac8
--- /dev/null
+++ b/base/parsenetaddress_test.cpp
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "android-base/parsenetaddress.h"
+
+#include <gtest/gtest.h>
+
+using android::base::ParseNetAddress;
+
+TEST(ParseNetAddressTest, TestUrl) {
+  std::string canonical, host, error;
+  int port = 123;
+
+  EXPECT_TRUE(
+      ParseNetAddress("www.google.com", &host, &port, &canonical, &error));
+  EXPECT_EQ("www.google.com:123", canonical);
+  EXPECT_EQ("www.google.com", host);
+  EXPECT_EQ(123, port);
+
+  EXPECT_TRUE(
+      ParseNetAddress("www.google.com:666", &host, &port, &canonical, &error));
+  EXPECT_EQ("www.google.com:666", canonical);
+  EXPECT_EQ("www.google.com", host);
+  EXPECT_EQ(666, port);
+}
+
+TEST(ParseNetAddressTest, TestIpv4) {
+  std::string canonical, host, error;
+  int port = 123;
+
+  EXPECT_TRUE(ParseNetAddress("1.2.3.4", &host, &port, &canonical, &error));
+  EXPECT_EQ("1.2.3.4:123", canonical);
+  EXPECT_EQ("1.2.3.4", host);
+  EXPECT_EQ(123, port);
+
+  EXPECT_TRUE(ParseNetAddress("1.2.3.4:666", &host, &port, &canonical, &error));
+  EXPECT_EQ("1.2.3.4:666", canonical);
+  EXPECT_EQ("1.2.3.4", host);
+  EXPECT_EQ(666, port);
+}
+
+TEST(ParseNetAddressTest, TestIpv6) {
+  std::string canonical, host, error;
+  int port = 123;
+
+  EXPECT_TRUE(ParseNetAddress("::1", &host, &port, &canonical, &error));
+  EXPECT_EQ("[::1]:123", canonical);
+  EXPECT_EQ("::1", host);
+  EXPECT_EQ(123, port);
+
+  EXPECT_TRUE(ParseNetAddress("fe80::200:5aee:feaa:20a2", &host, &port,
+                              &canonical, &error));
+  EXPECT_EQ("[fe80::200:5aee:feaa:20a2]:123", canonical);
+  EXPECT_EQ("fe80::200:5aee:feaa:20a2", host);
+  EXPECT_EQ(123, port);
+
+  EXPECT_TRUE(ParseNetAddress("[::1]:666", &host, &port, &canonical, &error));
+  EXPECT_EQ("[::1]:666", canonical);
+  EXPECT_EQ("::1", host);
+  EXPECT_EQ(666, port);
+
+  EXPECT_TRUE(ParseNetAddress("[fe80::200:5aee:feaa:20a2]:666", &host, &port,
+                              &canonical, &error));
+  EXPECT_EQ("[fe80::200:5aee:feaa:20a2]:666", canonical);
+  EXPECT_EQ("fe80::200:5aee:feaa:20a2", host);
+  EXPECT_EQ(666, port);
+}
+
+TEST(ParseNetAddressTest, TestInvalidAddress) {
+  std::string canonical, host;
+  int port;
+
+  std::string failure_cases[] = {
+      // Invalid IPv4.
+      "1.2.3.4:",
+      "1.2.3.4::",
+      ":123",
+
+      // Invalid IPv6.
+      ":1",
+      "::::::::1",
+      "[::1",
+      "[::1]",
+      "[::1]:",
+      "[::1]::",
+
+      // Invalid port.
+      "1.2.3.4:-1",
+      "1.2.3.4:0",
+      "1.2.3.4:65536"
+      "1.2.3.4:hello",
+      "[::1]:-1",
+      "[::1]:0",
+      "[::1]:65536",
+      "[::1]:hello",
+  };
+
+  for (const auto& address : failure_cases) {
+    // Failure should give some non-empty error string.
+    std::string error;
+    EXPECT_FALSE(ParseNetAddress(address, &host, &port, &canonical, &error));
+    EXPECT_NE("", error);
+  }
+}
+
+// Null canonical address argument.
+TEST(ParseNetAddressTest, TestNullCanonicalAddress) {
+  std::string host, error;
+  int port = 42;
+
+  EXPECT_TRUE(ParseNetAddress("www.google.com", &host, &port, nullptr, &error));
+  EXPECT_TRUE(ParseNetAddress("1.2.3.4", &host, &port, nullptr, &error));
+  EXPECT_TRUE(ParseNetAddress("::1", &host, &port, nullptr, &error));
+}
diff --git a/base/utf8_test.cpp b/base/utf8_test.cpp
index dde7490..ae8fc8c 100755
--- a/base/utf8_test.cpp
+++ b/base/utf8_test.cpp
@@ -44,7 +44,7 @@
   // specific replacement character that UTF8ToWide() may replace the invalid
   // UTF-8 characters with because we want to allow that to change if the
   // implementation changes.
-  EXPECT_EQ(0, wide.find(L"before"));
+  EXPECT_EQ(0U, wide.find(L"before"));
   const wchar_t after_wide[] = L"after";
   EXPECT_EQ(wide.length() - (arraysize(after_wide) - 1), wide.find(after_wide));
 }
diff --git a/bootstat/Android.mk b/bootstat/Android.mk
new file mode 100644
index 0000000..bbb903d
--- /dev/null
+++ b/bootstat/Android.mk
@@ -0,0 +1,150 @@
+#
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+bootstat_c_includes := external/gtest/include
+
+bootstat_lib_src_files := \
+        boot_event_record_store.cpp \
+        event_log_list_builder.cpp
+
+bootstat_src_files := \
+        bootstat.cpp
+
+bootstat_test_src_files := \
+        boot_event_record_store_test.cpp \
+        event_log_list_builder_test.cpp \
+        testrunner.cpp
+
+bootstat_shared_libs := \
+        libbase \
+        liblog
+
+bootstat_cflags := \
+        -Wall \
+        -Wextra \
+        -Werror
+
+bootstat_cppflags := \
+        -Wno-non-virtual-dtor
+
+bootstat_debug_cflags := \
+        $(bootstat_cflags) \
+        -UNDEBUG
+
+# 524291 corresponds to sysui_histogram, from
+# frameworks/base/core/java/com/android/internal/logging/EventLogTags.logtags
+bootstat_cflags += -DHISTOGRAM_LOG_TAG=524291
+
+
+# bootstat static library
+# -----------------------------------------------------------------------------
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libbootstat
+LOCAL_CFLAGS := $(bootstat_cflags)
+LOCAL_CPPFLAGS := $(bootstat_cppflags)
+LOCAL_C_INCLUDES := $(bootstat_c_includes)
+LOCAL_SHARED_LIBRARIES := $(bootstat_shared_libs)
+LOCAL_SRC_FILES := $(bootstat_lib_src_files)
+# Clang is required because of C++14
+LOCAL_CLANG := true
+
+include $(BUILD_STATIC_LIBRARY)
+
+# bootstat static library, debug
+# -----------------------------------------------------------------------------
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libbootstat_debug
+LOCAL_CFLAGS := $(bootstat_cflags)
+LOCAL_CPPFLAGS := $(bootstat_debug_cppflags)
+LOCAL_C_INCLUDES := $(bootstat_c_includes)
+LOCAL_SHARED_LIBRARIES := $(bootstat_shared_libs)
+LOCAL_SRC_FILES := $(bootstat_lib_src_files)
+# Clang is required because of C++14
+LOCAL_CLANG := true
+
+include $(BUILD_STATIC_LIBRARY)
+
+# bootstat host static library, debug
+# -----------------------------------------------------------------------------
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libbootstat_host_debug
+LOCAL_CFLAGS := $(bootstat_debug_cflags)
+LOCAL_CPPFLAGS := $(bootstat_cppflags)
+LOCAL_C_INCLUDES := $(bootstat_c_includes)
+LOCAL_SHARED_LIBRARIES := $(bootstat_shared_libs)
+LOCAL_SRC_FILES := $(bootstat_lib_src_files)
+# Clang is required because of C++14
+LOCAL_CLANG := true
+
+include $(BUILD_HOST_STATIC_LIBRARY)
+
+# bootstat binary
+# -----------------------------------------------------------------------------
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := bootstat
+LOCAL_CFLAGS := $(bootstat_cflags)
+LOCAL_CPPFLAGS := $(bootstat_cppflags)
+LOCAL_C_INCLUDES := $(bootstat_c_includes)
+LOCAL_SHARED_LIBRARIES := $(bootstat_shared_libs)
+LOCAL_STATIC_LIBRARIES := libbootstat
+LOCAL_INIT_RC := bootstat.rc
+LOCAL_SRC_FILES := $(bootstat_src_files)
+# Clang is required because of C++14
+LOCAL_CLANG := true
+
+include $(BUILD_EXECUTABLE)
+
+# Native tests
+# -----------------------------------------------------------------------------
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := bootstat_tests
+LOCAL_CFLAGS := $(bootstat_tests_cflags)
+LOCAL_CPPFLAGS := $(bootstat_cppflags)
+LOCAL_SHARED_LIBRARIES := $(bootstat_shared_libs)
+LOCAL_STATIC_LIBRARIES := libbootstat_debug libgmock
+LOCAL_SRC_FILES := $(bootstat_test_src_files)
+# Clang is required because of C++14
+LOCAL_CLANG := true
+
+include $(BUILD_NATIVE_TEST)
+
+# Host native tests
+# -----------------------------------------------------------------------------
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := bootstat_tests
+LOCAL_CFLAGS := $(bootstat_tests_cflags)
+LOCAL_CPPFLAGS := $(bootstat_cppflags)
+LOCAL_SHARED_LIBRARIES := $(bootstat_shared_libs)
+LOCAL_STATIC_LIBRARIES := libbootstat_host_debug libgmock_host
+LOCAL_SRC_FILES := $(bootstat_test_src_files)
+# Clang is required because of C++14
+LOCAL_CLANG := true
+
+include $(BUILD_HOST_NATIVE_TEST)
diff --git a/bootstat/README.md b/bootstat/README.md
new file mode 100644
index 0000000..1b4bf7f
--- /dev/null
+++ b/bootstat/README.md
@@ -0,0 +1,48 @@
+# bootstat #
+
+The bootstat command records boot events (e.g., `firmware_loaded`,
+`boot_complete`) and the relative time at which these events occurred. The
+command also aggregates boot event metrics locally and logs the metrics for
+analysis.
+
+    Usage: bootstat [options]
+    options include:
+      -d              Dump the boot event records to the console.
+      -h              Show this help.
+      -l              Log all metrics to logstorage.
+      -r              Record the relative time of a named boot event.
+
+## Relative time ##
+
+The timestamp recorded by bootstat is the uptime of the system, i.e., the
+number of seconds since the system booted.
+
+## Recording boot events ##
+
+To record the relative time of an event during the boot phase, call `bootstat`
+with the `-r` option and the name of the boot event.
+
+    $ bootstat -r boot_complete
+
+The relative time at which the command runs is recorded along with the name of
+the boot event to be persisted.
+
+## Logging boot events ##
+
+To log the persisted boot events, call `bootstat` with the `-l` option.
+
+    $ bootstat -l
+
+bootstat logs all boot events recorded using the `-r` option to the EventLog
+using the Tron histogram. These logs may be uploaded by interested parties
+for aggregation and analysis of boot time across different devices and
+versions.
+
+## Printing boot events ##
+
+To print the set of persisted boot events, call `bootstat` with the `-p` option.
+
+    $ bootstat -p
+    Boot events:
+    ------------
+    boot_complete   71
\ No newline at end of file
diff --git a/bootstat/boot_event_record_store.cpp b/bootstat/boot_event_record_store.cpp
new file mode 100644
index 0000000..1bdcf2b
--- /dev/null
+++ b/bootstat/boot_event_record_store.cpp
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "boot_event_record_store.h"
+
+#include <dirent.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <utime.h>
+#include <cstdlib>
+#include <utility>
+#include <android-base/file.h>
+#include <android-base/logging.h>
+
+namespace {
+
+const char BOOTSTAT_DATA_DIR[] = "/data/misc/bootstat/";
+
+// Given a boot even record file at |path|, extracts the event's relative time
+// from the record into |uptime|.
+bool ParseRecordEventTime(const std::string& path, int32_t* uptime) {
+  DCHECK_NE(static_cast<int32_t*>(nullptr), uptime);
+
+  struct stat file_stat;
+  if (stat(path.c_str(), &file_stat) == -1) {
+    PLOG(ERROR) << "Failed to read " << path;
+    return false;
+  }
+
+  *uptime = file_stat.st_mtime;
+  return true;
+}
+
+}  // namespace
+
+BootEventRecordStore::BootEventRecordStore() {
+  SetStorePath(BOOTSTAT_DATA_DIR);
+}
+
+void BootEventRecordStore::AddBootEvent(const std::string& name) {
+  std::string uptime_str;
+  if (!android::base::ReadFileToString("/proc/uptime", &uptime_str)) {
+    LOG(ERROR) << "Failed to read /proc/uptime";
+  }
+
+  std::string record_path = GetBootEventPath(name);
+  if (creat(record_path.c_str(), S_IRUSR | S_IWUSR) == -1) {
+    PLOG(ERROR) << "Failed to create " << record_path;
+  }
+
+  struct stat file_stat;
+  if (stat(record_path.c_str(), &file_stat) == -1) {
+    PLOG(ERROR) << "Failed to read " << record_path;
+  }
+
+  // Cast intentionally rounds down.
+  time_t uptime = static_cast<time_t>(strtod(uptime_str.c_str(), NULL));
+  struct utimbuf times = {file_stat.st_atime, uptime};
+  if (utime(record_path.c_str(), &times) == -1) {
+    PLOG(ERROR) << "Failed to set mtime for " << record_path;
+  }
+}
+
+std::vector<BootEventRecordStore::BootEventRecord> BootEventRecordStore::
+    GetAllBootEvents() const {
+  std::vector<BootEventRecord> events;
+
+  std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(store_path_.c_str()), closedir);
+
+  // This case could happen due to external manipulation of the filesystem,
+  // so crash out if the record store doesn't exist.
+  CHECK_NE(static_cast<DIR*>(nullptr), dir.get());
+
+  struct dirent* entry;
+  while ((entry = readdir(dir.get())) != NULL) {
+    // Only parse regular files.
+    if (entry->d_type != DT_REG) {
+      continue;
+    }
+
+    const std::string event = entry->d_name;
+    const std::string record_path = GetBootEventPath(event);
+    int32_t uptime;
+    if (!ParseRecordEventTime(record_path, &uptime)) {
+      LOG(ERROR) << "Failed to parse boot time record: " << record_path;
+      continue;
+    }
+
+    events.push_back(std::make_pair(event, uptime));
+  }
+
+  return events;
+}
+
+void BootEventRecordStore::SetStorePath(const std::string& path) {
+  DCHECK_EQ('/', path.back());
+  store_path_ = path;
+}
+
+std::string BootEventRecordStore::GetBootEventPath(
+    const std::string& event) const {
+  DCHECK_EQ('/', store_path_.back());
+  return store_path_ + event;
+}
diff --git a/bootstat/boot_event_record_store.h b/bootstat/boot_event_record_store.h
new file mode 100644
index 0000000..77978ef
--- /dev/null
+++ b/bootstat/boot_event_record_store.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 BOOT_EVENT_RECORD_STORE_H_
+#define BOOT_EVENT_RECORD_STORE_H_
+
+#include <cstdint>
+#include <string>
+#include <utility>
+#include <vector>
+#include <android-base/macros.h>
+#include <gtest/gtest_prod.h>
+
+// BootEventRecordStore manages the persistence of boot events to the record
+// store and the retrieval of all boot event records from the store.
+class BootEventRecordStore {
+ public:
+  // A BootEventRecord consists of the event name and the timestamp the event
+  // occurred.
+  typedef std::pair<std::string, int32_t> BootEventRecord;
+
+  BootEventRecordStore();
+
+  // Persists the boot event named |name| in the record store.
+  void AddBootEvent(const std::string& name);
+
+  // Returns a list of all of the boot events persisted in the record store.
+  std::vector<BootEventRecord> GetAllBootEvents() const;
+
+ private:
+  // The tests call SetStorePath to override the default store location with a
+  // more test-friendly path.
+  FRIEND_TEST(BootEventRecordStoreTest, AddSingleBootEvent);
+  FRIEND_TEST(BootEventRecordStoreTest, AddMultipleBootEvents);
+
+  // Sets the filesystem path of the record store.
+  void SetStorePath(const std::string& path);
+
+  // Constructs the full path of the given boot |event|.
+  std::string GetBootEventPath(const std::string& event) const;
+
+  // The filesystem path of the record store.
+  std::string store_path_;
+
+  DISALLOW_COPY_AND_ASSIGN(BootEventRecordStore);
+};
+
+#endif  // BOOT_EVENT_RECORD_STORE_H_
\ No newline at end of file
diff --git a/bootstat/boot_event_record_store_test.cpp b/bootstat/boot_event_record_store_test.cpp
new file mode 100644
index 0000000..384f84d
--- /dev/null
+++ b/bootstat/boot_event_record_store_test.cpp
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "boot_event_record_store.h"
+
+#include <dirent.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <cstdint>
+#include <cstdlib>
+#include <android-base/file.h>
+#include <android-base/test_utils.h>
+#include <gtest/gtest.h>
+#include <gmock/gmock.h>
+
+using testing::UnorderedElementsAreArray;
+
+namespace {
+
+// Returns true if the time difference between |a| and |b| is no larger
+// than 10 seconds.  This allow for a relatively large fuzz when comparing
+// two timestamps taken back-to-back.
+bool FuzzUptimeEquals(int32_t a, int32_t b) {
+  const int32_t FUZZ_SECONDS = 10;
+  return (abs(a - b) <= FUZZ_SECONDS);
+}
+
+// Returns the uptime as read from /proc/uptime, rounded down to an integer.
+int32_t ReadUptime() {
+  std::string uptime_str;
+  if (!android::base::ReadFileToString("/proc/uptime", &uptime_str)) {
+    return -1;
+  }
+
+  // Cast to int to round down.
+  return static_cast<int32_t>(strtod(uptime_str.c_str(), NULL));
+}
+
+// Recursively deletes the directory at |path|.
+void DeleteDirectory(const std::string& path) {
+  typedef std::unique_ptr<DIR, decltype(&closedir)> ScopedDIR;
+  ScopedDIR dir(opendir(path.c_str()), closedir);
+  ASSERT_NE(nullptr, dir.get());
+
+  struct dirent* entry;
+  while ((entry = readdir(dir.get())) != NULL) {
+    const std::string entry_name(entry->d_name);
+    if (entry_name == "." || entry_name == "..") {
+      continue;
+    }
+
+    const std::string entry_path = path + "/" + entry_name;
+    if (entry->d_type == DT_DIR) {
+      DeleteDirectory(entry_path);
+    } else {
+      unlink(entry_path.c_str());
+    }
+  }
+
+  rmdir(path.c_str());
+}
+
+class BootEventRecordStoreTest : public ::testing::Test {
+ public:
+  BootEventRecordStoreTest() {
+    store_path_ = std::string(store_dir_.path) + "/";
+  }
+
+  const std::string& GetStorePathForTesting() const {
+    return store_path_;
+  }
+
+ private:
+  void TearDown() {
+    // This removes the record store temporary directory even though
+    // TemporaryDir should already take care of it, but this method cleans up
+    // the test files added to the directory which prevent TemporaryDir from
+    // being able to remove the directory.
+    DeleteDirectory(store_path_);
+  }
+
+  // A scoped temporary directory. Using this abstraction provides creation of
+  // the directory and the path to the directory, which is stored in
+  // |store_path_|.
+  TemporaryDir store_dir_;
+
+  // The path to the temporary directory used by the BootEventRecordStore to
+  // persist records.  The directory is created and destroyed for each test.
+  std::string store_path_;
+
+  DISALLOW_COPY_AND_ASSIGN(BootEventRecordStoreTest);
+};
+
+}  // namespace
+
+TEST_F(BootEventRecordStoreTest, AddSingleBootEvent) {
+  BootEventRecordStore store;
+  store.SetStorePath(GetStorePathForTesting());
+
+  int32_t uptime = ReadUptime();
+  ASSERT_NE(-1, uptime);
+
+  store.AddBootEvent("cenozoic");
+
+  auto events = store.GetAllBootEvents();
+  ASSERT_EQ(1U, events.size());
+  EXPECT_EQ("cenozoic", events[0].first);
+  EXPECT_TRUE(FuzzUptimeEquals(uptime, events[0].second));
+}
+
+TEST_F(BootEventRecordStoreTest, AddMultipleBootEvents) {
+  BootEventRecordStore store;
+  store.SetStorePath(GetStorePathForTesting());
+
+  int32_t uptime = ReadUptime();
+  ASSERT_NE(-1, uptime);
+
+  store.AddBootEvent("cretaceous");
+  store.AddBootEvent("jurassic");
+  store.AddBootEvent("triassic");
+
+  const std::string EXPECTED_NAMES[] = {
+    "cretaceous",
+    "jurassic",
+    "triassic",
+  };
+
+  auto events = store.GetAllBootEvents();
+  ASSERT_EQ(3U, events.size());
+
+  std::vector<std::string> names;
+  std::vector<int32_t> timestamps;
+  for (auto i = events.begin(); i != events.end(); ++i) {
+    names.push_back(i->first);
+    timestamps.push_back(i->second);
+  }
+
+  EXPECT_THAT(names, UnorderedElementsAreArray(EXPECTED_NAMES));
+
+  for (auto i = timestamps.cbegin(); i != timestamps.cend(); ++i) {
+    EXPECT_TRUE(FuzzUptimeEquals(uptime, *i));
+  }
+}
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
new file mode 100644
index 0000000..a83f8ad
--- /dev/null
+++ b/bootstat/bootstat.cpp
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// The bootstat command provides options to persist boot events with the current
+// timestamp, dump the persisted events, and log all events to EventLog to be
+// uploaded to Android log storage via Tron.
+
+//#define LOG_TAG "bootstat"
+
+#include <unistd.h>
+#include <cstddef>
+#include <cstdio>
+#include <memory>
+#include <string>
+#include <android-base/logging.h>
+#include <log/log.h>
+#include "boot_event_record_store.h"
+#include "event_log_list_builder.h"
+
+namespace {
+
+// Builds an EventLog buffer named |event| containing |data| and writes
+// the log into the Tron histogram logs.
+void LogBootEvent(const std::string& event, int32_t data) {
+  LOG(INFO) << "Logging boot time: " << event << " " << data;
+
+  EventLogListBuilder log_builder;
+  log_builder.Append(event);
+  log_builder.Append(data);
+
+  std::unique_ptr<uint8_t[]> log;
+  size_t size;
+  log_builder.Release(&log, &size);
+
+  android_bWriteLog(HISTOGRAM_LOG_TAG, log.get(), size);
+}
+
+// Scans the boot event record store for record files and logs each boot event
+// via EventLog.
+void LogBootEvents() {
+  BootEventRecordStore boot_event_store;
+
+  auto events = boot_event_store.GetAllBootEvents();
+  for (auto i = events.cbegin(); i != events.cend(); ++i) {
+    LogBootEvent(i->first, i->second);
+  }
+}
+
+void PrintBootEvents() {
+  printf("Boot events:\n");
+  printf("------------\n");
+
+  BootEventRecordStore boot_event_store;
+  auto events = boot_event_store.GetAllBootEvents();
+  for (auto i = events.cbegin(); i != events.cend(); ++i) {
+    printf("%s\t%d\n", i->first.c_str(), i->second);
+  }
+}
+
+void ShowHelp(const char *cmd) {
+  fprintf(stderr, "Usage: %s [options]\n", cmd);
+  fprintf(stderr,
+          "options include:\n"
+          "  -d              Dump the boot event records to the console.\n"
+          "  -h              Show this help.\n"
+          "  -l              Log all metrics to logstorage.\n"
+          "  -r              Record the timestamp of a named boot event.\n");
+}
+
+// Constructs a readable, printable string from the givencommand line
+// arguments.
+std::string GetCommandLine(int argc, char **argv) {
+  std::string cmd;
+  for (int i = 0; i < argc; ++i) {
+    cmd += argv[i];
+    cmd += " ";
+  }
+
+  return cmd;
+}
+
+}  // namespace
+
+int main(int argc, char **argv) {
+  android::base::InitLogging(argv);
+
+  const std::string cmd_line = GetCommandLine(argc, argv);
+  LOG(INFO) << "Service started: " << cmd_line;
+
+  int opt = 0;
+  while ((opt = getopt(argc, argv, "hlpr:")) != -1) {
+    switch (opt) {
+      case 'h': {
+        ShowHelp(argv[0]);
+        break;
+      }
+
+      case 'l': {
+        LogBootEvents();
+        break;
+      }
+
+      case 'p': {
+        PrintBootEvents();
+        break;
+      }
+
+      case 'r': {
+        // |optarg| is an external variable set by getopt representing
+        // the option argument.
+        const char* event = optarg;
+
+        BootEventRecordStore boot_event_store;
+        boot_event_store.AddBootEvent(event);
+        break;
+      }
+
+      default: {
+        DCHECK_EQ(opt, '?');
+
+        // |optopt| is an external variable set by getopt representing
+        // the value of the invalid option.
+        LOG(ERROR) << "Invalid option: " << optopt;
+        ShowHelp(argv[0]);
+        return EXIT_FAILURE;
+      }
+    }
+  }
+
+  return 0;
+}
diff --git a/bootstat/bootstat.rc b/bootstat/bootstat.rc
new file mode 100644
index 0000000..2c37dd2
--- /dev/null
+++ b/bootstat/bootstat.rc
@@ -0,0 +1,14 @@
+# This file is the LOCAL_INIT_RC file for the bootstat command.
+
+on post-fs-data
+    mkdir /data/misc/bootstat 0700 root root
+
+# This marker, boot animation stopped, is considered the point at which the
+# the user may interact with the device, so it is a good proxy for the boot
+# complete signal.
+on property:init.svc.bootanim=stopped
+    # Record boot_complete timing event.
+    exec - root root -- /system/bin/bootstat -r boot_complete
+
+    # Log all boot events.
+    exec - root root -- /system/bin/bootstat -l
diff --git a/bootstat/event_log_list_builder.cpp b/bootstat/event_log_list_builder.cpp
new file mode 100644
index 0000000..241e3d5
--- /dev/null
+++ b/bootstat/event_log_list_builder.cpp
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "event_log_list_builder.h"
+
+#include <cinttypes>
+#include <memory>
+#include <string>
+#include <android-base/logging.h>
+#include <log/log.h>
+
+namespace {
+
+const size_t MAX_EVENT_PAYLOAD_SIZE = 512 - 1;  // Leave room for final '\n'.
+const size_t EVENT_TYPE_SIZE = 1;  // Size in bytes of the event type marker.
+
+}  // namespace
+
+EventLogListBuilder::EventLogListBuilder()
+    : payload_count_(0),
+      payload_size_(0),
+      payload_(std::make_unique<uint8_t[]>(MAX_EVENT_PAYLOAD_SIZE)) {
+  memset(payload_.get(), 0, MAX_EVENT_PAYLOAD_SIZE);
+
+  // Set up the top-level EventLog data type.
+  AppendByte(EVENT_TYPE_LIST);
+
+  // Skip over the byte prepresenting the number of items in the list. This
+  // value is set in Release().
+  payload_size_++;
+}
+
+bool EventLogListBuilder::Append(int value) {
+  DCHECK_NE(static_cast<uint8_t*>(nullptr), payload_.get());
+
+  if (!IsSpaceAvailable(sizeof(value) + EVENT_TYPE_SIZE)) {
+    return false;
+  }
+
+  AppendByte(EVENT_TYPE_INT);
+  AppendData(&value, sizeof(value));
+
+  payload_count_++;
+  return true;
+}
+
+bool EventLogListBuilder::Append(const std::string& value) {
+  DCHECK_NE(static_cast<uint8_t*>(nullptr), payload_.get());
+
+  int len = value.length();
+  if (!IsSpaceAvailable(sizeof(len) + len)) {
+    return false;
+  }
+
+  AppendByte(EVENT_TYPE_STRING);
+  AppendData(&len, sizeof(len));
+  AppendData(value.c_str(), len);
+
+  payload_count_++;
+  return true;
+}
+
+void EventLogListBuilder::Release(std::unique_ptr<uint8_t[]>* log,
+                                  size_t* size) {
+  // Finalize the log payload.
+  payload_[1] = payload_count_;
+
+  // Return the log payload.
+  *size = payload_size_;
+  *log = std::move(payload_);
+}
+
+void EventLogListBuilder::AppendData(const void* data, size_t size) {
+  DCHECK_LT(payload_size_ + size, MAX_EVENT_PAYLOAD_SIZE);
+  memcpy(&payload_[payload_size_], data, size);
+  payload_size_ += size;
+}
+
+void EventLogListBuilder::AppendByte(uint8_t byte) {
+  DCHECK_LT(payload_size_ + sizeof(byte), MAX_EVENT_PAYLOAD_SIZE);
+  payload_[payload_size_++] = byte;
+}
+
+bool EventLogListBuilder::IsSpaceAvailable(size_t value_size) {
+  size_t space_needed = value_size + EVENT_TYPE_SIZE;
+  if (payload_size_ + space_needed > MAX_EVENT_PAYLOAD_SIZE) {
+    size_t remaining = MAX_EVENT_PAYLOAD_SIZE - payload_size_;
+    LOG(WARNING) << "Not enough space for value. remain=" <<
+        remaining << "; needed=" << space_needed;
+    return false;
+  }
+
+  return true;
+}
diff --git a/bootstat/event_log_list_builder.h b/bootstat/event_log_list_builder.h
new file mode 100644
index 0000000..4e29b01
--- /dev/null
+++ b/bootstat/event_log_list_builder.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 EVENT_LOG_LIST_BUILDER_H_
+#define EVENT_LOG_LIST_BUILDER_H_
+
+#include <cstdint>
+#include <memory>
+
+#include <android-base/macros.h>
+
+// EventLogListBuilder provides a mechanism to build an EventLog list
+// consisting of int and string EventLog values.
+//
+// NOTE: This class does not provide the ability to append an embedded list,
+// i.e., a list containing a list.
+class EventLogListBuilder {
+ public:
+  EventLogListBuilder();
+
+  // Append a single value of a specified type.
+  bool Append(int value);
+  bool Append(const std::string& value);
+
+  // Finalizes construction of the EventLog list and releases the data
+  // to the caller. Caller takes ownership of the payload. No further calls
+  // to append* may be made once the payload is acquired by the caller.
+  void Release(std::unique_ptr<uint8_t[]>* log, size_t* size);
+
+ private:
+  // Appends |data| of the given |size| to the payload.
+  void AppendData(const void* data, size_t size);
+
+  // Appends a single byte to the payload.
+  void AppendByte(uint8_t byte);
+
+  // Returns true iff the remaining capacity in |payload_| is large enough to
+  // accommodate |value_size| bytes. The space required to log the event type
+  // is included in the internal calculation so must not be passed in to
+  // |value_size|.
+  bool IsSpaceAvailable(size_t value_size);
+
+  // The number of items in the EventLog list.
+  size_t payload_count_;
+
+  // The size of the data stored in |payload_|. Used to track where to insert
+  // new data.
+  size_t payload_size_;
+
+  // The payload constructed by calls to log*. The payload may only contain
+  // MAX_EVENT_PAYLOAD (512) bytes.
+  std::unique_ptr<uint8_t[]> payload_;
+
+  DISALLOW_COPY_AND_ASSIGN(EventLogListBuilder);
+};
+
+ #endif  // EVENT_LOG_LIST_BUILDER_H_
diff --git a/bootstat/event_log_list_builder_test.cpp b/bootstat/event_log_list_builder_test.cpp
new file mode 100644
index 0000000..affb4bf
--- /dev/null
+++ b/bootstat/event_log_list_builder_test.cpp
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "event_log_list_builder.h"
+
+#include <inttypes.h>
+#include <gtest/gtest.h>
+#include <gmock/gmock.h>
+#include <log/log.h>
+
+using testing::ElementsAreArray;
+
+TEST(EventLogListBuilder, Empty) {
+  EventLogListBuilder builder;
+
+  const uint8_t EXPECTED_LOG[] = {
+    EVENT_TYPE_LIST,
+    0,  // Number of items in the list.
+  };
+
+  std::unique_ptr<uint8_t[]> log;
+  size_t size;
+  builder.Release(&log, &size);
+  EXPECT_EQ(2U, size);
+
+  uint8_t* log_data = log.get();
+  EXPECT_THAT(std::vector<uint8_t>(log_data, log_data + size),
+              ElementsAreArray(EXPECTED_LOG));
+}
+
+TEST(EventLogListBuilder, SingleInt) {
+  EventLogListBuilder builder;
+
+  const uint8_t EXPECTED_LOG[] = {
+    EVENT_TYPE_LIST,
+    1,                // Number of items in the list.
+    EVENT_TYPE_INT,
+    42, 0, 0, 0,      // 4 byte integer value.
+  };
+
+  builder.Append(42);
+
+  std::unique_ptr<uint8_t[]> log;
+  size_t size;
+  builder.Release(&log, &size);
+  EXPECT_EQ(7U, size);
+
+  uint8_t* log_data = log.get();
+  EXPECT_THAT(std::vector<uint8_t>(log_data, log_data + size),
+              ElementsAreArray(EXPECTED_LOG));
+}
+
+TEST(EventLogListBuilder, SingleString) {
+  EventLogListBuilder builder;
+
+  const uint8_t EXPECTED_LOG[] = {
+    EVENT_TYPE_LIST,
+    1,                        // Number of items in the list.
+    EVENT_TYPE_STRING,
+    5, 0, 0, 0,               // 4 byte length of the string.
+    'D', 'r', 'o', 'i', 'd',
+  };
+
+  builder.Append("Droid");
+
+  std::unique_ptr<uint8_t[]> log;
+  size_t size;
+  builder.Release(&log, &size);
+  EXPECT_EQ(12U, size);
+
+  uint8_t* log_data = log.get();
+  EXPECT_THAT(std::vector<uint8_t>(log_data, log_data + size),
+              ElementsAreArray(EXPECTED_LOG));
+}
+
+TEST(EventLogListBuilder, IntThenString) {
+  EventLogListBuilder builder;
+
+  const uint8_t EXPECTED_LOG[] = {
+    EVENT_TYPE_LIST,
+    2,                        // Number of items in the list.
+    EVENT_TYPE_INT,
+    42, 0, 0, 0,              // 4 byte integer value.
+    EVENT_TYPE_STRING,
+    5, 0, 0, 0,               // 4 byte length of the string.
+    'D', 'r', 'o', 'i', 'd',
+  };
+
+  builder.Append(42);
+  builder.Append("Droid");
+
+  std::unique_ptr<uint8_t[]> log;
+  size_t size;
+  builder.Release(&log, &size);
+  EXPECT_EQ(17U, size);
+
+  uint8_t* log_data = log.get();
+  EXPECT_THAT(std::vector<uint8_t>(log_data, log_data + size),
+              ElementsAreArray(EXPECTED_LOG));
+}
diff --git a/bootstat/testrunner.cpp b/bootstat/testrunner.cpp
new file mode 100644
index 0000000..79b61d1
--- /dev/null
+++ b/bootstat/testrunner.cpp
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <android-base/logging.h>
+#include <gtest/gtest.h>
+
+int main(int argc, char** argv) {
+  ::testing::InitGoogleTest(&argc, argv);
+  android::base::InitLogging(argv, android::base::StderrLogger);
+  return RUN_ALL_TESTS();
+}
diff --git a/crash_reporter/Android.mk b/crash_reporter/Android.mk
index 565963c..fa564b2 100644
--- a/crash_reporter/Android.mk
+++ b/crash_reporter/Android.mk
@@ -19,7 +19,6 @@
 crash_reporter_src := crash_collector.cc \
     kernel_collector.cc \
     kernel_warning_collector.cc \
-    udev_collector.cc \
     unclean_shutdown_collector.cc \
     user_collector.cc
 
@@ -29,7 +28,6 @@
     crash_reporter_logs_test.cc \
     kernel_collector_test.cc \
     testrunner.cc \
-    udev_collector_test.cc \
     unclean_shutdown_collector_test.cc \
     user_collector_test.cc
 
@@ -41,7 +39,6 @@
 LOCAL_MODULE := libcrash
 LOCAL_CPP_EXTENSION := $(crash_reporter_cpp_extension)
 LOCAL_C_INCLUDES := $(crash_reporter_includes)
-LOCAL_RTTI_FLAG := -frtti
 LOCAL_SHARED_LIBRARIES := libchrome \
     libbinder \
     libbrillo \
@@ -63,7 +60,6 @@
     crash_sender \
     crash_server
 LOCAL_INIT_RC := crash_reporter.rc
-LOCAL_RTTI_FLAG := -frtti
 LOCAL_SHARED_LIBRARIES := libchrome \
     libbinder \
     libbrillo \
@@ -139,6 +135,9 @@
 include $(CLEAR_VARS)
 LOCAL_MODULE := crash_reporter_tests
 LOCAL_CPP_EXTENSION := $(crash_reporter_cpp_extension)
+ifdef BRILLO
+LOCAL_MODULE_TAGS := debug
+endif
 LOCAL_SHARED_LIBRARIES := libchrome \
     libbrillo \
     libcutils \
diff --git a/crash_reporter/README.md b/crash_reporter/README.md
new file mode 100644
index 0000000..9ac0a86
--- /dev/null
+++ b/crash_reporter/README.md
@@ -0,0 +1,61 @@
+# crash_reporter
+
+`crash_reporter` is a deamon running on the device that saves the call stack of
+crashing programs. It makes use of the
+[Breakpad](https://chromium.googlesource.com/breakpad/breakpad/) library.
+
+During a build, Breakpad symbol files are generated for all binaries.  They are
+packaged into a zip file when running `m dist`, so that a developer can upload
+them to the crash server.
+
+On a device, if the user has opted in to metrics and crash reporting, a
+Breakpad minidump is generated when an executable crashes, which is then
+uploaded to the crash server.
+
+On the crash server, it compares the minidump's signature to the symbol files
+that the developer has uploaded, and extracts and symbolizes the stack trace
+from the minidump.
+
+## SELinux policies
+
+In order to correctly generate a minidump, `crash_reporter` needs to be given
+the proper SELinux permissions for accessing the domain of the crashing
+executable.  By default, `crash_reporter` has only been given access to a select
+number of system domains, such as `metricsd`, `weave`, and `update_engine`.  If
+a developer wants their executable's crashes to be caught by `crash_reporter`,
+they will have to set their SELinux policies in their .te file to allow
+`crash_reporter` access to their domain.  This can be done through a simple
+[macro](https://android.googlesource.com/device/generic/brillo/+/master/sepolicy/te_macros):
+
+    allow_crash_reporter(domain_name)
+
+Replace *domain_name* with whatever domain is assigned to the executable in
+the `file_contexts` file.
+
+## Configuration
+
+`crash_reporter` has a few different configuration options that have to be set.
+
+- Crashes are only handled and uploaded if analytics reporting is enabled,
+  either via the weave call to set `_metrics.enableAnalyticsReporting` or by
+  manually creating the file `/data/misc/metrics/enabled` (for testing only).
+- The `BRILLO_CRASH_SERVER` make variable should be set in the `product.mk`
+  file to the URL of the crash server.  For Brillo builds, it is set
+  automatically through the product configuration.  Setting this variable will
+  populate the `/etc/os-release.d/crash_server` file on the device, which is
+  read by `crash_sender`.
+- The `BRILLO_PRODUCT_ID` make variable should be set in the `product.mk` file
+  to the product's ID.  For Brillo builds, it is set automatically through the
+  product configuration.  Setting this variable will populate the
+  `/etc/os-release.d/product_id`, which is read by `crash_sender`.
+
+## Uploading crash reports in *eng* builds
+
+By default, crash reports are only uploaded to the server for production
+*user* and *userdebug* images.  In *eng* builds, with crash reporting enabled
+the device will generate minidumps for any crashing executables but will not
+send them to the crash server.  If a developer does want to force an upload,
+they can do so by issuing the command `SECONDS_SEND_SPREAD=5 FORCE_OFFICIAL=1
+crash_sender` from an ADB shell.  This will send the report to the server, with
+the *image_type* field set to *force-official* so that these reports can be
+differentiated from normal reports.
diff --git a/crash_reporter/crash_collector_test.cc b/crash_reporter/crash_collector_test.cc
index b55c324..11c8c0d 100644
--- a/crash_reporter/crash_collector_test.cc
+++ b/crash_reporter/crash_collector_test.cc
@@ -20,6 +20,7 @@
 #include <utility>
 
 #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 <brillo/syslog_logging.h>
@@ -52,20 +53,17 @@
     EXPECT_CALL(collector_, SetUpDBus()).WillRepeatedly(Return());
 
     collector_.Initialize(CountCrash, IsMetrics);
-    test_dir_ = FilePath("test");
-    base::CreateDirectory(test_dir_);
+    EXPECT_TRUE(test_dir_.CreateUniqueTempDir());
     brillo::ClearLog();
   }
 
-  void TearDown() {
-    base::DeleteFile(test_dir_, true);
-  }
-
   bool CheckHasCapacity();
 
  protected:
   CrashCollectorMock collector_;
-  FilePath test_dir_;
+
+  // Temporary directory used for tests.
+  base::ScopedTempDir test_dir_;
 };
 
 TEST_F(CrashCollectorTest, Initialize) {
@@ -74,7 +72,7 @@
 }
 
 TEST_F(CrashCollectorTest, WriteNewFile) {
-  FilePath test_file = test_dir_.Append("test_new");
+  FilePath test_file = test_dir_.path().Append("test_new");
   const char kBuffer[] = "buffer";
   EXPECT_EQ(strlen(kBuffer),
             collector_.WriteNewFile(test_file,
@@ -122,8 +120,10 @@
 
 
 bool CrashCollectorTest::CheckHasCapacity() {
-  static const char kFullMessage[] = "Crash directory test already full";
-  bool has_capacity = collector_.CheckHasCapacity(test_dir_);
+  const char* kFullMessage =
+      StringPrintf("Crash directory %s already full",
+                   test_dir_.path().value().c_str()).c_str();
+  bool has_capacity = collector_.CheckHasCapacity(test_dir_.path());
   bool has_message = FindLog(kFullMessage);
   EXPECT_EQ(has_message, !has_capacity);
   return has_capacity;
@@ -132,19 +132,22 @@
 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);
+    base::WriteFile(test_dir_.path().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);
+    base::WriteFile(test_dir_.path().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);
+    base::WriteFile(test_dir_.path().Append(StringPrintf("overage%d.meta", i)),
+                    "", 0);
     EXPECT_FALSE(CheckHasCapacity());
   }
 }
@@ -152,50 +155,52 @@
 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);
+    base::WriteFile(test_dir_.path().Append(StringPrintf("file.%d.core", i)),
+                    "", 0);
     EXPECT_TRUE(CheckHasCapacity());
   }
-  base::WriteFile(test_dir_.Append("file.last.core"), "", 0);
+  base::WriteFile(test_dir_.path().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);
+    base::WriteFile(test_dir_.path().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);
+    base::WriteFile(test_dir_.path().Append(StringPrintf(".file%d", i)), "", 0);
     EXPECT_TRUE(CheckHasCapacity());
   }
-  base::WriteFile(test_dir_.Append("normal.meta"), "", 0);
+  base::WriteFile(test_dir_.path().Append("normal.meta"), "", 0);
   EXPECT_FALSE(CheckHasCapacity());
 }
 
 TEST_F(CrashCollectorTest, MetaData) {
   const char kMetaFileBasename[] = "generated.meta";
-  FilePath meta_file = test_dir_.Append(kMetaFileBasename);
-  FilePath payload_file = test_dir_.Append("payload-file");
+  FilePath meta_file = test_dir_.path().Append(kMetaFileBasename);
+  FilePath payload_file = test_dir_.path().Append("payload-file");
   std::string contents;
   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"
-      "payload=test/payload-file\n"
-      "payload_size=3\n"
-      "done=1\n";
+  const std::string kExpectedMeta =
+      StringPrintf("foo=bar\n"
+          "exec_name=kernel\n"
+          "payload=%s\n"
+          "payload_size=3\n"
+          "done=1\n",
+          test_dir_.path().Append("payload-file").value().c_str());
   EXPECT_EQ(kExpectedMeta, contents);
 
   // Test target of symlink is not overwritten.
-  payload_file = test_dir_.Append("payload2-file");
+  payload_file = test_dir_.path().Append("payload2-file");
   ASSERT_TRUE(base::WriteFile(payload_file, kPayload, strlen(kPayload)));
-  FilePath meta_symlink_path = test_dir_.Append("symlink.meta");
+  FilePath meta_symlink_path = test_dir_.path().Append("symlink.meta");
   ASSERT_EQ(0,
             symlink(kMetaFileBasename,
                     meta_symlink_path.value().c_str()));
@@ -221,8 +226,8 @@
 }
 
 TEST_F(CrashCollectorTest, GetLogContents) {
-  FilePath config_file = test_dir_.Append("crash_config");
-  FilePath output_file = test_dir_.Append("crash_log");
+  FilePath config_file = test_dir_.path().Append("crash_config");
+  FilePath output_file = test_dir_.path().Append("crash_log");
   const char kConfigContents[] =
       "foobar=echo hello there | \\\n  sed -e \"s/there/world/\"";
   ASSERT_TRUE(
diff --git a/crash_reporter/crash_reporter.cc b/crash_reporter/crash_reporter.cc
index 26ffa38..b69492a 100644
--- a/crash_reporter/crash_reporter.cc
+++ b/crash_reporter/crash_reporter.cc
@@ -35,10 +35,13 @@
 
 #include "kernel_collector.h"
 #include "kernel_warning_collector.h"
-#include "udev_collector.h"
 #include "unclean_shutdown_collector.h"
 #include "user_collector.h"
 
+#if !defined(__ANDROID__)
+#include "udev_collector.h"
+#endif
+
 static const char kCrashCounterHistogram[] = "Logging.CrashCounter";
 static const char kKernelCrashDetected[] = "/var/run/kernel-crash-detected";
 static const char kUncleanShutdownDetected[] =
@@ -176,6 +179,7 @@
   return 0;
 }
 
+#if !defined(__ANDROID__)
 static int HandleUdevCrash(UdevCollector *udev_collector,
                            const std::string& udev_event) {
   // Handle a crash indicated by a udev event.
@@ -189,6 +193,7 @@
     return 1;
   return 0;
 }
+#endif
 
 static int HandleKernelWarning(KernelWarningCollector
                                *kernel_warning_collector) {
@@ -249,7 +254,11 @@
   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");
+
+#if !defined(__ANDROID__)
   DEFINE_string(udev, "", "Udev event description (type:device:subsystem)");
+#endif
+
   DEFINE_bool(kernel_warning, false, "Report collected kernel warning");
   DEFINE_string(pid, "", "PID of crashing process");
   DEFINE_string(uid, "", "UID of crashing process");
@@ -279,8 +288,11 @@
   UncleanShutdownCollector unclean_shutdown_collector;
   unclean_shutdown_collector.Initialize(CountUncleanShutdown,
                                         IsFeedbackAllowed);
+
+#if !defined(__ANDROID__)
   UdevCollector udev_collector;
   udev_collector.Initialize(CountUdevCrash, IsFeedbackAllowed);
+#endif
 
   KernelWarningCollector kernel_warning_collector;
   kernel_warning_collector.Initialize(CountUdevCrash, IsFeedbackAllowed);
@@ -304,9 +316,11 @@
                                    FLAGS_generate_kernel_signature);
   }
 
+#if !defined(__ANDROID__)
   if (!FLAGS_udev.empty()) {
     return HandleUdevCrash(&udev_collector, FLAGS_udev);
   }
+#endif
 
   if (FLAGS_kernel_warning) {
     return HandleKernelWarning(&kernel_warning_collector);
diff --git a/crash_reporter/kernel_collector.cc b/crash_reporter/kernel_collector.cc
index 12b00b9..cb3a315 100644
--- a/crash_reporter/kernel_collector.cc
+++ b/crash_reporter/kernel_collector.cc
@@ -68,8 +68,8 @@
   " RIP  \\[<.*>\\] ([^\\+ ]+).*",  // X86_64 uses RIP for the program counter
 };
 
-COMPILE_ASSERT(arraysize(kPCRegex) == KernelCollector::kArchCount,
-               missing_arch_pc_regexp);
+static_assert(arraysize(kPCRegex) == KernelCollector::kArchCount,
+              "Missing Arch PC regexp");
 
 }  // namespace
 
diff --git a/crash_reporter/list_proxies.cc b/crash_reporter/list_proxies.cc
index d445557..3374b5f 100644
--- a/crash_reporter/list_proxies.cc
+++ b/crash_reporter/list_proxies.cc
@@ -75,13 +75,12 @@
     // Start by finding the first space (if any).
     std::string::iterator space;
     for (space = token.begin(); space != token.end(); ++space) {
-      if (IsAsciiWhitespace(*space)) {
+      if (base::IsAsciiWhitespace(*space)) {
         break;
       }
     }
 
-    std::string scheme = std::string(token.begin(), space);
-    base::StringToLowerASCII(&scheme);
+    std::string scheme = base::ToLowerASCII(std::string(token.begin(), space));
     // Chrome uses "socks" to mean socks4 and "proxy" to mean http.
     if (scheme == "socks")
       scheme += "4";
@@ -183,7 +182,7 @@
     timeout_callback_.Cancel();
     proxies_ = ParseProxyString(proxy_info);
     LOG(INFO) << "Found proxies via browser signal: "
-              << JoinString(proxies_, 'x');
+              << base::JoinString(proxies_, "x");
 
     Quit();
   }
diff --git a/crash_reporter/unclean_shutdown_collector_test.cc b/crash_reporter/unclean_shutdown_collector_test.cc
index 3bdeca1..56d2704 100644
--- a/crash_reporter/unclean_shutdown_collector_test.cc
+++ b/crash_reporter/unclean_shutdown_collector_test.cc
@@ -19,6 +19,7 @@
 #include <unistd.h>
 
 #include <base/files/file_util.h>
+#include <base/files/scoped_temp_dir.h>
 #include <base/strings/string_util.h>
 #include <brillo/syslog_logging.h>
 #include <gmock/gmock.h>
@@ -32,10 +33,6 @@
 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;
 }
@@ -59,12 +56,17 @@
 
     collector_.Initialize(CountCrash,
                           IsMetrics);
-    rmdir(kTestDirectory);
-    test_unclean_ = FilePath(kTestUnclean);
-    collector_.unclean_shutdown_file_ = kTestUnclean;
+
+    EXPECT_TRUE(test_dir_.CreateUniqueTempDir());
+
+    test_directory_ = test_dir_.path().Append("test");
+    test_unclean_ = test_dir_.path().Append("test/unclean");
+
+    collector_.unclean_shutdown_file_ = test_unclean_.value().c_str();
     base::DeleteFile(test_unclean_, true);
     // Set up an alternate power manager state file as well
-    collector_.powerd_suspended_file_ = FilePath(kTestSuspended);
+    collector_.powerd_suspended_file_ =
+        test_dir_.path().Append("test/suspended");
     brillo::ClearLog();
   }
 
@@ -75,6 +77,10 @@
   }
 
   UncleanShutdownCollectorMock collector_;
+
+  // Temporary directory used for tests.
+  base::ScopedTempDir test_dir_;
+  FilePath test_directory_;
   FilePath test_unclean_;
 };
 
@@ -84,7 +90,7 @@
 }
 
 TEST_F(UncleanShutdownCollectorTest, EnableWithParent) {
-  mkdir(kTestDirectory, 0777);
+  mkdir(test_directory_.value().c_str(), 0777);
   ASSERT_TRUE(collector_.Enable());
   ASSERT_TRUE(base::PathExists(test_unclean_));
 }
@@ -133,15 +139,15 @@
 }
 
 TEST_F(UncleanShutdownCollectorTest, CantDisable) {
-  mkdir(kTestDirectory, 0700);
-  if (mkdir(kTestUnclean, 0700)) {
+  mkdir(test_directory_.value().c_str(), 0700);
+  if (mkdir(test_unclean_.value().c_str(), 0700)) {
     ASSERT_EQ(EEXIST, errno)
-        << "Error while creating directory '" << kTestUnclean
+        << "Error while creating directory '" << test_unclean_.value()
         << "': " << 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);
+  rmdir(test_unclean_.value().c_str());
 }
diff --git a/crash_reporter/user_collector.cc b/crash_reporter/user_collector.cc
index 6714f52..98d7448 100644
--- a/crash_reporter/user_collector.cc
+++ b/crash_reporter/user_collector.cc
@@ -151,8 +151,8 @@
     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);
+  std::vector<std::string> ids = base::SplitString(
+      id_substring, "\t", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
   if (ids.size() != kIdMax || kind < 0 || kind >= kIdMax) {
     return false;
   }
@@ -313,8 +313,8 @@
 
   uid_t uid;
   if (base::ReadFileToString(process_path.Append("status"), &status)) {
-    std::vector<std::string> status_lines;
-    base::SplitString(status, '\n', &status_lines);
+    std::vector<std::string> status_lines = base::SplitString(
+        status, "\n", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
 
     std::string process_state;
     if (!GetStateFromStatus(status_lines, &process_state)) {
diff --git a/crash_reporter/user_collector_test.cc b/crash_reporter/user_collector_test.cc
index 638ea34..d9c9a5b 100644
--- a/crash_reporter/user_collector_test.cc
+++ b/crash_reporter/user_collector_test.cc
@@ -65,8 +65,10 @@
                           false,
                           false,
                           "");
-    base::DeleteFile(FilePath("test"), true);
-    mkdir("test", 0777);
+
+    EXPECT_TRUE(test_dir_.CreateUniqueTempDir());
+
+    mkdir(test_dir_.path().Append("test").value().c_str(), 0777);
     pid_ = getpid();
     brillo::ClearLog();
   }
@@ -80,13 +82,13 @@
   }
 
   std::vector<std::string> SplitLines(const std::string &lines) const {
-    std::vector<std::string> result;
-    base::SplitString(lines, '\n', &result);
-    return result;
+    return base::SplitString(lines, "\n", base::TRIM_WHITESPACE,
+                             base::SPLIT_WANT_ALL);
   }
 
   UserCollectorMock collector_;
   pid_t pid_;
+  base::ScopedTempDir test_dir_;
 };
 
 TEST_F(UserCollectorTest, ParseCrashAttributes) {
@@ -173,14 +175,15 @@
                                            &result));
   ASSERT_TRUE(FindLog(
       "Readlink failed on /does_not_exist with 2"));
-  std::string long_link;
+  std::string long_link = test_dir_.path().value();
   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";
+    static const char* kLink =
+        test_dir_.path().Append("test/this_link").value().c_str();
     this_link.assign(long_link.c_str(), len);
     ASSERT_EQ(len, this_link.size());
     unlink(kLink);
@@ -341,13 +344,13 @@
 }
 
 TEST_F(UserCollectorTest, CopyOffProcFilesBadPid) {
-  FilePath container_path("test/container");
+  FilePath container_path(test_dir_.path().Append("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");
+  FilePath container_path(test_dir_.path().Append("test/container"));
   ASSERT_TRUE(collector_.CopyOffProcFiles(pid_, container_path));
   EXPECT_FALSE(FindLog("Could not copy"));
   static struct {
@@ -371,9 +374,7 @@
 }
 
 TEST_F(UserCollectorTest, ValidateProcFiles) {
-  base::ScopedTempDir temp_dir;
-  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
-  FilePath container_dir = temp_dir.path();
+  FilePath container_dir = test_dir_.path();
 
   // maps file not exists (i.e. GetFileSize fails)
   EXPECT_FALSE(collector_.ValidateProcFiles(container_dir));
@@ -392,9 +393,7 @@
 }
 
 TEST_F(UserCollectorTest, ValidateCoreFile) {
-  base::ScopedTempDir temp_dir;
-  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
-  FilePath container_dir = temp_dir.path();
+  FilePath container_dir = test_dir_.path();
   FilePath core_file = container_dir.Append("core");
 
   // Core file does not exist
diff --git a/debuggerd/Android.mk b/debuggerd/Android.mk
index de0f943..9e4f1f7 100644
--- a/debuggerd/Android.mk
+++ b/debuggerd/Android.mk
@@ -62,7 +62,7 @@
 LOCAL_SRC_FILES_x86_64 := x86_64/crashglue.S
 LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
 LOCAL_MODULE_TAGS := optional
-LOCAL_CFLAGS += -fstack-protector-all -Werror -Wno-free-nonheap-object
+LOCAL_CFLAGS += -fstack-protector-all -Werror -Wno-free-nonheap-object -Wno-date-time
 #LOCAL_FORCE_STATIC_EXECUTABLE := true
 LOCAL_SHARED_LIBRARIES := libcutils liblog libc
 
diff --git a/debuggerd/backtrace.cpp b/debuggerd/backtrace.cpp
index b46f8f4..b6916e5 100644
--- a/debuggerd/backtrace.cpp
+++ b/debuggerd/backtrace.cpp
@@ -67,8 +67,7 @@
   _LOG(log, logtype::BACKTRACE, "\n----- end %d -----\n", pid);
 }
 
-static void dump_thread(
-    log_t* log, pid_t tid, bool attached, bool* detach_failed, int* total_sleep_time_usec) {
+static void dump_thread(log_t* log, BacktraceMap* map, pid_t pid, pid_t tid) {
   char path[PATH_MAX];
   char threadnamebuf[1024];
   char* threadname = NULL;
@@ -88,56 +87,25 @@
 
   _LOG(log, logtype::BACKTRACE, "\n\"%s\" sysTid=%d\n", threadname ? threadname : "<unknown>", tid);
 
-  if (!attached && ptrace(PTRACE_ATTACH, tid, 0, 0) < 0) {
-    _LOG(log, logtype::BACKTRACE, "Could not attach to thread: %s\n", strerror(errno));
-    return;
-  }
-
-  if (!attached && wait_for_sigstop(tid, total_sleep_time_usec, detach_failed) == -1) {
-    return;
-  }
-
-  std::unique_ptr<Backtrace> backtrace(Backtrace::Create(tid, BACKTRACE_CURRENT_THREAD));
+  std::unique_ptr<Backtrace> backtrace(Backtrace::Create(pid, tid, map));
   if (backtrace->Unwind(0)) {
     dump_backtrace_to_log(backtrace.get(), log, "  ");
   } else {
     ALOGE("Unwind failed: tid = %d", tid);
   }
-
-  if (!attached && ptrace(PTRACE_DETACH, tid, 0, 0) != 0) {
-    ALOGE("ptrace detach from %d failed: %s\n", tid, strerror(errno));
-    *detach_failed = true;
-  }
 }
 
-void dump_backtrace(int fd, int amfd, pid_t pid, pid_t tid, bool* detach_failed,
-                    int* total_sleep_time_usec) {
+void dump_backtrace(int fd, int amfd, BacktraceMap* map, pid_t pid, pid_t tid,
+                    const std::set<pid_t>& siblings) {
   log_t log;
   log.tfd = fd;
   log.amfd = amfd;
 
   dump_process_header(&log, pid);
-  dump_thread(&log, tid, true, detach_failed, total_sleep_time_usec);
+  dump_thread(&log, map, pid, tid);
 
-  char task_path[64];
-  snprintf(task_path, sizeof(task_path), "/proc/%d/task", pid);
-  DIR* d = opendir(task_path);
-  if (d != NULL) {
-    struct dirent* de = NULL;
-    while ((de = readdir(d)) != NULL) {
-      if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) {
-        continue;
-      }
-
-      char* end;
-      pid_t new_tid = strtoul(de->d_name, &end, 10);
-      if (*end || new_tid == tid) {
-        continue;
-      }
-
-      dump_thread(&log, new_tid, false, detach_failed, total_sleep_time_usec);
-    }
-    closedir(d);
+  for (pid_t sibling : siblings) {
+    dump_thread(&log, map, pid, sibling);
   }
 
   dump_process_footer(&log, pid);
diff --git a/debuggerd/backtrace.h b/debuggerd/backtrace.h
index da14cd4..98c433b 100644
--- a/debuggerd/backtrace.h
+++ b/debuggerd/backtrace.h
@@ -19,14 +19,17 @@
 
 #include <sys/types.h>
 
+#include <set>
+
 #include "utility.h"
 
 class Backtrace;
+class BacktraceMap;
 
 // Dumps a backtrace using a format similar to what Dalvik uses so that the result
 // can be intermixed in a bug report.
-void dump_backtrace(int fd, int amfd, pid_t pid, pid_t tid, bool* detach_failed,
-                    int* total_sleep_time_usec);
+void dump_backtrace(int fd, int amfd, BacktraceMap* map, pid_t pid, pid_t tid,
+                    const std::set<pid_t>& siblings);
 
 /* Dumps the backtrace in the backtrace data structure to the log. */
 void dump_backtrace_to_log(Backtrace* backtrace, log_t* log, const char* prefix);
diff --git a/debuggerd/debuggerd.cpp b/debuggerd/debuggerd.cpp
index 0afa895..8efbacc 100644
--- a/debuggerd/debuggerd.cpp
+++ b/debuggerd/debuggerd.cpp
@@ -14,21 +14,24 @@
  * limitations under the License.
  */
 
-#include <stdio.h>
-#include <errno.h>
-#include <signal.h>
-#include <pthread.h>
-#include <stdarg.h>
-#include <fcntl.h>
-#include <sys/types.h>
 #include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <sys/types.h>
 #include <time.h>
 
-#include <sys/ptrace.h>
-#include <sys/wait.h>
 #include <elf.h>
-#include <sys/stat.h>
 #include <sys/poll.h>
+#include <sys/prctl.h>
+#include <sys/ptrace.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#include <set>
 
 #include <selinux/android.h>
 
@@ -70,7 +73,7 @@
         "* Process %d has been suspended while crashing.\n"
         "* To attach gdbserver and start gdb, run this on the host:\n"
         "*\n"
-        "*     gdbclient %d\n"
+        "*     gdbclient.py -p %d\n"
         "*\n"
         "* Wait for gdb to start, then press the VOLUME DOWN key\n"
         "* to let the process continue crashing.\n"
@@ -78,16 +81,13 @@
         request.pid, request.tid);
 
   // Wait for VOLUME DOWN.
-  if (init_getevent() == 0) {
-    while (true) {
-      input_event e;
-      if (get_event(&e, -1) == 0) {
-        if (e.type == EV_KEY && e.code == KEY_VOLUMEDOWN && e.value == 0) {
-          break;
-        }
+  while (true) {
+    input_event e;
+    if (get_event(&e, -1) == 0) {
+      if (e.type == EV_KEY && e.code == KEY_VOLUMEDOWN && e.value == 0) {
+        break;
       }
     }
-    uninit_getevent();
   }
 
   ALOGI("debuggerd resuming process %d", request.pid);
@@ -334,6 +334,180 @@
 }
 #endif
 
+static void ptrace_siblings(pid_t pid, pid_t main_tid, std::set<pid_t>& tids) {
+  char task_path[64];
+
+  snprintf(task_path, sizeof(task_path), "/proc/%d/task", pid);
+
+  std::unique_ptr<DIR, int (*)(DIR*)> d(opendir(task_path), closedir);
+
+  // Bail early if the task directory cannot be opened.
+  if (!d) {
+    ALOGE("debuggerd: failed to open /proc/%d/task: %s", pid, strerror(errno));
+    return;
+  }
+
+  struct dirent* de;
+  while ((de = readdir(d.get())) != NULL) {
+    // Ignore "." and "..".
+    if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) {
+      continue;
+    }
+
+    char* end;
+    pid_t tid = strtoul(de->d_name, &end, 10);
+    if (*end) {
+      continue;
+    }
+
+    if (tid == main_tid) {
+      continue;
+    }
+
+    if (ptrace(PTRACE_ATTACH, tid, 0, 0) < 0) {
+      ALOGE("debuggerd: ptrace attach to %d failed: %s", tid, strerror(errno));
+      continue;
+    }
+
+    tids.insert(tid);
+  }
+}
+
+static bool perform_dump(const debugger_request_t& request, int fd, int tombstone_fd,
+                         BacktraceMap* backtrace_map, const std::set<pid_t>& siblings) {
+  if (TEMP_FAILURE_RETRY(write(fd, "\0", 1)) != 1) {
+    ALOGE("debuggerd: failed to respond to client: %s\n", strerror(errno));
+    return false;
+  }
+
+  int total_sleep_time_usec = 0;
+  while (true) {
+    int signal = wait_for_signal(request.tid, &total_sleep_time_usec);
+    switch (signal) {
+      case -1:
+        ALOGE("debuggerd: timed out waiting for signal");
+        return false;
+
+      case SIGSTOP:
+        if (request.action == DEBUGGER_ACTION_DUMP_TOMBSTONE) {
+          ALOGV("debuggerd: stopped -- dumping to tombstone");
+          engrave_tombstone(tombstone_fd, backtrace_map, request.pid, request.tid, siblings, signal,
+                            request.original_si_code, request.abort_msg_address);
+        } else if (request.action == DEBUGGER_ACTION_DUMP_BACKTRACE) {
+          ALOGV("debuggerd: stopped -- dumping to fd");
+          dump_backtrace(fd, -1, backtrace_map, request.pid, request.tid, siblings);
+        } else {
+          ALOGV("debuggerd: stopped -- continuing");
+          if (ptrace(PTRACE_CONT, request.tid, 0, 0) != 0) {
+            ALOGE("debuggerd: ptrace continue failed: %s", strerror(errno));
+            return false;
+          }
+          continue;  // loop again
+        }
+        break;
+
+      case SIGABRT:
+      case SIGBUS:
+      case SIGFPE:
+      case SIGILL:
+      case SIGSEGV:
+#ifdef SIGSTKFLT
+      case SIGSTKFLT:
+#endif
+      case SIGTRAP:
+        ALOGV("stopped -- fatal signal\n");
+        // Send a SIGSTOP to the process to make all of
+        // the non-signaled threads stop moving.  Without
+        // this we get a lot of "ptrace detach failed:
+        // No such process".
+        kill(request.pid, SIGSTOP);
+        engrave_tombstone(tombstone_fd, backtrace_map, request.pid, request.tid, siblings, signal,
+                          request.original_si_code, request.abort_msg_address);
+        break;
+
+      default:
+        ALOGE("debuggerd: process stopped due to unexpected signal %d\n", signal);
+        break;
+    }
+    break;
+  }
+
+  return true;
+}
+
+static bool drop_privileges() {
+  if (setresgid(AID_DEBUGGERD, AID_DEBUGGERD, AID_DEBUGGERD) != 0) {
+    ALOGE("debuggerd: failed to setresgid");
+    return false;
+  }
+
+  if (setresuid(AID_DEBUGGERD, AID_DEBUGGERD, AID_DEBUGGERD) != 0) {
+    ALOGE("debuggerd: failed to setresuid");
+    return false;
+  }
+
+  return true;
+}
+
+static bool fork_signal_sender(int* in_fd, int* out_fd, pid_t* sender_pid, pid_t target_pid) {
+  int input_pipe[2];
+  int output_pipe[2];
+  if (pipe(input_pipe) != 0) {
+    ALOGE("debuggerd: failed to create input pipe for signal sender: %s", strerror(errno));
+    return false;
+  }
+
+  if (pipe(output_pipe) != 0) {
+    close(input_pipe[0]);
+    close(input_pipe[1]);
+    ALOGE("debuggerd: failed to create output pipe for signal sender: %s", strerror(errno));
+    return false;
+  }
+
+  pid_t fork_pid = fork();
+  if (fork_pid == -1) {
+    ALOGE("debuggerd: failed to initialize signal sender: fork failed: %s", strerror(errno));
+    return false;
+  } else if (fork_pid == 0) {
+    close(input_pipe[1]);
+    close(output_pipe[0]);
+    auto wait = [=]() {
+      char buf[1];
+      if (TEMP_FAILURE_RETRY(read(input_pipe[0], buf, 1)) != 1) {
+        ALOGE("debuggerd: signal sender failed to read from pipe");
+        exit(1);
+      }
+    };
+    auto notify_done = [=]() {
+      if (TEMP_FAILURE_RETRY(write(output_pipe[1], "", 1)) != 1) {
+        ALOGE("debuggerd: signal sender failed to write to pipe");
+        exit(1);
+      }
+    };
+
+    wait();
+    if (kill(target_pid, SIGSTOP) != 0) {
+      ALOGE("debuggerd: failed to stop target '%d': %s", target_pid, strerror(errno));
+    }
+    notify_done();
+
+    wait();
+    if (kill(target_pid, SIGCONT) != 0) {
+      ALOGE("debuggerd: failed to resume target '%d': %s", target_pid, strerror(errno));
+    }
+    notify_done();
+
+    exit(0);
+  } else {
+    close(input_pipe[0]);
+    close(output_pipe[1]);
+    *in_fd = input_pipe[1];
+    *out_fd = output_pipe[0];
+    *sender_pid = fork_pid;
+    return true;
+  }
+}
+
 static void handle_request(int fd) {
   ALOGV("handle_request(%d)\n", fd);
 
@@ -363,6 +537,37 @@
   }
 #endif
 
+  // Fork a child to handle the rest of the request.
+  pid_t fork_pid = fork();
+  if (fork_pid == -1) {
+    ALOGE("debuggerd: failed to fork: %s\n", strerror(errno));
+    return;
+  } else if (fork_pid != 0) {
+    waitpid(fork_pid, nullptr, 0);
+    return;
+  }
+
+  // Open the tombstone file if we need it.
+  std::string tombstone_path;
+  int tombstone_fd = -1;
+  switch (request.action) {
+    case DEBUGGER_ACTION_DUMP_TOMBSTONE:
+    case DEBUGGER_ACTION_CRASH:
+      tombstone_fd = open_tombstone(&tombstone_path);
+      if (tombstone_fd == -1) {
+        ALOGE("debuggerd: failed to open tombstone file: %s\n", strerror(errno));
+        exit(1);
+      }
+      break;
+
+    case DEBUGGER_ACTION_DUMP_BACKTRACE:
+      break;
+
+    default:
+      ALOGE("debuggerd: unexpected request action: %d", request.action);
+      exit(1);
+  }
+
   // At this point, the thread that made the request is blocked in
   // a read() call.  If the thread has crashed, then this gives us
   // time to PTRACE_ATTACH to it before it has a chance to really fault.
@@ -373,111 +578,88 @@
   // ensure that it can run as soon as we call PTRACE_CONT below.
   // See details in bionic/libc/linker/debugger.c, in function
   // debugger_signal_handler().
-  if (ptrace(PTRACE_ATTACH, request.tid, 0, 0)) {
-    ALOGE("ptrace attach failed: %s\n", strerror(errno));
-    return;
+
+  // Attach to the target process.
+  if (ptrace(PTRACE_ATTACH, request.tid, 0, 0) != 0) {
+    ALOGE("debuggerd: ptrace attach failed: %s", strerror(errno));
+    exit(1);
   }
 
-  bool detach_failed = false;
-  bool tid_unresponsive = false;
+  // Don't attach to the sibling threads if we want to attach gdb.
+  // Supposedly, it makes the process less reliable.
   bool attach_gdb = should_attach_gdb(&request);
-  if (TEMP_FAILURE_RETRY(write(fd, "\0", 1)) != 1) {
-    ALOGE("failed responding to client: %s\n", strerror(errno));
-    return;
-  }
-
-  std::unique_ptr<char> tombstone_path;
-  int total_sleep_time_usec = 0;
-  while (true) {
-    int signal = wait_for_sigstop(request.tid, &total_sleep_time_usec, &detach_failed);
-    if (signal == -1) {
-      tid_unresponsive = true;
-      break;
+  int signal_in_fd = -1;
+  int signal_out_fd = -1;
+  pid_t signal_pid = 0;
+  if (attach_gdb) {
+    // Open all of the input devices we need to listen for VOLUMEDOWN before dropping privileges.
+    if (init_getevent() != 0) {
+      ALOGE("debuggerd: failed to initialize input device, not waiting for gdb");
+      attach_gdb = false;
     }
 
-    switch (signal) {
-      case SIGSTOP:
-        if (request.action == DEBUGGER_ACTION_DUMP_TOMBSTONE) {
-          ALOGV("stopped -- dumping to tombstone\n");
-          tombstone_path.reset(engrave_tombstone(
-            request.pid, request.tid, signal, request.original_si_code, request.abort_msg_address,
-            true, &detach_failed, &total_sleep_time_usec));
-        } else if (request.action == DEBUGGER_ACTION_DUMP_BACKTRACE) {
-          ALOGV("stopped -- dumping to fd\n");
-          dump_backtrace(fd, -1, request.pid, request.tid, &detach_failed, &total_sleep_time_usec);
-        } else {
-          ALOGV("stopped -- continuing\n");
-          status = ptrace(PTRACE_CONT, request.tid, 0, 0);
-          if (status) {
-            ALOGE("ptrace continue failed: %s\n", strerror(errno));
-          }
-          continue;  // loop again
+    // Fork a process that stays root, and listens on a pipe to pause and resume the target.
+    if (!fork_signal_sender(&signal_in_fd, &signal_out_fd, &signal_pid, request.pid)) {
+      attach_gdb = false;
+    }
+  }
+
+  auto notify_signal_sender = [=]() {
+    char buf[1];
+    if (TEMP_FAILURE_RETRY(write(signal_in_fd, "", 1)) != 1) {
+      ALOGE("debuggerd: failed to notify signal process: %s", strerror(errno));
+    } else if (TEMP_FAILURE_RETRY(read(signal_out_fd, buf, 1)) != 1) {
+      ALOGE("debuggerd: failed to read response from signal process: %s", strerror(errno));
+    }
+  };
+
+  std::set<pid_t> siblings;
+  if (!attach_gdb) {
+    ptrace_siblings(request.pid, request.tid, siblings);
+  }
+
+  // Generate the backtrace map before dropping privileges.
+  std::unique_ptr<BacktraceMap> backtrace_map(BacktraceMap::Create(request.pid));
+
+  bool succeeded = false;
+
+  // Now that we've done everything that requires privileges, we can drop them.
+  if (drop_privileges()) {
+    succeeded = perform_dump(request, fd, tombstone_fd, backtrace_map.get(), siblings);
+    if (succeeded) {
+      if (request.action == DEBUGGER_ACTION_DUMP_TOMBSTONE) {
+        if (!tombstone_path.empty()) {
+          write(fd, tombstone_path.c_str(), tombstone_path.length());
         }
-        break;
-
-      case SIGABRT:
-      case SIGBUS:
-      case SIGFPE:
-      case SIGILL:
-      case SIGSEGV:
-#ifdef SIGSTKFLT
-      case SIGSTKFLT:
-#endif
-      case SIGTRAP:
-        ALOGV("stopped -- fatal signal\n");
-        // Send a SIGSTOP to the process to make all of
-        // the non-signaled threads stop moving.  Without
-        // this we get a lot of "ptrace detach failed:
-        // No such process".
-        kill(request.pid, SIGSTOP);
-        // don't dump sibling threads when attaching to GDB because it
-        // makes the process less reliable, apparently...
-        tombstone_path.reset(engrave_tombstone(
-          request.pid, request.tid, signal, request.original_si_code, request.abort_msg_address,
-          !attach_gdb, &detach_failed, &total_sleep_time_usec));
-        break;
-
-      default:
-        ALOGE("process stopped due to unexpected signal %d\n", signal);
-        break;
+      }
     }
-    break;
-  }
 
-  if (request.action == DEBUGGER_ACTION_DUMP_TOMBSTONE) {
-    if (tombstone_path) {
-      write(fd, tombstone_path.get(), strlen(tombstone_path.get()));
-    }
-  }
-
-  if (!tid_unresponsive) {
-    ALOGV("detaching");
     if (attach_gdb) {
-      // stop the process so we can debug
-      kill(request.pid, SIGSTOP);
-    }
-    if (ptrace(PTRACE_DETACH, request.tid, 0, 0)) {
-      ALOGE("ptrace detach from %d failed: %s", request.tid, strerror(errno));
-      detach_failed = true;
-    } else if (attach_gdb) {
-      // if debug.db.uid is set, its value indicates if we should wait
-      // for user action for the crashing process.
-      // in this case, we log a message and turn the debug LED on
-      // waiting for a gdb connection (for instance)
-      wait_for_user_action(request);
+      // Tell the signal process to send SIGSTOP to the target.
+      notify_signal_sender();
     }
   }
 
-  // resume stopped process (so it can crash in peace).
-  kill(request.pid, SIGCONT);
-
-  // If we didn't successfully detach, we're still the parent, and the
-  // actual parent won't receive a death notification via wait(2).  At this point
-  // there's not much we can do about that.
-  if (detach_failed) {
-    ALOGE("debuggerd committing suicide to free the zombie!\n");
-    kill(getpid(), SIGKILL);
+  if (ptrace(PTRACE_DETACH, request.tid, 0, 0) != 0) {
+    ALOGE("debuggerd: ptrace detach from %d failed: %s", request.tid, strerror(errno));
   }
+
+  for (pid_t sibling : siblings) {
+    ptrace(PTRACE_DETACH, sibling, 0, 0);
+  }
+
+  // Wait for gdb, if requested.
+  if (attach_gdb && succeeded) {
+    wait_for_user_action(request);
+
+    // Tell the signal process to send SIGCONT to the target.
+    notify_signal_sender();
+
+    uninit_getevent();
+    waitpid(signal_pid, nullptr, 0);
+  }
+
+  exit(!succeeded);
 }
 
 static int do_server() {
diff --git a/debuggerd/test/selinux/android.h b/debuggerd/test/selinux/android.h
new file mode 100644
index 0000000..abed087
--- /dev/null
+++ b/debuggerd/test/selinux/android.h
@@ -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.
+ */
+
+extern "C" int selinux_android_restorecon(const char*, unsigned int);
diff --git a/debuggerd/tombstone.cpp b/debuggerd/tombstone.cpp
index 5f422e3..dda6677 100644
--- a/debuggerd/tombstone.cpp
+++ b/debuggerd/tombstone.cpp
@@ -328,6 +328,33 @@
   return addr_str;
 }
 
+static void dump_abort_message(Backtrace* backtrace, log_t* log, uintptr_t address) {
+  if (address == 0) {
+    return;
+  }
+
+  address += sizeof(size_t);  // Skip the buffer length.
+
+  char msg[512];
+  memset(msg, 0, sizeof(msg));
+  char* p = &msg[0];
+  while (p < &msg[sizeof(msg)]) {
+    word_t data;
+    size_t len = sizeof(word_t);
+    if (!backtrace->ReadWord(address, &data)) {
+      break;
+    }
+    address += sizeof(word_t);
+
+    while (len > 0 && (*p++ = (data >> (sizeof(word_t) - len) * 8) & 0xff) != 0) {
+      len--;
+    }
+  }
+  msg[sizeof(msg) - 1] = '\0';
+
+  _LOG(log, logtype::HEADER, "Abort message: '%s'\n", msg);
+}
+
 static void dump_all_maps(Backtrace* backtrace, BacktraceMap* map, log_t* log, pid_t tid) {
   bool print_fault_address_marker = false;
   uintptr_t addr = 0;
@@ -416,67 +443,37 @@
   }
 }
 
-// Return true if some thread is not detached cleanly
-static bool dump_sibling_thread_report(
-    log_t* log, pid_t pid, pid_t tid, int* total_sleep_time_usec, BacktraceMap* map) {
-  char task_path[64];
-
-  snprintf(task_path, sizeof(task_path), "/proc/%d/task", pid);
-
-  DIR* d = opendir(task_path);
-  // Bail early if the task directory cannot be opened
-  if (d == NULL) {
-    ALOGE("Cannot open /proc/%d/task\n", pid);
-    return false;
-  }
-
-  bool detach_failed = false;
-  struct dirent* de;
-  while ((de = readdir(d)) != NULL) {
-    // Ignore "." and ".."
-    if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) {
-      continue;
-    }
-
-    // The main thread at fault has been handled individually
-    char* end;
-    pid_t new_tid = strtoul(de->d_name, &end, 10);
-    if (*end || new_tid == tid) {
-      continue;
-    }
-
-    // Skip this thread if cannot ptrace it
-    if (ptrace(PTRACE_ATTACH, new_tid, 0, 0) < 0) {
-      ALOGE("ptrace attach to %d failed: %s\n", new_tid, strerror(errno));
-      continue;
-    }
-
-    if (wait_for_sigstop(new_tid, total_sleep_time_usec, &detach_failed) == -1) {
-      continue;
-    }
-
-    log->current_tid = new_tid;
+static void dump_thread(log_t* log, pid_t pid, pid_t tid, BacktraceMap* map, int signal,
+                        int si_code, uintptr_t abort_msg_address, bool primary_thread) {
+  log->current_tid = tid;
+  if (!primary_thread) {
     _LOG(log, logtype::THREAD, "--- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---\n");
-    dump_thread_info(log, pid, new_tid);
+  }
+  dump_thread_info(log, pid, tid);
 
-    dump_registers(log, new_tid);
-    std::unique_ptr<Backtrace> backtrace(Backtrace::Create(pid, new_tid, map));
-    if (backtrace->Unwind(0)) {
-      dump_backtrace_and_stack(backtrace.get(), log);
-    } else {
-      ALOGE("Unwind of sibling failed: pid = %d, tid = %d", pid, new_tid);
-    }
+  if (signal) {
+    dump_signal_info(log, tid, signal, si_code);
+  }
 
-    log->current_tid = log->crashed_tid;
+  std::unique_ptr<Backtrace> backtrace(Backtrace::Create(pid, tid, map));
+  if (primary_thread) {
+    dump_abort_message(backtrace.get(), log, abort_msg_address);
+  }
+  dump_registers(log, tid);
+  if (backtrace->Unwind(0)) {
+    dump_backtrace_and_stack(backtrace.get(), log);
+  } else {
+    ALOGE("Unwind failed: pid = %d, tid = %d", pid, tid);
+  }
 
-    if (ptrace(PTRACE_DETACH, new_tid, 0, 0) != 0) {
-      ALOGE("ptrace detach from %d failed: %s\n", new_tid, strerror(errno));
-      detach_failed = true;
+  if (primary_thread) {
+    dump_memory_and_code(log, backtrace.get());
+    if (map) {
+      dump_all_maps(backtrace.get(), map, log, tid);
     }
   }
 
-  closedir(d);
-  return detach_failed;
+  log->current_tid = log->crashed_tid;
 }
 
 // Reads the contents of the specified log device, filters out the entries
@@ -605,36 +602,10 @@
   dump_log_file(log, pid, "main", tail);
 }
 
-static void dump_abort_message(Backtrace* backtrace, log_t* log, uintptr_t address) {
-  if (address == 0) {
-    return;
-  }
-
-  address += sizeof(size_t); // Skip the buffer length.
-
-  char msg[512];
-  memset(msg, 0, sizeof(msg));
-  char* p = &msg[0];
-  while (p < &msg[sizeof(msg)]) {
-    word_t data;
-    size_t len = sizeof(word_t);
-    if (!backtrace->ReadWord(address, &data)) {
-      break;
-    }
-    address += sizeof(word_t);
-
-    while (len > 0 && (*p++ = (data >> (sizeof(word_t) - len) * 8) & 0xff) != 0)
-       len--;
-  }
-  msg[sizeof(msg) - 1] = '\0';
-
-  _LOG(log, logtype::HEADER, "Abort message: '%s'\n", msg);
-}
-
 // Dumps all information about the specified pid to the tombstone.
-static bool dump_crash(log_t* log, pid_t pid, pid_t tid, int signal, int si_code,
-                       uintptr_t abort_msg_address, bool dump_sibling_threads,
-                       int* total_sleep_time_usec) {
+static void dump_crash(log_t* log, BacktraceMap* map, pid_t pid, pid_t tid,
+                       const std::set<pid_t>& siblings, int signal, int si_code,
+                       uintptr_t abort_msg_address) {
   // don't copy log messages to tombstone unless this is a dev device
   char value[PROPERTY_VALUE_MAX];
   property_get("ro.debuggable", value, "0");
@@ -653,33 +624,15 @@
   _LOG(log, logtype::HEADER,
        "*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***\n");
   dump_header_info(log);
-  dump_thread_info(log, pid, tid);
-
-  if (signal) {
-    dump_signal_info(log, tid, signal, si_code);
-  }
-
-  std::unique_ptr<BacktraceMap> map(BacktraceMap::Create(pid));
-  std::unique_ptr<Backtrace> backtrace(Backtrace::Create(pid, tid, map.get()));
-  dump_abort_message(backtrace.get(), log, abort_msg_address);
-  dump_registers(log, tid);
-  if (backtrace->Unwind(0)) {
-    dump_backtrace_and_stack(backtrace.get(), log);
-  } else {
-    ALOGE("Unwind failed: pid = %d, tid = %d", pid, tid);
-  }
-  dump_memory_and_code(log, backtrace.get());
-  if (map.get() != nullptr) {
-    dump_all_maps(backtrace.get(), map.get(), log, tid);
-  }
-
+  dump_thread(log, pid, tid, map, signal, si_code, abort_msg_address, true);
   if (want_logs) {
     dump_logs(log, pid, 5);
   }
 
-  bool detach_failed = false;
-  if (dump_sibling_threads) {
-    detach_failed = dump_sibling_thread_report(log, pid, tid, total_sleep_time_usec, map.get());
+  if (!siblings.empty()) {
+    for (pid_t sibling : siblings) {
+      dump_thread(log, pid, sibling, map, 0, 0, 0, false);
+    }
   }
 
   if (want_logs) {
@@ -695,56 +648,60 @@
     TEMP_FAILURE_RETRY( read(log->amfd, &eodMarker, 1) );
   }
 
-  return detach_failed;
+  return;
 }
 
-// find_and_open_tombstone - find an available tombstone slot, if any, of the
+// open_tombstone - find an available tombstone slot, if any, of the
 // form tombstone_XX where XX is 00 to MAX_TOMBSTONES-1, inclusive. If no
 // file is available, we reuse the least-recently-modified file.
-//
-// Returns the path of the tombstone file, allocated using malloc().  Caller must free() it.
-static char* find_and_open_tombstone(int* fd) {
+int open_tombstone(std::string* out_path) {
   // In a single pass, find an available slot and, in case none
   // exist, find and record the least-recently-modified file.
   char path[128];
+  int fd = -1;
   int oldest = -1;
   struct stat oldest_sb;
   for (int i = 0; i < MAX_TOMBSTONES; i++) {
     snprintf(path, sizeof(path), TOMBSTONE_TEMPLATE, i);
 
     struct stat sb;
-    if (!stat(path, &sb)) {
+    if (stat(path, &sb) == 0) {
       if (oldest < 0 || sb.st_mtime < oldest_sb.st_mtime) {
         oldest = i;
         oldest_sb.st_mtime = sb.st_mtime;
       }
       continue;
     }
-    if (errno != ENOENT)
-      continue;
+    if (errno != ENOENT) continue;
 
-    *fd = open(path, O_CREAT | O_EXCL | O_WRONLY | O_NOFOLLOW | O_CLOEXEC, 0600);
-    if (*fd < 0)
-      continue;   // raced ?
+    fd = open(path, O_CREAT | O_EXCL | O_WRONLY | O_NOFOLLOW | O_CLOEXEC, 0600);
+    if (fd < 0) continue;  // raced ?
 
-    fchown(*fd, AID_SYSTEM, AID_SYSTEM);
-    return strdup(path);
+    if (out_path) {
+      *out_path = path;
+    }
+    fchown(fd, AID_SYSTEM, AID_SYSTEM);
+    return fd;
   }
 
   if (oldest < 0) {
-    ALOGE("Failed to find a valid tombstone, default to using tombstone 0.\n");
+    ALOGE("debuggerd: failed to find a valid tombstone, default to using tombstone 0.\n");
     oldest = 0;
   }
 
   // we didn't find an available file, so we clobber the oldest one
   snprintf(path, sizeof(path), TOMBSTONE_TEMPLATE, oldest);
-  *fd = open(path, O_CREAT | O_TRUNC | O_WRONLY | O_NOFOLLOW | O_CLOEXEC, 0600);
-  if (*fd < 0) {
-    ALOGE("failed to open tombstone file '%s': %s\n", path, strerror(errno));
-    return NULL;
+  fd = open(path, O_CREAT | O_TRUNC | O_WRONLY | O_NOFOLLOW | O_CLOEXEC, 0600);
+  if (fd < 0) {
+    ALOGE("debuggerd: failed to open tombstone file '%s': %s\n", path, strerror(errno));
+    return -1;
   }
-  fchown(*fd, AID_SYSTEM, AID_SYSTEM);
-  return strdup(path);
+
+  if (out_path) {
+    *out_path = path;
+  }
+  fchown(fd, AID_SYSTEM, AID_SYSTEM);
+  return fd;
 }
 
 static int activity_manager_connect() {
@@ -777,49 +734,25 @@
   return amfd;
 }
 
-char* engrave_tombstone(pid_t pid, pid_t tid, int signal, int original_si_code,
-                        uintptr_t abort_msg_address, bool dump_sibling_threads,
-                        bool* detach_failed, int* total_sleep_time_usec) {
-
+void engrave_tombstone(int tombstone_fd, BacktraceMap* map, pid_t pid, pid_t tid,
+                       const std::set<pid_t>& siblings, int signal, int original_si_code,
+                       uintptr_t abort_msg_address) {
   log_t log;
   log.current_tid = tid;
   log.crashed_tid = tid;
 
-  if ((mkdir(TOMBSTONE_DIR, 0755) == -1) && (errno != EEXIST)) {
-    ALOGE("failed to create %s: %s\n", TOMBSTONE_DIR, strerror(errno));
+  if (tombstone_fd < 0) {
+    ALOGE("debuggerd: skipping tombstone write, nothing to do.\n");
+    return;
   }
 
-  if (chown(TOMBSTONE_DIR, AID_SYSTEM, AID_SYSTEM) == -1) {
-    ALOGE("failed to change ownership of %s: %s\n", TOMBSTONE_DIR, strerror(errno));
-  }
-
-  int fd = -1;
-  char* path = NULL;
-  if (selinux_android_restorecon(TOMBSTONE_DIR, 0) == 0) {
-    path = find_and_open_tombstone(&fd);
-  } else {
-    ALOGE("Failed to restore security context, not writing tombstone.\n");
-  }
-
-  if (fd < 0) {
-    ALOGE("Skipping tombstone write, nothing to do.\n");
-    *detach_failed = false;
-    return NULL;
-  }
-
-  log.tfd = fd;
+  log.tfd = tombstone_fd;
   // Preserve amfd since it can be modified through the calls below without
   // being closed.
   int amfd = activity_manager_connect();
   log.amfd = amfd;
-  *detach_failed = dump_crash(&log, pid, tid, signal, original_si_code, abort_msg_address,
-                              dump_sibling_threads, total_sleep_time_usec);
+  dump_crash(&log, map, pid, tid, siblings, signal, original_si_code, abort_msg_address);
 
-  _LOG(&log, logtype::BACKTRACE, "\nTombstone written to: %s\n", path);
-
-  // Either of these file descriptors can be -1, any error is ignored.
+  // This file descriptor can be -1, any error is ignored.
   close(amfd);
-  close(fd);
-
-  return path;
 }
diff --git a/debuggerd/tombstone.h b/debuggerd/tombstone.h
index 7e2b2fe..2b8b8be 100644
--- a/debuggerd/tombstone.h
+++ b/debuggerd/tombstone.h
@@ -17,15 +17,23 @@
 #ifndef _DEBUGGERD_TOMBSTONE_H
 #define _DEBUGGERD_TOMBSTONE_H
 
-#include <stddef.h>
 #include <stdbool.h>
+#include <stddef.h>
 #include <sys/types.h>
+#include <set>
+#include <string>
 
-/* Creates a tombstone file and writes the crash dump to it.
- * Returns the path of the tombstone, which must be freed using free(). */
-char* engrave_tombstone(pid_t pid, pid_t tid, int signal, int original_si_code,
-                        uintptr_t abort_msg_address,
-                        bool dump_sibling_threads, bool* detach_failed,
-                        int* total_sleep_time_usec);
+class BacktraceMap;
+
+/* Create and open a tombstone file for writing.
+ * Returns a writable file descriptor, or -1 with errno set appropriately.
+ * If out_path is non-null, *out_path is set to the path of the tombstone file.
+ */
+int open_tombstone(std::string* path);
+
+/* Creates a tombstone file and writes the crash dump to it. */
+void engrave_tombstone(int tombstone_fd, BacktraceMap* map, pid_t pid, pid_t tid,
+                       const std::set<pid_t>& siblings, int signal, int original_si_code,
+                       uintptr_t abort_msg_address);
 
 #endif // _DEBUGGERD_TOMBSTONE_H
diff --git a/debuggerd/utility.cpp b/debuggerd/utility.cpp
index ce214f9..cd252ce 100644
--- a/debuggerd/utility.cpp
+++ b/debuggerd/utility.cpp
@@ -30,8 +30,8 @@
 #include <backtrace/Backtrace.h>
 #include <log/log.h>
 
-const int SLEEP_TIME_USEC = 50000;         // 0.05 seconds
-const int MAX_TOTAL_SLEEP_USEC = 10000000; // 10 seconds
+constexpr int SLEEP_TIME_USEC = 50000;          // 0.05 seconds
+constexpr int MAX_TOTAL_SLEEP_USEC = 10000000;  // 10 seconds
 
 // Whitelist output desired in the logcat output.
 bool is_allowed_in_logcat(enum logtype ltype) {
@@ -78,14 +78,13 @@
   }
 }
 
-int wait_for_sigstop(pid_t tid, int* total_sleep_time_usec, bool* detach_failed) {
-  bool allow_dead_tid = false;
-  for (;;) {
+int wait_for_signal(pid_t tid, int* total_sleep_time_usec) {
+  while (true) {
     int status;
     pid_t n = TEMP_FAILURE_RETRY(waitpid(tid, &status, __WALL | WNOHANG));
     if (n == -1) {
       ALOGE("waitpid failed: tid %d, %s", tid, strerror(errno));
-      break;
+      return -1;
     } else if (n == tid) {
       if (WIFSTOPPED(status)) {
         return WSTOPSIG(status);
@@ -93,29 +92,18 @@
         ALOGE("unexpected waitpid response: n=%d, status=%08x\n", n, status);
         // This is the only circumstance under which we can allow a detach
         // to fail with ESRCH, which indicates the tid has exited.
-        allow_dead_tid = true;
-        break;
+        return -1;
       }
     }
 
     if (*total_sleep_time_usec > MAX_TOTAL_SLEEP_USEC) {
       ALOGE("timed out waiting for stop signal: tid=%d", tid);
-      break;
+      return -1;
     }
 
     usleep(SLEEP_TIME_USEC);
     *total_sleep_time_usec += SLEEP_TIME_USEC;
   }
-
-  if (ptrace(PTRACE_DETACH, tid, 0, 0) != 0) {
-    if (allow_dead_tid && errno == ESRCH) {
-      ALOGE("tid exited before attach completed: tid %d", tid);
-    } else {
-      *detach_failed = true;
-      ALOGE("detach failed: tid %d, %s", tid, strerror(errno));
-    }
-  }
-  return -1;
 }
 
 #define MEMORY_BYTES_TO_DUMP 256
diff --git a/debuggerd/utility.h b/debuggerd/utility.h
index 8bef192..ed08ddc 100644
--- a/debuggerd/utility.h
+++ b/debuggerd/utility.h
@@ -74,7 +74,7 @@
 void _LOG(log_t* log, logtype ltype, const char *fmt, ...)
         __attribute__ ((format(printf, 3, 4)));
 
-int wait_for_sigstop(pid_t, int*, bool*);
+int wait_for_signal(pid_t tid, int* total_sleep_time_usec);
 
 void dump_memory(log_t* log, Backtrace* backtrace, uintptr_t addr, const char* fmt, ...);
 
diff --git a/fastboot/.clang-format b/fastboot/.clang-format
new file mode 100644
index 0000000..bcb8d8a
--- /dev/null
+++ b/fastboot/.clang-format
@@ -0,0 +1,15 @@
+BasedOnStyle: Google
+AllowShortBlocksOnASingleLine: false
+AllowShortFunctionsOnASingleLine: Inline
+
+ColumnLimit: 100
+CommentPragmas: NOLINT:.*
+DerivePointerAlignment: false
+IndentWidth: 4
+ContinuationIndentWidth: 8
+ConstructorInitializerIndentWidth: 8
+AccessModifierOffset: -2
+PointerAlignment: Left
+TabWidth: 4
+UseTab: Never
+PenaltyExcessCharacter: 32
diff --git a/fastboot/Android.mk b/fastboot/Android.mk
index 8cbc79b..fcec5b1 100644
--- a/fastboot/Android.mk
+++ b/fastboot/Android.mk
@@ -24,7 +24,15 @@
   $(LOCAL_PATH)/../../extras/ext4_utils \
   $(LOCAL_PATH)/../../extras/f2fs_utils \
 
-LOCAL_SRC_FILES := protocol.cpp engine.cpp bootimg_utils.cpp fastboot.cpp util.cpp fs.cpp
+LOCAL_SRC_FILES := \
+    bootimg_utils.cpp \
+    engine.cpp \
+    fastboot.cpp \
+    fs.cpp\
+    protocol.cpp \
+    socket.cpp \
+    util.cpp \
+
 LOCAL_MODULE := fastboot
 LOCAL_MODULE_TAGS := debug
 LOCAL_MODULE_HOST_OS := darwin linux windows
@@ -33,15 +41,15 @@
 
 LOCAL_CFLAGS += -DFASTBOOT_REVISION='"$(fastboot_version)"'
 
-LOCAL_SRC_FILES_linux := socket_unix.cpp usb_linux.cpp util_linux.cpp
-LOCAL_STATIC_LIBRARIES_linux := libcutils libselinux
+LOCAL_SRC_FILES_linux := usb_linux.cpp util_linux.cpp
+LOCAL_STATIC_LIBRARIES_linux := libselinux
 
-LOCAL_SRC_FILES_darwin := socket_unix.cpp usb_osx.cpp util_osx.cpp
-LOCAL_STATIC_LIBRARIES_darwin := libcutils libselinux
+LOCAL_SRC_FILES_darwin := usb_osx.cpp util_osx.cpp
+LOCAL_STATIC_LIBRARIES_darwin := libselinux
 LOCAL_LDLIBS_darwin := -lpthread -framework CoreFoundation -framework IOKit -framework Carbon
 LOCAL_CFLAGS_darwin := -Wno-unused-parameter
 
-LOCAL_SRC_FILES_windows := socket_windows.cpp usb_windows.cpp util_windows.cpp
+LOCAL_SRC_FILES_windows := usb_windows.cpp util_windows.cpp
 LOCAL_STATIC_LIBRARIES_windows := AdbWinApi
 LOCAL_REQUIRED_MODULES_windows := AdbWinApi
 LOCAL_LDLIBS_windows := -lws2_32
@@ -56,6 +64,7 @@
     libz \
     libdiagnose_usb \
     libbase \
+    libcutils \
 
 # libf2fs_dlutils_host will dlopen("libf2fs_fmt_host_dyn")
 LOCAL_CFLAGS_linux := -DUSE_F2FS
@@ -97,20 +106,14 @@
 LOCAL_MODULE := fastboot_test
 LOCAL_MODULE_HOST_OS := darwin linux windows
 
-LOCAL_SRC_FILES := socket_test.cpp
-LOCAL_STATIC_LIBRARIES := libbase
+LOCAL_SRC_FILES := socket.cpp socket_test.cpp
+LOCAL_STATIC_LIBRARIES := libbase libcutils
 
 LOCAL_CFLAGS += -Wall -Wextra -Werror -Wunreachable-code
 
-LOCAL_SRC_FILES_linux := socket_unix.cpp
-LOCAL_STATIC_LIBRARIES_linux := libcutils
-
-LOCAL_SRC_FILES_darwin := socket_unix.cpp
 LOCAL_LDLIBS_darwin := -lpthread -framework CoreFoundation -framework IOKit -framework Carbon
 LOCAL_CFLAGS_darwin := -Wno-unused-parameter
-LOCAL_STATIC_LIBRARIES_darwin := libcutils
 
-LOCAL_SRC_FILES_windows := socket_windows.cpp
 LOCAL_LDLIBS_windows := -lws2_32
 
 include $(BUILD_HOST_NATIVE_TEST)
diff --git a/fastboot/engine.cpp b/fastboot/engine.cpp
index ac5a17a..47567c0 100644
--- a/fastboot/engine.cpp
+++ b/fastboot/engine.cpp
@@ -157,17 +157,17 @@
     a->msg = mkmsg("writing '%s'", ptn);
 }
 
-void fb_queue_flash_sparse(const char *ptn, struct sparse_file *s, unsigned sz)
-{
+void fb_queue_flash_sparse(const char* ptn, struct sparse_file* s, unsigned sz, size_t current,
+                           size_t total) {
     Action *a;
 
     a = queue_action(OP_DOWNLOAD_SPARSE, "");
     a->data = s;
     a->size = 0;
-    a->msg = mkmsg("sending sparse '%s' (%d KB)", ptn, sz / 1024);
+    a->msg = mkmsg("sending sparse '%s' %zu/%zu (%d KB)", ptn, current, total, sz / 1024);
 
     a = queue_action(OP_COMMAND, "flash:%s", ptn);
-    a->msg = mkmsg("writing '%s'", ptn);
+    a->msg = mkmsg("writing '%s' %zu/%zu", ptn, current, total);
 }
 
 static int match(const char* str, const char** value, unsigned count) {
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 5b66366..4573da0 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -43,6 +43,8 @@
 #include <sys/types.h>
 #include <unistd.h>
 #include <functional>
+#include <utility>
+#include <vector>
 
 #include <android-base/parseint.h>
 #include <android-base/strings.h>
@@ -282,8 +284,6 @@
             "  getvar <variable>                        Display a bootloader variable.\n"
             "  set_active <suffix>                      Sets the active slot. If slots are\n"
             "                                           not supported, this does nothing.\n"
-            "                                           note: suffixes starting with a '-'\n"
-            "                                           must use set_active -- <suffix>\n"
             "  boot <kernel> [ <ramdisk> [ <second> ] ] Download and boot kernel.\n"
             "  flash:raw boot <kernel> [ <ramdisk> [ <second> ] ]\n"
             "                                           Create bootimage and flash it.\n"
@@ -320,9 +320,10 @@
             "                                           device supports slots. This will be\n"
             "                                           added to all partition names that use\n"
             "                                           slots. 'all' can be given to refer\n"
-            "                                           to all slots. If this is not given,\n"
-            "                                           slotted partitions will default to\n"
-            "                                           the current active slot.\n"
+            "                                           to all slots. 'other' can be given to\n"
+            "                                           refer to a non-current slot. If this\n"
+            "                                           flag is not used, slotted partitions\n"
+            "                                           will default to the current active slot.\n"
             "  -a, --set-active[=<suffix>]              Sets the active slot. If no suffix is\n"
             "                                           provided, this will default to the value\n"
             "                                           given by --slot. If slots are not\n"
@@ -706,13 +707,22 @@
     sparse_file** s;
 
     switch (buf->type) {
-        case FB_BUFFER_SPARSE:
+        case FB_BUFFER_SPARSE: {
+            std::vector<std::pair<sparse_file*, int64_t>> sparse_files;
             s = reinterpret_cast<sparse_file**>(buf->data);
             while (*s) {
                 int64_t sz = sparse_file_len(*s, true, false);
-                fb_queue_flash_sparse(pname, *s++, sz);
+                sparse_files.emplace_back(*s, sz);
+                ++s;
+            }
+
+            for (size_t i = 0; i < sparse_files.size(); ++i) {
+                const auto& pair = sparse_files[i];
+                fb_queue_flash_sparse(pname, pair.first, pair.second, i + 1, sparse_files.size());
             }
             break;
+        }
+
         case FB_BUFFER:
             fb_queue_flash(pname, buf->data, buf->sz);
             break;
@@ -739,12 +749,28 @@
             if (!suffixes.empty()) {
                 return suffixes[0];
             } else {
-                fprintf(stderr, "No known slots.\n");
-                exit(1);
+                die("No known slots.");
             }
         }
     }
+
     std::vector<std::string> suffixes = get_suffixes(transport);
+
+    if (strcmp(slot, "other") == 0) {
+        std::string current_slot;
+        if (!fb_getvar(transport, "current-slot", &current_slot)) {
+            die("Failed to identify current slot.");
+        }
+        if (!suffixes.empty()) {
+            for (size_t i = 0; i < suffixes.size(); i++) {
+                if (current_slot == suffixes[i])
+                    return suffixes[(i+1)%suffixes.size()];
+            }
+        } else {
+            die("No known slots.");
+        }
+    }
+
     for (const std::string &suffix : suffixes) {
         if (suffix == slot)
             return slot;
@@ -1411,7 +1437,6 @@
             std::string slot = verify_slot(transport, argv[1], false);
             fb_set_active(slot.c_str());
             skip(2);
-            wants_reboot = true;
         } else if(!strcmp(*argv, "oem")) {
             argc = do_oem_command(argc, argv);
         } else if(!strcmp(*argv, "flashing")) {
diff --git a/fastboot/fastboot.h b/fastboot/fastboot.h
index acfbc13..1932bab 100644
--- a/fastboot/fastboot.h
+++ b/fastboot/fastboot.h
@@ -51,7 +51,8 @@
 /* engine.c - high level command queue engine */
 bool fb_getvar(Transport* transport, const std::string& key, std::string* value);
 void fb_queue_flash(const char *ptn, void *data, uint32_t sz);
-void fb_queue_flash_sparse(const char *ptn, struct sparse_file *s, uint32_t sz);
+void fb_queue_flash_sparse(const char* ptn, struct sparse_file* s, uint32_t sz, size_t current,
+                           size_t total);
 void fb_queue_erase(const char *ptn);
 void fb_queue_format(const char *ptn, int skip_if_not_supported, int32_t max_chunk_sz);
 void fb_queue_require(const char *prod, const char *var, bool invert,
diff --git a/fastboot/socket.cpp b/fastboot/socket.cpp
new file mode 100644
index 0000000..d41f1fe
--- /dev/null
+++ b/fastboot/socket.cpp
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "socket.h"
+
+#include <android-base/stringprintf.h>
+
+Socket::Socket(cutils_socket_t sock) : sock_(sock) {}
+
+Socket::~Socket() {
+    Close();
+}
+
+int Socket::Close() {
+    int ret = 0;
+
+    if (sock_ != INVALID_SOCKET) {
+        ret = socket_close(sock_);
+        sock_ = INVALID_SOCKET;
+    }
+
+    return ret;
+}
+
+bool Socket::SetReceiveTimeout(int timeout_ms) {
+    if (timeout_ms != receive_timeout_ms_) {
+        if (socket_set_receive_timeout(sock_, timeout_ms) == 0) {
+            receive_timeout_ms_ = timeout_ms;
+            return true;
+        }
+        return false;
+    }
+
+    return true;
+}
+
+ssize_t Socket::ReceiveAll(void* data, size_t length, int timeout_ms) {
+    size_t total = 0;
+
+    while (total < length) {
+        ssize_t bytes = Receive(reinterpret_cast<char*>(data) + total, length - total, timeout_ms);
+
+        if (bytes == -1) {
+            if (total == 0) {
+                return -1;
+            }
+            break;
+        }
+        total += bytes;
+    }
+
+    return total;
+}
+
+// Implements the Socket interface for UDP.
+class UdpSocket : public Socket {
+  public:
+    enum class Type { kClient, kServer };
+
+    UdpSocket(Type type, cutils_socket_t sock);
+
+    ssize_t Send(const void* data, size_t length) override;
+    ssize_t Receive(void* data, size_t length, int timeout_ms) override;
+
+  private:
+    std::unique_ptr<sockaddr_storage> addr_;
+    socklen_t addr_size_ = 0;
+
+    DISALLOW_COPY_AND_ASSIGN(UdpSocket);
+};
+
+UdpSocket::UdpSocket(Type type, cutils_socket_t sock) : Socket(sock) {
+    // Only servers need to remember addresses; clients are connected to a server in NewClient()
+    // so will send to that server without needing to specify the address again.
+    if (type == Type::kServer) {
+        addr_.reset(new sockaddr_storage);
+        addr_size_ = sizeof(*addr_);
+        memset(addr_.get(), 0, addr_size_);
+    }
+}
+
+ssize_t UdpSocket::Send(const void* data, size_t length) {
+    return TEMP_FAILURE_RETRY(sendto(sock_, reinterpret_cast<const char*>(data), length, 0,
+                                     reinterpret_cast<sockaddr*>(addr_.get()), addr_size_));
+}
+
+ssize_t UdpSocket::Receive(void* data, size_t length, int timeout_ms) {
+    if (!SetReceiveTimeout(timeout_ms)) {
+        return -1;
+    }
+
+    socklen_t* addr_size_ptr = nullptr;
+    if (addr_ != nullptr) {
+        // Reset addr_size as it may have been modified by previous recvfrom() calls.
+        addr_size_ = sizeof(*addr_);
+        addr_size_ptr = &addr_size_;
+    }
+
+    return TEMP_FAILURE_RETRY(recvfrom(sock_, reinterpret_cast<char*>(data), length, 0,
+                                       reinterpret_cast<sockaddr*>(addr_.get()), addr_size_ptr));
+}
+
+// Implements the Socket interface for TCP.
+class TcpSocket : public Socket {
+  public:
+    TcpSocket(cutils_socket_t sock) : Socket(sock) {}
+
+    ssize_t Send(const void* data, size_t length) override;
+    ssize_t Receive(void* data, size_t length, int timeout_ms) override;
+
+    std::unique_ptr<Socket> Accept() override;
+
+  private:
+    DISALLOW_COPY_AND_ASSIGN(TcpSocket);
+};
+
+ssize_t TcpSocket::Send(const void* data, size_t length) {
+    size_t total = 0;
+
+    while (total < length) {
+        ssize_t bytes = TEMP_FAILURE_RETRY(
+                send(sock_, reinterpret_cast<const char*>(data) + total, length - total, 0));
+
+        if (bytes == -1) {
+            if (total == 0) {
+                return -1;
+            }
+            break;
+        }
+        total += bytes;
+    }
+
+    return total;
+}
+
+ssize_t TcpSocket::Receive(void* data, size_t length, int timeout_ms) {
+    if (!SetReceiveTimeout(timeout_ms)) {
+        return -1;
+    }
+
+    return TEMP_FAILURE_RETRY(recv(sock_, reinterpret_cast<char*>(data), length, 0));
+}
+
+std::unique_ptr<Socket> TcpSocket::Accept() {
+    cutils_socket_t handler = accept(sock_, nullptr, nullptr);
+    if (handler == INVALID_SOCKET) {
+        return nullptr;
+    }
+    return std::unique_ptr<TcpSocket>(new TcpSocket(handler));
+}
+
+std::unique_ptr<Socket> Socket::NewClient(Protocol protocol, const std::string& host, int port,
+                                          std::string* error) {
+    if (protocol == Protocol::kUdp) {
+        cutils_socket_t sock = socket_network_client(host.c_str(), port, SOCK_DGRAM);
+        if (sock != INVALID_SOCKET) {
+            return std::unique_ptr<UdpSocket>(new UdpSocket(UdpSocket::Type::kClient, sock));
+        }
+    } else {
+        cutils_socket_t sock = socket_network_client(host.c_str(), port, SOCK_STREAM);
+        if (sock != INVALID_SOCKET) {
+            return std::unique_ptr<TcpSocket>(new TcpSocket(sock));
+        }
+    }
+
+    if (error) {
+        *error = android::base::StringPrintf("Failed to connect to %s:%d", host.c_str(), port);
+    }
+    return nullptr;
+}
+
+// This functionality is currently only used by tests so we don't need any error messages.
+std::unique_ptr<Socket> Socket::NewServer(Protocol protocol, int port) {
+    if (protocol == Protocol::kUdp) {
+        cutils_socket_t sock = socket_inaddr_any_server(port, SOCK_DGRAM);
+        if (sock != INVALID_SOCKET) {
+            return std::unique_ptr<UdpSocket>(new UdpSocket(UdpSocket::Type::kServer, sock));
+        }
+    } else {
+        cutils_socket_t sock = socket_inaddr_any_server(port, SOCK_STREAM);
+        if (sock != INVALID_SOCKET) {
+            return std::unique_ptr<TcpSocket>(new TcpSocket(sock));
+        }
+    }
+
+    return nullptr;
+}
diff --git a/fastboot/socket.h b/fastboot/socket.h
index 888b530..3e66c27 100644
--- a/fastboot/socket.h
+++ b/fastboot/socket.h
@@ -26,36 +26,41 @@
  * SUCH DAMAGE.
  */
 
-// This file provides a class interface for cross-platform UDP functionality. The main fastboot
+// This file provides a class interface for cross-platform socket functionality. The main fastboot
 // engine should not be using this interface directly, but instead should use a higher-level
-// interface that enforces the fastboot UDP protocol.
+// interface that enforces the fastboot protocol.
 
 #ifndef SOCKET_H_
 #define SOCKET_H_
 
-#include "android-base/macros.h"
-
 #include <memory>
 #include <string>
 
-// UdpSocket interface to be implemented for each platform.
-class UdpSocket {
+#include <android-base/macros.h>
+#include <cutils/sockets.h>
+
+// Socket interface to be implemented for each platform.
+class Socket {
   public:
+    enum class Protocol { kTcp, kUdp };
+
     // Creates a new client connection. Clients are connected to a specific hostname/port and can
     // only send to that destination.
     // On failure, |error| is filled (if non-null) and nullptr is returned.
-    static std::unique_ptr<UdpSocket> NewUdpClient(const std::string& hostname, int port,
-                                                   std::string* error);
+    static std::unique_ptr<Socket> NewClient(Protocol protocol, const std::string& hostname,
+                                             int port, std::string* error);
 
     // Creates a new server bound to local |port|. This is only meant for testing, during normal
     // fastboot operation the device acts as the server.
-    // The server saves sender addresses in Receive(), and uses the most recent address during
+    // A UDP server saves sender addresses in Receive(), and uses the most recent address during
     // calls to Send().
-    static std::unique_ptr<UdpSocket> NewUdpServer(int port);
+    static std::unique_ptr<Socket> NewServer(Protocol protocol, int port);
 
-    virtual ~UdpSocket() = default;
+    // Destructor closes the socket if it's open.
+    virtual ~Socket();
 
-    // Sends |length| bytes of |data|. Returns the number of bytes actually sent or -1 on error.
+    // Sends |length| bytes of |data|. For TCP sockets this will continue trying to send until all
+    // bytes are transmitted. Returns the number of bytes actually sent or -1 on error.
     virtual ssize_t Send(const void* data, size_t length) = 0;
 
     // Waits up to |timeout_ms| to receive up to |length| bytes of data. |timout_ms| of 0 will
@@ -63,14 +68,29 @@
     // errno will be set to EAGAIN or EWOULDBLOCK.
     virtual ssize_t Receive(void* data, size_t length, int timeout_ms) = 0;
 
+    // Calls Receive() until exactly |length| bytes have been received or an error occurs.
+    virtual ssize_t ReceiveAll(void* data, size_t length, int timeout_ms);
+
     // Closes the socket. Returns 0 on success, -1 on error.
-    virtual int Close() = 0;
+    virtual int Close();
+
+    // Accepts an incoming TCP connection. No effect for UDP sockets. Returns a new Socket
+    // connected to the client on success, nullptr on failure.
+    virtual std::unique_ptr<Socket> Accept() { return nullptr; }
 
   protected:
     // Protected constructor to force factory function use.
-    UdpSocket() = default;
+    Socket(cutils_socket_t sock);
 
-    DISALLOW_COPY_AND_ASSIGN(UdpSocket);
+    // Update the socket receive timeout if necessary.
+    bool SetReceiveTimeout(int timeout_ms);
+
+    cutils_socket_t sock_ = INVALID_SOCKET;
+
+  private:
+    int receive_timeout_ms_ = 0;
+
+    DISALLOW_COPY_AND_ASSIGN(Socket);
 };
 
 #endif  // SOCKET_H_
diff --git a/fastboot/socket_test.cpp b/fastboot/socket_test.cpp
index 6ada964..1fd9d7c 100644
--- a/fastboot/socket_test.cpp
+++ b/fastboot/socket_test.cpp
@@ -14,184 +14,113 @@
  * limitations under the License.
  */
 
-// Tests UDP functionality using loopback connections. Requires that kDefaultPort is available
+// Tests UDP functionality using loopback connections. Requires that kTestPort is available
 // for loopback communication on the host. These tests also assume that no UDP packets are lost,
 // which should be the case for loopback communication, but is not guaranteed.
 
 #include "socket.h"
 
-#include <errno.h>
-#include <time.h>
-
-#include <memory>
-#include <string>
-#include <vector>
-
 #include <gtest/gtest.h>
 
 enum {
     // This port must be available for loopback communication.
-    kDefaultPort = 54321,
+    kTestPort = 54321,
 
     // Don't wait forever in a unit test.
-    kDefaultTimeoutMs = 3000,
+    kTestTimeoutMs = 3000,
 };
 
-static const char kReceiveStringError[] = "Error receiving string";
-
-// Test fixture to provide some helper functions. Makes each test a little simpler since we can
-// just check a bool for socket creation and don't have to pass hostname or port information.
-class SocketTest : public ::testing::Test {
-  protected:
-    bool StartServer(int port = kDefaultPort) {
-        server_ = UdpSocket::NewUdpServer(port);
-        return server_ != nullptr;
+// Creates connected sockets |server| and |client|. Returns true on success.
+bool MakeConnectedSockets(Socket::Protocol protocol, std::unique_ptr<Socket>* server,
+                          std::unique_ptr<Socket>* client, const std::string hostname = "localhost",
+                          int port = kTestPort) {
+    *server = Socket::NewServer(protocol, port);
+    if (*server == nullptr) {
+        ADD_FAILURE() << "Failed to create server.";
+        return false;
     }
 
-    bool StartClient(const std::string hostname = "localhost", int port = kDefaultPort) {
-        client_ = UdpSocket::NewUdpClient(hostname, port, nullptr);
-        return client_ != nullptr;
+    *client = Socket::NewClient(protocol, hostname, port, nullptr);
+    if (*client == nullptr) {
+        ADD_FAILURE() << "Failed to create client.";
+        return false;
     }
 
-    bool StartClient2(const std::string hostname = "localhost", int port = kDefaultPort) {
-        client2_ = UdpSocket::NewUdpClient(hostname, port, nullptr);
-        return client2_ != nullptr;
+    // TCP passes the client off to a new socket.
+    if (protocol == Socket::Protocol::kTcp) {
+        *server = (*server)->Accept();
+        if (*server == nullptr) {
+            ADD_FAILURE() << "Failed to accept client connection.";
+            return false;
+        }
     }
 
-    std::unique_ptr<UdpSocket> server_, client_, client2_;
-};
+    return true;
+}
 
-// Sends a string over a UdpSocket. Returns true if the full string (without terminating char)
+// Sends a string over a Socket. Returns true if the full string (without terminating char)
 // was sent.
-static bool SendString(UdpSocket* udp, const std::string& message) {
-    return udp->Send(message.c_str(), message.length()) == static_cast<ssize_t>(message.length());
+static bool SendString(Socket* sock, const std::string& message) {
+    return sock->Send(message.c_str(), message.length()) == static_cast<ssize_t>(message.length());
 }
 
-// Receives a string from a UdpSocket. Returns the string, or kReceiveStringError on failure.
-static std::string ReceiveString(UdpSocket* udp, size_t receive_size = 128) {
-    std::vector<char> buffer(receive_size);
-
-    ssize_t result = udp->Receive(buffer.data(), buffer.size(), kDefaultTimeoutMs);
-    if (result >= 0) {
-        return std::string(buffer.data(), result);
-    }
-    return kReceiveStringError;
-}
-
-// Calls Receive() on the UdpSocket with the given timeout. Returns true if the call timed out.
-static bool ReceiveTimeout(UdpSocket* udp, int timeout_ms) {
-    char buffer[1];
-
-    errno = 0;
-    return udp->Receive(buffer, 1, timeout_ms) == -1 && (errno == EAGAIN || errno == EWOULDBLOCK);
+// Receives a string from a Socket. Returns true if the full string (without terminating char)
+// was received.
+static bool ReceiveString(Socket* sock, const std::string& message) {
+    std::string received(message.length(), '\0');
+    ssize_t bytes = sock->ReceiveAll(&received[0], received.length(), kTestTimeoutMs);
+    return static_cast<size_t>(bytes) == received.length() && received == message;
 }
 
 // Tests sending packets client -> server, then server -> client.
-TEST_F(SocketTest, SendAndReceive) {
-    ASSERT_TRUE(StartServer());
-    ASSERT_TRUE(StartClient());
+TEST(SocketTest, TestSendAndReceive) {
+    std::unique_ptr<Socket> server, client;
 
-    EXPECT_TRUE(SendString(client_.get(), "foo"));
-    EXPECT_EQ("foo", ReceiveString(server_.get()));
+    for (Socket::Protocol protocol : {Socket::Protocol::kUdp, Socket::Protocol::kTcp}) {
+        ASSERT_TRUE(MakeConnectedSockets(protocol, &server, &client));
 
-    EXPECT_TRUE(SendString(server_.get(), "bar baz"));
-    EXPECT_EQ("bar baz", ReceiveString(client_.get()));
+        EXPECT_TRUE(SendString(client.get(), "foo"));
+        EXPECT_TRUE(ReceiveString(server.get(), "foo"));
+
+        EXPECT_TRUE(SendString(server.get(), "bar baz"));
+        EXPECT_TRUE(ReceiveString(client.get(), "bar baz"));
+    }
 }
 
 // Tests sending and receiving large packets.
-TEST_F(SocketTest, LargePackets) {
-    std::string message(512, '\0');
+TEST(SocketTest, TestLargePackets) {
+    std::string message(1024, '\0');
+    std::unique_ptr<Socket> server, client;
 
-    ASSERT_TRUE(StartServer());
-    ASSERT_TRUE(StartClient());
+    for (Socket::Protocol protocol : {Socket::Protocol::kUdp, Socket::Protocol::kTcp}) {
+        ASSERT_TRUE(MakeConnectedSockets(protocol, &server, &client));
 
-    // Run through the test a few times.
-    for (int i = 0; i < 10; ++i) {
-        // Use a different message each iteration to prevent false positives.
-        for (size_t j = 0; j < message.length(); ++j) {
-            message[j] = static_cast<char>(i + j);
+        // Run through the test a few times.
+        for (int i = 0; i < 10; ++i) {
+            // Use a different message each iteration to prevent false positives.
+            for (size_t j = 0; j < message.length(); ++j) {
+                message[j] = static_cast<char>(i + j);
+            }
+
+            EXPECT_TRUE(SendString(client.get(), message));
+            EXPECT_TRUE(ReceiveString(server.get(), message));
         }
-
-        EXPECT_TRUE(SendString(client_.get(), message));
-        EXPECT_EQ(message, ReceiveString(server_.get(), message.length()));
     }
 }
 
-// Tests IPv4 client/server.
-TEST_F(SocketTest, IPv4) {
-    ASSERT_TRUE(StartServer());
-    ASSERT_TRUE(StartClient("127.0.0.1"));
+// Tests UDP receive overflow when the UDP packet is larger than the receive buffer.
+TEST(SocketTest, TestUdpReceiveOverflow) {
+    std::unique_ptr<Socket> server, client;
+    ASSERT_TRUE(MakeConnectedSockets(Socket::Protocol::kUdp, &server, &client));
 
-    EXPECT_TRUE(SendString(client_.get(), "foo"));
-    EXPECT_EQ("foo", ReceiveString(server_.get()));
+    EXPECT_TRUE(SendString(client.get(), "1234567890"));
 
-    EXPECT_TRUE(SendString(server_.get(), "bar"));
-    EXPECT_EQ("bar", ReceiveString(client_.get()));
-}
-
-// Tests IPv6 client/server.
-TEST_F(SocketTest, IPv6) {
-    ASSERT_TRUE(StartServer());
-    ASSERT_TRUE(StartClient("::1"));
-
-    EXPECT_TRUE(SendString(client_.get(), "foo"));
-    EXPECT_EQ("foo", ReceiveString(server_.get()));
-
-    EXPECT_TRUE(SendString(server_.get(), "bar"));
-    EXPECT_EQ("bar", ReceiveString(client_.get()));
-}
-
-// Tests receive timeout. The timing verification logic must be very coarse to make sure different
-// systems running different loads can all pass these tests.
-TEST_F(SocketTest, ReceiveTimeout) {
-    time_t start_time;
-
-    ASSERT_TRUE(StartServer());
-
-    // Make sure a 20ms timeout completes in 1 second or less.
-    start_time = time(nullptr);
-    EXPECT_TRUE(ReceiveTimeout(server_.get(), 20));
-    EXPECT_LE(difftime(time(nullptr), start_time), 1.0);
-
-    // Make sure a 1250ms timeout takes 1 second or more.
-    start_time = time(nullptr);
-    EXPECT_TRUE(ReceiveTimeout(server_.get(), 1250));
-    EXPECT_LE(1.0, difftime(time(nullptr), start_time));
-}
-
-// Tests receive overflow (the UDP packet is larger than the receive buffer).
-TEST_F(SocketTest, ReceiveOverflow) {
-    ASSERT_TRUE(StartServer());
-    ASSERT_TRUE(StartClient());
-
-    EXPECT_TRUE(SendString(client_.get(), "1234567890"));
-
-    // This behaves differently on different systems; some give us a truncated UDP packet, others
-    // will error out and not return anything at all.
-    std::string rx_string = ReceiveString(server_.get(), 5);
-
-    // If we didn't get an error then the packet should have been truncated.
-    if (rx_string != kReceiveStringError) {
-        EXPECT_EQ("12345", rx_string);
+    // This behaves differently on different systems, either truncating the packet or returning -1.
+    char buffer[5];
+    ssize_t bytes = server->Receive(buffer, 5, kTestTimeoutMs);
+    if (bytes == 5) {
+        EXPECT_EQ(0, memcmp(buffer, "12345", 5));
+    } else {
+        EXPECT_EQ(-1, bytes);
     }
 }
-
-// Tests multiple clients sending to the same server.
-TEST_F(SocketTest, MultipleClients) {
-    ASSERT_TRUE(StartServer());
-    ASSERT_TRUE(StartClient());
-    ASSERT_TRUE(StartClient2());
-
-    EXPECT_TRUE(SendString(client_.get(), "client"));
-    EXPECT_TRUE(SendString(client2_.get(), "client2"));
-
-    // Receive the packets and send a response for each (note that packets may be received
-    // out-of-order).
-    for (int i = 0; i < 2; ++i) {
-        std::string received = ReceiveString(server_.get());
-        EXPECT_TRUE(SendString(server_.get(), received + " response"));
-    }
-
-    EXPECT_EQ("client response", ReceiveString(client_.get()));
-    EXPECT_EQ("client2 response", ReceiveString(client2_.get()));
-}
diff --git a/fastboot/socket_unix.cpp b/fastboot/socket_unix.cpp
deleted file mode 100644
index 462256a..0000000
--- a/fastboot/socket_unix.cpp
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *  * Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *  * Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in
- *    the documentation and/or other materials provided with the
- *    distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#include "socket.h"
-
-#include <errno.h>
-#include <netdb.h>
-
-#include <android-base/stringprintf.h>
-#include <cutils/sockets.h>
-
-class UnixUdpSocket : public UdpSocket {
-  public:
-    enum class Type { kClient, kServer };
-
-    UnixUdpSocket(int fd, Type type);
-    ~UnixUdpSocket() override;
-
-    ssize_t Send(const void* data, size_t length) override;
-    ssize_t Receive(void* data, size_t length, int timeout_ms) override;
-    int Close() override;
-
-  private:
-    int fd_;
-    int receive_timeout_ms_ = 0;
-    std::unique_ptr<sockaddr_storage> addr_;
-    socklen_t addr_size_ = 0;
-
-    DISALLOW_COPY_AND_ASSIGN(UnixUdpSocket);
-};
-
-UnixUdpSocket::UnixUdpSocket(int fd, Type type) : fd_(fd) {
-    // Only servers need to remember addresses; clients are connected to a server in NewUdpClient()
-    // so will send to that server without needing to specify the address again.
-    if (type == Type::kServer) {
-        addr_.reset(new sockaddr_storage);
-        addr_size_ = sizeof(*addr_);
-        memset(addr_.get(), 0, addr_size_);
-    }
-}
-
-UnixUdpSocket::~UnixUdpSocket() {
-    Close();
-}
-
-ssize_t UnixUdpSocket::Send(const void* data, size_t length) {
-    return TEMP_FAILURE_RETRY(
-            sendto(fd_, data, length, 0, reinterpret_cast<sockaddr*>(addr_.get()), addr_size_));
-}
-
-ssize_t UnixUdpSocket::Receive(void* data, size_t length, int timeout_ms) {
-    // Only set socket timeout if it's changed.
-    if (receive_timeout_ms_ != timeout_ms) {
-        timeval tv;
-        tv.tv_sec = timeout_ms / 1000;
-        tv.tv_usec = (timeout_ms % 1000) * 1000;
-        if (setsockopt(fd_, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0) {
-            return -1;
-        }
-        receive_timeout_ms_ = timeout_ms;
-    }
-
-    socklen_t* addr_size_ptr = nullptr;
-    if (addr_ != nullptr) {
-        // Reset addr_size as it may have been modified by previous recvfrom() calls.
-        addr_size_ = sizeof(*addr_);
-        addr_size_ptr = &addr_size_;
-    }
-    return TEMP_FAILURE_RETRY(recvfrom(fd_, data, length, 0,
-                                       reinterpret_cast<sockaddr*>(addr_.get()), addr_size_ptr));
-}
-
-int UnixUdpSocket::Close() {
-    int result = 0;
-    if (fd_ != -1) {
-        result = close(fd_);
-        fd_ = -1;
-    }
-    return result;
-}
-
-std::unique_ptr<UdpSocket> UdpSocket::NewUdpClient(const std::string& host, int port,
-                                                   std::string* error) {
-    int getaddrinfo_error = 0;
-    int fd = socket_network_client_timeout(host.c_str(), port, SOCK_DGRAM, 0, &getaddrinfo_error);
-    if (fd == -1) {
-        if (error) {
-            *error = android::base::StringPrintf(
-                    "Failed to connect to %s:%d: %s", host.c_str(), port,
-                    getaddrinfo_error ? gai_strerror(getaddrinfo_error) : strerror(errno));
-        }
-        return nullptr;
-    }
-
-    return std::unique_ptr<UdpSocket>(new UnixUdpSocket(fd, UnixUdpSocket::Type::kClient));
-}
-
-std::unique_ptr<UdpSocket> UdpSocket::NewUdpServer(int port) {
-    int fd = socket_inaddr_any_server(port, SOCK_DGRAM);
-    if (fd == -1) {
-        // This is just used in testing, no need for an error message.
-        return nullptr;
-    }
-
-    return std::unique_ptr<UdpSocket>(new UnixUdpSocket(fd, UnixUdpSocket::Type::kServer));
-}
diff --git a/fastboot/socket_windows.cpp b/fastboot/socket_windows.cpp
deleted file mode 100644
index 4ad379f..0000000
--- a/fastboot/socket_windows.cpp
+++ /dev/null
@@ -1,246 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *  * Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *  * Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in
- *    the documentation and/or other materials provided with the
- *    distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#include "socket.h"
-
-#include <winsock2.h>
-#include <ws2tcpip.h>
-
-#include <memory>
-
-#include <android-base/stringprintf.h>
-
-// Windows UDP socket functionality.
-class WindowsUdpSocket : public UdpSocket {
-  public:
-    enum class Type { kClient, kServer };
-
-    WindowsUdpSocket(SOCKET sock, Type type);
-    ~WindowsUdpSocket() override;
-
-    ssize_t Send(const void* data, size_t len) override;
-    ssize_t Receive(void* data, size_t len, int timeout_ms) override;
-    int Close() override;
-
-  private:
-    SOCKET sock_;
-    int receive_timeout_ms_ = 0;
-    std::unique_ptr<sockaddr_storage> addr_;
-    int addr_size_ = 0;
-
-    DISALLOW_COPY_AND_ASSIGN(WindowsUdpSocket);
-};
-
-WindowsUdpSocket::WindowsUdpSocket(SOCKET sock, Type type) : sock_(sock) {
-    // Only servers need to remember addresses; clients are connected to a server in NewUdpClient()
-    // so will send to that server without needing to specify the address again.
-    if (type == Type::kServer) {
-        addr_.reset(new sockaddr_storage);
-        addr_size_ = sizeof(*addr_);
-        memset(addr_.get(), 0, addr_size_);
-    }
-}
-
-WindowsUdpSocket::~WindowsUdpSocket() {
-    Close();
-}
-
-ssize_t WindowsUdpSocket::Send(const void* data, size_t len) {
-    return sendto(sock_, reinterpret_cast<const char*>(data), len, 0,
-                  reinterpret_cast<sockaddr*>(addr_.get()), addr_size_);
-}
-
-ssize_t WindowsUdpSocket::Receive(void* data, size_t len, int timeout_ms) {
-    // Only set socket timeout if it's changed.
-    if (receive_timeout_ms_ != timeout_ms) {
-        if (setsockopt(sock_, SOL_SOCKET, SO_RCVTIMEO, reinterpret_cast<const char*>(&timeout_ms),
-                       sizeof(timeout_ms)) < 0) {
-            return -1;
-        }
-        receive_timeout_ms_ = timeout_ms;
-    }
-
-    int* addr_size_ptr = nullptr;
-    if (addr_ != nullptr) {
-        // Reset addr_size as it may have been modified by previous recvfrom() calls.
-        addr_size_ = sizeof(*addr_);
-        addr_size_ptr = &addr_size_;
-    }
-    int result = recvfrom(sock_, reinterpret_cast<char*>(data), len, 0,
-                          reinterpret_cast<sockaddr*>(addr_.get()), addr_size_ptr);
-    if (result < 0 && WSAGetLastError() == WSAETIMEDOUT) {
-        errno = EAGAIN;
-    }
-    return result;
-}
-
-int WindowsUdpSocket::Close() {
-    int result = 0;
-    if (sock_ != INVALID_SOCKET) {
-        result = closesocket(sock_);
-        sock_ = INVALID_SOCKET;
-    }
-    return result;
-}
-
-static int GetProtocol(int sock_type) {
-    switch (sock_type) {
-        case SOCK_DGRAM:
-            return IPPROTO_UDP;
-        case SOCK_STREAM:
-            return IPPROTO_TCP;
-        default:
-            // 0 lets the system decide which protocol to use.
-            return 0;
-    }
-}
-
-// Windows implementation of this libcutils function. This function does not make any calls to
-// WSAStartup() or WSACleanup() so that must be handled by the caller.
-// TODO(dpursell): share this code with adb.
-static SOCKET socket_network_client(const std::string& host, int port, int type) {
-    // First resolve the host and port parameters into a usable network address.
-    addrinfo hints;
-    memset(&hints, 0, sizeof(hints));
-    hints.ai_socktype = type;
-    hints.ai_protocol = GetProtocol(type);
-
-    addrinfo* address = nullptr;
-    getaddrinfo(host.c_str(), android::base::StringPrintf("%d", port).c_str(), &hints, &address);
-    if (address == nullptr) {
-        return INVALID_SOCKET;
-    }
-
-    // Now create and connect the socket.
-    SOCKET sock = socket(address->ai_family, address->ai_socktype, address->ai_protocol);
-    if (sock == INVALID_SOCKET) {
-        freeaddrinfo(address);
-        return INVALID_SOCKET;
-    }
-
-    if (connect(sock, address->ai_addr, address->ai_addrlen) == SOCKET_ERROR) {
-        closesocket(sock);
-        freeaddrinfo(address);
-        return INVALID_SOCKET;
-    }
-
-    freeaddrinfo(address);
-    return sock;
-}
-
-// Windows implementation of this libcutils function. This implementation creates a dual-stack
-// server socket that can accept incoming IPv4 or IPv6 packets. This function does not make any
-// calls to WSAStartup() or WSACleanup() so that must be handled by the caller.
-// TODO(dpursell): share this code with adb.
-static SOCKET socket_inaddr_any_server(int port, int type) {
-    SOCKET sock = socket(AF_INET6, type, GetProtocol(type));
-    if (sock == INVALID_SOCKET) {
-        return INVALID_SOCKET;
-    }
-
-    // Enforce exclusive addresses (1), and enable dual-stack so both IPv4 and IPv6 work (2).
-    // (1) https://msdn.microsoft.com/en-us/library/windows/desktop/ms740621(v=vs.85).aspx.
-    // (2) https://msdn.microsoft.com/en-us/library/windows/desktop/bb513665(v=vs.85).aspx.
-    int exclusive = 1;
-    DWORD v6_only = 0;
-    if (setsockopt(sock, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, reinterpret_cast<const char*>(&exclusive),
-                   sizeof(exclusive)) == SOCKET_ERROR ||
-        setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast<const char*>(&v6_only),
-                   sizeof(v6_only)) == SOCKET_ERROR) {
-        closesocket(sock);
-        return INVALID_SOCKET;
-    }
-
-    // Bind the socket to our local port.
-    sockaddr_in6 addr;
-    memset(&addr, 0, sizeof(addr));
-    addr.sin6_family = AF_INET6;
-    addr.sin6_port = htons(port);
-    addr.sin6_addr = in6addr_any;
-    if (bind(sock, reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) == SOCKET_ERROR) {
-        closesocket(sock);
-        return INVALID_SOCKET;
-    }
-
-    return sock;
-}
-
-// Documentation at https://msdn.microsoft.com/en-us/library/windows/desktop/ms741549(v=vs.85).aspx
-// claims WSACleanup() should be called before program exit, but general consensus seems to be that
-// it hasn't actually been necessary for a long time, possibly since Windows 3.1.
-//
-// Both adb (1) and Chrome (2) purposefully avoid WSACleanup(), and since no adverse affects have
-// been found we may as well do the same here to keep this code simpler.
-// (1) https://android.googlesource.com/platform/system/core.git/+/master/adb/sysdeps_win32.cpp#816
-// (2) https://code.google.com/p/chromium/codesearch#chromium/src/net/base/winsock_init.cc&l=35
-static bool InitWinsock() {
-    static bool init_success = false;
-
-    if (!init_success) {
-        WSADATA wsaData;
-        init_success = (WSAStartup(MAKEWORD(2, 2), &wsaData) == 0);
-    }
-
-    return init_success;
-}
-
-std::unique_ptr<UdpSocket> UdpSocket::NewUdpClient(const std::string& host, int port,
-                                                   std::string* error) {
-    if (!InitWinsock()) {
-        if (error) {
-            *error = android::base::StringPrintf("Failed to initialize Winsock (error %d)",
-                                                 WSAGetLastError());
-        }
-        return nullptr;
-    }
-
-    SOCKET sock = socket_network_client(host, port, SOCK_DGRAM);
-    if (sock == INVALID_SOCKET) {
-        if (error) {
-            *error = android::base::StringPrintf("Failed to connect to %s:%d (error %d)",
-                                                 host.c_str(), port, WSAGetLastError());
-        }
-        return nullptr;
-    }
-
-    return std::unique_ptr<UdpSocket>(new WindowsUdpSocket(sock, WindowsUdpSocket::Type::kClient));
-}
-
-// This functionality is currently only used by tests so we don't need any error messages.
-std::unique_ptr<UdpSocket> UdpSocket::NewUdpServer(int port) {
-    if (!InitWinsock()) {
-        return nullptr;
-    }
-
-    SOCKET sock = socket_inaddr_any_server(port, SOCK_DGRAM);
-    if (sock == INVALID_SOCKET) {
-        return nullptr;
-    }
-
-    return std::unique_ptr<UdpSocket>(new WindowsUdpSocket(sock, WindowsUdpSocket::Type::kServer));
-}
diff --git a/healthd/BatteryMonitor.cpp b/healthd/BatteryMonitor.cpp
index 0085a07..cdfe9c5 100644
--- a/healthd/BatteryMonitor.cpp
+++ b/healthd/BatteryMonitor.cpp
@@ -30,7 +30,6 @@
 #include <batteryservice/BatteryService.h>
 #include <cutils/klog.h>
 #include <cutils/properties.h>
-#include <log/log_read.h>
 #include <utils/Errors.h>
 #include <utils/String8.h>
 #include <utils/Vector.h>
@@ -313,25 +312,6 @@
                  props.chargerUsbOnline ? "u" : "",
                  props.chargerWirelessOnline ? "w" : "");
 
-        log_time realtime(CLOCK_REALTIME);
-        time_t t = realtime.tv_sec;
-        struct tm *tmp = gmtime(&t);
-        if (tmp) {
-            static const char fmt[] = " %Y-%m-%d %H:%M:%S.XXXXXXXXX UTC";
-            len = strlen(dmesgline);
-            if ((len < (sizeof(dmesgline) - sizeof(fmt) - 8)) // margin
-                    && strftime(dmesgline + len, sizeof(dmesgline) - len,
-                                fmt, tmp)) {
-                char *usec = strchr(dmesgline + len, 'X');
-                if (usec) {
-                    len = usec - dmesgline;
-                    snprintf(dmesgline + len, sizeof(dmesgline) - len,
-                             "%09u", realtime.tv_nsec);
-                    usec[9] = ' ';
-                }
-            }
-        }
-
         KLOG_WARNING(LOG_TAG, "%s\n", dmesgline);
     }
 
diff --git a/healthd/BatteryPropertiesRegistrar.cpp b/healthd/BatteryPropertiesRegistrar.cpp
index 09667a1..d3a89d7 100644
--- a/healthd/BatteryPropertiesRegistrar.cpp
+++ b/healthd/BatteryPropertiesRegistrar.cpp
@@ -30,8 +30,9 @@
 
 namespace android {
 
-void BatteryPropertiesRegistrar::publish() {
-    defaultServiceManager()->addService(String16("batteryproperties"), this);
+void BatteryPropertiesRegistrar::publish(
+    const sp<BatteryPropertiesRegistrar>& service) {
+    defaultServiceManager()->addService(String16("batteryproperties"), service);
 }
 
 void BatteryPropertiesRegistrar::notifyListeners(struct BatteryProperties props) {
diff --git a/healthd/BatteryPropertiesRegistrar.h b/healthd/BatteryPropertiesRegistrar.h
index 8853874..d17e4a3 100644
--- a/healthd/BatteryPropertiesRegistrar.h
+++ b/healthd/BatteryPropertiesRegistrar.h
@@ -30,7 +30,7 @@
 class BatteryPropertiesRegistrar : public BnBatteryPropertiesRegistrar,
                                    public IBinder::DeathRecipient {
 public:
-    void publish();
+    void publish(const sp<BatteryPropertiesRegistrar>& service);
     void notifyListeners(struct BatteryProperties props);
 
 private:
diff --git a/healthd/healthd_mode_android.cpp b/healthd/healthd_mode_android.cpp
index fd153a2..0a64099 100644
--- a/healthd/healthd_mode_android.cpp
+++ b/healthd/healthd_mode_android.cpp
@@ -58,5 +58,5 @@
     }
 
     gBatteryPropertiesRegistrar = new BatteryPropertiesRegistrar();
-    gBatteryPropertiesRegistrar->publish();
+    gBatteryPropertiesRegistrar->publish(gBatteryPropertiesRegistrar);
 }
diff --git a/healthd/healthd_mode_charger.cpp b/healthd/healthd_mode_charger.cpp
index 6800ad2..46bad4e 100644
--- a/healthd/healthd_mode_charger.cpp
+++ b/healthd/healthd_mode_charger.cpp
@@ -699,7 +699,10 @@
 
     GRSurface** scale_frames;
     int scale_count;
-    ret = res_create_multi_display_surface("charger/battery_scale", &scale_count, &scale_frames);
+    int scale_fps;  // Not in use (charger/battery_scale doesn't have FPS text
+                    // chunk). We are using hard-coded frame.disp_time instead.
+    ret = res_create_multi_display_surface("charger/battery_scale", &scale_count, &scale_fps,
+                                           &scale_frames);
     if (ret < 0) {
         LOGE("Cannot load battery_scale image\n");
         charger->batt_anim->num_frames = 0;
diff --git a/include/binderwrapper/binder_wrapper.h b/include/binderwrapper/binder_wrapper.h
index 921c4ed..ccda825 100644
--- a/include/binderwrapper/binder_wrapper.h
+++ b/include/binderwrapper/binder_wrapper.h
@@ -30,6 +30,7 @@
 class IBinder;
 
 // Wraps libbinder to make it testable.
+// NOTE: Static methods of this class are not thread-safe.
 class BinderWrapper {
  public:
   virtual ~BinderWrapper() {}
@@ -50,6 +51,10 @@
   // InitForTesting().
   static BinderWrapper* Get();
 
+  // Returns the singleton instance if it was previously created by Create() or
+  // set by InitForTesting(), or creates a new one by calling Create().
+  static BinderWrapper* GetOrCreateInstance();
+
   // Gets the binder for communicating with the service identified by
   // |service_name|, returning null immediately if it doesn't exist.
   virtual sp<IBinder> GetService(const std::string& service_name) = 0;
diff --git a/include/cutils/sockets.h b/include/cutils/sockets.h
index 2d3c743..e25c555 100644
--- a/include/cutils/sockets.h
+++ b/include/cutils/sockets.h
@@ -24,10 +24,20 @@
 #include <stdbool.h>
 
 #if defined(_WIN32)
+
 #include <winsock2.h>
+#include <ws2tcpip.h>
+
 typedef int  socklen_t;
+typedef SOCKET cutils_socket_t;
+
 #else
+
 #include <sys/socket.h>
+
+typedef int cutils_socket_t;
+#define INVALID_SOCKET (-1)
+
 #endif
 
 #define ANDROID_SOCKET_ENV_PREFIX	"ANDROID_SOCKET_"
@@ -45,7 +55,7 @@
  * This is inline and not in libcutils proper because we want to use this in
  * third-party daemons with minimal modification.
  */
-static inline int android_get_control_socket(const char *name)
+static inline int android_get_control_socket(const char* name)
 {
 	char key[64];
 	snprintf(key, sizeof(key), ANDROID_SOCKET_ENV_PREFIX "%s", name);
@@ -74,17 +84,52 @@
 // Normal filesystem namespace
 #define ANDROID_SOCKET_NAMESPACE_FILESYSTEM 2
 
-extern int socket_loopback_client(int port, int type);
-extern int socket_network_client(const char *host, int port, int type);
-extern int socket_network_client_timeout(const char *host, int port, int type,
-                                         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);
-extern int socket_local_client_connect(int fd, 
-        const char *name, int namespaceId, int type);
-extern int socket_local_client(const char *name, int namespaceId, int type);
-extern int socket_inaddr_any_server(int port, int type);
+/*
+ * Functions to create sockets for some common usages.
+ *
+ * All these functions are implemented for Unix, but only a few are implemented
+ * for Windows. Those which are can be identified by the cutils_socket_t
+ * return type. The idea is to be able to use this return value with the
+ * standard Unix socket functions on any platform.
+ *
+ * On Unix the returned cutils_socket_t is a standard int file descriptor and
+ * can always be used as normal with all file descriptor functions.
+ *
+ * On Windows utils_socket_t is an unsigned int pointer, and is only valid
+ * with functions that specifically take a socket, e.g. send(), sendto(),
+ * recv(), and recvfrom(). General file descriptor functions such as read(),
+ * write(), and close() will not work with utils_socket_t and will require
+ * special handling.
+ *
+ * These functions return INVALID_SOCKET (-1) on failure for all platforms.
+ */
+int socket_loopback_client(int port, int type);
+cutils_socket_t 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);
+int socket_loopback_server(int port, int type);
+int socket_local_server(const char* name, int namespaceId, int type);
+int socket_local_server_bind(int s, const char* name, int namespaceId);
+int socket_local_client_connect(int fd, const char *name, int namespaceId,
+                                int type);
+int socket_local_client(const char* name, int namespaceId, int type);
+cutils_socket_t socket_inaddr_any_server(int port, int type);
+
+/*
+ * Closes a cutils_socket_t. Windows doesn't allow calling close() on a socket
+ * so this is a cross-platform way to close a cutils_socket_t.
+ *
+ * Returns 0 on success.
+ */
+int socket_close(cutils_socket_t sock);
+
+/*
+ * Sets socket receive timeout using SO_RCVTIMEO. Setting |timeout_ms| to 0
+ * disables receive timeouts.
+ *
+ * Return 0 on success.
+ */
+int socket_set_receive_timeout(cutils_socket_t sock, int timeout_ms);
 
 /*
  * socket_peer_is_trusted - Takes a socket which is presumed to be a
@@ -101,4 +146,4 @@
 }
 #endif
 
-#endif /* __CUTILS_SOCKETS_H */ 
+#endif /* __CUTILS_SOCKETS_H */
diff --git a/include/log/log.h b/include/log/log.h
index 3d9240d..1bd9165 100644
--- a/include/log/log.h
+++ b/include/log/log.h
@@ -585,14 +585,6 @@
     (__android_log_is_loggable(prio, tag, ANDROID_LOG_VERBOSE) != 0)
 #endif
 
-// TODO: remove these prototypes and their users
-#define android_writevLog(vec,num) do{}while(0)
-#define android_write1Log(str,len) do{}while (0)
-#define android_setMinPriority(tag, prio) do{}while(0)
-//#define android_logToCallback(func) do{}while(0)
-#define android_logToFile(tag, file) (0)
-#define android_logToFd(tag, fd) (0)
-
 typedef enum log_id {
     LOG_ID_MIN = 0,
 
diff --git a/include/log/logd.h b/include/log/logd.h
index b7aedaf..b271602 100644
--- a/include/log/logd.h
+++ b/include/log/logd.h
@@ -45,6 +45,7 @@
 int __android_log_bswrite(int32_t tag, const char *payload);
 
 int __android_log_security_bwrite(int32_t tag, const void *payload, size_t len);
+int __android_log_security_bswrite(int32_t tag, const char *payload);
 
 #ifdef __cplusplus
 }
diff --git a/include/nativeloader/native_loader.h b/include/nativeloader/native_loader.h
index e7c69d6..da07253 100644
--- a/include/nativeloader/native_loader.h
+++ b/include/nativeloader/native_loader.h
@@ -24,7 +24,8 @@
 
 __attribute__((visibility("default")))
 void* OpenNativeLibrary(JNIEnv* env, int32_t target_sdk_version, const char* path,
-                        jobject class_loader, jstring library_path, jstring permitted_path);
+                        jobject class_loader, bool is_shared, jstring library_path,
+                        jstring permitted_path);
 
 };  // namespace android
 
diff --git a/include/private/android_filesystem_config.h b/include/private/android_filesystem_config.h
index e2133e9..85d6c19 100644
--- a/include/private/android_filesystem_config.h
+++ b/include/private/android_filesystem_config.h
@@ -81,6 +81,13 @@
 #define AID_SHARED_RELRO  1037  /* creator of shared GNU RELRO files */
 #define AID_DBUS          1038  /* dbus-daemon IPC broker process */
 #define AID_TLSDATE       1039  /* tlsdate unprivileged user */
+#define AID_MEDIA_EX      1040  /* mediaextractor process */
+#define AID_AUDIOSERVER   1041  /* audioserver process */
+#define AID_METRICS_COLL  1042  /* metrics_collector process */
+#define AID_METRICSD      1043  /* metricsd process */
+#define AID_WEBSERV       1044  /* webservd process */
+#define AID_DEBUGGERD     1045  /* debuggerd unprivileged user */
+#define AID_MEDIA_CODEC   1046  /* mediacodec process */
 
 #define AID_SHELL         2000  /* adb and debug shell user */
 #define AID_CACHE         2001  /* cache access */
@@ -102,6 +109,7 @@
 #define AID_NET_BW_ACCT   3007  /* change bandwidth statistics accounting */
 #define AID_NET_BT_STACK  3008  /* bluetooth: access config files */
 #define AID_READPROC      3009  /* Allow /proc read access */
+#define AID_WAKELOCK      3010  /* Allow system wakelock read/write access */
 
 /* The range 5000-5999 is also reserved for OEM, and must never be used here. */
 #define AID_OEM_RESERVED_2_START 5000
@@ -179,6 +187,13 @@
     { "shared_relro",  AID_SHARED_RELRO, },
     { "dbus",          AID_DBUS, },
     { "tlsdate",       AID_TLSDATE, },
+    { "mediaex",       AID_MEDIA_EX, },
+    { "audioserver",   AID_AUDIOSERVER, },
+    { "metrics_coll",  AID_METRICS_COLL },
+    { "metricsd",      AID_METRICSD },
+    { "webserv",       AID_WEBSERV },
+    { "debuggerd",     AID_DEBUGGERD, },
+    { "mediacodec",    AID_MEDIA_CODEC, },
 
     { "shell",         AID_SHELL, },
     { "cache",         AID_CACHE, },
@@ -193,6 +208,7 @@
     { "net_bw_acct",   AID_NET_BW_ACCT, },
     { "net_bt_stack",  AID_NET_BT_STACK, },
     { "readproc",      AID_READPROC, },
+    { "wakelock",      AID_WAKELOCK, },
 
     { "everybody",     AID_EVERYBODY, },
     { "misc",          AID_MISC, },
diff --git a/include/ziparchive/zip_archive_stream_entry.h b/include/ziparchive/zip_archive_stream_entry.h
new file mode 100644
index 0000000..a40b799
--- /dev/null
+++ b/include/ziparchive/zip_archive_stream_entry.h
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+
+// Read-only stream access to Zip archives entries.
+#ifndef LIBZIPARCHIVE_ZIPARCHIVESTREAMENTRY_H_
+#define LIBZIPARCHIVE_ZIPARCHIVESTREAMENTRY_H_
+
+#include <vector>
+
+#include <ziparchive/zip_archive.h>
+
+class ZipArchiveStreamEntry {
+ public:
+  virtual ~ZipArchiveStreamEntry() {}
+
+  virtual const std::vector<uint8_t>* Read() = 0;
+
+  virtual bool Verify() = 0;
+
+  static ZipArchiveStreamEntry* Create(ZipArchiveHandle handle, const ZipEntry& entry);
+  static ZipArchiveStreamEntry* CreateRaw(ZipArchiveHandle handle, const ZipEntry& entry);
+
+ protected:
+  ZipArchiveStreamEntry(ZipArchiveHandle handle) : handle_(handle) {}
+
+  virtual bool Init(const ZipEntry& entry);
+
+  ZipArchiveHandle handle_;
+
+  uint32_t crc32_;
+};
+
+#endif  // LIBZIPARCHIVE_ZIPARCHIVESTREAMENTRY_H_
diff --git a/include/ziparchive/zip_writer.h b/include/ziparchive/zip_writer.h
index 0efade8..0b6ede4 100644
--- a/include/ziparchive/zip_writer.h
+++ b/include/ziparchive/zip_writer.h
@@ -87,11 +87,27 @@
   int32_t StartEntry(const char* path, size_t flags);
 
   /**
+   * Starts a new zip entry with the given path and flags, where the
+   * entry will be aligned to the given alignment.
+   * Flags can only be ZipWriter::kCompress. Using the flag ZipWriter::kAlign32
+   * will result in an error.
+   * Subsequent calls to WriteBytes(const void*, size_t) will add data to this entry.
+   * Returns 0 on success, and an error value < 0 on failure.
+   */
+  int32_t StartAlignedEntry(const char* path, size_t flags, uint32_t alignment);
+
+  /**
    * Same as StartEntry(const char*, size_t), but sets a last modified time for the entry.
    */
   int32_t StartEntryWithTime(const char* path, size_t flags, time_t time);
 
   /**
+   * Same as StartAlignedEntry(const char*, size_t), but sets a last modified time for the entry.
+   */
+  int32_t StartAlignedEntryWithTime(const char* path, size_t flags, time_t time,
+                                    uint32_t alignment);
+
+  /**
    * Writes bytes to the zip file for the previously started zip entry.
    * Returns 0 on success, and an error value < 0 on failure.
    */
diff --git a/init/Android.mk b/init/Android.mk
index d6cb4e5..66ce8a8 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -52,7 +52,7 @@
     service.cpp \
     util.cpp \
 
-LOCAL_STATIC_LIBRARIES := libbase
+LOCAL_STATIC_LIBRARIES := libbase libselinux
 LOCAL_MODULE := libinit
 LOCAL_SANITIZE := integer
 LOCAL_CLANG := true
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 10f9d81..35f1a9e 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -39,6 +39,7 @@
 #include <selinux/label.h>
 
 #include <fs_mgr.h>
+#include <android-base/parseint.h>
 #include <android-base/stringprintf.h>
 #include <cutils/partition_utils.h>
 #include <cutils/android_reboot.h>
@@ -53,6 +54,7 @@
 #include "log.h"
 #include "property_service.h"
 #include "service.h"
+#include "signal_handler.h"
 #include "util.h"
 
 #define chmod DO_NOT_USE_CHMOD_USE_FCHMODAT_SYMLINK_NOFOLLOW
@@ -62,6 +64,8 @@
 // System call provided by bionic but not in any header file.
 extern "C" int init_module(void *, unsigned long, const char *);
 
+static const int kTerminateServiceDelayMicroSeconds = 50000;
+
 static int insmod(const char *filename, const char *options) {
     std::string module;
     if (!read_file(filename, &module)) {
@@ -414,20 +418,32 @@
     while (1) { pause(); }  // never reached
 }
 
-static void import_late() {
-    static const std::vector<std::string> init_directories = {
-        "/system/etc/init",
-        "/vendor/etc/init",
-        "/odm/etc/init"
-    };
-
+/* Imports .rc files from the specified paths. Default ones are applied if none is given.
+ *
+ * start_index: index of the first path in the args list
+ */
+static void import_late(const std::vector<std::string>& args, size_t start_index) {
     Parser& parser = Parser::GetInstance();
-    for (const auto& dir : init_directories) {
-        parser.ParseConfig(dir.c_str());
+    if (args.size() <= start_index) {
+        // Use the default set if no path is given
+        static const std::vector<std::string> init_directories = {
+            "/system/etc/init",
+            "/vendor/etc/init",
+            "/odm/etc/init"
+        };
+
+        for (const auto& dir : init_directories) {
+            parser.ParseConfig(dir);
+        }
+    } else {
+        for (size_t i = start_index; i < args.size(); ++i) {
+            parser.ParseConfig(args[i]);
+        }
     }
 }
 
-/*
+/* mount_all <fstab> [ <path> ]*
+ *
  * This function might request a reboot, in which case it will
  * not return.
  */
@@ -474,7 +490,8 @@
         return -1;
     }
 
-    import_late();
+    /* Paths of .rc files are specified at the 2nd argument and beyond */
+    import_late(args, 2);
 
     if (ret == FS_MGR_MNTALL_DEV_NEEDS_ENCRYPTION) {
         property_set("vold.decrypt", "trigger_encryption");
@@ -608,6 +625,42 @@
         return -EINVAL;
     }
 
+    std::string timeout = property_get("ro.build.shutdown_timeout");
+    unsigned int delay = 0;
+
+    if (android::base::ParseUint(timeout.c_str(), &delay) && delay > 0) {
+        Timer t;
+        // Ask all services to terminate.
+        ServiceManager::GetInstance().ForEachService(
+            [] (Service* s) { s->Terminate(); });
+
+        while (t.duration() < delay) {
+            ServiceManager::GetInstance().ReapAnyOutstandingChildren();
+
+            int service_count = 0;
+            ServiceManager::GetInstance().ForEachService(
+                [&service_count] (Service* s) {
+                    // Count the number of services running.
+                    // Exclude the console as it will ignore the SIGTERM signal
+                    // and not exit.
+                    // Note: SVC_CONSOLE actually means "requires console" but
+                    // it is only used by the shell.
+                    if (s->pid() != 0 && (s->flags() & SVC_CONSOLE) == 0) {
+                        service_count++;
+                    }
+                });
+
+            if (service_count == 0) {
+                // All terminable services terminated. We can exit early.
+                break;
+            }
+
+            // Wait a bit before recounting the number or running services.
+            usleep(kTerminateServiceDelayMicroSeconds);
+        }
+        NOTICE("Terminating running services took %.02f seconds", t.duration());
+    }
+
     return android_reboot_with_callback(cmd, 0, reboot_target,
                                         callback_on_ro_remount);
 }
@@ -860,7 +913,7 @@
         {"load_system_props",       {0,     0,    do_load_system_props}},
         {"loglevel",                {1,     1,    do_loglevel}},
         {"mkdir",                   {1,     4,    do_mkdir}},
-        {"mount_all",               {1,     1,    do_mount_all}},
+        {"mount_all",               {1,     kMax, do_mount_all}},
         {"mount",                   {3,     kMax, do_mount}},
         {"powerctl",                {1,     1,    do_powerctl}},
         {"restart",                 {1,     1,    do_restart}},
diff --git a/init/log.cpp b/init/log.cpp
index a72906b..ace9fd7 100644
--- a/init/log.cpp
+++ b/init/log.cpp
@@ -27,6 +27,8 @@
 static void init_klog_vwrite(int level, const char* fmt, va_list ap) {
     static const char* tag = basename(getprogname());
 
+    if (level > klog_get_level()) return;
+
     // 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?
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 806608e..5c1ae79 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -113,18 +113,6 @@
     return check_mac_perms(ctl_name, sctx, cr);
 }
 
-/*
- * Checks permissions for setting system properties.
- * Returns 1 if uid allowed, 0 otherwise.
- */
-static int check_perms(const char *name, char *sctx, struct ucred *cr)
-{
-    if(!strncmp(name, "ro.", 3))
-        name +=3;
-
-    return check_mac_perms(name, sctx, cr);
-}
-
 std::string property_get(const char* name) {
     char value[PROP_VALUE_MAX] = {0};
     __system_property_get(name, value);
@@ -312,7 +300,7 @@
                         msg.name + 4, msg.value, cr.uid, cr.gid, cr.pid);
             }
         } else {
-            if (check_perms(msg.name, source_ctx, &cr)) {
+            if (check_mac_perms(msg.name, source_ctx, &cr)) {
                 property_set((char*) msg.name, (char*) msg.value);
             } else {
                 ERROR("sys_prop: permission denied uid:%d  name:%s\n",
diff --git a/init/readme.txt b/init/readme.txt
index bacd6bd..ef85ccf 100644
--- a/init/readme.txt
+++ b/init/readme.txt
@@ -40,15 +40,43 @@
 These directories are intended for all Actions and Services used after
 file system mounting.
 
+One may specify paths in the mount_all command line to have it import
+.rc files at the specified paths instead of the default ones listed above.
+This is primarily for supporting factory mode and other non-standard boot
+modes.  The three default paths should be used for the normal boot process.
+
 The intention of these directories is as follows
    1) /system/etc/init/ is for core system items such as
-      SurfaceFlinger and MediaService.
+      SurfaceFlinger, MediaService, and logcatd.
    2) /vendor/etc/init/ is for SoC vendor items such as actions or
       daemons needed for core SoC functionality.
    3) /odm/etc/init/ is for device manufacturer items such as
       actions or daemons needed for motion sensor or other peripheral
       functionality.
 
+All services whose binaries reside on the system, vendor, or odm
+partitions should have their service entries placed into a
+corresponding init .rc file, located in the /etc/init/
+directory of the partition where they reside.  There is a build
+system macro, LOCAL_INIT_RC, that handles this for developers.  Each
+init .rc file should additionally contain any actions associated with
+its service.
+
+An example is the logcatd.rc and Android.mk files located in the
+system/core/logcat directory.  The LOCAL_INIT_RC macro in the
+Android.mk file places logcatd.rc in /system/etc/init/ during the
+build process.  Init loads logcatd.rc during the mount_all command and
+allows the service to be run and the action to be queued when
+appropriate.
+
+This break up of init .rc files according to their daemon is preferred
+to the previously used monolithic init .rc files.  This approach
+ensures that the only service entries that init reads and the only
+actions that init performs correspond to services whose binaries are in
+fact present on the file system, which was not the case with the
+monolithic init .rc files.  This additionally will aid in merge
+conflict resolution when multiple services are added to the system, as
+each one will go into a separate file.
 
 Actions
 -------
@@ -263,8 +291,10 @@
    owned by the root user and root group. If provided, the mode, owner and group
    will be updated if the directory exists already.
 
-mount_all <fstab>
-   Calls fs_mgr_mount_all on the given fs_mgr-format fstab.
+mount_all <fstab> [ <path> ]*
+   Calls fs_mgr_mount_all on the given fs_mgr-format fstab and imports .rc files
+   at the specified paths (e.g., on the partitions just mounted). Refer to the
+   section of "Init .rc Files" for detail.
 
 mount <type> <device> <dir> [ <flag> ]* [<options>]
    Attempt to mount the named device at the directory <dir>
@@ -358,7 +388,8 @@
 
 There are only two times where the init executable imports .rc files,
    1) When it imports /init.rc during initial boot
-   2) When it imports /{system,vendor,odm}/etc/init/ during mount_all
+   2) When it imports /{system,vendor,odm}/etc/init/ or .rc files at specified
+      paths during mount_all
 
 
 Properties
diff --git a/init/service.cpp b/init/service.cpp
index 40a4bc7..0ddc484 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -19,6 +19,7 @@
 #include <fcntl.h>
 #include <sys/stat.h>
 #include <sys/types.h>
+#include <sys/wait.h>
 #include <termios.h>
 #include <unistd.h>
 
@@ -531,6 +532,17 @@
     StopOrReset(SVC_DISABLED);
 }
 
+void Service::Terminate() {
+    flags_ &= ~(SVC_RESTARTING | SVC_DISABLED_START);
+    flags_ |= SVC_DISABLED;
+    if (pid_) {
+        NOTICE("Sending SIGTERM to service '%s' (pid %d)...\n", name_.c_str(),
+               pid_);
+        kill(-pid_, SIGTERM);
+        NotifyStateChange("stopping");
+    }
+}
+
 void Service::Restart() {
     if (flags_ & SVC_RUNNING) {
         /* Stop, wait, then start the service. */
@@ -724,9 +736,9 @@
     return nullptr;
 }
 
-void ServiceManager::ForEachService(void (*func)(Service* svc)) const {
+void ServiceManager::ForEachService(std::function<void(Service*)> callback) const {
     for (const auto& s : services_) {
-        func(s.get());
+        callback(s.get());
     }
 }
 
@@ -767,6 +779,53 @@
     INFO("\n");
 }
 
+bool ServiceManager::ReapOneProcess() {
+    int status;
+    pid_t pid = TEMP_FAILURE_RETRY(waitpid(-1, &status, WNOHANG));
+    if (pid == 0) {
+        return false;
+    } else if (pid == -1) {
+        ERROR("waitpid failed: %s\n", strerror(errno));
+        return false;
+    }
+
+    Service* svc = FindServiceByPid(pid);
+
+    std::string name;
+    if (svc) {
+        name = android::base::StringPrintf("Service '%s' (pid %d)",
+                                           svc->name().c_str(), pid);
+    } else {
+        name = android::base::StringPrintf("Untracked pid %d", pid);
+    }
+
+    if (WIFEXITED(status)) {
+        NOTICE("%s exited with status %d\n", name.c_str(), WEXITSTATUS(status));
+    } else if (WIFSIGNALED(status)) {
+        NOTICE("%s killed by signal %d\n", name.c_str(), WTERMSIG(status));
+    } else if (WIFSTOPPED(status)) {
+        NOTICE("%s stopped by signal %d\n", name.c_str(), WSTOPSIG(status));
+    } else {
+        NOTICE("%s state changed", name.c_str());
+    }
+
+    if (!svc) {
+        return true;
+    }
+
+    if (svc->Reap()) {
+        waiting_for_exec = false;
+        RemoveService(*svc);
+    }
+
+    return true;
+}
+
+void ServiceManager::ReapAnyOutstandingChildren() {
+    while (ReapOneProcess()) {
+    }
+}
+
 bool ServiceParser::ParseSection(const std::vector<std::string>& args,
                                  std::string* err) {
     if (args.size() < 3) {
diff --git a/init/service.h b/init/service.h
index 10eb736..35abde9 100644
--- a/init/service.h
+++ b/init/service.h
@@ -82,6 +82,7 @@
     bool Enable();
     void Reset();
     void Stop();
+    void Terminate();
     void Restart();
     void RestartIfNeeded(time_t& process_needs_restart);
     bool Reap();
@@ -167,17 +168,22 @@
     Service* FindServiceByName(const std::string& name) const;
     Service* FindServiceByPid(pid_t pid) const;
     Service* FindServiceByKeychord(int keychord_id) const;
-    void ForEachService(void (*func)(Service* svc)) const;
+    void ForEachService(std::function<void(Service*)> callback) const;
     void ForEachServiceInClass(const std::string& classname,
                                void (*func)(Service* svc)) const;
     void ForEachServiceWithFlags(unsigned matchflags,
                              void (*func)(Service* svc)) const;
+    void ReapAnyOutstandingChildren();
     void RemoveService(const Service& svc);
     void DumpState() const;
 
 private:
     ServiceManager();
 
+    // Cleans up a child process that exited.
+    // Returns true iff a children was cleaned up.
+    bool ReapOneProcess();
+
     static int exec_count_; // Every service needs a unique name.
     std::vector<std::unique_ptr<Service>> services_;
 };
diff --git a/init/signal_handler.cpp b/init/signal_handler.cpp
index e7d42cb..ea483d4 100644
--- a/init/signal_handler.cpp
+++ b/init/signal_handler.cpp
@@ -37,62 +37,12 @@
 static int signal_write_fd = -1;
 static int signal_read_fd = -1;
 
-static std::string DescribeStatus(int status) {
-    if (WIFEXITED(status)) {
-        return android::base::StringPrintf("exited with status %d", WEXITSTATUS(status));
-    } else if (WIFSIGNALED(status)) {
-        return android::base::StringPrintf("killed by signal %d", WTERMSIG(status));
-    } else if (WIFSTOPPED(status)) {
-        return android::base::StringPrintf("stopped by signal %d", WSTOPSIG(status));
-    } else {
-        return "state changed";
-    }
-}
-
-static bool wait_for_one_process() {
-    int status;
-    pid_t pid = TEMP_FAILURE_RETRY(waitpid(-1, &status, WNOHANG));
-    if (pid == 0) {
-        return false;
-    } else if (pid == -1) {
-        ERROR("waitpid failed: %s\n", strerror(errno));
-        return false;
-    }
-
-    Service* svc = ServiceManager::GetInstance().FindServiceByPid(pid);
-
-    std::string name;
-    if (svc) {
-        name = android::base::StringPrintf("Service '%s' (pid %d)", svc->name().c_str(), pid);
-    } else {
-        name = android::base::StringPrintf("Untracked pid %d", pid);
-    }
-
-    NOTICE("%s %s\n", name.c_str(), DescribeStatus(status).c_str());
-
-    if (!svc) {
-        return true;
-    }
-
-    if (svc->Reap()) {
-        waiting_for_exec = false;
-        ServiceManager::GetInstance().RemoveService(*svc);
-    }
-
-    return true;
-}
-
-static void reap_any_outstanding_children() {
-    while (wait_for_one_process()) {
-    }
-}
-
 static void handle_signal() {
     // Clear outstanding requests.
     char buf[32];
     read(signal_read_fd, buf, sizeof(buf));
 
-    reap_any_outstanding_children();
+    ServiceManager::GetInstance().ReapAnyOutstandingChildren();
 }
 
 static void SIGCHLD_handler(int) {
@@ -119,7 +69,7 @@
     act.sa_flags = SA_NOCLDSTOP;
     sigaction(SIGCHLD, &act, 0);
 
-    reap_any_outstanding_children();
+    ServiceManager::GetInstance().ReapAnyOutstandingChildren();
 
     register_epoll_handler(signal_read_fd, handle_signal);
 }
diff --git a/libbacktrace/Android.mk b/libbacktrace/Android.mk
index 5d3dd86..6cffb11 100644
--- a/libbacktrace/Android.mk
+++ b/libbacktrace/Android.mk
@@ -48,7 +48,6 @@
 	Backtrace.cpp \
 	BacktraceCurrent.cpp \
 	BacktraceMap.cpp \
-	BacktraceOffline.cpp \
 	BacktracePtrace.cpp \
 	thread_utils.c \
 	ThreadEntry.cpp \
@@ -61,25 +60,6 @@
 	liblog \
 	libunwind \
 
-# Use shared llvm library on device to save space.
-libbacktrace_shared_libraries_target := \
-	libLLVM \
-
-# Use static llvm libraries on host to remove dependency on 32-bit llvm shared library
-# which is not included in the prebuilt.
-libbacktrace_static_libraries_host := \
-	libcutils \
-	libLLVMObject \
-	libLLVMBitReader \
-	libLLVMMC \
-	libLLVMMCParser \
-	libLLVMCore \
-	libLLVMSupport \
-
-libbacktrace_ldlibs_host := \
-	-lpthread \
-	-lrt \
-
 module := libbacktrace
 module_tag := optional
 build_type := target
@@ -98,6 +78,40 @@
 libbacktrace_static_libraries :=
 
 #-------------------------------------------------------------------------
+# The libbacktrace_offline shared library.
+#-------------------------------------------------------------------------
+libbacktrace_offline_src_files := \
+	BacktraceOffline.cpp \
+
+libbacktrace_offline_shared_libraries := \
+	libbacktrace \
+	liblog \
+	libunwind \
+
+# Use shared llvm library on device to save space.
+libbacktrace_offline_shared_libraries_target := \
+	libLLVM \
+
+# Use static llvm libraries on host to remove dependency on 32-bit llvm shared library
+# which is not included in the prebuilt.
+libbacktrace_offline_static_libraries_host := \
+	libLLVMObject \
+	libLLVMBitReader \
+	libLLVMMC \
+	libLLVMMCParser \
+	libLLVMCore \
+	libLLVMSupport \
+
+module := libbacktrace_offline
+module_tag := optional
+build_type := target
+build_target := SHARED_LIBRARY
+include $(LOCAL_PATH)/Android.build.mk
+build_type := host
+libbacktrace_multilib := both
+include $(LOCAL_PATH)/Android.build.mk
+
+#-------------------------------------------------------------------------
 # The libbacktrace_test library needed by backtrace_test.
 #-------------------------------------------------------------------------
 libbacktrace_test_cflags := \
@@ -141,6 +155,7 @@
 backtrace_test_shared_libraries := \
 	libbacktrace_test \
 	libbacktrace \
+	libbacktrace_offline \
 	libbase \
 	libcutils \
 	libunwind \
diff --git a/libbacktrace/Backtrace.cpp b/libbacktrace/Backtrace.cpp
index 3c8f879..baa3d0f 100644
--- a/libbacktrace/Backtrace.cpp
+++ b/libbacktrace/Backtrace.cpp
@@ -28,7 +28,6 @@
 #include <backtrace/BacktraceMap.h>
 
 #include "BacktraceLog.h"
-#include "BacktraceOffline.h"
 #include "thread_utils.h"
 #include "UnwindCurrent.h"
 #include "UnwindPtrace.h"
@@ -149,8 +148,3 @@
     return new UnwindPtrace(pid, tid, map);
   }
 }
-
-Backtrace* Backtrace::CreateOffline(pid_t pid, pid_t tid, BacktraceMap* map,
-                                    const backtrace_stackinfo_t& stack, bool cache_file) {
-  return new BacktraceOffline(pid, tid, map, stack, cache_file);
-}
diff --git a/libbacktrace/BacktraceOffline.cpp b/libbacktrace/BacktraceOffline.cpp
index abc186b..e84ae74 100644
--- a/libbacktrace/BacktraceOffline.cpp
+++ b/libbacktrace/BacktraceOffline.cpp
@@ -659,3 +659,8 @@
   }
   return nullptr;
 }
+
+Backtrace* Backtrace::CreateOffline(pid_t pid, pid_t tid, BacktraceMap* map,
+                                    const backtrace_stackinfo_t& stack, bool cache_file) {
+  return new BacktraceOffline(pid, tid, map, stack, cache_file);
+}
diff --git a/libbacktrace/backtrace_test.cpp b/libbacktrace/backtrace_test.cpp
index 23636db..7d829fe 100644
--- a/libbacktrace/backtrace_test.cpp
+++ b/libbacktrace/backtrace_test.cpp
@@ -1156,7 +1156,7 @@
   int fd = open(tmp_so_name, O_RDONLY);
   ASSERT_TRUE(fd != -1);
 
-  void* map = mmap(NULL, map_size, PROT_READ, MAP_PRIVATE, fd, 0);
+  void* map = mmap(NULL, map_size, PROT_READ | PROT_EXEC, MAP_PRIVATE, fd, 0);
   ASSERT_TRUE(map != MAP_FAILED);
   close(fd);
   ASSERT_TRUE(unlink(tmp_so_name) != -1);
@@ -1206,7 +1206,7 @@
       exit(0);
     }
 
-    void* map = mmap(NULL, map_size, PROT_READ, MAP_PRIVATE, fd, 0);
+    void* map = mmap(NULL, map_size, PROT_READ | PROT_EXEC, MAP_PRIVATE, fd, 0);
     if (map == MAP_FAILED) {
       fprintf(stderr, "Failed to map in memory: %s\n", strerror(errno));
       unlink(tmp_so_name);
diff --git a/libbinderwrapper/binder_wrapper.cc b/libbinderwrapper/binder_wrapper.cc
index 0b5ff96..ca9c1df 100644
--- a/libbinderwrapper/binder_wrapper.cc
+++ b/libbinderwrapper/binder_wrapper.cc
@@ -50,4 +50,11 @@
   return instance_;
 }
 
+// static
+BinderWrapper* BinderWrapper::GetOrCreateInstance() {
+  if (!instance_)
+    instance_ = new RealBinderWrapper();
+  return instance_;
+}
+
 }  // namespace android
diff --git a/libcutils/Android.mk b/libcutils/Android.mk
index dd08108..25b056b 100644
--- a/libcutils/Android.mk
+++ b/libcutils/Android.mk
@@ -39,26 +39,33 @@
 libcutils_nonwindows_sources := \
         fs.c \
         multiuser.c \
-        socket_inaddr_any_server.c \
-        socket_local_client.c \
-        socket_local_server.c \
-        socket_loopback_client.c \
-        socket_loopback_server.c \
-        socket_network_client.c \
-        sockets.c \
+        socket_inaddr_any_server_unix.c \
+        socket_local_client_unix.c \
+        socket_local_server_unix.c \
+        socket_loopback_client_unix.c \
+        socket_loopback_server_unix.c \
+        socket_network_client_unix.c \
+        sockets_unix.c \
         str_parms.c \
 
 libcutils_nonwindows_host_sources := \
         ashmem-host.c \
-        trace-host.c
+        trace-host.c \
 
+libcutils_windows_host_sources := \
+        socket_inaddr_any_server_windows.c \
+        socket_network_client_windows.c \
+        sockets_windows.c \
 
 # Shared and static library for host
+# Note: when linking this library on Windows, you must also link to Winsock2
+# using "LOCAL_LDLIBS_windows := -lws2_32".
 # ========================================================
 LOCAL_MODULE := libcutils
 LOCAL_SRC_FILES := $(libcutils_common_sources) dlmalloc_stubs.c
 LOCAL_SRC_FILES_darwin := $(libcutils_nonwindows_sources) $(libcutils_nonwindows_host_sources)
 LOCAL_SRC_FILES_linux := $(libcutils_nonwindows_sources) $(libcutils_nonwindows_host_sources)
+LOCAL_SRC_FILES_windows := $(libcutils_windows_host_sources)
 LOCAL_STATIC_LIBRARIES := liblog
 LOCAL_CFLAGS := -Werror -Wall -Wextra
 LOCAL_MULTILIB := both
diff --git a/libcutils/klog.c b/libcutils/klog.c
index 710dc66..7402903 100644
--- a/libcutils/klog.c
+++ b/libcutils/klog.c
@@ -62,6 +62,7 @@
 }
 
 void klog_write(int level, const char* fmt, ...) {
+    if (level > klog_level) return;
     char buf[LOG_BUF_MAX];
     va_list ap;
     va_start(ap, fmt);
diff --git a/libcutils/socket_inaddr_any_server.c b/libcutils/socket_inaddr_any_server_unix.c
similarity index 97%
rename from libcutils/socket_inaddr_any_server.c
rename to libcutils/socket_inaddr_any_server_unix.c
index e1b7d84..387258f 100644
--- a/libcutils/socket_inaddr_any_server.c
+++ b/libcutils/socket_inaddr_any_server_unix.c
@@ -20,12 +20,10 @@
 #include <string.h>
 #include <unistd.h>
 
-#if !defined(_WIN32)
 #include <sys/socket.h>
 #include <sys/select.h>
 #include <sys/types.h>
 #include <netinet/in.h>
-#endif
 
 #include <cutils/sockets.h>
 
diff --git a/libcutils/socket_inaddr_any_server_windows.c b/libcutils/socket_inaddr_any_server_windows.c
new file mode 100644
index 0000000..c15200a
--- /dev/null
+++ b/libcutils/socket_inaddr_any_server_windows.c
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <errno.h>
+
+#include <cutils/sockets.h>
+
+#define LISTEN_BACKLOG 4
+
+extern bool initialize_windows_sockets();
+
+SOCKET socket_inaddr_any_server(int port, int type) {
+    if (!initialize_windows_sockets()) {
+      return INVALID_SOCKET;
+    }
+
+    SOCKET sock = socket(AF_INET6, type, 0);
+    if (sock == INVALID_SOCKET) {
+        return INVALID_SOCKET;
+    }
+
+    // Enforce exclusive addresses so nobody can steal the port from us (1),
+    // and enable dual-stack so both IPv4 and IPv6 work (2).
+    // (1) https://msdn.microsoft.com/en-us/library/windows/desktop/ms740621(v=vs.85).aspx.
+    // (2) https://msdn.microsoft.com/en-us/library/windows/desktop/bb513665(v=vs.85).aspx.
+    int exclusive = 1;
+    DWORD v6_only = 0;
+    if (setsockopt(sock, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (char*)&exclusive,
+                   sizeof(exclusive)) == SOCKET_ERROR ||
+        setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&v6_only,
+                   sizeof(v6_only)) == SOCKET_ERROR) {
+        closesocket(sock);
+        return INVALID_SOCKET;
+    }
+
+    // Bind the socket to our local port.
+    struct sockaddr_in6 addr;
+    memset(&addr, 0, sizeof(addr));
+    addr.sin6_family = AF_INET6;
+    addr.sin6_port = htons(port);
+    addr.sin6_addr = in6addr_any;
+    if (bind(sock, (struct sockaddr*)&addr, sizeof(addr)) == SOCKET_ERROR) {
+        closesocket(sock);
+        return INVALID_SOCKET;
+    }
+
+    // Start listening for connections if this is a TCP socket.
+    if (type == SOCK_STREAM && listen(sock, LISTEN_BACKLOG) == SOCKET_ERROR) {
+        closesocket(sock);
+        return INVALID_SOCKET;
+    }
+
+    return sock;
+}
diff --git a/libcutils/socket_local_client.c b/libcutils/socket_local_client_unix.c
similarity index 98%
rename from libcutils/socket_local_client.c
rename to libcutils/socket_local_client_unix.c
index 2526146..92fb9f1 100644
--- a/libcutils/socket_local_client.c
+++ b/libcutils/socket_local_client_unix.c
@@ -37,7 +37,7 @@
 #include <sys/select.h>
 #include <sys/types.h>
 
-#include "socket_local.h"
+#include "socket_local_unix.h"
 
 #define UNUSED __attribute__((unused))
 
diff --git a/libcutils/socket_local_server.c b/libcutils/socket_local_server_unix.c
similarity index 98%
rename from libcutils/socket_local_server.c
rename to libcutils/socket_local_server_unix.c
index c9acdad..db9e1e0 100644
--- a/libcutils/socket_local_server.c
+++ b/libcutils/socket_local_server_unix.c
@@ -39,7 +39,7 @@
 #include <sys/types.h>
 #include <netinet/in.h>
 
-#include "socket_local.h"
+#include "socket_local_unix.h"
 
 #define LISTEN_BACKLOG 4
 
diff --git a/libcutils/socket_local.h b/libcutils/socket_local_unix.h
similarity index 100%
rename from libcutils/socket_local.h
rename to libcutils/socket_local_unix.h
diff --git a/libcutils/socket_loopback_client.c b/libcutils/socket_loopback_client_unix.c
similarity index 100%
rename from libcutils/socket_loopback_client.c
rename to libcutils/socket_loopback_client_unix.c
diff --git a/libcutils/socket_loopback_server.c b/libcutils/socket_loopback_server_unix.c
similarity index 100%
rename from libcutils/socket_loopback_server.c
rename to libcutils/socket_loopback_server_unix.c
diff --git a/libcutils/socket_network_client.c b/libcutils/socket_network_client_unix.c
similarity index 100%
rename from libcutils/socket_network_client.c
rename to libcutils/socket_network_client_unix.c
diff --git a/libcutils/socket_network_client_windows.c b/libcutils/socket_network_client_windows.c
new file mode 100644
index 0000000..ab1a52f
--- /dev/null
+++ b/libcutils/socket_network_client_windows.c
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <cutils/sockets.h>
+
+extern bool initialize_windows_sockets();
+
+SOCKET socket_network_client(const char* host, int port, int type) {
+    if (!initialize_windows_sockets()) {
+        return INVALID_SOCKET;
+    }
+
+    // First resolve the host and port parameters into a usable network address.
+    struct addrinfo hints;
+    memset(&hints, 0, sizeof(hints));
+    hints.ai_socktype = type;
+
+    struct addrinfo* address = NULL;
+    char port_str[16];
+    snprintf(port_str, sizeof(port_str), "%d", port);
+    if (getaddrinfo(host, port_str, &hints, &address) != 0 || address == NULL) {
+        if (address != NULL) {
+            freeaddrinfo(address);
+        }
+        return INVALID_SOCKET;
+    }
+
+    // Now create and connect the socket.
+    SOCKET sock = socket(address->ai_family, address->ai_socktype,
+                         address->ai_protocol);
+    if (sock == INVALID_SOCKET) {
+        freeaddrinfo(address);
+        return INVALID_SOCKET;
+    }
+
+    if (connect(sock, address->ai_addr, address->ai_addrlen) == SOCKET_ERROR) {
+        closesocket(sock);
+        freeaddrinfo(address);
+        return INVALID_SOCKET;
+    }
+
+    freeaddrinfo(address);
+    return sock;
+}
diff --git a/libcutils/sockets.c b/libcutils/sockets_unix.c
similarity index 82%
rename from libcutils/sockets.c
rename to libcutils/sockets_unix.c
index d438782..5eddc4b 100644
--- a/libcutils/sockets.c
+++ b/libcutils/sockets_unix.c
@@ -45,3 +45,14 @@
 
     return true;
 }
+
+int socket_close(int sock) {
+  return close(sock);
+}
+
+int socket_set_receive_timeout(cutils_socket_t sock, int timeout_ms) {
+  struct timeval tv;
+  tv.tv_sec = timeout_ms / 1000;
+  tv.tv_usec = (timeout_ms % 1000) * 1000;
+  return setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
+}
diff --git a/libcutils/sockets_windows.c b/libcutils/sockets_windows.c
new file mode 100644
index 0000000..1bf2933
--- /dev/null
+++ b/libcutils/sockets_windows.c
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <cutils/sockets.h>
+
+// https://msdn.microsoft.com/en-us/library/windows/desktop/ms741549(v=vs.85).aspx
+// claims WSACleanup() should be called before program exit, but general
+// consensus seems to be that it hasn't actually been necessary for a long time,
+// likely since Windows 3.1. Additionally, trying to properly use WSACleanup()
+// can be extremely tricky and cause deadlock when using threads or atexit().
+//
+// Both adb (1) and Chrome (2) purposefully avoid WSACleanup() with no issues.
+// (1) https://android.googlesource.com/platform/system/core.git/+/master/adb/sysdeps_win32.cpp
+// (2) https://code.google.com/p/chromium/codesearch#chromium/src/net/base/winsock_init.cc
+bool initialize_windows_sockets() {
+    // There's no harm in calling WSAStartup() multiple times but no benefit
+    // either, we may as well skip it after the first.
+    static bool init_success = false;
+
+    if (!init_success) {
+        WSADATA wsaData;
+        init_success = (WSAStartup(MAKEWORD(2, 2), &wsaData) == 0);
+    }
+
+    return init_success;
+}
+
+int socket_close(cutils_socket_t sock) {
+    return closesocket(sock);
+}
+
+int socket_set_receive_timeout(cutils_socket_t sock, int timeout_ms) {
+    return setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout_ms,
+                      sizeof(timeout_ms));
+}
diff --git a/libcutils/tests/Android.mk b/libcutils/tests/Android.mk
index cf70345..4da5ed6 100644
--- a/libcutils/tests/Android.mk
+++ b/libcutils/tests/Android.mk
@@ -15,6 +15,9 @@
 LOCAL_PATH := $(call my-dir)
 
 test_src_files := \
+    sockets_test.cpp \
+
+test_src_files_nonwindows := \
     test_str_parms.cpp \
 
 test_target_only_src_files := \
@@ -55,7 +58,7 @@
 
 include $(CLEAR_VARS)
 LOCAL_MODULE := libcutils_test
-LOCAL_SRC_FILES := $(test_src_files)
+LOCAL_SRC_FILES := $(test_src_files) $(test_src_files_nonwindows)
 LOCAL_SHARED_LIBRARIES := $(test_libraries)
 LOCAL_MULTILIB := both
 LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
@@ -65,9 +68,13 @@
 include $(CLEAR_VARS)
 LOCAL_MODULE := libcutils_test_static
 LOCAL_SRC_FILES := $(test_src_files)
+LOCAL_SRC_FILES_darwin := $(test_src_files_nonwindows)
+LOCAL_SRC_FILES_linux := $(test_src_files_nonwindows)
 LOCAL_STATIC_LIBRARIES := $(test_libraries)
+LOCAL_LDLIBS_windows := -lws2_32
 LOCAL_CXX_STL := libc++_static
 LOCAL_MULTILIB := both
 LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
 LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+LOCAL_MODULE_HOST_OS := darwin linux windows
 include $(BUILD_HOST_NATIVE_TEST)
diff --git a/libcutils/tests/sockets_test.cpp b/libcutils/tests/sockets_test.cpp
new file mode 100644
index 0000000..966dfe7
--- /dev/null
+++ b/libcutils/tests/sockets_test.cpp
@@ -0,0 +1,158 @@
+/*
+ * 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 socket functionality using loopback connections. Requires IPv4 and
+// IPv6 capabilities, and that kTestPort is available for loopback
+// communication. These tests also assume that no UDP packets are lost,
+// which should be the case for loopback communication, but is not guaranteed.
+
+#include <cutils/sockets.h>
+
+#include <time.h>
+
+#include <gtest/gtest.h>
+
+enum {
+    // This port must be available for loopback communication.
+    kTestPort = 54321
+};
+
+// Makes sure the passed sockets are valid, sends data between them, and closes
+// them. Any failures are logged with gtest.
+//
+// On Mac recvfrom() will not fill in the address for TCP sockets, so we need
+// separate logic paths depending on socket type.
+static void TestConnectedSockets(cutils_socket_t server, cutils_socket_t client,
+                                 int type) {
+    ASSERT_NE(INVALID_SOCKET, server);
+    ASSERT_NE(INVALID_SOCKET, client);
+
+    char buffer[3];
+    sockaddr_storage addr;
+    socklen_t addr_size = sizeof(addr);
+
+    // Send client -> server first to get the UDP client's address.
+    ASSERT_EQ(3, send(client, "foo", 3, 0));
+    if (type == SOCK_DGRAM) {
+      EXPECT_EQ(3, recvfrom(server, buffer, 3, 0,
+                            reinterpret_cast<sockaddr*>(&addr), &addr_size));
+    } else {
+      EXPECT_EQ(3, recv(server, buffer, 3, 0));
+    }
+    EXPECT_EQ(0, memcmp(buffer, "foo", 3));
+
+    // Now send server -> client.
+    if (type == SOCK_DGRAM) {
+      ASSERT_EQ(3, sendto(server, "bar", 3, 0,
+                          reinterpret_cast<sockaddr*>(&addr), addr_size));
+    } else {
+      ASSERT_EQ(3, send(server, "bar", 3, 0));
+    }
+    EXPECT_EQ(3, recv(client, buffer, 3, 0));
+    EXPECT_EQ(0, memcmp(buffer, "bar", 3));
+
+    EXPECT_EQ(0, socket_close(server));
+    EXPECT_EQ(0, socket_close(client));
+}
+
+// Tests receive timeout. The timing verification logic must be very coarse to
+// make sure different systems can all pass these tests.
+void TestReceiveTimeout(cutils_socket_t sock) {
+    time_t start_time;
+    char buffer[32];
+
+    // Make sure a 20ms timeout completes in 1 second or less.
+    EXPECT_EQ(0, socket_set_receive_timeout(sock, 20));
+    start_time = time(nullptr);
+    EXPECT_EQ(-1, recv(sock, buffer, sizeof(buffer), 0));
+    EXPECT_LE(difftime(time(nullptr), start_time), 1.0);
+
+    // Make sure a 1250ms timeout takes 1 second or more.
+    EXPECT_EQ(0, socket_set_receive_timeout(sock, 1250));
+    start_time = time(nullptr);
+    EXPECT_EQ(-1, recv(sock, buffer, sizeof(buffer), 0));
+    EXPECT_LE(1.0, difftime(time(nullptr), start_time));
+}
+
+// Tests socket_inaddr_any_server() and socket_network_client() for IPv4 UDP.
+TEST(SocketsTest, TestIpv4UdpLoopback) {
+    cutils_socket_t server = socket_inaddr_any_server(kTestPort, SOCK_DGRAM);
+    cutils_socket_t client = socket_network_client("127.0.0.1", kTestPort,
+                                                   SOCK_DGRAM);
+
+    TestConnectedSockets(server, client, SOCK_DGRAM);
+}
+
+// Tests socket_inaddr_any_server() and socket_network_client() for IPv4 TCP.
+TEST(SocketsTest, TestIpv4TcpLoopback) {
+    cutils_socket_t server = socket_inaddr_any_server(kTestPort, SOCK_STREAM);
+    ASSERT_NE(INVALID_SOCKET, server);
+
+    cutils_socket_t client = socket_network_client("127.0.0.1", kTestPort,
+                                                   SOCK_STREAM);
+    cutils_socket_t handler = accept(server, nullptr, nullptr);
+    EXPECT_EQ(0, socket_close(server));
+
+    TestConnectedSockets(handler, client, SOCK_STREAM);
+}
+
+// Tests socket_inaddr_any_server() and socket_network_client() for IPv6 UDP.
+TEST(SocketsTest, TestIpv6UdpLoopback) {
+    cutils_socket_t server = socket_inaddr_any_server(kTestPort, SOCK_DGRAM);
+    cutils_socket_t client = socket_network_client("::1", kTestPort,
+                                                   SOCK_DGRAM);
+
+    TestConnectedSockets(server, client, SOCK_DGRAM);
+}
+
+// Tests socket_inaddr_any_server() and socket_network_client() for IPv6 TCP.
+TEST(SocketsTest, TestIpv6TcpLoopback) {
+    cutils_socket_t server = socket_inaddr_any_server(kTestPort, SOCK_STREAM);
+    ASSERT_NE(INVALID_SOCKET, server);
+
+    cutils_socket_t client = socket_network_client("::1", kTestPort,
+                                                   SOCK_STREAM);
+    cutils_socket_t handler = accept(server, nullptr, nullptr);
+    EXPECT_EQ(0, socket_close(server));
+
+    TestConnectedSockets(handler, client, SOCK_STREAM);
+}
+
+// Tests setting a receive timeout for UDP sockets.
+TEST(SocketsTest, TestUdpReceiveTimeout) {
+    cutils_socket_t sock = socket_inaddr_any_server(kTestPort, SOCK_DGRAM);
+    ASSERT_NE(INVALID_SOCKET, sock);
+
+    TestReceiveTimeout(sock);
+
+    EXPECT_EQ(0, socket_close(sock));
+}
+
+// Tests setting a receive timeout for TCP sockets.
+TEST(SocketsTest, TestTcpReceiveTimeout) {
+    cutils_socket_t server = socket_inaddr_any_server(kTestPort, SOCK_STREAM);
+    ASSERT_NE(INVALID_SOCKET, server);
+
+    cutils_socket_t client = socket_network_client("localhost", kTestPort,
+                                                   SOCK_STREAM);
+    cutils_socket_t handler = accept(server, nullptr, nullptr);
+    EXPECT_EQ(0, socket_close(server));
+
+    TestReceiveTimeout(handler);
+
+    EXPECT_EQ(0, socket_close(client));
+    EXPECT_EQ(0, socket_close(handler));
+}
diff --git a/liblog/Android.bp b/liblog/Android.bp
index 878feb8..ee883f0 100644
--- a/liblog/Android.bp
+++ b/liblog/Android.bp
@@ -71,6 +71,7 @@
         //       $(LOCAL_PATH)/event.logtags)
         // so make sure we do not regret hard-coding it as follows:
         "-DLIBLOG_LOG_TAG=1005",
+        "-DSNET_EVENT_LOG_TAG=1397638484",
     ],
     compile_multilib: "both",
     stl: "none",
diff --git a/liblog/log_is_loggable.c b/liblog/log_is_loggable.c
index b4711d2..0f81efc 100644
--- a/liblog/log_is_loggable.c
+++ b/liblog/log_is_loggable.c
@@ -25,13 +25,18 @@
 
 static pthread_mutex_t lock_loggable = PTHREAD_MUTEX_INITIALIZER;
 
-static void lock()
+static int lock()
 {
     /*
      * If we trigger a signal handler in the middle of locked activity and the
      * signal handler logs a message, we could get into a deadlock state.
      */
-    pthread_mutex_lock(&lock_loggable);
+    /*
+     *  Any contention, and we can turn around and use the non-cached method
+     * in less time than the system call associated with a mutex to deal with
+     * the contention.
+     */
+    return pthread_mutex_trylock(&lock_loggable);
 }
 
 static void unlock()
@@ -45,6 +50,12 @@
     unsigned char c;
 };
 
+static int check_cache(struct cache *cache)
+{
+    return cache->pinfo
+        && __system_property_serial(cache->pinfo) != cache->serial;
+}
+
 #define BOOLEAN_TRUE 0xFF
 #define BOOLEAN_FALSE 0xFE
 
@@ -58,6 +69,7 @@
         if (!cache->pinfo) {
             return;
         }
+        cache->serial = -1;
     }
     serial = __system_property_serial(cache->pinfo);
     if (serial == cache->serial) {
@@ -85,7 +97,7 @@
     /* calculate the size of our key temporary buffer */
     const size_t taglen = (tag && *tag) ? strlen(tag) : 0;
     /* sizeof(log_namespace) = strlen(log_namespace) + 1 */
-    char key[sizeof(log_namespace) + taglen];
+    char key[sizeof(log_namespace) + taglen]; /* may be > PROPERTY_KEY_MAX */
     char *kp;
     size_t i;
     char c = 0;
@@ -100,49 +112,77 @@
      */
     static char *last_tag;
     static uint32_t global_serial;
-    uint32_t current_global_serial;
-    static struct cache tag_cache[2] = {
-        { NULL, -1, 0 },
-        { NULL, -1, 0 }
-    };
-    static struct cache global_cache[2] = {
-        { NULL, -1, 0 },
-        { NULL, -1, 0 }
-    };
+    /* some compilers erroneously see uninitialized use. !not_locked */
+    uint32_t current_global_serial = 0;
+    static struct cache tag_cache[2];
+    static struct cache global_cache[2];
+    int change_detected;
+    int global_change_detected;
+    int not_locked;
 
     strcpy(key, log_namespace);
 
-    lock();
+    global_change_detected = change_detected = not_locked = lock();
 
-    current_global_serial = __system_property_area_serial();
+    if (!not_locked) {
+        /*
+         *  check all known serial numbers to changes.
+         */
+        for (i = 0; i < (sizeof(tag_cache) / sizeof(tag_cache[0])); ++i) {
+            if (check_cache(&tag_cache[i])) {
+                change_detected = 1;
+            }
+        }
+        for (i = 0; i < (sizeof(global_cache) / sizeof(global_cache[0])); ++i) {
+            if (check_cache(&global_cache[i])) {
+                global_change_detected = 1;
+            }
+        }
+
+        current_global_serial = __system_property_area_serial();
+        if (current_global_serial != global_serial) {
+            change_detected = 1;
+            global_change_detected = 1;
+        }
+    }
 
     if (taglen) {
-        uint32_t current_local_serial = current_global_serial;
-
-        if (!last_tag || (last_tag[0] != tag[0]) || strcmp(last_tag + 1, tag + 1)) {
-            /* invalidate log.tag.<tag> cache */
-            for(i = 0; i < (sizeof(tag_cache) / sizeof(tag_cache[0])); ++i) {
-                tag_cache[i].pinfo = NULL;
-                tag_cache[i].serial = -1;
-                tag_cache[i].c = '\0';
+        int local_change_detected = change_detected;
+        if (!not_locked) {
+            if (!last_tag
+                    || (last_tag[0] != tag[0])
+                    || strcmp(last_tag + 1, tag + 1)) {
+                /* invalidate log.tag.<tag> cache */
+                for (i = 0; i < (sizeof(tag_cache) / sizeof(tag_cache[0])); ++i) {
+                    tag_cache[i].pinfo = NULL;
+                    tag_cache[i].c = '\0';
+                }
+                free(last_tag);
+                last_tag = NULL;
+                local_change_detected = 1;
             }
-            free(last_tag);
-            last_tag = NULL;
-            current_global_serial = -1;
-        }
-        if (!last_tag) {
-            last_tag = strdup(tag);
+            if (!last_tag) {
+                last_tag = strdup(tag);
+            }
         }
         strcpy(key + sizeof(log_namespace) - 1, tag);
 
         kp = key;
-        for(i = 0; i < (sizeof(tag_cache) / sizeof(tag_cache[0])); ++i) {
-            if (current_local_serial != global_serial) {
-                refresh_cache(&tag_cache[i], kp);
+        for (i = 0; i < (sizeof(tag_cache) / sizeof(tag_cache[0])); ++i) {
+            struct cache *cache = &tag_cache[i];
+            struct cache temp_cache;
+
+            if (not_locked) {
+                temp_cache.pinfo = NULL;
+                temp_cache.c = '\0';
+                cache = &temp_cache;
+            }
+            if (local_change_detected) {
+                refresh_cache(cache, kp);
             }
 
-            if (tag_cache[i].c) {
-                c = tag_cache[i].c;
+            if (cache->c) {
+                c = cache->c;
                 break;
             }
 
@@ -166,13 +206,24 @@
         key[sizeof(log_namespace) - 2] = '\0';
 
         kp = key;
-        for(i = 0; i < (sizeof(global_cache) / sizeof(global_cache[0])); ++i) {
-            if (current_global_serial != global_serial) {
-                refresh_cache(&global_cache[i], kp);
+        for (i = 0; i < (sizeof(global_cache) / sizeof(global_cache[0])); ++i) {
+            struct cache *cache = &global_cache[i];
+            struct cache temp_cache;
+
+            if (not_locked) {
+                temp_cache = *cache;
+                if (temp_cache.pinfo != cache->pinfo) { /* check atomic */
+                    temp_cache.pinfo = NULL;
+                    temp_cache.c = '\0';
+                }
+                cache = &temp_cache;
+            }
+            if (global_change_detected) {
+                refresh_cache(cache, kp);
             }
 
-            if (global_cache[i].c) {
-                c = global_cache[i].c;
+            if (cache->c) {
+                c = cache->c;
                 break;
             }
 
@@ -181,9 +232,10 @@
         break;
     }
 
-    global_serial = current_global_serial;
-
-    unlock();
+    if (!not_locked) {
+        global_serial = current_global_serial;
+        unlock();
+    }
 
     switch (toupper(c)) {
     case 'V': return ANDROID_LOG_VERBOSE;
@@ -206,70 +258,103 @@
 }
 
 /*
- * Timestamp state generally remains constant, since a change is
- * rare, we can accept a trylock failure gracefully. Use a separate
- * lock from is_loggable to keep contention down b/25563384.
+ * For properties that are read often, but generally remain constant.
+ * Since a change is rare, we will accept a trylock failure gracefully.
+ * Use a separate lock from is_loggable to keep contention down b/25563384.
  */
-static pthread_mutex_t lock_clockid = PTHREAD_MUTEX_INITIALIZER;
+struct cache2 {
+    pthread_mutex_t lock;
+    uint32_t serial;
+    const char *key_persist;
+    struct cache cache_persist;
+    const char *key_ro;
+    struct cache cache_ro;
+    unsigned char (*const evaluate)(const struct cache2 *self);
+};
 
-clockid_t android_log_clockid()
+static inline unsigned char do_cache2(struct cache2 *self)
 {
-    static struct cache r_time_cache = { NULL, -1, 0 };
-    static struct cache p_time_cache = { NULL, -1, 0 };
-    char c;
+    uint32_t current_serial;
+    int change_detected;
+    unsigned char c;
 
-    if (pthread_mutex_trylock(&lock_clockid)) {
+    if (pthread_mutex_trylock(&self->lock)) {
         /* We are willing to accept some race in this context */
-        if (!(c = p_time_cache.c)) {
-            c = r_time_cache.c;
-        }
-    } else {
-        static uint32_t serial;
-        uint32_t current_serial = __system_property_area_serial();
-        if (current_serial != serial) {
-            refresh_cache(&r_time_cache, "ro.logd.timestamp");
-            refresh_cache(&p_time_cache, "persist.logd.timestamp");
-            serial = current_serial;
-        }
-        if (!(c = p_time_cache.c)) {
-            c = r_time_cache.c;
-        }
-
-        pthread_mutex_unlock(&lock_clockid);
+        return self->evaluate(self);
     }
 
-    return (tolower(c) == 'm') ? CLOCK_MONOTONIC : CLOCK_REALTIME;
+    change_detected = check_cache(&self->cache_persist)
+                   || check_cache(&self->cache_ro);
+    current_serial = __system_property_area_serial();
+    if (current_serial != self->serial) {
+        change_detected = 1;
+    }
+    if (change_detected) {
+        refresh_cache(&self->cache_persist, self->key_persist);
+        refresh_cache(&self->cache_ro, self->key_ro);
+        self->serial = current_serial;
+    }
+    c = self->evaluate(self);
+
+    pthread_mutex_unlock(&self->lock);
+
+    return c;
+}
+
+static unsigned char evaluate_persist_ro(const struct cache2 *self)
+{
+    unsigned char c = self->cache_persist.c;
+
+    if (c) {
+        return c;
+    }
+
+    return self->cache_ro.c;
 }
 
 /*
- * security state generally remains constant, since a change is
- * rare, we can accept a trylock failure gracefully.
+ * Timestamp state generally remains constant, but can change at any time
+ * to handle developer requirements.
  */
-static pthread_mutex_t lock_security = PTHREAD_MUTEX_INITIALIZER;
+clockid_t android_log_clockid()
+{
+    static struct cache2 clockid = {
+        PTHREAD_MUTEX_INITIALIZER,
+        0,
+        "persist.logd.timestamp",
+        { NULL, -1, '\0' },
+        "ro.logd.timestamp",
+        { NULL, -1, '\0' },
+        evaluate_persist_ro
+    };
+
+    return (tolower(do_cache2(&clockid)) == 'm')
+        ? CLOCK_MONOTONIC
+        : CLOCK_REALTIME;
+}
+
+/*
+ * Security state generally remains constant, but the DO must be able
+ * to turn off logging should it become spammy after an attack is detected.
+ */
+static unsigned char evaluate_security(const struct cache2 *self)
+{
+    unsigned char c = self->cache_ro.c;
+
+    return (c != BOOLEAN_FALSE) && c && (self->cache_persist.c == BOOLEAN_TRUE);
+}
 
 int __android_log_security()
 {
-    static struct cache r_do_cache = { NULL, -1, BOOLEAN_FALSE };
-    static struct cache p_security_cache = { NULL, -1, BOOLEAN_FALSE };
-    int retval;
+    static struct cache2 security = {
+        PTHREAD_MUTEX_INITIALIZER,
+        0,
+        "persist.logd.security",
+        { NULL, -1, BOOLEAN_FALSE },
+        "ro.device_owner",
+        { NULL, -1, BOOLEAN_FALSE },
+        evaluate_security
+    };
 
-    if (pthread_mutex_trylock(&lock_security)) {
-        /* We are willing to accept some race in this context */
-        retval = (r_do_cache.c != BOOLEAN_FALSE) && r_do_cache.c &&
-                 (p_security_cache.c == BOOLEAN_TRUE);
-    } else {
-        static uint32_t serial;
-        uint32_t current_serial = __system_property_area_serial();
-        if (current_serial != serial) {
-            refresh_cache(&r_do_cache, "ro.device_owner");
-            refresh_cache(&p_security_cache, "persist.logd.security");
-            serial = current_serial;
-        }
-        retval = (r_do_cache.c != BOOLEAN_FALSE) && r_do_cache.c &&
-                 (p_security_cache.c == BOOLEAN_TRUE);
-
-        pthread_mutex_unlock(&lock_security);
-    }
-
-    return retval;
+    return do_cache2(&security);
 }
diff --git a/liblog/log_read.c b/liblog/log_read.c
index 8aa887b..1aff272 100644
--- a/liblog/log_read.c
+++ b/liblog/log_read.c
@@ -501,6 +501,14 @@
         remaining -= n;
         cp += n;
     }
+
+    if (logger_list->pid) {
+        n = snprintf(cp, remaining, " pid=%u", logger_list->pid);
+        n = min(n, remaining);
+        remaining -= n;
+        cp += n;
+    }
+
     return send_log_msg(NULL, NULL, buf, len);
 }
 
@@ -880,18 +888,10 @@
             sigaction(SIGALRM, &old_sigaction, NULL);
         }
 
-        if (ret <= 0) {
-            if ((ret == -1) && e) {
-                return -e;
-            }
-            return ret;
+        if ((ret == -1) && e) {
+            return -e;
         }
-
-        logger_for_each(logger, logger_list) {
-            if (log_msg->entry.lid == logger->id) {
-                return ret;
-            }
-        }
+        return ret;
     }
     /* NOTREACH */
     return ret;
diff --git a/liblog/logd_write.c b/liblog/logd_write.c
index c2b0ec2..4946073 100644
--- a/liblog/logd_write.c
+++ b/liblog/logd_write.c
@@ -39,6 +39,7 @@
 #include <android/set_abort_message.h>
 #endif
 
+#include <log/event_tag_map.h>
 #include <log/logd.h>
 #include <log/logger.h>
 #include <log/log_read.h>
@@ -71,6 +72,11 @@
     pthread_mutex_lock(&log_init_lock);
 }
 
+static int trylock()
+{
+    return pthread_mutex_trylock(&log_init_lock);
+}
+
 static void unlock()
 {
     pthread_mutex_unlock(&log_init_lock);
@@ -79,6 +85,7 @@
 #else   /* !defined(_WIN32) */
 
 #define lock() ((void)0)
+#define trylock() (0) /* success */
 #define unlock() ((void)0)
 
 #endif  /* !defined(_WIN32) */
@@ -194,22 +201,124 @@
         last_pid = getpid();
     }
     if (log_id == LOG_ID_SECURITY) {
-        if ((last_uid != AID_SYSTEM) && (last_uid != AID_ROOT)) {
+        if (vec[0].iov_len < 4) {
+            return -EINVAL;
+        }
+        /* Matches clientHasLogCredentials() in logd */
+        if ((last_uid != AID_SYSTEM) && (last_uid != AID_ROOT) && (last_uid != AID_LOG)) {
             uid_t uid = geteuid();
-            if ((uid != AID_SYSTEM) && (uid != AID_ROOT)) {
+            if ((uid != AID_SYSTEM) && (uid != AID_ROOT) && (uid != AID_LOG)) {
                 gid_t gid = getgid();
-                if ((gid != AID_SYSTEM) && (gid != AID_ROOT)) {
+                if ((gid != AID_SYSTEM) && (gid != AID_ROOT) && (gid != AID_LOG)) {
                     gid = getegid();
-                    if ((gid != AID_SYSTEM) && (gid != AID_ROOT)) {
-                        return -EPERM;
+                    if ((gid != AID_SYSTEM) && (gid != AID_ROOT) && (gid != AID_LOG)) {
+                        int num_groups;
+                        gid_t *groups;
+
+                        num_groups = getgroups(0, NULL);
+                        if (num_groups <= 0) {
+                            return -EPERM;
+                        }
+                        groups = calloc(num_groups, sizeof(gid_t));
+                        if (!groups) {
+                            return -ENOMEM;
+                        }
+                        num_groups = getgroups(num_groups, groups);
+                        while (num_groups > 0) {
+                            if (groups[num_groups - 1] == AID_LOG) {
+                                break;
+                            }
+                            --num_groups;
+                        }
+                        free(groups);
+                        if (num_groups <= 0) {
+                            return -EPERM;
+                        }
                     }
                 }
             }
         }
         if (!__android_log_security()) {
+            atomic_store(&dropped_security, 0);
+            return -EPERM;
+        }
+    } else if (log_id == LOG_ID_EVENTS) {
+        static atomic_uintptr_t map;
+        int ret;
+        const char *tag;
+        EventTagMap *m, *f;
+
+        if (vec[0].iov_len < 4) {
+            return -EINVAL;
+        }
+
+        tag = NULL;
+        f = NULL;
+        m = (EventTagMap *)atomic_load(&map);
+
+        if (!m) {
+            ret = trylock();
+            m = (EventTagMap *)atomic_load(&map); /* trylock flush cache */
+            if (!m) {
+                m = android_openEventTagMap(EVENT_TAG_MAP_FILE);
+                if (ret) { /* trylock failed, use local copy, mark for close */
+                    f = m;
+                } else {
+                    if (!m) { /* One chance to open map file */
+                        m = (EventTagMap *)(uintptr_t)-1LL;
+                    }
+                    atomic_store(&map, (uintptr_t)m);
+                }
+            }
+            if (!ret) { /* trylock succeeded, unlock */
+                unlock();
+            }
+        }
+        if (m && (m != (EventTagMap *)(uintptr_t)-1LL)) {
+            tag = android_lookupEventTag(
+                                    m,
+                                    htole32(((uint32_t *)vec[0].iov_base)[0]));
+        }
+        ret = __android_log_is_loggable(ANDROID_LOG_INFO,
+                                        tag,
+                                        ANDROID_LOG_VERBOSE);
+        if (f) { /* local copy marked for close */
+            android_closeEventTagMap(f);
+        }
+        if (!ret) {
+            return -EPERM;
+        }
+    } else {
+        /* Validate the incoming tag, tag content can not split across iovec */
+        char prio = ANDROID_LOG_VERBOSE;
+        const char *tag = vec[0].iov_base;
+        size_t len = vec[0].iov_len;
+        if (!tag) {
+            len = 0;
+        }
+        if (len > 0) {
+            prio = *tag;
+            if (len > 1) {
+                --len;
+                ++tag;
+            } else {
+                len = vec[1].iov_len;
+                tag = ((const char *)vec[1].iov_base);
+                if (!tag) {
+                    len = 0;
+                }
+            }
+        }
+        /* tag must be nul terminated */
+        if (strnlen(tag, len) >= len) {
+            tag = NULL;
+        }
+
+        if (!__android_log_is_loggable(prio, tag, ANDROID_LOG_VERBOSE)) {
             return -EPERM;
         }
     }
+
     /*
      *  struct {
      *      // what we provide to pstore
@@ -267,7 +376,9 @@
             }
         }
         snapshot = atomic_exchange_explicit(&dropped, 0, memory_order_relaxed);
-        if (snapshot) {
+        if (snapshot && __android_log_is_loggable(ANDROID_LOG_INFO,
+                                                  "liblog",
+                                                  ANDROID_LOG_VERBOSE)) {
             android_log_event_int_t buffer;
 
             header.id = LOG_ID_EVENTS;
@@ -579,3 +690,25 @@
 
     return write_to_log(LOG_ID_EVENTS, vec, 4);
 }
+
+/*
+ * Like __android_log_security_bwrite, but used for writing strings to the
+ * security log.
+ */
+int __android_log_security_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_SECURITY, vec, 4);
+}
diff --git a/liblog/logprint.c b/liblog/logprint.c
index bd36cdd..4ef62a1 100644
--- a/liblog/logprint.c
+++ b/liblog/logprint.c
@@ -1488,7 +1488,7 @@
         strcat(p, suffixBuf);
         p += suffixLen;
     } else {
-        while(pm < (entry->message + entry->messageLen)) {
+        do {
             const char *lineStart;
             size_t lineLen;
             lineStart = pm;
@@ -1510,7 +1510,7 @@
             p += suffixLen;
 
             if (*pm == '\n') pm++;
-        }
+        } while (pm < (entry->message + entry->messageLen));
     }
 
     if (p_outLength != NULL) {
diff --git a/liblog/tests/liblog_benchmark.cpp b/liblog/tests/liblog_benchmark.cpp
index b594634..01fb50f 100644
--- a/liblog/tests/liblog_benchmark.cpp
+++ b/liblog/tests/liblog_benchmark.cpp
@@ -14,11 +14,17 @@
  * limitations under the License.
  */
 
+#include <fcntl.h>
+#include <sys/endian.h>
 #include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+
 #include <cutils/sockets.h>
 #include <log/log.h>
 #include <log/logger.h>
 #include <log/log_read.h>
+#include <private/android_logger.h>
 
 #include "benchmark.h"
 
@@ -85,6 +91,380 @@
 BENCHMARK(BM_clock_overhead);
 
 /*
+ * Measure the time it takes to submit the android logging data to pstore
+ */
+static void BM_pmsg_short(int iters) {
+
+    int pstore_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY));
+    if (pstore_fd < 0) {
+        return;
+    }
+
+    /*
+     *  struct {
+     *      // what we provide to pstore
+     *      android_pmsg_log_header_t pmsg_header;
+     *      // what we provide to socket
+     *      android_log_header_t header;
+     *      // caller provides
+     *      union {
+     *          struct {
+     *              char     prio;
+     *              char     payload[];
+     *          } string;
+     *          struct {
+     *              uint32_t tag
+     *              char     payload[];
+     *          } binary;
+     *      };
+     *  };
+     */
+
+    struct timespec ts;
+    clock_gettime(android_log_clockid(), &ts);
+
+    android_pmsg_log_header_t pmsg_header;
+    pmsg_header.magic = LOGGER_MAGIC;
+    pmsg_header.len = sizeof(android_pmsg_log_header_t)
+                    + sizeof(android_log_header_t);
+    pmsg_header.uid = getuid();
+    pmsg_header.pid = getpid();
+
+    android_log_header_t header;
+    header.tid = gettid();
+    header.realtime.tv_sec = ts.tv_sec;
+    header.realtime.tv_nsec = ts.tv_nsec;
+
+    static const unsigned nr = 1;
+    static const unsigned header_length = 2;
+    struct iovec newVec[nr + header_length];
+
+    newVec[0].iov_base   = (unsigned char *) &pmsg_header;
+    newVec[0].iov_len    = sizeof(pmsg_header);
+    newVec[1].iov_base   = (unsigned char *) &header;
+    newVec[1].iov_len    = sizeof(header);
+
+    android_log_event_int_t buffer;
+
+    header.id = LOG_ID_EVENTS;
+    buffer.header.tag = 0;
+    buffer.payload.type = EVENT_TYPE_INT;
+    uint32_t snapshot = 0;
+    buffer.payload.data = htole32(snapshot);
+
+    newVec[2].iov_base = &buffer;
+    newVec[2].iov_len  = sizeof(buffer);
+
+    StartBenchmarkTiming();
+    for (int i = 0; i < iters; ++i) {
+        ++snapshot;
+        buffer.payload.data = htole32(snapshot);
+        writev(pstore_fd, newVec, nr);
+    }
+    StopBenchmarkTiming();
+    close(pstore_fd);
+}
+BENCHMARK(BM_pmsg_short);
+
+/*
+ * Measure the time it takes to submit the android logging data to pstore
+ * best case aligned single block.
+ */
+static void BM_pmsg_short_aligned(int iters) {
+
+    int pstore_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY));
+    if (pstore_fd < 0) {
+        return;
+    }
+
+    /*
+     *  struct {
+     *      // what we provide to pstore
+     *      android_pmsg_log_header_t pmsg_header;
+     *      // what we provide to socket
+     *      android_log_header_t header;
+     *      // caller provides
+     *      union {
+     *          struct {
+     *              char     prio;
+     *              char     payload[];
+     *          } string;
+     *          struct {
+     *              uint32_t tag
+     *              char     payload[];
+     *          } binary;
+     *      };
+     *  };
+     */
+
+    struct timespec ts;
+    clock_gettime(android_log_clockid(), &ts);
+
+    struct packet {
+        android_pmsg_log_header_t pmsg_header;
+        android_log_header_t header;
+        android_log_event_int_t payload;
+    };
+    char buf[sizeof(struct packet) + 8] __aligned(8);
+    memset(buf, 0, sizeof(buf));
+    struct packet *buffer = (struct packet*)(((uintptr_t)buf + 7) & ~7);
+    if (((uintptr_t)&buffer->pmsg_header) & 7) {
+        fprintf (stderr, "&buffer=0x%p iters=%d\n", &buffer->pmsg_header, iters);
+    }
+
+    buffer->pmsg_header.magic = LOGGER_MAGIC;
+    buffer->pmsg_header.len = sizeof(android_pmsg_log_header_t)
+                            + sizeof(android_log_header_t);
+    buffer->pmsg_header.uid = getuid();
+    buffer->pmsg_header.pid = getpid();
+
+    buffer->header.tid = gettid();
+    buffer->header.realtime.tv_sec = ts.tv_sec;
+    buffer->header.realtime.tv_nsec = ts.tv_nsec;
+
+    buffer->header.id = LOG_ID_EVENTS;
+    buffer->payload.header.tag = 0;
+    buffer->payload.payload.type = EVENT_TYPE_INT;
+    uint32_t snapshot = 0;
+    buffer->payload.payload.data = htole32(snapshot);
+
+    StartBenchmarkTiming();
+    for (int i = 0; i < iters; ++i) {
+        ++snapshot;
+        buffer->payload.payload.data = htole32(snapshot);
+        write(pstore_fd, &buffer->pmsg_header,
+            sizeof(android_pmsg_log_header_t) +
+            sizeof(android_log_header_t) +
+            sizeof(android_log_event_int_t));
+    }
+    StopBenchmarkTiming();
+    close(pstore_fd);
+}
+BENCHMARK(BM_pmsg_short_aligned);
+
+/*
+ * Measure the time it takes to submit the android logging data to pstore
+ * best case aligned single block.
+ */
+static void BM_pmsg_short_unaligned1(int iters) {
+
+    int pstore_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY));
+    if (pstore_fd < 0) {
+        return;
+    }
+
+    /*
+     *  struct {
+     *      // what we provide to pstore
+     *      android_pmsg_log_header_t pmsg_header;
+     *      // what we provide to socket
+     *      android_log_header_t header;
+     *      // caller provides
+     *      union {
+     *          struct {
+     *              char     prio;
+     *              char     payload[];
+     *          } string;
+     *          struct {
+     *              uint32_t tag
+     *              char     payload[];
+     *          } binary;
+     *      };
+     *  };
+     */
+
+    struct timespec ts;
+    clock_gettime(android_log_clockid(), &ts);
+
+    struct packet {
+        android_pmsg_log_header_t pmsg_header;
+        android_log_header_t header;
+        android_log_event_int_t payload;
+    };
+    char buf[sizeof(struct packet) + 8] __aligned(8);
+    memset(buf, 0, sizeof(buf));
+    struct packet *buffer = (struct packet*)((((uintptr_t)buf + 7) & ~7) + 1);
+    if ((((uintptr_t)&buffer->pmsg_header) & 7) != 1) {
+        fprintf (stderr, "&buffer=0x%p iters=%d\n", &buffer->pmsg_header, iters);
+    }
+
+    buffer->pmsg_header.magic = LOGGER_MAGIC;
+    buffer->pmsg_header.len = sizeof(android_pmsg_log_header_t)
+                            + sizeof(android_log_header_t);
+    buffer->pmsg_header.uid = getuid();
+    buffer->pmsg_header.pid = getpid();
+
+    buffer->header.tid = gettid();
+    buffer->header.realtime.tv_sec = ts.tv_sec;
+    buffer->header.realtime.tv_nsec = ts.tv_nsec;
+
+    buffer->header.id = LOG_ID_EVENTS;
+    buffer->payload.header.tag = 0;
+    buffer->payload.payload.type = EVENT_TYPE_INT;
+    uint32_t snapshot = 0;
+    buffer->payload.payload.data = htole32(snapshot);
+
+    StartBenchmarkTiming();
+    for (int i = 0; i < iters; ++i) {
+        ++snapshot;
+        buffer->payload.payload.data = htole32(snapshot);
+        write(pstore_fd, &buffer->pmsg_header,
+            sizeof(android_pmsg_log_header_t) +
+            sizeof(android_log_header_t) +
+            sizeof(android_log_event_int_t));
+    }
+    StopBenchmarkTiming();
+    close(pstore_fd);
+}
+BENCHMARK(BM_pmsg_short_unaligned1);
+
+/*
+ * Measure the time it takes to submit the android logging data to pstore
+ * best case aligned single block.
+ */
+static void BM_pmsg_long_aligned(int iters) {
+
+    int pstore_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY));
+    if (pstore_fd < 0) {
+        return;
+    }
+
+    /*
+     *  struct {
+     *      // what we provide to pstore
+     *      android_pmsg_log_header_t pmsg_header;
+     *      // what we provide to socket
+     *      android_log_header_t header;
+     *      // caller provides
+     *      union {
+     *          struct {
+     *              char     prio;
+     *              char     payload[];
+     *          } string;
+     *          struct {
+     *              uint32_t tag
+     *              char     payload[];
+     *          } binary;
+     *      };
+     *  };
+     */
+
+    struct timespec ts;
+    clock_gettime(android_log_clockid(), &ts);
+
+    struct packet {
+        android_pmsg_log_header_t pmsg_header;
+        android_log_header_t header;
+        android_log_event_int_t payload;
+    };
+    char buf[sizeof(struct packet) + 8 + LOGGER_ENTRY_MAX_PAYLOAD] __aligned(8);
+    memset(buf, 0, sizeof(buf));
+    struct packet *buffer = (struct packet*)(((uintptr_t)buf + 7) & ~7);
+    if (((uintptr_t)&buffer->pmsg_header) & 7) {
+        fprintf (stderr, "&buffer=0x%p iters=%d\n", &buffer->pmsg_header, iters);
+    }
+
+    buffer->pmsg_header.magic = LOGGER_MAGIC;
+    buffer->pmsg_header.len = sizeof(android_pmsg_log_header_t)
+                            + sizeof(android_log_header_t);
+    buffer->pmsg_header.uid = getuid();
+    buffer->pmsg_header.pid = getpid();
+
+    buffer->header.tid = gettid();
+    buffer->header.realtime.tv_sec = ts.tv_sec;
+    buffer->header.realtime.tv_nsec = ts.tv_nsec;
+
+    buffer->header.id = LOG_ID_EVENTS;
+    buffer->payload.header.tag = 0;
+    buffer->payload.payload.type = EVENT_TYPE_INT;
+    uint32_t snapshot = 0;
+    buffer->payload.payload.data = htole32(snapshot);
+
+    StartBenchmarkTiming();
+    for (int i = 0; i < iters; ++i) {
+        ++snapshot;
+        buffer->payload.payload.data = htole32(snapshot);
+        write(pstore_fd, &buffer->pmsg_header, LOGGER_ENTRY_MAX_PAYLOAD);
+    }
+    StopBenchmarkTiming();
+    close(pstore_fd);
+}
+BENCHMARK(BM_pmsg_long_aligned);
+
+/*
+ * Measure the time it takes to submit the android logging data to pstore
+ * best case aligned single block.
+ */
+static void BM_pmsg_long_unaligned1(int iters) {
+
+    int pstore_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY));
+    if (pstore_fd < 0) {
+        return;
+    }
+
+    /*
+     *  struct {
+     *      // what we provide to pstore
+     *      android_pmsg_log_header_t pmsg_header;
+     *      // what we provide to socket
+     *      android_log_header_t header;
+     *      // caller provides
+     *      union {
+     *          struct {
+     *              char     prio;
+     *              char     payload[];
+     *          } string;
+     *          struct {
+     *              uint32_t tag
+     *              char     payload[];
+     *          } binary;
+     *      };
+     *  };
+     */
+
+    struct timespec ts;
+    clock_gettime(android_log_clockid(), &ts);
+
+    struct packet {
+        android_pmsg_log_header_t pmsg_header;
+        android_log_header_t header;
+        android_log_event_int_t payload;
+    };
+    char buf[sizeof(struct packet) + 8 + LOGGER_ENTRY_MAX_PAYLOAD] __aligned(8);
+    memset(buf, 0, sizeof(buf));
+    struct packet *buffer = (struct packet*)((((uintptr_t)buf + 7) & ~7) + 1);
+    if ((((uintptr_t)&buffer->pmsg_header) & 7) != 1) {
+        fprintf (stderr, "&buffer=0x%p iters=%d\n", &buffer->pmsg_header, iters);
+    }
+
+    buffer->pmsg_header.magic = LOGGER_MAGIC;
+    buffer->pmsg_header.len = sizeof(android_pmsg_log_header_t)
+                            + sizeof(android_log_header_t);
+    buffer->pmsg_header.uid = getuid();
+    buffer->pmsg_header.pid = getpid();
+
+    buffer->header.tid = gettid();
+    buffer->header.realtime.tv_sec = ts.tv_sec;
+    buffer->header.realtime.tv_nsec = ts.tv_nsec;
+
+    buffer->header.id = LOG_ID_EVENTS;
+    buffer->payload.header.tag = 0;
+    buffer->payload.payload.type = EVENT_TYPE_INT;
+    uint32_t snapshot = 0;
+    buffer->payload.payload.data = htole32(snapshot);
+
+    StartBenchmarkTiming();
+    for (int i = 0; i < iters; ++i) {
+        ++snapshot;
+        buffer->payload.payload.data = htole32(snapshot);
+        write(pstore_fd, &buffer->pmsg_header, LOGGER_ENTRY_MAX_PAYLOAD);
+    }
+    StopBenchmarkTiming();
+    close(pstore_fd);
+}
+BENCHMARK(BM_pmsg_long_unaligned1);
+
+/*
  *	Measure the time it takes to submit the android logging call using
  * discrete acquisition under light load. Expect this to be a dozen or so
  * syscall periods (40us).
@@ -280,3 +660,31 @@
     StopBenchmarkTiming();
 }
 BENCHMARK(BM_is_loggable);
+
+/*
+ *	Measure the time it takes for android_log_clockid.
+ */
+static void BM_clockid(int iters) {
+    StartBenchmarkTiming();
+
+    for (int i = 0; i < iters; ++i) {
+        android_log_clockid();
+    }
+
+    StopBenchmarkTiming();
+}
+BENCHMARK(BM_clockid);
+
+/*
+ *	Measure the time it takes for __android_log_security.
+ */
+static void BM_security(int iters) {
+    StartBenchmarkTiming();
+
+    for (int i = 0; i < iters; ++i) {
+        __android_log_security();
+    }
+
+    StopBenchmarkTiming();
+}
+BENCHMARK(BM_security);
diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp
index 621101c..a936455 100644
--- a/liblog/tests/liblog_test.cpp
+++ b/liblog/tests/liblog_test.cpp
@@ -165,6 +165,133 @@
     android_logger_list_close(logger_list);
 }
 
+static inline int32_t get4LE(const char* src)
+{
+    return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
+}
+
+TEST(liblog, __android_log_bswrite) {
+    struct logger_list *logger_list;
+
+    pid_t pid = getpid();
+
+    ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
+        LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid)));
+
+    static const char buffer[] = "Hello World";
+    log_time ts(android_log_clockid());
+
+    ASSERT_LT(0, __android_log_bswrite(0, buffer));
+    usleep(1000000);
+
+    int count = 0;
+
+    for (;;) {
+        log_msg log_msg;
+        if (android_logger_list_read(logger_list, &log_msg) <= 0) {
+            break;
+        }
+
+        ASSERT_EQ(log_msg.entry.pid, pid);
+
+        if ((log_msg.entry.sec < (ts.tv_sec - 1))
+         || ((ts.tv_sec + 1) < log_msg.entry.sec)
+         || (log_msg.entry.len != (4 + 1 + 4 + sizeof(buffer) - 1))
+         || (log_msg.id() != LOG_ID_EVENTS)) {
+            continue;
+        }
+
+        char *eventData = log_msg.msg();
+
+        if (eventData[4] != EVENT_TYPE_STRING) {
+            continue;
+        }
+
+        int len = get4LE(eventData + 4 + 1);
+        if (len == (sizeof(buffer) - 1)) {
+            ++count;
+
+            AndroidLogFormat *logformat = android_log_format_new();
+            EXPECT_TRUE(NULL != logformat);
+            AndroidLogEntry entry;
+            char msgBuf[1024];
+            EXPECT_EQ(0, android_log_processBinaryLogBuffer(&log_msg.entry_v1,
+                                                            &entry,
+                                                            NULL,
+                                                            msgBuf,
+                                                            sizeof(msgBuf)));
+            fflush(stderr);
+            EXPECT_EQ(31, android_log_printLogLine(logformat, fileno(stderr), &entry));
+            android_log_format_free(logformat);
+        }
+    }
+
+    EXPECT_EQ(1, count);
+
+    android_logger_list_close(logger_list);
+}
+
+TEST(liblog, __android_log_bswrite__empty_string) {
+    struct logger_list *logger_list;
+
+    pid_t pid = getpid();
+
+    ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
+        LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid)));
+
+    static const char buffer[] = "";
+    log_time ts(android_log_clockid());
+
+    ASSERT_LT(0, __android_log_bswrite(0, buffer));
+    usleep(1000000);
+
+    int count = 0;
+
+    for (;;) {
+        log_msg log_msg;
+        if (android_logger_list_read(logger_list, &log_msg) <= 0) {
+            break;
+        }
+
+        ASSERT_EQ(log_msg.entry.pid, pid);
+
+        if ((log_msg.entry.sec < (ts.tv_sec - 1))
+         || ((ts.tv_sec + 1) < log_msg.entry.sec)
+         || (log_msg.entry.len != (4 + 1 + 4))
+         || (log_msg.id() != LOG_ID_EVENTS)) {
+            continue;
+        }
+
+        char *eventData = log_msg.msg();
+
+        if (eventData[4] != EVENT_TYPE_STRING) {
+            continue;
+        }
+
+        int len = get4LE(eventData + 4 + 1);
+        if (len == 0) {
+            ++count;
+
+            AndroidLogFormat *logformat = android_log_format_new();
+            EXPECT_TRUE(NULL != logformat);
+            AndroidLogEntry entry;
+            char msgBuf[1024];
+            EXPECT_EQ(0, android_log_processBinaryLogBuffer(&log_msg.entry_v1,
+                                                            &entry,
+                                                            NULL,
+                                                            msgBuf,
+                                                            sizeof(msgBuf)));
+            fflush(stderr);
+            EXPECT_EQ(20, android_log_printLogLine(logformat, fileno(stderr), &entry));
+            android_log_format_free(logformat);
+        }
+    }
+
+    EXPECT_EQ(1, count);
+
+    android_logger_list_close(logger_list);
+}
+
 TEST(liblog, __security) {
     static const char persist_key[] = "persist.logd.security";
     static const char readonly_key[] = "ro.device_owner";
@@ -866,13 +993,27 @@
                 continue;
             }
             fprintf(stderr, "i=%zu j=%zu\r", i, j);
+            bool android_log_is_loggable = __android_log_is_loggable(
+                levels[i].level, tag, levels[j].level);
             if ((levels[i].level < levels[j].level)
                     || (levels[j].level == -1)) {
-                EXPECT_FALSE(__android_log_is_loggable(levels[i].level, tag,
-                                                       levels[j].level));
+                if (android_log_is_loggable) {
+                    fprintf(stderr, "\n");
+                }
+                EXPECT_FALSE(android_log_is_loggable);
+                for(size_t k = 1000; k; --k) {
+                    EXPECT_FALSE(__android_log_is_loggable(
+                        levels[i].level, tag, levels[j].level));
+                }
             } else {
-                EXPECT_TRUE(__android_log_is_loggable(levels[i].level, tag,
-                                                      levels[j].level));
+                if (!android_log_is_loggable) {
+                    fprintf(stderr, "\n");
+                }
+                EXPECT_TRUE(android_log_is_loggable);
+                for(size_t k = 1000; k; --k) {
+                    EXPECT_TRUE(__android_log_is_loggable(
+                        levels[i].level, tag, levels[j].level));
+                }
             }
         }
     }
@@ -891,30 +1032,58 @@
             fprintf(stderr, "i=%zu j=%zu property_set(\"%s\",\"%s\")\r",
                     i, j, key, buf);
             property_set(key, buf);
+            bool android_log_is_loggable = __android_log_is_loggable(
+                levels[i].level, tag, ANDROID_LOG_DEBUG);
             if ((levels[i].level < levels[j].level)
                     || (levels[j].level == -1)
                     || ((levels[i].level < ANDROID_LOG_DEBUG)
                         && (levels[j].level == -2))) {
-                EXPECT_FALSE(__android_log_is_loggable(levels[i].level, tag,
-                                                       ANDROID_LOG_DEBUG));
+                if (android_log_is_loggable) {
+                    fprintf(stderr, "\n");
+                }
+                EXPECT_FALSE(android_log_is_loggable);
+                for(size_t k = 1000; k; --k) {
+                    EXPECT_FALSE(__android_log_is_loggable(
+                        levels[i].level, tag, ANDROID_LOG_DEBUG));
+                }
             } else {
-                EXPECT_TRUE(__android_log_is_loggable(levels[i].level, tag,
-                                                      ANDROID_LOG_DEBUG));
+                if (!android_log_is_loggable) {
+                    fprintf(stderr, "\n");
+                }
+                EXPECT_TRUE(android_log_is_loggable);
+                for(size_t k = 1000; k; --k) {
+                    EXPECT_TRUE(__android_log_is_loggable(
+                        levels[i].level, tag, ANDROID_LOG_DEBUG));
+                }
             }
             property_set(key, "");
 
             fprintf(stderr, "i=%zu j=%zu property_set(\"%s\",\"%s\")\r",
                     i, j, key + base_offset, buf);
             property_set(key + base_offset, buf);
+            android_log_is_loggable = __android_log_is_loggable(
+                levels[i].level, tag, ANDROID_LOG_DEBUG);
             if ((levels[i].level < levels[j].level)
                     || (levels[j].level == -1)
                     || ((levels[i].level < ANDROID_LOG_DEBUG)
                         && (levels[j].level == -2))) {
-                EXPECT_FALSE(__android_log_is_loggable(levels[i].level, tag,
-                                                       ANDROID_LOG_DEBUG));
+                if (android_log_is_loggable) {
+                    fprintf(stderr, "\n");
+                }
+                EXPECT_FALSE(android_log_is_loggable);
+                for(size_t k = 1000; k; --k) {
+                    EXPECT_FALSE(__android_log_is_loggable(
+                        levels[i].level, tag, ANDROID_LOG_DEBUG));
+                }
             } else {
-                EXPECT_TRUE(__android_log_is_loggable(levels[i].level, tag,
-                                                      ANDROID_LOG_DEBUG));
+                if (!android_log_is_loggable) {
+                    fprintf(stderr, "\n");
+                }
+                EXPECT_TRUE(android_log_is_loggable);
+                for(size_t k = 1000; k; --k) {
+                    EXPECT_TRUE(__android_log_is_loggable(
+                        levels[i].level, tag, ANDROID_LOG_DEBUG));
+                }
             }
             property_set(key + base_offset, "");
 
@@ -923,30 +1092,58 @@
             fprintf(stderr, "i=%zu j=%zu property_set(\"%s\",\"%s\")\r",
                     i, j, key, buf);
             property_set(key, buf);
+            android_log_is_loggable = __android_log_is_loggable(
+                levels[i].level, tag, ANDROID_LOG_DEBUG);
             if ((levels[i].level < levels[j].level)
                     || (levels[j].level == -1)
                     || ((levels[i].level < ANDROID_LOG_DEBUG)
                         && (levels[j].level == -2))) {
-                EXPECT_FALSE(__android_log_is_loggable(levels[i].level, tag,
-                                                       ANDROID_LOG_DEBUG));
+                if (android_log_is_loggable) {
+                    fprintf(stderr, "\n");
+                }
+                EXPECT_FALSE(android_log_is_loggable);
+                for(size_t k = 1000; k; --k) {
+                    EXPECT_FALSE(__android_log_is_loggable(
+                        levels[i].level, tag, ANDROID_LOG_DEBUG));
+                }
             } else {
-                EXPECT_TRUE(__android_log_is_loggable(levels[i].level, tag,
-                                                      ANDROID_LOG_DEBUG));
+                if (!android_log_is_loggable) {
+                    fprintf(stderr, "\n");
+                }
+                EXPECT_TRUE(android_log_is_loggable);
+                for(size_t k = 1000; k; --k) {
+                    EXPECT_TRUE(__android_log_is_loggable(
+                        levels[i].level, tag, ANDROID_LOG_DEBUG));
+                }
             }
             property_set(key, "");
 
             fprintf(stderr, "i=%zu j=%zu property_set(\"%s\",\"%s\")\r",
                     i, j, key + base_offset, buf);
             property_set(key + base_offset, buf);
+            android_log_is_loggable = __android_log_is_loggable(
+                levels[i].level, tag, ANDROID_LOG_DEBUG);
             if ((levels[i].level < levels[j].level)
                     || (levels[j].level == -1)
                     || ((levels[i].level < ANDROID_LOG_DEBUG)
                         && (levels[j].level == -2))) {
-                EXPECT_FALSE(__android_log_is_loggable(levels[i].level, tag,
-                                                       ANDROID_LOG_DEBUG));
+                if (android_log_is_loggable) {
+                    fprintf(stderr, "\n");
+                }
+                EXPECT_FALSE(android_log_is_loggable);
+                for(size_t k = 1000; k; --k) {
+                    EXPECT_FALSE(__android_log_is_loggable(
+                        levels[i].level, tag, ANDROID_LOG_DEBUG));
+                }
             } else {
-                EXPECT_TRUE(__android_log_is_loggable(levels[i].level, tag,
-                                                      ANDROID_LOG_DEBUG));
+                if (!android_log_is_loggable) {
+                    fprintf(stderr, "\n");
+                }
+                EXPECT_TRUE(android_log_is_loggable);
+                for(size_t k = 1000; k; --k) {
+                    EXPECT_TRUE(__android_log_is_loggable(
+                        levels[i].level, tag, ANDROID_LOG_DEBUG));
+                }
             }
             property_set(key + base_offset, "");
         }
@@ -969,30 +1166,58 @@
             fprintf(stderr, "i=%zu j=%zu property_set(\"%s\",\"%s\")\r",
                     i, j, key, buf);
             property_set(key, buf);
+            bool android_log_is_loggable = __android_log_is_loggable(
+                levels[i].level, tag, ANDROID_LOG_DEBUG);
             if ((levels[i].level < levels[j].level)
                     || (levels[j].level == -1)
                     || ((levels[i].level < ANDROID_LOG_INFO) // Yes INFO
                         && (levels[j].level == -2))) {
-                EXPECT_FALSE(__android_log_is_loggable(levels[i].level, tag,
-                                                       ANDROID_LOG_DEBUG));
+                if (android_log_is_loggable) {
+                    fprintf(stderr, "\n");
+                }
+                EXPECT_FALSE(android_log_is_loggable);
+                for(size_t k = 1000; k; --k) {
+                    EXPECT_FALSE(__android_log_is_loggable(
+                        levels[i].level, tag, ANDROID_LOG_DEBUG));
+                }
             } else {
-                EXPECT_TRUE(__android_log_is_loggable(levels[i].level, tag,
-                                                      ANDROID_LOG_DEBUG));
+                if (!android_log_is_loggable) {
+                    fprintf(stderr, "\n");
+                }
+                EXPECT_TRUE(android_log_is_loggable);
+                for(size_t k = 1000; k; --k) {
+                    EXPECT_TRUE(__android_log_is_loggable(
+                        levels[i].level, tag, ANDROID_LOG_DEBUG));
+                }
             }
             property_set(key, "");
 
             fprintf(stderr, "i=%zu j=%zu property_set(\"%s\",\"%s\")\r",
                     i, j, key + base_offset, buf);
             property_set(key + base_offset, buf);
+            android_log_is_loggable = __android_log_is_loggable(
+                levels[i].level, tag, ANDROID_LOG_DEBUG);
             if ((levels[i].level < levels[j].level)
                     || (levels[j].level == -1)
                     || ((levels[i].level < ANDROID_LOG_INFO) // Yes INFO
                         && (levels[j].level == -2))) {
-                EXPECT_FALSE(__android_log_is_loggable(levels[i].level, tag,
-                                                       ANDROID_LOG_DEBUG));
+                if (android_log_is_loggable) {
+                    fprintf(stderr, "\n");
+                }
+                EXPECT_FALSE(android_log_is_loggable);
+                for(size_t k = 1000; k; --k) {
+                    EXPECT_FALSE(__android_log_is_loggable(
+                        levels[i].level, tag, ANDROID_LOG_DEBUG));
+                }
             } else {
-                EXPECT_TRUE(__android_log_is_loggable(levels[i].level, tag,
-                                                      ANDROID_LOG_DEBUG));
+                if (!android_log_is_loggable) {
+                    fprintf(stderr, "\n");
+                }
+                EXPECT_TRUE(android_log_is_loggable);
+                for(size_t k = 1000; k; --k) {
+                    EXPECT_TRUE(__android_log_is_loggable(
+                        levels[i].level, tag, ANDROID_LOG_DEBUG));
+                }
             }
             property_set(key + base_offset, "");
         }
@@ -1008,11 +1233,6 @@
     property_set(key + base_offset, hold[3]);
 }
 
-static inline int32_t get4LE(const char* src)
-{
-    return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
-}
-
 TEST(liblog, android_errorWriteWithInfoLog__android_logger_list_read__typical) {
     const int TAG = 123456781;
     const char SUBTAG[] = "test-subtag";
diff --git a/libnativeloader/native_loader.cpp b/libnativeloader/native_loader.cpp
index 17b7742..6e6b0b9 100644
--- a/libnativeloader/native_loader.cpp
+++ b/libnativeloader/native_loader.cpp
@@ -48,7 +48,9 @@
                                             "libm.so:"
                                             "libOpenMAXAL.so:"
                                             "libOpenSLES.so:"
+                                            "libRS.so:"
                                             "libstdc++.so:"
+                                            "libwebviewchromium_plat_support.so:"
                                             "libz.so";
 
 class LibraryNamespaces {
@@ -56,6 +58,7 @@
   LibraryNamespaces() : initialized_(false) { }
 
   android_namespace_t* GetOrCreate(JNIEnv* env, jobject class_loader,
+                                   bool is_shared,
                                    jstring java_library_path,
                                    jstring java_permitted_path) {
     ScopedUtfChars library_path(env, java_library_path);
@@ -78,11 +81,16 @@
       return it->second;
     }
 
+    uint64_t namespace_type = ANDROID_NAMESPACE_TYPE_ISOLATED;
+    if (is_shared) {
+      namespace_type |= ANDROID_NAMESPACE_TYPE_SHARED;
+    }
+
     android_namespace_t* ns =
             android_create_namespace("classloader-namespace",
                                      nullptr,
                                      library_path.c_str(),
-                                     true,
+                                     namespace_type,
                                      java_permitted_path != nullptr ?
                                         permitted_path.c_str() :
                                         nullptr);
@@ -129,16 +137,16 @@
 
 
 void* OpenNativeLibrary(JNIEnv* env, int32_t target_sdk_version, const char* path,
-                        jobject class_loader, jstring java_library_path,
+                        jobject class_loader, bool is_shared, jstring java_library_path,
                         jstring java_permitted_path) {
 #if defined(__ANDROID__)
-  if (target_sdk_version <= INT_MAX || class_loader == nullptr) {
+  if (target_sdk_version == 0 || class_loader == nullptr) {
     return dlopen(path, RTLD_NOW);
   }
 
   android_namespace_t* ns =
-      g_namespaces->GetOrCreate(env, class_loader, java_library_path,
-                                java_permitted_path);
+      g_namespaces->GetOrCreate(env, class_loader, is_shared,
+                                java_library_path, java_permitted_path);
 
   if (ns == nullptr) {
     return nullptr;
@@ -150,7 +158,7 @@
 
   return android_dlopen_ext(path, RTLD_NOW, &extinfo);
 #else
-  UNUSED(env, target_sdk_version, class_loader,
+  UNUSED(env, target_sdk_version, class_loader, is_shared,
          java_library_path, java_permitted_path);
   return dlopen(path, RTLD_NOW);
 #endif
diff --git a/libnetutils/ifc_utils.c b/libnetutils/ifc_utils.c
index 956ed30..e0a9f7f 100644
--- a/libnetutils/ifc_utils.c
+++ b/libnetutils/ifc_utils.c
@@ -253,6 +253,7 @@
                        int prefixlen) {
     int ifindex, s, len, ret;
     struct sockaddr_storage ss;
+    int saved_errno;
     void *addr;
     size_t addrlen;
     struct {
@@ -317,15 +318,21 @@
     memcpy(RTA_DATA(rta), addr, addrlen);
 
     s = socket(PF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_ROUTE);
-    if (send(s, &req, req.n.nlmsg_len, 0) < 0) {
-        close(s);
+    if (s < 0) {
         return -errno;
     }
 
+    if (send(s, &req, req.n.nlmsg_len, 0) < 0) {
+        saved_errno = errno;
+        close(s);
+        return -saved_errno;
+    }
+
     len = recv(s, buf, sizeof(buf), 0);
+    saved_errno = errno;
     close(s);
     if (len < 0) {
-        return -errno;
+        return -saved_errno;
     }
 
     // Parse the acknowledgement to find the return code.
diff --git a/libpixelflinger/codeflinger/CodeCache.cpp b/libpixelflinger/codeflinger/CodeCache.cpp
index d770302..4b498c1 100644
--- a/libpixelflinger/codeflinger/CodeCache.cpp
+++ b/libpixelflinger/codeflinger/CodeCache.cpp
@@ -63,7 +63,7 @@
 #define USAGE_ERROR_ACTION(m,p) \
     heap_error("ARGUMENT IS INVALID HEAP ADDRESS", __FUNCTION__, p)
 
-#include "../../../../bionic/libc/upstream-dlmalloc/malloc.c"
+#include "../../../../external/dlmalloc/malloc.c"
 
 static void heap_error(const char* msg, const char* function, void* p) {
     ALOG(LOG_FATAL, LOG_TAG, "@@@ ABORTING: CODE FLINGER: %s IN %s addr=%p",
diff --git a/libsparse/append2simg.c b/libsparse/append2simg.c
index 1cf827c..eef8764 100644
--- a/libsparse/append2simg.c
+++ b/libsparse/append2simg.c
@@ -82,7 +82,7 @@
         exit(-1);
     }
 
-    sparse_output = sparse_file_import_auto(output, true, true);
+    sparse_output = sparse_file_import_auto(output, false, true);
     if (!sparse_output) {
         fprintf(stderr, "Couldn't import output file\n");
         exit(-1);
diff --git a/libutils/Looper.cpp b/libutils/Looper.cpp
index b14884b..952c992 100644
--- a/libutils/Looper.cpp
+++ b/libutils/Looper.cpp
@@ -74,7 +74,7 @@
         mAllowNonCallbacks(allowNonCallbacks), mSendingMessage(false),
         mPolling(false), mEpollFd(-1), mEpollRebuildRequired(false),
         mNextRequestSeq(0), mResponseIndex(0), mNextMessageUptime(LLONG_MAX) {
-    mWakeEventFd = eventfd(0, EFD_NONBLOCK);
+    mWakeEventFd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
     LOG_ALWAYS_FATAL_IF(mWakeEventFd < 0, "Could not make wake event fd: %s",
                         strerror(errno));
 
diff --git a/libziparchive/Android.mk b/libziparchive/Android.mk
index 8a4921f..056b3e1 100644
--- a/libziparchive/Android.mk
+++ b/libziparchive/Android.mk
@@ -15,34 +15,46 @@
 
 LOCAL_PATH := $(call my-dir)
 
-source_files := zip_archive.cc zip_writer.cc
-test_files := zip_archive_test.cc zip_writer_test.cc entry_name_utils_test.cc
+libziparchive_source_files := \
+    zip_archive.cc \
+    zip_archive_stream_entry.cc \
+    zip_writer.cc \
+
+libziparchive_test_files := \
+    entry_name_utils_test.cc \
+    zip_archive_test.cc \
+    zip_writer_test.cc \
 
 # ZLIB_CONST turns on const for input buffers, which is pretty standard.
-common_c_flags := -Werror -Wall -DZLIB_CONST
+libziparchive_common_c_flags := \
+    -DZLIB_CONST \
+    -Werror \
+    -Wall \
 
 # Incorrectly warns when C++11 empty brace {} initializer is used.
 # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61489
-common_cpp_flags := -Wold-style-cast -Wno-missing-field-initializers
+libziparchive_common_cpp_flags := \
+    -Wold-style-cast \
+    -Wno-missing-field-initializers \
 
 include $(CLEAR_VARS)
 LOCAL_CPP_EXTENSION := .cc
-LOCAL_SRC_FILES := ${source_files}
+LOCAL_SRC_FILES := $(libziparchive_source_files)
 LOCAL_STATIC_LIBRARIES := libz
 LOCAL_SHARED_LIBRARIES := libutils libbase
 LOCAL_MODULE:= libziparchive
-LOCAL_CFLAGS := $(common_c_flags)
-LOCAL_CPPFLAGS := $(common_cpp_flags)
+LOCAL_CFLAGS := $(libziparchive_common_c_flags)
+LOCAL_CPPFLAGS := $(libziparchive_common_cpp_flags)
 include $(BUILD_STATIC_LIBRARY)
 
 include $(CLEAR_VARS)
 LOCAL_CPP_EXTENSION := .cc
-LOCAL_SRC_FILES := ${source_files}
+LOCAL_SRC_FILES := $(libziparchive_source_files)
 LOCAL_STATIC_LIBRARIES := libz libutils libbase
 LOCAL_MODULE:= libziparchive-host
-LOCAL_CFLAGS := $(common_c_flags)
+LOCAL_CFLAGS := $(libziparchive_common_c_flags)
 LOCAL_CFLAGS_windows := -mno-ms-bitfields
-LOCAL_CPPFLAGS := $(common_cpp_flags)
+LOCAL_CPPFLAGS := $(libziparchive_common_cpp_flags)
 
 LOCAL_MULTILIB := both
 LOCAL_MODULE_HOST_OS := darwin linux windows
@@ -50,12 +62,12 @@
 
 include $(CLEAR_VARS)
 LOCAL_CPP_EXTENSION := .cc
-LOCAL_SRC_FILES := ${source_files}
+LOCAL_SRC_FILES := $(libziparchive_source_files)
 LOCAL_STATIC_LIBRARIES := libutils
 LOCAL_SHARED_LIBRARIES := libz-host liblog libbase
 LOCAL_MODULE:= libziparchive-host
-LOCAL_CFLAGS := $(common_c_flags)
-LOCAL_CPPFLAGS := $(common_cpp_flags)
+LOCAL_CFLAGS := $(libziparchive_common_c_flags)
+LOCAL_CPPFLAGS := $(libziparchive_common_cpp_flags)
 LOCAL_MULTILIB := both
 include $(BUILD_HOST_SHARED_LIBRARY)
 
@@ -63,21 +75,33 @@
 include $(CLEAR_VARS)
 LOCAL_MODULE := ziparchive-tests
 LOCAL_CPP_EXTENSION := .cc
-LOCAL_CFLAGS := $(common_c_flags)
-LOCAL_CPPFLAGS := $(common_cpp_flags)
-LOCAL_SRC_FILES := $(test_files)
-LOCAL_SHARED_LIBRARIES := liblog libbase
-LOCAL_STATIC_LIBRARIES := libziparchive libz libutils
+LOCAL_CFLAGS := $(libziparchive_common_c_flags)
+LOCAL_CPPFLAGS := $(libziparchive_common_cpp_flags)
+LOCAL_SRC_FILES := $(libziparchive_test_files)
+LOCAL_SHARED_LIBRARIES := \
+    libbase \
+    liblog \
+
+LOCAL_STATIC_LIBRARIES := \
+    libziparchive \
+    libz \
+    libutils \
+
 include $(BUILD_NATIVE_TEST)
 
 include $(CLEAR_VARS)
 LOCAL_MODULE := ziparchive-tests-host
 LOCAL_CPP_EXTENSION := .cc
-LOCAL_CFLAGS := $(common_c_flags)
-LOCAL_CPPFLAGS := -Wno-unnamed-type-template-args $(common_cpp_flags)
-LOCAL_SRC_FILES := $(test_files)
-LOCAL_SHARED_LIBRARIES := libziparchive-host liblog libbase
+LOCAL_CFLAGS := $(libziparchive_common_c_flags)
+LOCAL_CPPFLAGS := -Wno-unnamed-type-template-args $(libziparchive_common_cpp_flags)
+LOCAL_SRC_FILES := $(libziparchive_test_files)
+LOCAL_SHARED_LIBRARIES := \
+    libziparchive-host \
+    liblog \
+    libbase \
+
 LOCAL_STATIC_LIBRARIES := \
+    libutils \
     libz \
-    libutils
+
 include $(BUILD_HOST_NATIVE_TEST)
diff --git a/libziparchive/testdata/bad_crc.zip b/libziparchive/testdata/bad_crc.zip
new file mode 100644
index 0000000..e12ba07
--- /dev/null
+++ b/libziparchive/testdata/bad_crc.zip
Binary files differ
diff --git a/libziparchive/testdata/large.zip b/libziparchive/testdata/large.zip
new file mode 100644
index 0000000..49659c8
--- /dev/null
+++ b/libziparchive/testdata/large.zip
Binary files differ
diff --git a/libziparchive/zip_archive.cc b/libziparchive/zip_archive.cc
index 07ef6cd..3b1e972 100644
--- a/libziparchive/zip_archive.cc
+++ b/libziparchive/zip_archive.cc
@@ -36,11 +36,12 @@
 #include "log/log.h"
 #include "utils/Compat.h"
 #include "utils/FileMap.h"
+#include "ziparchive/zip_archive.h"
 #include "zlib.h"
 
 #include "entry_name_utils-inl.h"
 #include "zip_archive_common.h"
-#include "ziparchive/zip_archive.h"
+#include "zip_archive_private.h"
 
 using android::base::get_unaligned;
 
@@ -134,43 +135,6 @@
  * every page that the Central Directory touches.  Easier to tuck a copy
  * of the string length into the hash table entry.
  */
-struct ZipArchive {
-  /* open Zip archive */
-  const int fd;
-  const bool close_file;
-
-  /* mapped central directory area */
-  off64_t directory_offset;
-  android::FileMap directory_map;
-
-  /* number of entries in the Zip archive */
-  uint16_t num_entries;
-
-  /*
-   * We know how many entries are in the Zip archive, so we can have a
-   * fixed-size hash table. We define a load factor of 0.75 and overallocat
-   * so the maximum number entries can never be higher than
-   * ((4 * UINT16_MAX) / 3 + 1) which can safely fit into a uint32_t.
-   */
-  uint32_t hash_table_size;
-  ZipString* hash_table;
-
-  ZipArchive(const int fd, bool assume_ownership) :
-      fd(fd),
-      close_file(assume_ownership),
-      directory_offset(0),
-      num_entries(0),
-      hash_table_size(0),
-      hash_table(NULL) {}
-
-  ~ZipArchive() {
-    if (close_file && fd >= 0) {
-      close(fd);
-    }
-
-    free(hash_table);
-  }
-};
 
 /*
  * Round up to the next highest power of 2.
diff --git a/libziparchive/zip_archive_private.h b/libziparchive/zip_archive_private.h
new file mode 100644
index 0000000..ab52368
--- /dev/null
+++ b/libziparchive/zip_archive_private.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LIBZIPARCHIVE_ZIPARCHIVE_PRIVATE_H_
+#define LIBZIPARCHIVE_ZIPARCHIVE_PRIVATE_H_
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <utils/FileMap.h>
+#include <ziparchive/zip_archive.h>
+
+struct ZipArchive {
+  // open Zip archive
+  const int fd;
+  const bool close_file;
+
+  // mapped central directory area
+  off64_t directory_offset;
+  android::FileMap directory_map;
+
+  // number of entries in the Zip archive
+  uint16_t num_entries;
+
+  // We know how many entries are in the Zip archive, so we can have a
+  // fixed-size hash table. We define a load factor of 0.75 and over
+  // allocate so the maximum number entries can never be higher than
+  // ((4 * UINT16_MAX) / 3 + 1) which can safely fit into a uint32_t.
+  uint32_t hash_table_size;
+  ZipString* hash_table;
+
+  ZipArchive(const int fd, bool assume_ownership) :
+      fd(fd),
+      close_file(assume_ownership),
+      directory_offset(0),
+      num_entries(0),
+      hash_table_size(0),
+      hash_table(NULL) {}
+
+  ~ZipArchive() {
+    if (close_file && fd >= 0) {
+      close(fd);
+    }
+
+    free(hash_table);
+  }
+};
+
+#endif  // LIBZIPARCHIVE_ZIPARCHIVE_PRIVATE_H_
diff --git a/libziparchive/zip_archive_stream_entry.cc b/libziparchive/zip_archive_stream_entry.cc
new file mode 100644
index 0000000..f618835
--- /dev/null
+++ b/libziparchive/zip_archive_stream_entry.cc
@@ -0,0 +1,305 @@
+/*
+ * 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.
+ */
+
+// Read-only stream access to Zip Archive entries.
+#include <errno.h>
+#include <inttypes.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <memory>
+#include <vector>
+
+#define LOG_TAG "ZIPARCHIVE"
+#include <android-base/file.h>
+#include <log/log.h>
+#include <ziparchive/zip_archive.h>
+#include <ziparchive/zip_archive_stream_entry.h>
+#include <zlib.h>
+
+#include "zip_archive_private.h"
+
+static constexpr size_t kBufSize = 65535;
+
+bool ZipArchiveStreamEntry::Init(const ZipEntry& entry) {
+  ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle_);
+  off64_t data_offset = entry.offset;
+  if (lseek64(archive->fd, data_offset, SEEK_SET) != data_offset) {
+    ALOGW("lseek to data at %" PRId64 " failed: %s", data_offset, strerror(errno));
+    return false;
+  }
+  crc32_ = entry.crc32;
+  return true;
+}
+
+class ZipArchiveStreamEntryUncompressed : public ZipArchiveStreamEntry {
+ public:
+  ZipArchiveStreamEntryUncompressed(ZipArchiveHandle handle) : ZipArchiveStreamEntry(handle) {}
+  virtual ~ZipArchiveStreamEntryUncompressed() {}
+
+  const std::vector<uint8_t>* Read() override;
+
+  bool Verify() override;
+
+ protected:
+  bool Init(const ZipEntry& entry) override;
+
+  uint32_t length_;
+
+ private:
+  std::vector<uint8_t> data_;
+  uint32_t computed_crc32_;
+};
+
+bool ZipArchiveStreamEntryUncompressed::Init(const ZipEntry& entry) {
+  if (!ZipArchiveStreamEntry::Init(entry)) {
+    return false;
+  }
+
+  length_ = entry.uncompressed_length;
+
+  data_.resize(kBufSize);
+  computed_crc32_ = 0;
+
+  return true;
+}
+
+const std::vector<uint8_t>* ZipArchiveStreamEntryUncompressed::Read() {
+  if (length_ == 0) {
+    return nullptr;
+  }
+
+  size_t bytes = (length_ > data_.size()) ? data_.size() : length_;
+  ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle_);
+  errno = 0;
+  if (!android::base::ReadFully(archive->fd, data_.data(), bytes)) {
+    if (errno != 0) {
+      ALOGE("Error reading from archive fd: %s", strerror(errno));
+    } else {
+      ALOGE("Short read of zip file, possibly corrupted zip?");
+    }
+    length_ = 0;
+    return nullptr;
+  }
+
+  if (bytes < data_.size()) {
+    data_.resize(bytes);
+  }
+  computed_crc32_ = crc32(computed_crc32_, data_.data(), data_.size());
+  length_ -= bytes;
+  return &data_;
+}
+
+bool ZipArchiveStreamEntryUncompressed::Verify() {
+  return length_ == 0 && crc32_ == computed_crc32_;
+}
+
+class ZipArchiveStreamEntryCompressed : public ZipArchiveStreamEntry {
+ public:
+  ZipArchiveStreamEntryCompressed(ZipArchiveHandle handle) : ZipArchiveStreamEntry(handle) {}
+  virtual ~ZipArchiveStreamEntryCompressed();
+
+  const std::vector<uint8_t>* Read() override;
+
+  bool Verify() override;
+
+ protected:
+  bool Init(const ZipEntry& entry) override;
+
+ private:
+  bool z_stream_init_ = false;
+  z_stream z_stream_;
+  std::vector<uint8_t> in_;
+  std::vector<uint8_t> out_;
+  uint32_t uncompressed_length_;
+  uint32_t compressed_length_;
+  uint32_t computed_crc32_;
+};
+
+// This method is using libz macros with old-style-casts
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wold-style-cast"
+static inline int zlib_inflateInit2(z_stream* stream, int window_bits) {
+  return inflateInit2(stream, window_bits);
+}
+#pragma GCC diagnostic pop
+
+bool ZipArchiveStreamEntryCompressed::Init(const ZipEntry& entry) {
+  if (!ZipArchiveStreamEntry::Init(entry)) {
+    return false;
+  }
+
+  // Initialize the zlib stream struct.
+  memset(&z_stream_, 0, sizeof(z_stream_));
+  z_stream_.zalloc = Z_NULL;
+  z_stream_.zfree = Z_NULL;
+  z_stream_.opaque = Z_NULL;
+  z_stream_.next_in = nullptr;
+  z_stream_.avail_in = 0;
+  z_stream_.avail_out = 0;
+  z_stream_.data_type = Z_UNKNOWN;
+
+  // Use the undocumented "negative window bits" feature to tell zlib
+  // that there's no zlib header waiting for it.
+  int zerr = zlib_inflateInit2(&z_stream_, -MAX_WBITS);
+  if (zerr != Z_OK) {
+    if (zerr == Z_VERSION_ERROR) {
+      ALOGE("Installed zlib is not compatible with linked version (%s)",
+        ZLIB_VERSION);
+    } else {
+      ALOGE("Call to inflateInit2 failed (zerr=%d)", zerr);
+    }
+
+    return false;
+  }
+
+  z_stream_init_ = true;
+
+  uncompressed_length_ = entry.uncompressed_length;
+  compressed_length_ = entry.compressed_length;
+
+  out_.resize(kBufSize);
+  in_.resize(kBufSize);
+
+  computed_crc32_ = 0;
+
+  return true;
+}
+
+ZipArchiveStreamEntryCompressed::~ZipArchiveStreamEntryCompressed() {
+  if (z_stream_init_) {
+    inflateEnd(&z_stream_);
+    z_stream_init_ = false;
+  }
+}
+
+bool ZipArchiveStreamEntryCompressed::Verify() {
+  return z_stream_init_ && uncompressed_length_ == 0 && compressed_length_ == 0 &&
+      crc32_ == computed_crc32_;
+}
+
+const std::vector<uint8_t>* ZipArchiveStreamEntryCompressed::Read() {
+  if (z_stream_.avail_out == 0) {
+    z_stream_.next_out = out_.data();
+    z_stream_.avail_out = out_.size();;
+  }
+
+  while (true) {
+    if (z_stream_.avail_in == 0) {
+      if (compressed_length_ == 0) {
+        return nullptr;
+      }
+      size_t bytes = (compressed_length_ > in_.size()) ? in_.size() : compressed_length_;
+      ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle_);
+      errno = 0;
+      if (!android::base::ReadFully(archive->fd, in_.data(), bytes)) {
+        if (errno != 0) {
+          ALOGE("Error reading from archive fd: %s", strerror(errno));
+        } else {
+          ALOGE("Short read of zip file, possibly corrupted zip?");
+        }
+        return nullptr;
+      }
+
+      compressed_length_ -= bytes;
+      z_stream_.next_in = in_.data();
+      z_stream_.avail_in = bytes;
+    }
+
+    int zerr = inflate(&z_stream_, Z_NO_FLUSH);
+    if (zerr != Z_OK && zerr != Z_STREAM_END) {
+      ALOGE("inflate zerr=%d (nIn=%p aIn=%u nOut=%p aOut=%u)",
+          zerr, z_stream_.next_in, z_stream_.avail_in,
+          z_stream_.next_out, z_stream_.avail_out);
+      return nullptr;
+    }
+
+    if (z_stream_.avail_out == 0) {
+      uncompressed_length_ -= out_.size();
+      computed_crc32_ = crc32(computed_crc32_, out_.data(), out_.size());
+      return &out_;
+    }
+    if (zerr == Z_STREAM_END) {
+      if (z_stream_.avail_out != 0) {
+        // Resize the vector down to the actual size of the data.
+        out_.resize(out_.size() - z_stream_.avail_out);
+        computed_crc32_ = crc32(computed_crc32_, out_.data(), out_.size());
+        uncompressed_length_ -= out_.size();
+        return &out_;
+      }
+      return nullptr;
+    }
+  }
+  return nullptr;
+}
+
+class ZipArchiveStreamEntryRawCompressed : public ZipArchiveStreamEntryUncompressed {
+ public:
+  ZipArchiveStreamEntryRawCompressed(ZipArchiveHandle handle)
+      : ZipArchiveStreamEntryUncompressed(handle) {}
+  virtual ~ZipArchiveStreamEntryRawCompressed() {}
+
+  bool Verify() override;
+
+ protected:
+  bool Init(const ZipEntry& entry) override;
+};
+
+bool ZipArchiveStreamEntryRawCompressed::Init(const ZipEntry& entry) {
+  if (!ZipArchiveStreamEntryUncompressed::Init(entry)) {
+    return false;
+  }
+  length_ = entry.compressed_length;
+
+  return true;
+}
+
+bool ZipArchiveStreamEntryRawCompressed::Verify() {
+  return length_ == 0;
+}
+
+ZipArchiveStreamEntry* ZipArchiveStreamEntry::Create(
+    ZipArchiveHandle handle, const ZipEntry& entry) {
+  ZipArchiveStreamEntry* stream = nullptr;
+  if (entry.method != kCompressStored) {
+    stream = new ZipArchiveStreamEntryCompressed(handle);
+  } else {
+    stream = new ZipArchiveStreamEntryUncompressed(handle);
+  }
+  if (stream && !stream->Init(entry)) {
+    delete stream;
+    stream = nullptr;
+  }
+
+  return stream;
+}
+
+ZipArchiveStreamEntry* ZipArchiveStreamEntry::CreateRaw(
+    ZipArchiveHandle handle, const ZipEntry& entry) {
+  ZipArchiveStreamEntry* stream = nullptr;
+  if (entry.method == kCompressStored) {
+    // Not compressed, don't need to do anything special.
+    stream = new ZipArchiveStreamEntryUncompressed(handle);
+  } else {
+    stream = new ZipArchiveStreamEntryRawCompressed(handle);
+  }
+  if (stream && !stream->Init(entry)) {
+    delete stream;
+    stream = nullptr;
+  }
+  return stream;
+}
diff --git a/libziparchive/zip_archive_test.cc b/libziparchive/zip_archive_test.cc
index cb0f410..d426dc4 100644
--- a/libziparchive/zip_archive_test.cc
+++ b/libziparchive/zip_archive_test.cc
@@ -14,54 +14,49 @@
  * limitations under the License.
  */
 
-#include "ziparchive/zip_archive.h"
-
 #include <errno.h>
 #include <fcntl.h>
 #include <getopt.h>
 #include <stdio.h>
+#include <string.h>
 #include <unistd.h>
+
 #include <vector>
 
 #include <android-base/file.h>
 #include <gtest/gtest.h>
+#include <ziparchive/zip_archive.h>
+#include <ziparchive/zip_archive_stream_entry.h>
 
 static std::string test_data_dir;
 
 static const std::string kMissingZip = "missing.zip";
 static const std::string kValidZip = "valid.zip";
+static const std::string kLargeZip = "large.zip";
+static const std::string kBadCrcZip = "bad_crc.zip";
 
-static const uint8_t kATxtContents[] = {
+static const std::vector<uint8_t> kATxtContents {
   'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
   'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
   '\n'
 };
 
-static const uint8_t kBTxtContents[] = {
+static const std::vector<uint8_t> kATxtContentsCompressed {
+  'K', 'L', 'J', 'N', 'I', 'M', 'K', 207, 'H',
+  132, 210, '\\', '\0'
+};
+
+static const std::vector<uint8_t> kBTxtContents {
   'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
   '\n'
 };
 
-static const uint16_t kATxtNameLength = 5;
-static const uint16_t kBTxtNameLength = 5;
-static const uint16_t kNonexistentTxtNameLength = 15;
-static const uint16_t kEmptyTxtNameLength = 9;
-
-static const uint8_t kATxtName[kATxtNameLength] = {
-  'a', '.', 't', 'x', 't'
-};
-
-static const uint8_t kBTxtName[kBTxtNameLength] = {
-  'b', '.', 't', 'x', 't'
-};
-
-static const uint8_t kNonexistentTxtName[kNonexistentTxtNameLength] = {
-  'n', 'o', 'n', 'e', 'x', 'i', 's', 't', 'e', 'n', 't', '.', 't', 'x' ,'t'
-};
-
-static const uint8_t kEmptyTxtName[kEmptyTxtNameLength] = {
-  'e', 'm', 'p', 't', 'y', '.', 't', 'x', 't'
-};
+static const std::string kATxtName("a.txt");
+static const std::string kBTxtName("b.txt");
+static const std::string kNonexistentTxtName("nonexistent.txt");
+static const std::string kEmptyTxtName("empty.txt");
+static const std::string kLargeCompressTxtName("compress.txt");
+static const std::string kLargeUncompressTxtName("uncompress.txt");
 
 static int32_t OpenArchiveWrapper(const std::string& name,
                                   ZipArchiveHandle* handle) {
@@ -75,6 +70,11 @@
   ASSERT_EQ(0, memcmp(name_str.c_str(), name.name, name.name_length));
 }
 
+static void SetZipString(ZipString* zip_str, const std::string& str) {
+  zip_str->name = reinterpret_cast<const uint8_t*>(str.c_str());
+  zip_str->name_length = str.size();
+}
+
 TEST(ziparchive, Open) {
   ZipArchiveHandle handle;
   ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
@@ -115,7 +115,7 @@
   ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
 
   void* iteration_cookie;
-  ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, NULL, NULL));
+  ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, nullptr, nullptr));
 
   ZipEntry data;
   ZipString name;
@@ -152,7 +152,7 @@
 
   void* iteration_cookie;
   ZipString prefix("b/");
-  ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, &prefix, NULL));
+  ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, &prefix, nullptr));
 
   ZipEntry data;
   ZipString name;
@@ -181,7 +181,7 @@
 
   void* iteration_cookie;
   ZipString suffix(".txt");
-  ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, NULL, &suffix));
+  ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, nullptr, &suffix));
 
   ZipEntry data;
   ZipString name;
@@ -262,8 +262,7 @@
 
   ZipEntry data;
   ZipString name;
-  name.name = kATxtName;
-  name.name_length = kATxtNameLength;
+  SetZipString(&name, kATxtName);
   ASSERT_EQ(0, FindEntry(handle, name, &data));
 
   // Known facts about a.txt, from zipinfo -v.
@@ -276,8 +275,7 @@
 
   // An entry that doesn't exist. Should be a negative return code.
   ZipString absent_name;
-  absent_name.name = kNonexistentTxtName;
-  absent_name.name_length = kNonexistentTxtNameLength;
+  SetZipString(&absent_name, kNonexistentTxtName);
   ASSERT_LT(FindEntry(handle, absent_name, &data), 0);
 
   CloseArchive(handle);
@@ -288,7 +286,7 @@
   ASSERT_EQ(0, OpenArchiveWrapper("declaredlength.zip", &handle));
 
   void* iteration_cookie;
-  ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, NULL, NULL));
+  ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, nullptr, nullptr));
 
   ZipString name;
   ZipEntry data;
@@ -306,26 +304,24 @@
   // An entry that's deflated.
   ZipEntry data;
   ZipString a_name;
-  a_name.name = kATxtName;
-  a_name.name_length = kATxtNameLength;
+  SetZipString(&a_name, kATxtName);
   ASSERT_EQ(0, FindEntry(handle, a_name, &data));
   const uint32_t a_size = data.uncompressed_length;
-  ASSERT_EQ(a_size, sizeof(kATxtContents));
+  ASSERT_EQ(a_size, kATxtContents.size());
   uint8_t* buffer = new uint8_t[a_size];
   ASSERT_EQ(0, ExtractToMemory(handle, &data, buffer, a_size));
-  ASSERT_EQ(0, memcmp(buffer, kATxtContents, a_size));
+  ASSERT_EQ(0, memcmp(buffer, kATxtContents.data(), a_size));
   delete[] buffer;
 
   // An entry that's stored.
   ZipString b_name;
-  b_name.name = kBTxtName;
-  b_name.name_length = kBTxtNameLength;
+  SetZipString(&b_name, kBTxtName);
   ASSERT_EQ(0, FindEntry(handle, b_name, &data));
   const uint32_t b_size = data.uncompressed_length;
-  ASSERT_EQ(b_size, sizeof(kBTxtContents));
+  ASSERT_EQ(b_size, kBTxtContents.size());
   buffer = new uint8_t[b_size];
   ASSERT_EQ(0, ExtractToMemory(handle, &data, buffer, b_size));
-  ASSERT_EQ(0, memcmp(buffer, kBTxtContents, b_size));
+  ASSERT_EQ(0, memcmp(buffer, kBTxtContents.data(), b_size));
   delete[] buffer;
 
   CloseArchive(handle);
@@ -374,8 +370,7 @@
   0x0100, 0x4c00, 0x0000, 0x5b00, 0x0001, 0x0000, 0x0000
 };
 
-static const uint8_t kAbTxtName[] = { 'a', 'b', '.', 't', 'x', 't' };
-static const uint16_t kAbTxtNameLength = sizeof(kAbTxtName);
+static const std::string kAbTxtName("ab.txt");
 static const size_t kAbUncompressedSize = 270216;
 
 static int make_temporary_file(const char* file_name_pattern) {
@@ -405,8 +400,7 @@
 
   ZipEntry entry;
   ZipString empty_name;
-  empty_name.name = kEmptyTxtName;
-  empty_name.name_length = kEmptyTxtNameLength;
+  SetZipString(&empty_name, kEmptyTxtName);
   ASSERT_EQ(0, FindEntry(handle, empty_name, &entry));
   ASSERT_EQ(static_cast<uint32_t>(0), entry.uncompressed_length);
   uint8_t buffer[1];
@@ -436,8 +430,7 @@
 
   ZipEntry entry;
   ZipString ab_name;
-  ab_name.name = kAbTxtName;
-  ab_name.name_length = kAbTxtNameLength;
+  SetZipString(&ab_name, kAbTxtName);
   ASSERT_EQ(0, FindEntry(handle, ab_name, &entry));
   ASSERT_EQ(kAbUncompressedSize, entry.uncompressed_length);
 
@@ -504,8 +497,7 @@
 
   ZipEntry entry;
   ZipString name;
-  name.name = kATxtName;
-  name.name_length = kATxtNameLength;
+  SetZipString(&name, kATxtName);
   ASSERT_EQ(0, FindEntry(handle, name, &entry));
   ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, fd));
 
@@ -521,22 +513,131 @@
   ASSERT_EQ(static_cast<ssize_t>(entry.uncompressed_length),
             TEMP_FAILURE_RETRY(
                 read(fd, &uncompressed_data[0], entry.uncompressed_length)));
-  ASSERT_EQ(0, memcmp(&uncompressed_data[0], kATxtContents,
-                      sizeof(kATxtContents)));
+  ASSERT_EQ(0, memcmp(&uncompressed_data[0], kATxtContents.data(),
+                      kATxtContents.size()));
 
   // Assert that the total length of the file is sane
-  ASSERT_EQ(data_size + static_cast<ssize_t>(sizeof(kATxtContents)),
+  ASSERT_EQ(data_size + static_cast<ssize_t>(kATxtContents.size()),
             lseek64(fd, 0, SEEK_END));
 
   close(fd);
 }
 
+static void ZipArchiveStreamTest(
+    ZipArchiveHandle& handle, const std::string& entry_name, bool raw,
+    bool verified, ZipEntry* entry, std::vector<uint8_t>* read_data) {
+  ZipString name;
+  SetZipString(&name, entry_name);
+  ASSERT_EQ(0, FindEntry(handle, name, entry));
+  std::unique_ptr<ZipArchiveStreamEntry> stream;
+  if (raw) {
+    stream.reset(ZipArchiveStreamEntry::CreateRaw(handle, *entry));
+    if (entry->method == kCompressStored) {
+      read_data->resize(entry->uncompressed_length);
+    } else {
+      read_data->resize(entry->compressed_length);
+    }
+  } else {
+    stream.reset(ZipArchiveStreamEntry::Create(handle, *entry));
+    read_data->resize(entry->uncompressed_length);
+  }
+  uint8_t* read_data_ptr = read_data->data();
+  ASSERT_TRUE(stream.get() != nullptr);
+  const std::vector<uint8_t>* data;
+  uint64_t total_size = 0;
+  while ((data = stream->Read()) != nullptr) {
+    total_size += data->size();
+    memcpy(read_data_ptr, data->data(), data->size());
+    read_data_ptr += data->size();
+  }
+  ASSERT_EQ(verified, stream->Verify());
+  ASSERT_EQ(total_size, read_data->size());
+}
+
+static void ZipArchiveStreamTestUsingContents(
+    const std::string& zip_file, const std::string& entry_name,
+    const std::vector<uint8_t>& contents, bool raw) {
+  ZipArchiveHandle handle;
+  ASSERT_EQ(0, OpenArchiveWrapper(zip_file, &handle));
+
+  ZipEntry entry;
+  std::vector<uint8_t> read_data;
+  ZipArchiveStreamTest(handle, entry_name, raw, true, &entry, &read_data);
+
+  ASSERT_EQ(contents.size(), read_data.size());
+  ASSERT_TRUE(memcmp(read_data.data(), contents.data(), read_data.size()) == 0);
+
+  CloseArchive(handle);
+}
+
+static void ZipArchiveStreamTestUsingMemory(const std::string& zip_file, const std::string& entry_name) {
+  ZipArchiveHandle handle;
+  ASSERT_EQ(0, OpenArchiveWrapper(zip_file, &handle));
+
+  ZipEntry entry;
+  std::vector<uint8_t> read_data;
+  ZipArchiveStreamTest(handle, entry_name, false, true, &entry, &read_data);
+
+  std::vector<uint8_t> cmp_data(entry.uncompressed_length);
+  ASSERT_EQ(entry.uncompressed_length, read_data.size());
+  ASSERT_EQ(0, ExtractToMemory(handle, &entry, cmp_data.data(), cmp_data.size()));
+  ASSERT_TRUE(memcmp(read_data.data(), cmp_data.data(), read_data.size()) == 0);
+
+  CloseArchive(handle);
+}
+
+TEST(ziparchive, StreamCompressed) {
+  ZipArchiveStreamTestUsingContents(kValidZip, kATxtName, kATxtContents, false);
+}
+
+TEST(ziparchive, StreamUncompressed) {
+  ZipArchiveStreamTestUsingContents(kValidZip, kBTxtName, kBTxtContents, false);
+}
+
+TEST(ziparchive, StreamRawCompressed) {
+  ZipArchiveStreamTestUsingContents(kValidZip, kATxtName, kATxtContentsCompressed, true);
+}
+
+TEST(ziparchive, StreamRawUncompressed) {
+  ZipArchiveStreamTestUsingContents(kValidZip, kBTxtName, kBTxtContents, true);
+}
+
+TEST(ziparchive, StreamLargeCompressed) {
+  ZipArchiveStreamTestUsingMemory(kLargeZip, kLargeCompressTxtName);
+}
+
+TEST(ziparchive, StreamLargeUncompressed) {
+  ZipArchiveStreamTestUsingMemory(kLargeZip, kLargeUncompressTxtName);
+}
+
+TEST(ziparchive, StreamCompressedBadCrc) {
+  ZipArchiveHandle handle;
+  ASSERT_EQ(0, OpenArchiveWrapper(kBadCrcZip, &handle));
+
+  ZipEntry entry;
+  std::vector<uint8_t> read_data;
+  ZipArchiveStreamTest(handle, kATxtName, false, false, &entry, &read_data);
+
+  CloseArchive(handle);
+}
+
+TEST(ziparchive, StreamUncompressedBadCrc) {
+  ZipArchiveHandle handle;
+  ASSERT_EQ(0, OpenArchiveWrapper(kBadCrcZip, &handle));
+
+  ZipEntry entry;
+  std::vector<uint8_t> read_data;
+  ZipArchiveStreamTest(handle, kBTxtName, false, false, &entry, &read_data);
+
+  CloseArchive(handle);
+}
+
 int main(int argc, char** argv) {
   ::testing::InitGoogleTest(&argc, argv);
 
   static struct option options[] = {
-    { "test_data_dir", required_argument, NULL, 't' },
-    { NULL, 0, NULL, 0 }
+    { "test_data_dir", required_argument, nullptr, 't' },
+    { nullptr, 0, nullptr, 0 }
   };
 
   while (true) {
@@ -557,9 +658,15 @@
   }
 
   if (test_data_dir[0] != '/') {
-    printf("Test data must be an absolute path, was %s\n\n",
-           test_data_dir.c_str());
-    return -2;
+    std::vector<char> cwd_buffer(1024);
+    const char* cwd = getcwd(cwd_buffer.data(), cwd_buffer.size() - 1);
+    if (cwd == nullptr) {
+      printf("Cannot get current working directory, use an absolute path instead, was %s\n\n",
+             test_data_dir.c_str());
+      return -2;
+    }
+    test_data_dir = '/' + test_data_dir;
+    test_data_dir = cwd + test_data_dir;
   }
 
   return RUN_ALL_TESTS();
diff --git a/libziparchive/zip_writer.cc b/libziparchive/zip_writer.cc
index f117cc5..1ebed30 100644
--- a/libziparchive/zip_writer.cc
+++ b/libziparchive/zip_writer.cc
@@ -20,12 +20,19 @@
 
 #include <utils/Log.h>
 
+#include <sys/param.h>
+
 #include <cassert>
 #include <cstdio>
 #include <memory>
+#include <vector>
 #include <zlib.h>
 #define DEF_MEM_LEVEL 8                // normally in zutil.h?
 
+#if !defined(powerof2)
+#define powerof2(x) ((((x)-1)&(x))==0)
+#endif
+
 /* Zip compression methods we support */
 enum {
   kCompressStored     = 0,        // no compression
@@ -50,6 +57,12 @@
 // An error occurred in zlib.
 static const int32_t kZlibError = -4;
 
+// The start aligned function was called with the aligned flag.
+static const int32_t kInvalidAlign32Flag = -5;
+
+// The alignment parameter is not a power of 2.
+static const int32_t kInvalidAlignment = -6;
+
 static const char* sErrorCodes[] = {
     "Invalid state",
     "IO error",
@@ -102,7 +115,25 @@
 }
 
 int32_t ZipWriter::StartEntry(const char* path, size_t flags) {
-  return StartEntryWithTime(path, flags, time_t());
+  uint32_t alignment = 0;
+  if (flags & kAlign32) {
+    flags &= ~kAlign32;
+    alignment = 4;
+  }
+  return StartAlignedEntryWithTime(path, flags, time_t(), alignment);
+}
+
+int32_t ZipWriter::StartAlignedEntry(const char* path, size_t flags, uint32_t alignment) {
+  return StartAlignedEntryWithTime(path, flags, time_t(), alignment);
+}
+
+int32_t ZipWriter::StartEntryWithTime(const char* path, size_t flags, time_t time) {
+  uint32_t alignment = 0;
+  if (flags & kAlign32) {
+    flags &= ~kAlign32;
+    alignment = 4;
+  }
+  return StartAlignedEntryWithTime(path, flags, time, alignment);
 }
 
 static void ExtractTimeAndDate(time_t when, uint16_t* out_time, uint16_t* out_date) {
@@ -126,11 +157,20 @@
   *out_time = ptm->tm_hour << 11 | ptm->tm_min << 5 | ptm->tm_sec >> 1;
 }
 
-int32_t ZipWriter::StartEntryWithTime(const char* path, size_t flags, time_t time) {
+int32_t ZipWriter::StartAlignedEntryWithTime(const char* path, size_t flags,
+                                             time_t time, uint32_t alignment) {
   if (state_ != State::kWritingZip) {
     return kInvalidState;
   }
 
+  if (flags & kAlign32) {
+    return kInvalidAlign32Flag;
+  }
+
+  if (powerof2(alignment) == 0) {
+    return kInvalidAlignment;
+  }
+
   FileInfo fileInfo = {};
   fileInfo.path = std::string(path);
   fileInfo.local_file_header_offset = current_offset_;
@@ -166,11 +206,14 @@
   header.file_name_length = fileInfo.path.size();
 
   off64_t offset = current_offset_ + sizeof(header) + fileInfo.path.size();
-  if ((flags & ZipWriter::kAlign32) && (offset & 0x03)) {
+  std::vector<char> zero_padding;
+  if (alignment != 0 && (offset & (alignment - 1))) {
     // Pad the extra field so the data will be aligned.
-    uint16_t padding = 4 - (offset % 4);
+    uint16_t padding = alignment - (offset % alignment);
     header.extra_field_length = padding;
     offset += padding;
+    zero_padding.resize(padding);
+    memset(zero_padding.data(), 0, zero_padding.size());
   }
 
   if (fwrite(&header, sizeof(header), 1, file_) != 1) {
@@ -181,7 +224,9 @@
     return HandleError(kIoError);
   }
 
-  if (fwrite("\0\0\0", 1, header.extra_field_length, file_) != header.extra_field_length) {
+  if (header.extra_field_length != 0 &&
+      fwrite(zero_padding.data(), 1, header.extra_field_length, file_)
+      != header.extra_field_length) {
     return HandleError(kIoError);
   }
 
diff --git a/libziparchive/zip_writer_test.cc b/libziparchive/zip_writer_test.cc
index b7d1458..fe0846d 100644
--- a/libziparchive/zip_writer_test.cc
+++ b/libziparchive/zip_writer_test.cc
@@ -19,6 +19,7 @@
 
 #include <android-base/test_utils.h>
 #include <gtest/gtest.h>
+#include <time.h>
 #include <memory>
 #include <vector>
 
@@ -122,7 +123,7 @@
   CloseArchive(handle);
 }
 
-TEST_F(zipwriter, WriteUncompressedZipWithAlignedFile) {
+TEST_F(zipwriter, WriteUncompressedZipFileWithAlignedFlag) {
   ZipWriter writer(file_);
 
   ASSERT_EQ(0, writer.StartEntry("align.txt", ZipWriter::kAlign32));
@@ -142,6 +143,103 @@
   CloseArchive(handle);
 }
 
+void ConvertZipTimeToTm(uint32_t& zip_time, struct tm* tm) {
+  memset(tm, 0, sizeof(struct tm));
+  tm->tm_hour = (zip_time >> 11) & 0x1f;
+  tm->tm_min = (zip_time >> 5) & 0x3f;
+  tm->tm_sec = (zip_time & 0x1f) << 1;
+
+  tm->tm_year = ((zip_time >> 25) & 0x7f) + 80;
+  tm->tm_mon = ((zip_time >> 21) & 0xf) - 1;
+  tm->tm_mday = (zip_time >> 16) & 0x1f;
+}
+
+TEST_F(zipwriter, WriteUncompressedZipFileWithAlignedFlagAndTime) {
+  ZipWriter writer(file_);
+
+  struct tm tm;
+  memset(&tm, 0, sizeof(struct tm));
+  ASSERT_TRUE(strptime("18:30:20 1/12/2001", "%H:%M:%S %d/%m/%Y", &tm) != nullptr);
+  time_t time = mktime(&tm);
+  ASSERT_EQ(0, writer.StartEntryWithTime("align.txt", ZipWriter::kAlign32, time));
+  ASSERT_EQ(0, writer.WriteBytes("he", 2));
+  ASSERT_EQ(0, writer.FinishEntry());
+  ASSERT_EQ(0, writer.Finish());
+
+  ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
+
+  ZipArchiveHandle handle;
+  ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
+
+  ZipEntry data;
+  ASSERT_EQ(0, FindEntry(handle, ZipString("align.txt"), &data));
+  EXPECT_EQ(0, data.offset & 0x03);
+
+  struct tm mod;
+  ConvertZipTimeToTm(data.mod_time, &mod);
+  EXPECT_EQ(tm.tm_sec, mod.tm_sec);
+  EXPECT_EQ(tm.tm_min, mod.tm_min);
+  EXPECT_EQ(tm.tm_hour, mod.tm_hour);
+  EXPECT_EQ(tm.tm_mday, mod.tm_mday);
+  EXPECT_EQ(tm.tm_mon, mod.tm_mon);
+  EXPECT_EQ(tm.tm_year, mod.tm_year);
+
+  CloseArchive(handle);
+}
+
+TEST_F(zipwriter, WriteUncompressedZipFileWithAlignedValue) {
+  ZipWriter writer(file_);
+
+  ASSERT_EQ(0, writer.StartAlignedEntry("align.txt", 0, 4096));
+  ASSERT_EQ(0, writer.WriteBytes("he", 2));
+  ASSERT_EQ(0, writer.FinishEntry());
+  ASSERT_EQ(0, writer.Finish());
+
+  ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
+
+  ZipArchiveHandle handle;
+  ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
+
+  ZipEntry data;
+  ASSERT_EQ(0, FindEntry(handle, ZipString("align.txt"), &data));
+  EXPECT_EQ(0, data.offset & 0xfff);
+
+  CloseArchive(handle);
+}
+
+TEST_F(zipwriter, WriteUncompressedZipFileWithAlignedValueAndTime) {
+  ZipWriter writer(file_);
+
+  struct tm tm;
+  memset(&tm, 0, sizeof(struct tm));
+  ASSERT_TRUE(strptime("18:30:20 1/12/2001", "%H:%M:%S %d/%m/%Y", &tm) != nullptr);
+  time_t time = mktime(&tm);
+  ASSERT_EQ(0, writer.StartAlignedEntryWithTime("align.txt", 0, time, 4096));
+  ASSERT_EQ(0, writer.WriteBytes("he", 2));
+  ASSERT_EQ(0, writer.FinishEntry());
+  ASSERT_EQ(0, writer.Finish());
+
+  ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
+
+  ZipArchiveHandle handle;
+  ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
+
+  ZipEntry data;
+  ASSERT_EQ(0, FindEntry(handle, ZipString("align.txt"), &data));
+  EXPECT_EQ(0, data.offset & 0xfff);
+
+  struct tm mod;
+  ConvertZipTimeToTm(data.mod_time, &mod);
+  EXPECT_EQ(tm.tm_sec, mod.tm_sec);
+  EXPECT_EQ(tm.tm_min, mod.tm_min);
+  EXPECT_EQ(tm.tm_hour, mod.tm_hour);
+  EXPECT_EQ(tm.tm_mday, mod.tm_mday);
+  EXPECT_EQ(tm.tm_mon, mod.tm_mon);
+  EXPECT_EQ(tm.tm_year, mod.tm_year);
+
+  CloseArchive(handle);
+}
+
 TEST_F(zipwriter, WriteCompressedZipWithOneFile) {
   ZipWriter writer(file_);
 
@@ -206,3 +304,10 @@
 
   CloseArchive(handle);
 }
+
+TEST_F(zipwriter, CheckStartEntryErrors) {
+  ZipWriter writer(file_);
+
+  ASSERT_EQ(-5, writer.StartAlignedEntry("align.txt", ZipWriter::kAlign32, 4096));
+  ASSERT_EQ(-6, writer.StartAlignedEntry("align.txt", 0, 3));
+}
diff --git a/lmkd/lmkd.c b/lmkd/lmkd.c
index 50bf6a4..aa3db8a 100644
--- a/lmkd/lmkd.c
+++ b/lmkd/lmkd.c
@@ -230,7 +230,7 @@
 }
 
 static void writefilestring(char *path, char *s) {
-    int fd = open(path, O_WRONLY);
+    int fd = open(path, O_WRONLY | O_CLOEXEC);
     int len = strlen(s);
     int ret;
 
@@ -487,7 +487,7 @@
 
     memset(mip, 0, sizeof(struct sysmeminfo));
 
-    fd = open(ZONEINFO_PATH, O_RDONLY);
+    fd = open(ZONEINFO_PATH, O_RDONLY | O_CLOEXEC);
     if (fd == -1) {
         ALOGE("%s open: errno=%d", ZONEINFO_PATH, errno);
         return -1;
@@ -518,7 +518,7 @@
     ssize_t ret;
 
     snprintf(path, PATH_MAX, "/proc/%d/statm", pid);
-    fd = open(path, O_RDONLY);
+    fd = open(path, O_RDONLY | O_CLOEXEC);
     if (fd == -1)
         return -1;
 
@@ -541,7 +541,7 @@
     ssize_t ret;
 
     snprintf(path, PATH_MAX, "/proc/%d/cmdline", pid);
-    fd = open(path, O_RDONLY);
+    fd = open(path, O_RDONLY | O_CLOEXEC);
     if (fd == -1)
         return NULL;
     ret = read_all(fd, line, sizeof(line) - 1);
@@ -686,19 +686,19 @@
     struct epoll_event epev;
     int ret;
 
-    mpfd = open(MEMCG_SYSFS_PATH "memory.pressure_level", O_RDONLY);
+    mpfd = open(MEMCG_SYSFS_PATH "memory.pressure_level", O_RDONLY | O_CLOEXEC);
     if (mpfd < 0) {
         ALOGI("No kernel memory.pressure_level support (errno=%d)", errno);
         goto err_open_mpfd;
     }
 
-    evctlfd = open(MEMCG_SYSFS_PATH "cgroup.event_control", O_WRONLY);
+    evctlfd = open(MEMCG_SYSFS_PATH "cgroup.event_control", O_WRONLY | O_CLOEXEC);
     if (evctlfd < 0) {
         ALOGI("No kernel memory cgroup event control (errno=%d)", errno);
         goto err_open_evctlfd;
     }
 
-    evfd = eventfd(0, EFD_NONBLOCK);
+    evfd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
     if (evfd < 0) {
         ALOGE("eventfd failed for level %s; errno=%d", levelstr, errno);
         goto err_eventfd;
diff --git a/logcat/logpersist b/logcat/logpersist
index 6f666f6..dab466d 100755
--- a/logcat/logpersist
+++ b/logcat/logpersist
@@ -10,7 +10,7 @@
 data=/data/misc/logd
 property=persist.logd.logpersistd
 service=logcatd
-if [ X"${1}" = "-h" -o X"${1}" = X"--help" ]; then
+if [ X"${1}" = X"-h" -o X"${1}" = X"--help" ]; then
   echo "${progname%.*}.cat            - dump current ${service%d} logs"
   echo "${progname%.*}.start          - start ${service} service"
   echo "${progname%.*}.stop [--clear] - stop ${service} service"
diff --git a/logcat/tests/logcat_test.cpp b/logcat/tests/logcat_test.cpp
index 61b020c..4f517bb 100644
--- a/logcat/tests/logcat_test.cpp
+++ b/logcat/tests/logcat_test.cpp
@@ -605,7 +605,7 @@
                 char c;
 
                 if ((2 == sscanf(buffer, "%d log.tx%c", &num, &c)) &&
-                        (num <= 24)) {
+                        (num <= 40)) {
                     ++count;
                 } else if (strncmp(buffer, total, sizeof(total) - 1)) {
                     fprintf(stderr, "WARNING: Parse error: %s", buffer);
diff --git a/logd/CommandListener.cpp b/logd/CommandListener.cpp
index e103359..7394f11 100644
--- a/logd/CommandListener.cpp
+++ b/logd/CommandListener.cpp
@@ -210,9 +210,20 @@
     }
 
     unsigned int logMask = -1;
+    pid_t pid = 0;
     if (argc > 1) {
         logMask = 0;
         for (int i = 1; i < argc; ++i) {
+            static const char _pid[] = "pid=";
+            if (!strncmp(argv[i], _pid, sizeof(_pid) - 1)) {
+                pid = atol(argv[i] + sizeof(_pid) - 1);
+                if (pid == 0) {
+                    cli->sendMsg("PID Error");
+                    return 0;
+                }
+                continue;
+            }
+
             int id = atoi(argv[i]);
             if ((id < LOG_ID_MIN) || (LOG_ID_MAX <= id)) {
                 cli->sendMsg("Range Error");
@@ -222,7 +233,8 @@
         }
     }
 
-    cli->sendMsg(package_string(mBuf.formatStatistics(uid, logMask)).c_str());
+    cli->sendMsg(package_string(mBuf.formatStatistics(uid, pid,
+                                                      logMask)).c_str());
     return 0;
 }
 
diff --git a/logd/FlushCommand.cpp b/logd/FlushCommand.cpp
index cb3d1c2..48036d3 100644
--- a/logd/FlushCommand.cpp
+++ b/logd/FlushCommand.cpp
@@ -36,7 +36,7 @@
         mLogMask(logMask),
         mPid(pid),
         mStart(start),
-        mTimeout(timeout) {
+        mTimeout((start > 1) ? timeout : 0) {
 }
 
 // runSocketCommand is called once for every open client on the
diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp
index 1e4f3b0..9e0d451 100644
--- a/logd/LogBuffer.cpp
+++ b/logd/LogBuffer.cpp
@@ -32,7 +32,7 @@
 #include "LogReader.h"
 
 // Default
-#define LOG_BUFFER_SIZE (256 * 1024) // Tuned on a per-platform basis here?
+#define LOG_BUFFER_SIZE (256 * 1024) // Tuned with ro.logd.size per-platform
 #define log_buffer_size(id) mMaxSize[id]
 #define LOG_BUFFER_MIN_SIZE (64 * 1024UL)
 #define LOG_BUFFER_MAX_SIZE (256 * 1024 * 1024UL)
@@ -100,9 +100,18 @@
     unsigned long default_size = property_get_size(global_tuneable);
     if (!default_size) {
         default_size = property_get_size(global_default);
+        if (!default_size) {
+            default_size = property_get_bool("ro.config.low_ram",
+                                             BOOL_DEFAULT_FALSE)
+                ? LOG_BUFFER_MIN_SIZE // 64K
+                : LOG_BUFFER_SIZE;    // 256K
+        }
     }
 
     log_id_for_each(i) {
+        mLastSet[i] = false;
+        mLast[i] = mLogElements.begin();
+
         char key[PROP_NAME_MAX];
 
         snprintf(key, sizeof(key), "%s.%s",
@@ -242,9 +251,9 @@
 
         LogTimeEntry::lock();
 
-        LastLogTimes::iterator t = mTimes.begin();
-        while(t != mTimes.end()) {
-            LogTimeEntry *entry = (*t);
+        LastLogTimes::iterator times = mTimes.begin();
+        while(times != mTimes.end()) {
+            LogTimeEntry *entry = (*times);
             if (entry->owned_Locked()) {
                 if (!entry->mNonBlock) {
                     end_always = true;
@@ -255,7 +264,7 @@
                     end_set = true;
                 }
             }
-            t++;
+            times++;
         }
 
         if (end_always
@@ -301,20 +310,51 @@
 
 LogBufferElementCollection::iterator LogBuffer::erase(
         LogBufferElementCollection::iterator it, bool coalesce) {
-    LogBufferElement *e = *it;
-    log_id_t id = e->getLogId();
+    LogBufferElement *element = *it;
+    log_id_t id = element->getLogId();
 
-    LogBufferIteratorMap::iterator f = mLastWorstUid[id].find(e->getUid());
-    if ((f != mLastWorstUid[id].end()) && (it == f->second)) {
-        mLastWorstUid[id].erase(f);
+    {   // start of scope for uid found iterator
+        LogBufferIteratorMap::iterator found =
+            mLastWorstUid[id].find(element->getUid());
+        if ((found != mLastWorstUid[id].end())
+                && (it == found->second)) {
+            mLastWorstUid[id].erase(found);
+        }
+    }
+
+    if (element->getUid() == AID_SYSTEM) {
+        // start of scope for pid found iterator
+        LogBufferPidIteratorMap::iterator found =
+            mLastWorstPidOfSystem[id].find(element->getPid());
+        if ((found != mLastWorstPidOfSystem[id].end())
+                && (it == found->second)) {
+            mLastWorstPidOfSystem[id].erase(found);
+        }
+    }
+
+    bool setLast[LOG_ID_MAX];
+    bool doSetLast = false;
+    log_id_for_each(i) {
+        doSetLast |= setLast[i] = mLastSet[i] && (it == mLast[i]);
     }
     it = mLogElements.erase(it);
-    if (coalesce) {
-        stats.erase(e);
-    } else {
-        stats.subtract(e);
+    if (doSetLast) {
+        log_id_for_each(i) {
+            if (setLast[i]) {
+                if (it == mLogElements.end()) { // unlikely
+                    mLastSet[i] = false;
+                } else {
+                    mLast[i] = it;
+                }
+            }
+        }
     }
-    delete e;
+    if (coalesce) {
+        stats.erase(element);
+    } else {
+        stats.subtract(element);
+    }
+    delete element;
 
     return it;
 }
@@ -334,8 +374,13 @@
     } __packed;
 
 public:
-    LogBufferElementKey(uid_t u, pid_t p, pid_t t):uid(u),pid(p),tid(t),padding(0) { }
-    LogBufferElementKey(uint64_t k):value(k) { }
+    LogBufferElementKey(uid_t uid, pid_t pid, pid_t tid):
+            uid(uid),
+            pid(pid),
+            tid(tid),
+            padding(0) {
+    }
+    LogBufferElementKey(uint64_t key):value(key) { }
 
     uint64_t getKey() { return value; }
 };
@@ -347,38 +392,42 @@
 
 public:
 
-    bool coalesce(LogBufferElement *e, unsigned short dropped) {
-        LogBufferElementKey key(e->getUid(), e->getPid(), e->getTid());
+    bool coalesce(LogBufferElement *element, unsigned short dropped) {
+        LogBufferElementKey key(element->getUid(),
+                                element->getPid(),
+                                element->getTid());
         LogBufferElementMap::iterator it = map.find(key.getKey());
         if (it != map.end()) {
-            LogBufferElement *l = it->second;
-            unsigned short d = l->getDropped();
-            if ((dropped + d) > USHRT_MAX) {
+            LogBufferElement *found = it->second;
+            unsigned short moreDropped = found->getDropped();
+            if ((dropped + moreDropped) > USHRT_MAX) {
                 map.erase(it);
             } else {
-                l->setDropped(dropped + d);
+                found->setDropped(dropped + moreDropped);
                 return true;
             }
         }
         return false;
     }
 
-    void add(LogBufferElement *e) {
-        LogBufferElementKey key(e->getUid(), e->getPid(), e->getTid());
-        map[key.getKey()] = e;
+    void add(LogBufferElement *element) {
+        LogBufferElementKey key(element->getUid(),
+                                element->getPid(),
+                                element->getTid());
+        map[key.getKey()] = element;
     }
 
     inline void clear() {
         map.clear();
     }
 
-    void clear(LogBufferElement *e) {
-        uint64_t current = e->getRealTime().nsec()
+    void clear(LogBufferElement *element) {
+        uint64_t current = element->getRealTime().nsec()
                          - (EXPIRE_RATELIMIT * NS_PER_SEC);
         for(LogBufferElementMap::iterator it = map.begin(); it != map.end();) {
-            LogBufferElement *l = it->second;
-            if ((l->getDropped() >= EXPIRE_THRESHOLD)
-                    && (current > l->getRealTime().nsec())) {
+            LogBufferElement *mapElement = it->second;
+            if ((mapElement->getDropped() >= EXPIRE_THRESHOLD)
+                    && (current > mapElement->getRealTime().nsec())) {
                 it = map.erase(it);
             } else {
                 ++it;
@@ -443,9 +492,9 @@
     LogTimeEntry::lock();
 
     // Region locked?
-    LastLogTimes::iterator t = mTimes.begin();
-    while(t != mTimes.end()) {
-        LogTimeEntry *entry = (*t);
+    LastLogTimes::iterator times = mTimes.begin();
+    while(times != mTimes.end()) {
+        LogTimeEntry *entry = (*times);
         if (entry->owned_Locked() && entry->isWatching(id)
                 && (!oldest ||
                     (oldest->mStart > entry->mStart) ||
@@ -453,22 +502,28 @@
                      (entry->mTimeout.tv_sec || entry->mTimeout.tv_nsec)))) {
             oldest = entry;
         }
-        t++;
+        times++;
     }
 
     LogBufferElementCollection::iterator it;
 
     if (caller_uid != AID_ROOT) {
         // Only here if clearAll condition (pruneRows == ULONG_MAX)
-        for(it = mLogElements.begin(); it != mLogElements.end();) {
-            LogBufferElement *e = *it;
+        it = mLastSet[id] ? mLast[id] : mLogElements.begin();
+        while (it != mLogElements.end()) {
+            LogBufferElement *element = *it;
 
-            if ((e->getLogId() != id) || (e->getUid() != caller_uid)) {
+            if ((element->getLogId() != id) || (element->getUid() != caller_uid)) {
                 ++it;
                 continue;
             }
 
-            if (oldest && (oldest->mStart <= e->getSequence())) {
+            if (!mLastSet[id] || ((*mLast[id])->getLogId() != id)) {
+                mLast[id] = it;
+                mLastSet[id] = true;
+            }
+
+            if (oldest && (oldest->mStart <= element->getSequence())) {
                 busy = true;
                 if (oldest->mTimeout.tv_sec || oldest->mTimeout.tv_nsec) {
                     oldest->triggerReader_Locked();
@@ -485,19 +540,21 @@
         return busy;
     }
 
-    // prune by worst offender by uid
+    // prune by worst offenders; by blacklist, UID, and by PID of system UID
     bool hasBlacklist = (id != LOG_ID_SECURITY) && mPrune.naughty();
     while (!clearAll && (pruneRows > 0)) {
         // recalculate the worst offender on every batched pass
         uid_t worst = (uid_t) -1;
         size_t worst_sizes = 0;
         size_t second_worst_sizes = 0;
+        pid_t worstPid = 0; // POSIX guarantees PID != 0
 
         if (worstUidEnabledForLogid(id) && mPrune.worstUidEnabled()) {
-            std::unique_ptr<const UidEntry *[]> sorted = stats.sort(2, id);
+            {   // begin scope for UID sorted list
+                std::unique_ptr<const UidEntry *[]> sorted = stats.sort(
+                    AID_ROOT, (pid_t)0, 2, id);
 
-            if (sorted.get()) {
-                if (sorted[0] && sorted[1]) {
+                if (sorted.get() && sorted[0] && sorted[1]) {
                     worst_sizes = sorted[0]->getSizes();
                     // Calculate threshold as 12.5% of available storage
                     size_t threshold = log_buffer_size(id) / 8;
@@ -513,6 +570,18 @@
                     }
                 }
             }
+
+            if ((worst == AID_SYSTEM) && mPrune.worstPidOfSystemEnabled()) {
+                // begin scope of PID sorted list
+                std::unique_ptr<const PidEntry *[]> sorted = stats.sort(
+                    worst, (pid_t)0, 2, id, worst);
+                if (sorted.get() && sorted[0] && sorted[1]) {
+                    worstPid = sorted[0]->getKey();
+                    second_worst_sizes = worst_sizes
+                                       - sorted[0]->getSizes()
+                                       + sorted[1]->getSizes();
+                }
+            }
         }
 
         // skip if we have neither worst nor naughty filters
@@ -522,18 +591,30 @@
 
         bool kick = false;
         bool leading = true;
-        it = mLogElements.begin();
+        it = mLastSet[id] ? mLast[id] : mLogElements.begin();
         // Perform at least one mandatory garbage collection cycle in following
         // - clear leading chatty tags
         // - coalesce chatty tags
         // - check age-out of preserved logs
         bool gc = pruneRows <= 1;
         if (!gc && (worst != (uid_t) -1)) {
-            LogBufferIteratorMap::iterator f = mLastWorstUid[id].find(worst);
-            if ((f != mLastWorstUid[id].end())
-                    && (f->second != mLogElements.end())) {
-                leading = false;
-                it = f->second;
+            {   // begin scope for uid worst found iterator
+                LogBufferIteratorMap::iterator found = mLastWorstUid[id].find(worst);
+                if ((found != mLastWorstUid[id].end())
+                        && (found->second != mLogElements.end())) {
+                    leading = false;
+                    it = found->second;
+                }
+            }
+            if (worstPid) {
+                // begin scope for pid worst found iterator
+                LogBufferPidIteratorMap::iterator found
+                    = mLastWorstPidOfSystem[id].find(worstPid);
+                if ((found != mLastWorstPidOfSystem[id].end())
+                        && (found->second != mLogElements.end())) {
+                    leading = false;
+                    it = found->second;
+                }
             }
         }
         static const timespec too_old = {
@@ -544,9 +625,9 @@
         --lastt;
         LogBufferElementLast last;
         while (it != mLogElements.end()) {
-            LogBufferElement *e = *it;
+            LogBufferElement *element = *it;
 
-            if (oldest && (oldest->mStart <= e->getSequence())) {
+            if (oldest && (oldest->mStart <= element->getSequence())) {
                 busy = true;
                 if (oldest->mTimeout.tv_sec || oldest->mTimeout.tv_nsec) {
                     oldest->triggerReader_Locked();
@@ -554,12 +635,17 @@
                 break;
             }
 
-            if (e->getLogId() != id) {
+            if (element->getLogId() != id) {
                 ++it;
                 continue;
             }
 
-            unsigned short dropped = e->getDropped();
+            if (leading && (!mLastSet[id] || ((*mLast[id])->getLogId() != id))) {
+                mLast[id] = it;
+                mLastSet[id] = true;
+            }
+
+            unsigned short dropped = element->getDropped();
 
             // remove any leading drops
             if (leading && dropped) {
@@ -567,13 +653,13 @@
                 continue;
             }
 
-            if (dropped && last.coalesce(e, dropped)) {
+            if (dropped && last.coalesce(element, dropped)) {
                 it = erase(it, true);
                 continue;
             }
 
-            if (hasBlacklist && mPrune.naughty(e)) {
-                last.clear(e);
+            if (hasBlacklist && mPrune.naughty(element)) {
+                last.clear(element);
                 it = erase(it);
                 if (dropped) {
                     continue;
@@ -584,35 +670,42 @@
                     break;
                 }
 
-                if (e->getUid() == worst) {
+                if (element->getUid() == worst) {
                     kick = true;
                     if (worst_sizes < second_worst_sizes) {
                         break;
                     }
-                    worst_sizes -= e->getMsgLen();
+                    worst_sizes -= element->getMsgLen();
                 }
                 continue;
             }
 
-            if ((e->getRealTime() < ((*lastt)->getRealTime() - too_old))
-                    || (e->getRealTime() > (*lastt)->getRealTime())) {
+            if ((element->getRealTime() < ((*lastt)->getRealTime() - too_old))
+                    || (element->getRealTime() > (*lastt)->getRealTime())) {
                 break;
             }
 
             if (dropped) {
-                last.add(e);
-                if ((!gc && (e->getUid() == worst))
-                        || (mLastWorstUid[id].find(e->getUid())
+                last.add(element);
+                if (worstPid
+                        && ((!gc && (element->getPid() == worstPid))
+                            || (mLastWorstPidOfSystem[id].find(element->getPid())
+                                == mLastWorstPidOfSystem[id].end()))) {
+                    mLastWorstPidOfSystem[id][element->getUid()] = it;
+                }
+                if ((!gc && !worstPid && (element->getUid() == worst))
+                        || (mLastWorstUid[id].find(element->getUid())
                             == mLastWorstUid[id].end())) {
-                    mLastWorstUid[id][e->getUid()] = it;
+                    mLastWorstUid[id][element->getUid()] = it;
                 }
                 ++it;
                 continue;
             }
 
-            if (e->getUid() != worst) {
+            if ((element->getUid() != worst)
+                    || (worstPid && (element->getPid() != worstPid))) {
                 leading = false;
-                last.clear(e);
+                last.clear(element);
                 ++it;
                 continue;
             }
@@ -624,19 +717,24 @@
 
             kick = true;
 
-            unsigned short len = e->getMsgLen();
+            unsigned short len = element->getMsgLen();
 
             // do not create any leading drops
             if (leading) {
                 it = erase(it);
             } else {
-                stats.drop(e);
-                e->setDropped(1);
-                if (last.coalesce(e, 1)) {
+                stats.drop(element);
+                element->setDropped(1);
+                if (last.coalesce(element, 1)) {
                     it = erase(it, true);
                 } else {
-                    last.add(e);
-                    if (!gc || (mLastWorstUid[id].find(worst)
+                    last.add(element);
+                    if (worstPid && (!gc
+                                || (mLastWorstPidOfSystem[id].find(worstPid)
+                                    == mLastWorstPidOfSystem[id].end()))) {
+                        mLastWorstPidOfSystem[id][worstPid] = it;
+                    }
+                    if ((!gc && !worstPid) || (mLastWorstUid[id].find(worst)
                                 == mLastWorstUid[id].end())) {
                         mLastWorstUid[id][worst] = it;
                     }
@@ -657,18 +755,22 @@
 
     bool whitelist = false;
     bool hasWhitelist = (id != LOG_ID_SECURITY) && mPrune.nice() && !clearAll;
-    it = mLogElements.begin();
+    it = mLastSet[id] ? mLast[id] : mLogElements.begin();
     while((pruneRows > 0) && (it != mLogElements.end())) {
-        LogBufferElement *e = *it;
+        LogBufferElement *element = *it;
 
-        if (e->getLogId() != id) {
+        if (element->getLogId() != id) {
             it++;
             continue;
         }
 
-        if (oldest && (oldest->mStart <= e->getSequence())) {
-            busy = true;
+        if (!mLastSet[id] || ((*mLast[id])->getLogId() != id)) {
+            mLast[id] = it;
+            mLastSet[id] = true;
+        }
 
+        if (oldest && (oldest->mStart <= element->getSequence())) {
+            busy = true;
             if (whitelist) {
                 break;
             }
@@ -684,7 +786,8 @@
             break;
         }
 
-        if (hasWhitelist && !e->getDropped() && mPrune.nice(e)) { // WhiteListed
+        if (hasWhitelist && !element->getDropped() && mPrune.nice(element)) {
+            // WhiteListed
             whitelist = true;
             it++;
             continue;
@@ -696,16 +799,21 @@
 
     // Do not save the whitelist if we are reader range limited
     if (whitelist && (pruneRows > 0)) {
-        it = mLogElements.begin();
+        it = mLastSet[id] ? mLast[id] : mLogElements.begin();
         while((it != mLogElements.end()) && (pruneRows > 0)) {
-            LogBufferElement *e = *it;
+            LogBufferElement *element = *it;
 
-            if (e->getLogId() != id) {
+            if (element->getLogId() != id) {
                 ++it;
                 continue;
             }
 
-            if (oldest && (oldest->mStart <= e->getSequence())) {
+            if (!mLastSet[id] || ((*mLast[id])->getLogId() != id)) {
+                mLast[id] = it;
+                mLastSet[id] = true;
+            }
+
+            if (oldest && (oldest->mStart <= element->getSequence())) {
                 busy = true;
                 if (stats.sizes(id) > (2 * log_buffer_size(id))) {
                     // kick a misbehaving log reader client off the island
@@ -861,10 +969,11 @@
     return max;
 }
 
-std::string LogBuffer::formatStatistics(uid_t uid, unsigned int logMask) {
+std::string LogBuffer::formatStatistics(uid_t uid, pid_t pid,
+                                        unsigned int logMask) {
     pthread_mutex_lock(&mLogElementsLock);
 
-    std::string ret = stats.format(uid, logMask);
+    std::string ret = stats.format(uid, pid, logMask);
 
     pthread_mutex_unlock(&mLogElementsLock);
 
diff --git a/logd/LogBuffer.h b/logd/LogBuffer.h
index c1fec73..03739c7 100644
--- a/logd/LogBuffer.h
+++ b/logd/LogBuffer.h
@@ -33,16 +33,42 @@
 #include "LogWhiteBlackList.h"
 
 //
-// We are either in 1970ish (MONOTONIC) or 2015+ish (REALTIME) so to
-// differentiate without prejudice, we use 1980 to delineate, earlier
-// is monotonic, later is real.
+// We are either in 1970ish (MONOTONIC) or 2016+ish (REALTIME) so to
+// differentiate without prejudice, we use 1972 to delineate, earlier
+// is likely monotonic, later is real. Otherwise we start using a
+// dividing line between monotonic and realtime if more than a minute
+// difference between them.
 //
 namespace android {
 
 static bool isMonotonic(const log_time &mono) {
-    static const uint32_t EPOCH_PLUS_10_YEARS = 10 * 1461 / 4 * 24 * 60 * 60;
+    static const uint32_t EPOCH_PLUS_2_YEARS = 2 * 24 * 60 * 60 * 1461 / 4;
+    static const uint32_t EPOCH_PLUS_MINUTE = 60;
 
-    return mono.tv_sec < EPOCH_PLUS_10_YEARS;
+    if (mono.tv_sec >= EPOCH_PLUS_2_YEARS) {
+        return false;
+    }
+
+    log_time now(CLOCK_REALTIME);
+
+    /* Timezone and ntp time setup? */
+    if (now.tv_sec >= EPOCH_PLUS_2_YEARS) {
+        return true;
+    }
+
+    /* no way to differentiate realtime from monotonic time */
+    if (now.tv_sec < EPOCH_PLUS_MINUTE) {
+        return false;
+    }
+
+    log_time cpu(CLOCK_MONOTONIC);
+    /* too close to call to differentiate monotonic times from realtime */
+    if ((cpu.tv_sec + EPOCH_PLUS_MINUTE) >= now.tv_sec) {
+        return false;
+    }
+
+    /* dividing line half way between monotonic and realtime */
+    return mono.tv_sec < ((cpu.tv_sec + now.tv_sec) / 2);
 }
 
 }
@@ -56,11 +82,19 @@
     LogStatistics stats;
 
     PruneList mPrune;
+    // watermark for last per log id
+    LogBufferElementCollection::iterator mLast[LOG_ID_MAX];
+    bool mLastSet[LOG_ID_MAX];
     // watermark of any worst/chatty uid processing
     typedef std::unordered_map<uid_t,
                                LogBufferElementCollection::iterator>
                 LogBufferIteratorMap;
     LogBufferIteratorMap mLastWorstUid[LOG_ID_MAX];
+    // watermark of any worst/chatty pid of system processing
+    typedef std::unordered_map<pid_t,
+                               LogBufferElementCollection::iterator>
+                LogBufferPidIteratorMap;
+    LogBufferPidIteratorMap mLastWorstPidOfSystem[LOG_ID_MAX];
 
     unsigned long mMaxSize[LOG_ID_MAX];
 
@@ -86,7 +120,7 @@
     int setSize(log_id_t id, unsigned long size);
     unsigned long getSizeUsed(log_id_t id);
     // *strp uses malloc, use free to release.
-    std::string formatStatistics(uid_t uid, unsigned int logMask);
+    std::string formatStatistics(uid_t uid, pid_t pid, unsigned int logMask);
 
     void enableStatistics() {
         stats.enableStatistics();
diff --git a/logd/LogKlog.cpp b/logd/LogKlog.cpp
index db7e682..9690489 100644
--- a/logd/LogKlog.cpp
+++ b/logd/LogKlog.cpp
@@ -206,7 +206,10 @@
     // NOTREACHED
 }
 
-log_time LogKlog::correction = log_time(CLOCK_REALTIME) - log_time(CLOCK_MONOTONIC);
+log_time LogKlog::correction =
+    (log_time(CLOCK_REALTIME) < log_time(CLOCK_MONOTONIC))
+        ? log_time::EPOCH
+        : (log_time(CLOCK_REALTIME) - log_time(CLOCK_MONOTONIC));
 
 LogKlog::LogKlog(LogBuffer *buf, LogReader *reader, int fdWrite, int fdRead, bool auditd) :
         SocketListener(fdRead, false),
@@ -272,7 +275,7 @@
                                   size_t len) {
     log_time real;
     const char *ep = real.strptime(real_string, "%Y-%m-%d %H:%M:%S.%09q UTC");
-    if (!ep || (ep > &real_string[len])) {
+    if (!ep || (ep > &real_string[len]) || (real > log_time(CLOCK_REALTIME))) {
         return;
     }
     // kernel report UTC, log_time::strptime is localtime from calendar.
@@ -283,8 +286,16 @@
     memset(&tm, 0, sizeof(tm));
     tm.tm_isdst = -1;
     localtime_r(&now, &tm);
-    real.tv_sec += tm.tm_gmtoff;
-    correction = real - monotonic;
+    if ((tm.tm_gmtoff < 0) && ((-tm.tm_gmtoff) > (long)real.tv_sec)) {
+        real = log_time::EPOCH;
+    } else {
+        real.tv_sec += tm.tm_gmtoff;
+    }
+    if (monotonic > real) {
+        correction = log_time::EPOCH;
+    } else {
+        correction = real - monotonic;
+    }
 }
 
 static const char suspendStr[] = "PM: suspend entry ";
@@ -319,11 +330,11 @@
     if (cp && (cp >= &(*buf)[len])) {
         cp = NULL;
     }
-    len -= cp - *buf;
     if (cp) {
         static const char healthd[] = "healthd";
         static const char battery[] = ": battery ";
 
+        len -= cp - *buf;
         if (len && isspace(*cp)) {
             ++cp;
             --len;
@@ -347,16 +358,11 @@
                 && ((size_t)((b += sizeof(healthd) - 1) - cp) < len)
                 && ((b = strnstr(b, len -= b - cp, battery)))
                 && ((size_t)((b += sizeof(battery) - 1) - cp) < len)) {
-            len -= b - cp;
-            // NB: healthd is roughly 150us late, worth the price to deal with
-            //     ntp-induced or hardware clock drift.
-            // look for " 2???-??-?? ??:??:??.????????? ???"
-            for (; len && *b && (*b != '\n'); ++b, --len) {
-                if ((b[0] == ' ') && (b[1] == '2') && (b[5] == '-')) {
-                    calculateCorrection(now, b + 1, len - 1);
-                    break;
-                }
-            }
+            // NB: healthd is roughly 150us late, so we use it instead to
+            //     trigger a check for ntp-induced or hardware clock drift.
+            log_time real(CLOCK_REALTIME);
+            log_time mono(CLOCK_MONOTONIC);
+            correction = (real < mono) ? log_time::EPOCH : (real - mono);
         } else if (((b = strnstr(cp, len, suspendedStr)))
                 && ((size_t)((b += sizeof(suspendStr) - 1) - cp) < len)) {
             len -= b - cp;
@@ -371,7 +377,11 @@
                     real.tv_nsec += (*endp - '0') * multiplier;
                 }
                 if (reverse) {
-                    correction -= real;
+                    if (real > correction) {
+                        correction = log_time::EPOCH;
+                    } else {
+                        correction -= real;
+                    }
                 } else {
                     correction += real;
                 }
diff --git a/logd/LogListener.cpp b/logd/LogListener.cpp
index 5348a2d..846dd7c 100644
--- a/logd/LogListener.cpp
+++ b/logd/LogListener.cpp
@@ -15,6 +15,7 @@
  */
 
 #include <limits.h>
+#include <sys/cdefs.h>
 #include <sys/prctl.h>
 #include <sys/socket.h>
 #include <sys/types.h>
@@ -46,7 +47,7 @@
         + LOGGER_ENTRY_MAX_PAYLOAD];
     struct iovec iov = { buffer, sizeof(buffer) };
 
-    char control[CMSG_SPACE(sizeof(struct ucred))];
+    char control[CMSG_SPACE(sizeof(struct ucred))] __aligned(4);
     struct msghdr hdr = {
         NULL,
         0,
diff --git a/logd/LogStatistics.cpp b/logd/LogStatistics.cpp
index bf0e30b..2b02bc1 100644
--- a/logd/LogStatistics.cpp
+++ b/logd/LogStatistics.cpp
@@ -75,6 +75,9 @@
     }
 
     uidTable[log_id].add(element->getUid(), element);
+    if (element->getUid() == AID_SYSTEM) {
+        pidSystemTable[log_id].add(element->getPid(), element);
+    }
 
     if (!enable) {
         return;
@@ -107,6 +110,9 @@
     }
 
     uidTable[log_id].subtract(element->getUid(), element);
+    if (element->getUid() == AID_SYSTEM) {
+        pidSystemTable[log_id].subtract(element->getPid(), element);
+    }
 
     if (!enable) {
         return;
@@ -134,6 +140,9 @@
     ++mDroppedElements[log_id];
 
     uidTable[log_id].drop(element->getUid(), element);
+    if (element->getUid() == AID_SYSTEM) {
+        pidSystemTable[log_id].drop(element->getPid(), element);
+    }
 
     if (!enable) {
         return;
@@ -205,7 +214,7 @@
 }
 
 std::string UidEntry::format(const LogStatistics &stat, log_id_t id) const {
-    uid_t uid = getKey();
+    uid_t uid = getUid();
     std::string name = android::base::StringPrintf("%u", uid);
     const char *nameTmp = stat.uidToName(uid);
     if (nameTmp) {
@@ -273,7 +282,43 @@
         }
     }
 
-    return formatLine(name, size, pruned);
+    std::string output = formatLine(name, size, pruned);
+
+    if (uid != AID_SYSTEM) {
+        return output;
+    }
+
+    static const size_t maximum_sorted_entries = 32;
+    std::unique_ptr<const PidEntry *[]> sorted
+        = stat.pidSystemTable[id].sort(uid, (pid_t)0, maximum_sorted_entries);
+
+    if (!sorted.get()) {
+        return output;
+    }
+    std::string byPid;
+    size_t index;
+    bool hasDropped = false;
+    for (index = 0; index < maximum_sorted_entries; ++index) {
+        const PidEntry *entry = sorted[index];
+        if (!entry) {
+            break;
+        }
+        if (entry->getSizes() <= (getSizes() / 100)) {
+            break;
+        }
+        if (entry->getDropped()) {
+            hasDropped = true;
+        }
+        byPid += entry->format(stat, id);
+    }
+    if (index > 1) { // print this only if interesting
+        std::string ditto("\" ");
+        output += formatLine(std::string("  PID/UID   COMMAND LINE"),
+                             ditto, hasDropped ? ditto : std::string(""));
+        output += byPid;
+    }
+
+    return output;
 }
 
 std::string PidEntry::formatHeader(const std::string &name, log_id_t /* id */) const {
@@ -287,8 +332,8 @@
 
 std::string PidEntry::format(const LogStatistics &stat, log_id_t /* id */) const {
     uid_t uid = getUid();
-    std::string name = android::base::StringPrintf("%5u/%u",
-                                                   getKey(), uid);
+    pid_t pid = getPid();
+    std::string name = android::base::StringPrintf("%5u/%u", pid, uid);
     const char *nameTmp = getName();
     if (nameTmp) {
         name += android::base::StringPrintf(
@@ -325,7 +370,7 @@
 std::string TidEntry::format(const LogStatistics &stat, log_id_t /* id */) const {
     uid_t uid = getUid();
     std::string name = android::base::StringPrintf("%5u/%u",
-                                                   getKey(), uid);
+                                                   getTid(), uid);
     const char *nameTmp = getName();
     if (nameTmp) {
         name += android::base::StringPrintf(
@@ -388,7 +433,8 @@
     return formatLine(name, size, pruned);
 }
 
-std::string LogStatistics::format(uid_t uid, unsigned int logMask) const {
+std::string LogStatistics::format(uid_t uid, pid_t pid,
+                                  unsigned int logMask) const {
     static const unsigned short spaces_total = 19;
 
     // Report on total logging, current and for all time
@@ -461,24 +507,38 @@
         name = (uid == AID_ROOT)
             ? "Chattiest UIDs in %s log buffer:"
             : "Logging for your UID in %s log buffer:";
-        output += uidTable[id].format(*this, uid, name, id);
+        output += uidTable[id].format(*this, uid, pid, name, id);
     }
 
     if (enable) {
-        name = (uid == AID_ROOT) ? "Chattiest PIDs:" : "Logging for this PID:";
-        output += pidTable.format(*this, uid, name);
-        name = "Chattiest TIDs:";
-        output += tidTable.format(*this, uid, name);
+        name = ((uid == AID_ROOT) && !pid)
+            ? "Chattiest PIDs:"
+            : "Logging for this PID:";
+        output += pidTable.format(*this, uid, pid, name);
+        name = "Chattiest TIDs";
+        if (pid) {
+            name += android::base::StringPrintf(" for PID %d", pid);
+        }
+        name += ":";
+        output += tidTable.format(*this, uid, pid, name);
     }
 
     if (enable && (logMask & (1 << LOG_ID_EVENTS))) {
-        name = "Chattiest events log buffer TAGs:";
-        output += tagTable.format(*this, uid, name, LOG_ID_EVENTS);
+        name = "Chattiest events log buffer TAGs";
+        if (pid) {
+            name += android::base::StringPrintf(" for PID %d", pid);
+        }
+        name += ":";
+        output += tagTable.format(*this, uid, pid, name, LOG_ID_EVENTS);
     }
 
     if (enable && (logMask & (1 << LOG_ID_SECURITY))) {
-        name = "Chattiest security log buffer TAGs:";
-        output += securityTagTable.format(*this, uid, name, LOG_ID_SECURITY);
+        name = "Chattiest security log buffer TAGs";
+        if (pid) {
+            name += android::base::StringPrintf(" for PID %d", pid);
+        }
+        name += ":";
+        output += securityTagTable.format(*this, uid, pid, name, LOG_ID_SECURITY);
     }
 
     return output;
diff --git a/logd/LogStatistics.h b/logd/LogStatistics.h
index 8558b06..6f7d264 100644
--- a/logd/LogStatistics.h
+++ b/logd/LogStatistics.h
@@ -47,7 +47,8 @@
     typedef typename std::unordered_map<TKey, TEntry>::iterator iterator;
     typedef typename std::unordered_map<TKey, TEntry>::const_iterator const_iterator;
 
-    std::unique_ptr<const TEntry *[]> sort(size_t len) const {
+    std::unique_ptr<const TEntry *[]> sort(uid_t uid, pid_t pid,
+                                           size_t len) const {
         if (!len) {
             std::unique_ptr<const TEntry *[]> sorted(NULL);
             return sorted;
@@ -58,6 +59,14 @@
 
         for(const_iterator it = map.begin(); it != map.end(); ++it) {
             const TEntry &entry = it->second;
+
+            if ((uid != AID_ROOT) && (uid != entry.getUid())) {
+                continue;
+            }
+            if (pid && entry.getPid() && (pid != entry.getPid())) {
+                continue;
+            }
+
             size_t sizes = entry.getSizes();
             ssize_t index = len - 1;
             while ((!retval[index] || (sizes > retval[index]->getSizes()))
@@ -118,12 +127,13 @@
     std::string format(
             const LogStatistics &stat,
             uid_t uid,
+            pid_t pid,
             const std::string &name = std::string(""),
             log_id_t id = LOG_ID_MAX) const {
         static const size_t maximum_sorted_entries = 32;
         std::string output;
-        std::unique_ptr<const TEntry *[]> sorted = sort(maximum_sorted_entries);
-
+        std::unique_ptr<const TEntry *[]> sorted = sort(uid, pid,
+                                                        maximum_sorted_entries);
         if (!sorted.get()) {
             return output;
         }
@@ -136,9 +146,6 @@
             if (entry->getSizes() <= (sorted[0]->getSizes() / 100)) {
                 break;
             }
-            if ((uid != AID_ROOT) && (uid != entry->getUid())) {
-                continue;
-            }
             if (!headerPrinted) {
                 output += "\n\n";
                 output += entry->formatHeader(name, id);
@@ -217,14 +224,24 @@
 
 struct UidEntry : public EntryBaseDropped {
     const uid_t uid;
+    pid_t pid;
 
     UidEntry(LogBufferElement *element):
             EntryBaseDropped(element),
-            uid(element->getUid()) {
+            uid(element->getUid()),
+            pid(element->getPid()) {
     }
 
     inline const uid_t&getKey() const { return uid; }
-    inline const uid_t&getUid() const { return uid; }
+    inline const uid_t&getUid() const { return getKey(); }
+    inline const pid_t&getPid() const { return pid; }
+
+    inline void add(LogBufferElement *element) {
+        if (pid != element->getPid()) {
+            pid = -1;
+        }
+        EntryBase::add(element);
+    }
 
     std::string formatHeader(const std::string &name, log_id_t id) const;
     std::string format(const LogStatistics &stat, log_id_t id) const;
@@ -260,6 +277,7 @@
     ~PidEntry() { free(name); }
 
     const pid_t&getKey() const { return pid; }
+    const pid_t&getPid() const { return getKey(); }
     const uid_t&getUid() const { return uid; }
     const char*getName() const { return name; }
 
@@ -291,30 +309,36 @@
 
 struct TidEntry : public EntryBaseDropped {
     const pid_t tid;
+    pid_t pid;
     uid_t uid;
     char *name;
 
-    TidEntry(pid_t tid):
+    TidEntry(pid_t tid, pid_t pid):
             EntryBaseDropped(),
             tid(tid),
+            pid(pid),
             uid(android::pidToUid(tid)),
             name(android::tidToName(tid)) {
     }
     TidEntry(LogBufferElement *element):
             EntryBaseDropped(element),
             tid(element->getTid()),
+            pid(element->getPid()),
             uid(element->getUid()),
             name(android::tidToName(tid)) {
     }
     TidEntry(const TidEntry &element):
             EntryBaseDropped(element),
             tid(element.tid),
+            pid(element.pid),
             uid(element.uid),
             name(element.name ? strdup(element.name) : NULL) {
     }
     ~TidEntry() { free(name); }
 
     const pid_t&getKey() const { return tid; }
+    const pid_t&getTid() const { return getKey(); }
+    const pid_t&getPid() const { return pid; }
     const uid_t&getUid() const { return uid; }
     const char*getName() const { return name; }
 
@@ -330,8 +354,10 @@
 
     inline void add(LogBufferElement *element) {
         uid_t incomingUid = element->getUid();
-        if (getUid() != incomingUid) {
+        pid_t incomingPid = element->getPid();
+        if ((getUid() != incomingUid) || (getPid() != incomingPid)) {
             uid = incomingUid;
+            pid = incomingPid;
             free(name);
             name = android::tidToName(element->getTid());
         } else {
@@ -346,23 +372,28 @@
 
 struct TagEntry : public EntryBase {
     const uint32_t tag;
+    pid_t pid;
     uid_t uid;
 
     TagEntry(LogBufferElement *element):
             EntryBase(element),
             tag(element->getTag()),
+            pid(element->getPid()),
             uid(element->getUid()) {
     }
 
     const uint32_t&getKey() const { return tag; }
+    const pid_t&getPid() const { return pid; }
     const uid_t&getUid() const { return uid; }
     const char*getName() const { return android::tagToName(tag); }
 
     inline void add(LogBufferElement *element) {
-        uid_t incomingUid = element->getUid();
-        if (uid != incomingUid) {
+        if (uid != element->getUid()) {
             uid = -1;
         }
+        if (pid != element->getPid()) {
+            pid = -1;
+        }
         EntryBase::add(element);
     }
 
@@ -385,6 +416,10 @@
     typedef LogHashtable<uid_t, UidEntry> uidTable_t;
     uidTable_t uidTable[LOG_ID_MAX];
 
+    // pid of system to size list
+    typedef LogHashtable<pid_t, PidEntry> pidSystemTable_t;
+    pidSystemTable_t pidSystemTable[LOG_ID_MAX];
+
     // pid to uid list
     typedef LogHashtable<pid_t, PidEntry> pidTable_t;
     pidTable_t pidTable;
@@ -416,8 +451,13 @@
         --mDroppedElements[log_id];
     }
 
-    std::unique_ptr<const UidEntry *[]> sort(size_t len, log_id id) {
-        return uidTable[id].sort(len);
+    std::unique_ptr<const UidEntry *[]> sort(uid_t uid, pid_t pid,
+                                             size_t len, log_id id) {
+        return uidTable[id].sort(uid, pid, len);
+    }
+    std::unique_ptr<const PidEntry *[]> sort(uid_t uid, pid_t pid,
+                                             size_t len, log_id id, uid_t) {
+        return pidSystemTable[id].sort(uid, pid, len);
     }
 
     // fast track current value by id only
@@ -429,7 +469,7 @@
     size_t sizesTotal(log_id_t id) const { return mSizesTotal[id]; }
     size_t elementsTotal(log_id_t id) const { return mElementsTotal[id]; }
 
-    std::string format(uid_t uid, unsigned int logMask) const;
+    std::string format(uid_t uid, pid_t pid, unsigned int logMask) const;
 
     // helper (must be locked directly or implicitly by mLogElementsLock)
     const char *pidToName(pid_t pid) const;
diff --git a/logd/LogUtils.h b/logd/LogUtils.h
index b591f28..fd4800e 100644
--- a/logd/LogUtils.h
+++ b/logd/LogUtils.h
@@ -43,6 +43,16 @@
 bool clientHasLogCredentials(uid_t uid, gid_t gid, pid_t pid);
 bool clientHasLogCredentials(SocketClient *cli);
 
+// Furnished in main.cpp
+#define BOOL_DEFAULT_FLAG_TRUE_FALSE 0x1
+#define BOOL_DEFAULT_FALSE       0x0     // false if property not present
+#define BOOL_DEFAULT_TRUE        0x1     // true if property not present
+#define BOOL_DEFAULT_FLAG_PERSIST    0x2 // <key>, persist.<key>, ro.<key>
+#define BOOL_DEFAULT_FLAG_ENG        0x4 // off for user
+#define BOOL_DEFAULT_FLAG_SVELTE     0x8 // off for low_ram
+
+bool property_get_bool(const char *key, int def);
+
 static inline bool worstUidEnabledForLogid(log_id_t id) {
     return (id == LOG_ID_MAIN) || (id == LOG_ID_SYSTEM) || (id == LOG_ID_RADIO);
 }
diff --git a/logd/LogWhiteBlackList.cpp b/logd/LogWhiteBlackList.cpp
index 29e637e..ae933b5 100644
--- a/logd/LogWhiteBlackList.cpp
+++ b/logd/LogWhiteBlackList.cpp
@@ -66,6 +66,7 @@
 
 int PruneList::init(const char *str) {
     mWorstUidEnabled = true;
+    mWorstPidOfSystemEnabled = true;
     PruneCollection::iterator it;
     for (it = mNice.begin(); it != mNice.end();) {
         it = mNice.erase(it);
@@ -103,13 +104,14 @@
     // default here means take internal default.
     if (filter == _default) {
         // See README.property for description of filter format
-        filter = "~!";
+        filter = "~! ~1000/!";
     }
     if (filter == _disable) {
         filter = "";
     }
 
     mWorstUidEnabled = false;
+    mWorstPidOfSystemEnabled = false;
 
     for(str = filter.c_str(); *str; ++str) {
         if (isspace(*str)) {
@@ -131,6 +133,19 @@
                 }
                 continue;
             }
+            // special case, translated to worst PID of System at priority
+            static const char worstPid[] = "1000/!";
+            if (!strncmp(str, worstPid, sizeof(worstPid) - 1)) {
+                mWorstPidOfSystemEnabled = true;
+                str += sizeof(worstPid) - 1;
+                if (!*str) {
+                    break;
+                }
+                if (!isspace(*str)) {
+                    return 1;
+                }
+                continue;
+            }
             if (!*str) {
                 return 1;
             }
@@ -209,6 +224,9 @@
     if (mWorstUidEnabled) {
         string = "~!";
         fmt = nice_format;
+        if (mWorstPidOfSystemEnabled) {
+            string += " ~1000/!";
+        }
     }
 
     PruneCollection::iterator it;
diff --git a/logd/LogWhiteBlackList.h b/logd/LogWhiteBlackList.h
index 6f17402..8b8e02f 100644
--- a/logd/LogWhiteBlackList.h
+++ b/logd/LogWhiteBlackList.h
@@ -53,6 +53,7 @@
     PruneCollection mNaughty;
     PruneCollection mNice;
     bool mWorstUidEnabled;
+    bool mWorstPidOfSystemEnabled;
 
 public:
     PruneList();
@@ -65,6 +66,7 @@
     bool nice(LogBufferElement *element);
     bool nice(void) { return !mNice.empty(); }
     bool worstUidEnabled() const { return mWorstUidEnabled; }
+    bool worstPidOfSystemEnabled() const { return mWorstPidOfSystemEnabled; }
 
     std::string format();
 };
diff --git a/logd/README.property b/logd/README.property
index e4b23a9..22f86b9 100644
--- a/logd/README.property
+++ b/logd/README.property
@@ -1,39 +1,63 @@
 The properties that logd responds to are:
 
 name                       type default  description
-logd.auditd                 bool  true   Enable selinux audit daemon
-logd.auditd.dmesg           bool  true   selinux audit messages duplicated and
+ro.logd.auditd             bool   true   Enable selinux audit daemon
+ro.logd.auditd.dmesg       bool   true   selinux audit messages duplicated and
                                          sent on to dmesg log
-logd.klogd                  bool depends Enable klogd daemon
-logd.statistics             bool depends Enable logcat -S statistics.
-ro.config.low_ram           bool  false  if true, logd.statistics & logd.klogd
-                                         default false
-ro.build.type               string       if user, logd.statistics & logd.klogd
-                                         default false
-persist.logd.logpersistd    string       Enable logpersist daemon, "logcatd"
+persist.logd.security      bool   false  Enable security buffer.
+ro.device_owner            bool   false  Override persist.logd.security to false
+ro.logd.kernel             bool+ svelte+ Enable klogd daemon
+ro.logd.statistics         bool+ svelte+ Enable logcat -S statistics.
+ro.build.type              string        if user, logd.statistics &
+                                         ro.logd.kernel default false.
+persist.logd.logpersistd   string        Enable logpersist daemon, "logcatd"
                                          turns on logcat -f in logd context
-persist.logd.size          number 256K   Global default size of the buffer for
+persist.logd.size          number  ro    Global default size of the buffer for
                                          all log ids at initial startup, at
                                          runtime use: logcat -b all -G <value>
-persist.logd.size.main     number 256K   Size of the buffer for the main log
-persist.logd.size.system   number 256K   Size of the buffer for the system log
-persist.logd.size.radio    number 256K   Size of the buffer for the radio log
-persist.logd.size.event    number 256K   Size of the buffer for the event log
-persist.logd.size.crash    number 256K   Size of the buffer for the crash log
-persist.logd.filter         string       Pruning filter to optimize content,
-                                         default is ro.logd.filter or
-                                         "~!" which means to prune the oldest
-                                         entries of chattiest UID. At runtime
-                                         use: logcat -P "<string>"
-persist.logd.timestamp      string       The recording timestamp source. Default
-                                         is ro.logd.timestamp. "m[onotonic]" is
-                                         the only supported key character,
-                                         otherwise assumes realtime.
+ro.logd.size               number svelte default for persist.logd.size. Larger
+                                         platform default sizes than 256KB are
+                                         known to not scale well under log spam
+                                         pressure. Address the spam first,
+                                         resist increasing the log buffer.
+persist.logd.size.<buffer> number  ro    Size of the buffer for <buffer> log
+ro.logd.size.<buffer>      number svelte default for persist.logd.size.<buffer>
+ro.config.low_ram          bool   false  if true, logd.statistics, logd.kernel
+                                         default false, logd.size 64K instead
+                                         of 256K.
+persist.logd.filter        string        Pruning filter to optimize content.
+                                         At runtime use: logcat -P "<string>"
+ro.logd.filter       string "~! ~1000/!" default for persist.logd.filter.
+                                         This default means to prune the
+                                         oldest entries of chattiest UID, and
+                                         the chattiest PID of system
+                                         (1000, or AID_SYSTEM).
+persist.logd.timestamp     string  ro    The recording timestamp source.
+                                         "m[onotonic]" is the only supported
+                                         key character, otherwise realtime.
+ro.logd.timestamp        string realtime default for persist.logd.timestamp
+log.tag                   string persist The global logging level, VERBOSE,
+                                         DEBUG, INFO, WARN, ERROR, ASSERT or
+                                         SILENT. Only the first character is
+                                         the key character.
+persist.log.tag            string build  default for log.tag
+log.tag.<tag>             string persist The <tag> specific logging level.
+persist.log.tag.<tag>      string build  default for log.tag.<tag>
 
 NB:
-- Number support multipliers (K or M) for convenience. Range is limited
+- bool+ - "true", "false" and comma separated list of "eng" (forced false if
+  ro.build.type is "user") or "svelte" (forced false if ro.config.low_ram is
+  true).
+- svelte - see ro.config.low_ram for details.
+- svelte+ - see ro.config.low_ram and ro.build.type for details.
+- ro - <base property> temporary override, ro.<base property> platform default.
+- persist - <base property> override, persist.<base property> platform default.
+- build - VERBOSE for native, DEBUG for jvm isLoggable, or developer option.
+- number - support multipliers (K or M) for convenience. Range is limited
   to between 64K and 256M for log buffer sizes. Individual log buffer ids
   such as main, system, ... override global default.
 - Pruning filter is of form of a space-separated list of [~][UID][/PID]
   references, where '~' prefix means to blacklist otherwise whitelist. For
-  blacklisting, UID may be a '!' to instead reference the chattiest client.
+  blacklisting, UID or PID may be a '!' to instead reference the chattiest
+  client, with the restriction that the PID must be in the UID group 1000
+  (system or AID_SYSTEM).
diff --git a/logd/main.cpp b/logd/main.cpp
index 0f55d60..ba56e57 100644
--- a/logd/main.cpp
+++ b/logd/main.cpp
@@ -143,18 +143,72 @@
 }
 
 // Property helper
-static bool property_get_bool(const char *key, bool def) {
-    char property[PROPERTY_VALUE_MAX];
-    property_get(key, property, "");
-
-    if (!strcasecmp(property, "true")) {
-        return true;
-    }
-    if (!strcasecmp(property, "false")) {
+static bool check_flag(const char *prop, const char *flag) {
+    const char *cp = strcasestr(prop, flag);
+    if (!cp) {
         return false;
     }
+    // We only will document comma (,)
+    static const char sep[] = ",:;|+ \t\f";
+    if ((cp != prop) && !strchr(sep, cp[-1])) {
+        return false;
+    }
+    cp += strlen(flag);
+    return !*cp || !!strchr(sep, *cp);
+}
 
-    return def;
+bool property_get_bool(const char *key, int flag) {
+    char def[PROPERTY_VALUE_MAX];
+    char property[PROPERTY_VALUE_MAX];
+    def[0] = '\0';
+    if (flag & BOOL_DEFAULT_FLAG_PERSIST) {
+        char newkey[PROPERTY_KEY_MAX];
+        snprintf(newkey, sizeof(newkey), "ro.%s", key);
+        property_get(newkey, property, "");
+        // persist properties set by /data require innoculation with
+        // logd-reinit. They may be set in init.rc early and function, but
+        // otherwise are defunct unless reset. Do not rely on persist
+        // properties for startup-only keys unless you are willing to restart
+        // logd daemon (not advised).
+        snprintf(newkey, sizeof(newkey), "persist.%s", key);
+        property_get(newkey, def, property);
+    }
+
+    property_get(key, property, def);
+
+    if (check_flag(property, "true")) {
+        return true;
+    }
+    if (check_flag(property, "false")) {
+        return false;
+    }
+    if (check_flag(property, "eng")) {
+       flag |= BOOL_DEFAULT_FLAG_ENG;
+    }
+    // this is really a "not" flag
+    if (check_flag(property, "svelte")) {
+       flag |= BOOL_DEFAULT_FLAG_SVELTE;
+    }
+
+    // Sanity Check
+    if (flag & (BOOL_DEFAULT_FLAG_SVELTE | BOOL_DEFAULT_FLAG_ENG)) {
+        flag &= ~BOOL_DEFAULT_FLAG_TRUE_FALSE;
+        flag |= BOOL_DEFAULT_TRUE;
+    }
+
+    if ((flag & BOOL_DEFAULT_FLAG_SVELTE)
+            && property_get_bool("ro.config.low_ram",
+                                 BOOL_DEFAULT_FALSE)) {
+        return false;
+    }
+    if (flag & BOOL_DEFAULT_FLAG_ENG) {
+        property_get("ro.build.type", property, "");
+        if (!strcmp(property, "user")) {
+            return false;
+        }
+    }
+
+    return (flag & BOOL_DEFAULT_FLAG_TRUE_FALSE) != BOOL_DEFAULT_FALSE;
 }
 
 // Remove the static, and use this variable
@@ -266,17 +320,6 @@
     return android_lookupEventTag(map, tag);
 }
 
-static bool property_get_bool_svelte(const char *key) {
-    bool not_user;
-    {
-        char property[PROPERTY_VALUE_MAX];
-        property_get("ro.build.type", property, "");
-        not_user = !!strcmp(property, "user");
-    }
-    return property_get_bool(key, not_user
-            && !property_get_bool("ro.config.low_ram", false));
-}
-
 static void readDmesg(LogAudit *al, LogKlog *kl) {
     if (!al && !kl) {
         return;
@@ -325,7 +368,11 @@
 // transitory per-client threads are created for each reader.
 int main(int argc, char *argv[]) {
     int fdPmesg = -1;
-    bool klogd = property_get_bool_svelte("logd.klogd");
+    bool klogd = property_get_bool("logd.kernel",
+                                   BOOL_DEFAULT_TRUE |
+                                   BOOL_DEFAULT_FLAG_PERSIST |
+                                   BOOL_DEFAULT_FLAG_ENG |
+                                   BOOL_DEFAULT_FLAG_SVELTE);
     if (klogd) {
         fdPmesg = open("/proc/kmsg", O_RDONLY | O_NDELAY);
     }
@@ -405,7 +452,11 @@
 
     signal(SIGHUP, reinit_signal_handler);
 
-    if (property_get_bool_svelte("logd.statistics")) {
+    if (property_get_bool("logd.statistics",
+                          BOOL_DEFAULT_TRUE |
+                          BOOL_DEFAULT_FLAG_PERSIST |
+                          BOOL_DEFAULT_FLAG_ENG |
+                          BOOL_DEFAULT_FLAG_SVELTE)) {
         logBuf->enableStatistics();
     }
 
@@ -439,12 +490,17 @@
     // initiated log messages. New log entries are added to LogBuffer
     // and LogReader is notified to send updates to connected clients.
 
-    bool auditd = property_get_bool("logd.auditd", true);
-
+    bool auditd = property_get_bool("logd.auditd",
+                                    BOOL_DEFAULT_TRUE |
+                                    BOOL_DEFAULT_FLAG_PERSIST);
     LogAudit *al = NULL;
     if (auditd) {
-        bool dmesg = property_get_bool("logd.auditd.dmesg", true);
-        al = new LogAudit(logBuf, reader, dmesg ? fdDmesg : -1);
+        al = new LogAudit(logBuf, reader,
+                          property_get_bool("logd.auditd.dmesg",
+                                            BOOL_DEFAULT_TRUE |
+                                            BOOL_DEFAULT_FLAG_PERSIST)
+                              ? fdDmesg
+                              : -1);
     }
 
     LogKlog *kl = NULL;
diff --git a/logd/tests/Android.mk b/logd/tests/Android.mk
index a7c6b53..808087a 100644
--- a/logd/tests/Android.mk
+++ b/logd/tests/Android.mk
@@ -43,6 +43,6 @@
 LOCAL_MODULE := $(test_module_prefix)unit-tests
 LOCAL_MODULE_TAGS := $(test_tags)
 LOCAL_CFLAGS += $(test_c_flags)
-LOCAL_SHARED_LIBRARIES := libcutils
+LOCAL_SHARED_LIBRARIES := libbase libcutils liblog
 LOCAL_SRC_FILES := $(test_src_files)
 include $(BUILD_NATIVE_TEST)
diff --git a/logd/tests/logd_test.cpp b/logd/tests/logd_test.cpp
index 4472c1d..de19790 100644
--- a/logd/tests/logd_test.cpp
+++ b/logd/tests/logd_test.cpp
@@ -15,18 +15,20 @@
  */
 
 #include <fcntl.h>
+#include <inttypes.h>
 #include <poll.h>
 #include <signal.h>
 #include <stdio.h>
 #include <string.h>
 
+#include <string>
+
 #include <gtest/gtest.h>
 
-#include "cutils/sockets.h"
-#include "log/log.h"
-#include "log/logger.h"
-
-#define __unused __attribute__((__unused__))
+#include <android-base/stringprintf.h>
+#include <cutils/sockets.h>
+#include <log/log.h>
+#include <log/logger.h>
 
 /*
  * returns statistics
@@ -196,10 +198,14 @@
     delete [] buf;
 }
 
-static void caught_signal(int signum __unused) { }
+static void caught_signal(int /* signum */) { }
 
 static void dump_log_msg(const char *prefix,
                          log_msg *msg, unsigned int version, int lid) {
+    std::cout << std::flush;
+    std::cerr << std::flush;
+    fflush(stdout);
+    fflush(stderr);
     switch(msg->entry.hdr_size) {
     case 0:
         version = 1;
@@ -284,6 +290,7 @@
         }
     }
     fprintf(stderr, "}\n");
+    fflush(stderr);
 }
 
 TEST(logd, both) {
@@ -526,7 +533,8 @@
     ASSERT_GT(totalSize, nowSpamSize * 2);
 }
 
-TEST(logd, timeout) {
+// b/26447386 confirm fixed
+void timeout_negative(const char *command) {
     log_msg msg_wrap, msg_timeout;
     bool content_wrap = false, content_timeout = false, written = false;
     unsigned int alarm_wrap = 0, alarm_timeout = 0;
@@ -540,6 +548,8 @@
                                      SOCK_SEQPACKET);
         ASSERT_LT(0, fd);
 
+        std::string ask(command);
+
         struct sigaction ignore, old_sigaction;
         memset(&ignore, 0, sizeof(ignore));
         ignore.sa_handler = caught_signal;
@@ -547,8 +557,8 @@
         sigaction(SIGALRM, &ignore, &old_sigaction);
         unsigned int old_alarm = alarm(3);
 
-        static const char ask[] = "dumpAndClose lids=0,1,2,3,4,5 timeout=6";
-        written = write(fd, ask, sizeof(ask)) == sizeof(ask);
+        size_t len = ask.length() + 1;
+        written = write(fd, ask.c_str(), len) == (ssize_t)len;
         if (!written) {
             alarm(old_alarm);
             sigaction(SIGALRM, &old_sigaction, NULL);
@@ -561,6 +571,9 @@
         alarm_wrap = alarm(5);
 
         content_timeout = recv(fd, msg_timeout.buf, sizeof(msg_timeout), 0) > 0;
+        if (!content_timeout) { // make sure we hit dumpAndClose
+            content_timeout = recv(fd, msg_timeout.buf, sizeof(msg_timeout), 0) > 0;
+        }
 
         alarm_timeout = alarm((old_alarm <= 0)
             ? old_alarm
@@ -571,7 +584,7 @@
 
         close(fd);
 
-        if (!content_wrap && !alarm_wrap && content_timeout && !alarm_timeout) {
+        if (!content_wrap && !alarm_wrap && content_timeout && alarm_timeout) {
             break;
         }
     }
@@ -585,6 +598,113 @@
     }
 
     EXPECT_TRUE(written);
+    EXPECT_TRUE(content_wrap);
+    EXPECT_NE(0U, alarm_wrap);
+    EXPECT_TRUE(content_timeout);
+    EXPECT_NE(0U, alarm_timeout);
+}
+
+TEST(logd, timeout_no_start) {
+    timeout_negative("dumpAndClose lids=0,1,2,3,4,5 timeout=6");
+}
+
+TEST(logd, timeout_start_epoch) {
+    timeout_negative("dumpAndClose lids=0,1,2,3,4,5 timeout=6 start=0.000000000");
+}
+
+// b/26447386 refined behavior
+TEST(logd, timeout) {
+    log_msg msg_wrap, msg_timeout;
+    bool content_wrap = false, content_timeout = false, written = false;
+    unsigned int alarm_wrap = 0, alarm_timeout = 0;
+    // A few tries to get it right just in case wrap kicks in due to
+    // content providers being active during the test
+    int i = 5;
+    log_time now(android_log_clockid());
+    now.tv_sec -= 30; // reach back a moderate period of time
+
+    while (--i) {
+        int fd = socket_local_client("logdr",
+                                     ANDROID_SOCKET_NAMESPACE_RESERVED,
+                                     SOCK_SEQPACKET);
+        ASSERT_LT(0, fd);
+
+        std::string ask = android::base::StringPrintf(
+            "dumpAndClose lids=0,1,2,3,4,5 timeout=6 start=%"
+                PRIu32 ".%09" PRIu32,
+            now.tv_sec, now.tv_nsec);
+
+        struct sigaction ignore, old_sigaction;
+        memset(&ignore, 0, sizeof(ignore));
+        ignore.sa_handler = caught_signal;
+        sigemptyset(&ignore.sa_mask);
+        sigaction(SIGALRM, &ignore, &old_sigaction);
+        unsigned int old_alarm = alarm(3);
+
+        size_t len = ask.length() + 1;
+        written = write(fd, ask.c_str(), len) == (ssize_t)len;
+        if (!written) {
+            alarm(old_alarm);
+            sigaction(SIGALRM, &old_sigaction, NULL);
+            close(fd);
+            continue;
+        }
+
+        content_wrap = recv(fd, msg_wrap.buf, sizeof(msg_wrap), 0) > 0;
+
+        alarm_wrap = alarm(5);
+
+        content_timeout = recv(fd, msg_timeout.buf, sizeof(msg_timeout), 0) > 0;
+        if (!content_timeout) { // make sure we hit dumpAndClose
+            content_timeout = recv(fd, msg_timeout.buf, sizeof(msg_timeout), 0) > 0;
+        }
+
+        alarm_timeout = alarm((old_alarm <= 0)
+            ? old_alarm
+            : (old_alarm > (1 + 3 - alarm_wrap))
+                ? old_alarm - 3 + alarm_wrap
+                : 2);
+        sigaction(SIGALRM, &old_sigaction, NULL);
+
+        close(fd);
+
+        if (!content_wrap && !alarm_wrap && content_timeout && alarm_timeout) {
+            break;
+        }
+
+        // modify start time in case content providers are relatively
+        // active _or_ inactive during the test.
+        if (content_timeout) {
+            log_time msg(msg_timeout.entry.sec, msg_timeout.entry.nsec);
+            EXPECT_FALSE(msg < now);
+            if (msg > now) {
+                now = msg;
+                now.tv_sec += 30;
+                msg = log_time(android_log_clockid());
+                if (now > msg) {
+                    now = msg;
+                    --now.tv_sec;
+                }
+            }
+        } else {
+            now.tv_sec -= 120; // inactive, reach further back!
+        }
+    }
+
+    if (content_wrap) {
+        dump_log_msg("wrap", &msg_wrap, 3, -1);
+    }
+
+    if (content_timeout) {
+        dump_log_msg("timeout", &msg_timeout, 3, -1);
+    }
+
+    if (content_wrap || !content_timeout) {
+        fprintf(stderr, "now=%" PRIu32 ".%09" PRIu32 "\n",
+                now.tv_sec, now.tv_nsec);
+    }
+
+    EXPECT_TRUE(written);
     EXPECT_FALSE(content_wrap);
     EXPECT_EQ(0U, alarm_wrap);
     EXPECT_TRUE(content_timeout);
diff --git a/metricsd/.clang-format b/metricsd/.clang-format
index 65d8277..c98efc2 100644
--- a/metricsd/.clang-format
+++ b/metricsd/.clang-format
@@ -2,6 +2,7 @@
 AllowShortFunctionsOnASingleLine: Inline
 AllowShortIfStatementsOnASingleLine: false
 AllowShortLoopsOnASingleLine: false
+BinPackArguments: false
 BinPackParameters: false
 CommentPragmas: NOLINT:.*
 DerivePointerAlignment: false
diff --git a/metricsd/Android.mk b/metricsd/Android.mk
index 2149a4b..250c657 100644
--- a/metricsd/Android.mk
+++ b/metricsd/Android.mk
@@ -28,7 +28,7 @@
   collectors/cpu_usage_collector.cc \
   collectors/disk_usage_collector.cc \
   metrics_collector.cc \
-  metrics_collector_service_trampoline.cc \
+  metrics_collector_service_impl.cc \
   persistent_integer.cc
 
 metricsd_common := \
@@ -38,6 +38,7 @@
   uploader/metrics_hashes.cc \
   uploader/metrics_log_base.cc \
   uploader/metrics_log.cc \
+  uploader/metricsd_service_runner.cc \
   uploader/sender_http.cc \
   uploader/system_profile_cache.cc \
   uploader/upload_service.cc
@@ -84,6 +85,7 @@
 metricsd_shared_libraries := \
   libbinder \
   libbrillo \
+  libbrillo-binder \
   libbrillo-http \
   libchrome \
   libprotobuf-cpp-lite \
@@ -102,15 +104,14 @@
 # ==========================================================
 include $(CLEAR_VARS)
 LOCAL_MODULE := libmetricscollectorservice
+LOCAL_CLANG := true
 LOCAL_SHARED_LIBRARIES := libbinder libbrillo-binder libchrome libutils
 LOCAL_CPP_EXTENSION := $(metrics_cpp_extension)
 LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
 LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
 LOCAL_SRC_FILES := \
   aidl/android/brillo/metrics/IMetricsCollectorService.aidl \
-  metrics_collector_service_impl.cc \
   metrics_collector_service_client.cc
-LOCAL_RTTI_FLAG := -fno-rtti
 include $(BUILD_STATIC_LIBRARY)
 
 # Shared library for metrics.
@@ -122,7 +123,6 @@
 LOCAL_CLANG := true
 LOCAL_CPP_EXTENSION := $(metrics_cpp_extension)
 LOCAL_CPPFLAGS := $(metrics_CPPFLAGS)
-LOCAL_RTTI_FLAG := -frtti
 LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
 LOCAL_SHARED_LIBRARIES := $(libmetrics_shared_libraries)
 LOCAL_SRC_FILES := $(libmetrics_sources)
@@ -166,7 +166,6 @@
 LOCAL_CPPFLAGS := $(metrics_CPPFLAGS)
 LOCAL_INIT_RC := metrics_collector.rc
 LOCAL_REQUIRED_MODULES := metrics.json
-LOCAL_RTTI_FLAG := -frtti
 LOCAL_SHARED_LIBRARIES := $(metrics_collector_shared_libraries)
 LOCAL_SRC_FILES := $(metrics_collector_common) \
   metrics_collector_main.cc
@@ -203,6 +202,9 @@
 LOCAL_SHARED_LIBRARIES := $(metricsd_shared_libraries)
 LOCAL_SRC_FILES := $(metricsd_tests_sources) $(metricsd_common)
 LOCAL_STATIC_LIBRARIES := libBionicGtestMain libgmock metricsd_protos metricsd_binder_proxy
+ifdef BRILLO
+LOCAL_MODULE_TAGS := debug
+endif
 include $(BUILD_NATIVE_TEST)
 
 # Unit tests for metrics_collector.
@@ -213,12 +215,14 @@
 LOCAL_CLANG := true
 LOCAL_CPP_EXTENSION := $(metrics_cpp_extension)
 LOCAL_CPPFLAGS := $(metrics_CPPFLAGS) -Wno-sign-compare
-LOCAL_RTTI_FLAG := -frtti
 LOCAL_SHARED_LIBRARIES := $(metrics_collector_shared_libraries)
 LOCAL_SRC_FILES := $(metrics_collector_tests_sources) \
   $(metrics_collector_common)
 LOCAL_STATIC_LIBRARIES := libBionicGtestMain libgmock metricsd_binder_proxy \
   $(metrics_collector_static_libraries)
+ifdef BRILLO
+LOCAL_MODULE_TAGS := debug
+endif
 include $(BUILD_NATIVE_TEST)
 
 # Weave schema files
diff --git a/metricsd/README b/metricsd/README
deleted file mode 100644
index d4c9a0e..0000000
--- a/metricsd/README
+++ /dev/null
@@ -1,150 +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.
-
-================================================================================
-
-The Chrome OS "metrics" package contains utilities for client-side user metric
-collection.
-When Chrome is installed, Chrome will take care of aggregating and uploading the
-metrics to the UMA server.
-When Chrome is not installed (embedded build) and the metrics_uploader USE flag
-is set, metrics_daemon will aggregate and upload the metrics itself.
-
-
-================================================================================
-The Metrics Library: libmetrics
-================================================================================
-
-libmetrics is a small library that implements the basic C and C++ API for
-metrics collection. All metrics collection is funneled through this library. The
-easiest and recommended way for a client-side module to collect user metrics is
-to link libmetrics and use its APIs to send metrics to Chrome for transport to
-UMA. In order to use the library in a module, you need to do the following:
-
-- Add a dependence (DEPEND and RDEPEND) on chromeos-base/metrics to the module's
-  ebuild.
-
-- Link the module with libmetrics (for example, by passing -lmetrics to the
-  module's link command). Both libmetrics.so and libmetrics.a are built and
-  installed under $SYSROOT/usr/lib/. Note that by default -lmetrics will link
-  against libmetrics.so, which is preferred.
-
-- To access the metrics library API in the module, include the
-  <metrics/metrics_library.h> header file. The file is installed in
-  $SYSROOT/usr/include/ when the metrics library is built and installed.
-
-- The API is documented in metrics_library.h under src/platform/metrics/. Before
-  using the API methods, a MetricsLibrary object needs to be constructed and
-  initialized through its Init method.
-
-  For more information on the C API see c_metrics_library.h.
-
-- Samples are sent to Chrome only if the "/home/chronos/Consent To Send Stats"
-  file exists or the metrics are declared enabled in the policy file (see the
-  AreMetricsEnabled API method).
-
-- On the target platform, shortly after the sample is sent, it should be visible
-  in Chrome through "about:histograms".
-
-
-================================================================================
-Histogram Naming Convention
-================================================================================
-
-Use TrackerArea.MetricName. For example:
-
-Platform.DailyUseTime
-Network.TimeToDrop
-
-
-================================================================================
-Server Side
-================================================================================
-
-If the histogram data is visible in about:histograms, it will be sent by an
-official Chrome build to UMA, assuming the user has opted into metrics
-collection. To make the histogram visible on "chromedashboard", the histogram
-description XML file needs to be updated (steps 2 and 3 after following the
-"Details on how to add your own histograms" link under the Histograms tab).
-Include the string "Chrome OS" in the histogram description so that it's easier
-to distinguish Chrome OS specific metrics from general Chrome histograms.
-
-The UMA server logs and keeps the collected field data even if the metric's name
-is not added to the histogram XML. However, the dashboard histogram for that
-metric will show field data as of the histogram XML update date; it will not
-include data for older dates. If past data needs to be displayed, manual
-server-side intervention is required. In other words, one should assume that
-field data collection starts only after the histogram XML has been updated.
-
-
-================================================================================
-The Metrics Client: metrics_client
-================================================================================
-
-metrics_client is a simple shell command-line utility for sending histogram
-samples and user actions. It's installed under /usr/bin on the target platform
-and uses libmetrics to send the data to Chrome. The utility is useful for
-generating metrics from shell scripts.
-
-For usage information and command-line options, run "metrics_client" on the
-target platform or look for "Usage:" in metrics_client.cc.
-
-
-================================================================================
-The Metrics Daemon: metrics_daemon
-================================================================================
-
-metrics_daemon is a daemon that runs in the background on the target platform
-and is intended for passive or ongoing metrics collection, or metrics collection
-requiring feedback from multiple modules. For example, it listens to D-Bus
-signals related to the user session and screen saver states to determine if the
-user is actively using the device or not and generates the corresponding
-data. The metrics daemon uses libmetrics to send the data to Chrome.
-
-The recommended way to generate metrics data from a module is to link and use
-libmetrics directly. However, the module could instead send signals to or
-communicate in some alternative way with the metrics daemon. Then the metrics
-daemon needs to monitor for the relevant events and take appropriate action --
-for example, aggregate data and send the histogram samples.
-
-
-================================================================================
-FAQ
-================================================================================
-
-Q. What should my histogram's |min| and |max| values be set at?
-
-A. You should set the values to a range that covers the vast majority of samples
-   that would appear in the field. Note that samples below the |min| will still
-   be collected in the underflow bucket and samples above the |max| will end up
-   in the overflow bucket. Also, the reported mean of the data will be correct
-   regardless of the range.
-
-Q. How many buckets should I use in my histogram?
-
-A. You should allocate as many buckets as necessary to perform proper analysis
-   on the collected data. Note, however, that the memory allocated in Chrome for
-   each histogram is proportional to the number of buckets. Therefore, it is
-   strongly recommended to keep this number low (e.g., 50 is normal, while 100
-   is probably high).
-
-Q. When should I use an enumeration (linear) histogram vs. a regular
-   (exponential) histogram?
-
-A. Enumeration histograms should really be used only for sampling enumerated
-   events and, in some cases, percentages. Normally, you should use a regular
-   histogram with exponential bucket layout that provides higher resolution at
-   the low end of the range and lower resolution at the high end. Regular
-   histograms are generally used for collecting performance data (e.g., timing,
-   memory usage, power) as well as aggregated event counts.
diff --git a/metricsd/README.md b/metricsd/README.md
new file mode 100644
index 0000000..8d4828c
--- /dev/null
+++ b/metricsd/README.md
@@ -0,0 +1,124 @@
+Metricsd
+========
+
+The metricsd daemon is used to gather metrics from the platform and application,
+aggregate them and upload them periodically to a server.
+The metrics will then be available in their aggregated form to the developer
+for analysis.
+
+Three components are provided to interact with `metricsd`: `libmetrics`,
+`metrics_collector` and `metrics_client`.
+
+The Metrics Library: libmetrics
+-------------------------------
+
+`libmetrics` is a small library that implements the basic 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 `metricsd` for transport to
+UMA. In order to use the library in a module, you need to do the following:
+
+- Add a dependency on the shared library in your Android.mk file:
+  `LOCAL_SHARED_LIBRARIES += libmetrics`
+
+- To access the metrics library API in the module, include the
+  <metrics/metrics_library.h> header file.
+
+- The API is documented in `metrics_library.h`. Before using the API methods, a
+  MetricsLibrary object needs to be constructed and initialized through its
+  Init method.
+
+- Samples are uploaded only if the `/data/misc/metrics/enabled` file exists.
+
+
+Server Side
+-----------
+
+You will be able to see all uploaded metrics on the metrics dashboard,
+accessible via the developer console.
+
+*** note
+It usually takes a day for metrics to be available on the dashboard.
+***
+
+
+The Metrics Client: metrics_client
+----------------------------------
+
+`metrics_client` is a simple shell command-line utility for sending histogram
+samples and querying `metricsd`. It's installed under `/system/bin` on the target
+platform and uses `libmetrics`.
+
+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: metricsd
+----------------------------
+
+`metricsd` is the daemon that listens for metrics logging calls (via Binder),
+aggregates the metrics and uploads them periodically. This daemon should start as
+early as possible so that depending daemons can log at any time.
+
+`metricsd` is made of two threads that work as follows:
+
+* The binder thread listens for one-way Binder calls, aggregates the metrics in
+  memory (via `base::StatisticsRecorder`) and increments the crash counters when a
+  crash is reported. This thread is kept as simple as possible to ensure the
+  maximum throughput possible.
+* The uploader thread takes care of backing up the metrics to disk periodically
+  (to avoid losing metrics on crashes), collecting metadata about the client
+  (version number, channel, etc..) and uploading the metrics periodically to the
+  server.
+
+
+The Metrics Collector: metrics_collector
+----------------------------------------
+
+metrics_collector is a daemon that runs in the background on the target platform,
+gathers health information about the system and maintains long running counters
+(ex: number of crashes per week).
+
+The recommended way to generate metrics data from a module is to link and use
+libmetrics directly. However, we may not want to add a dependency on libmetrics
+to some modules (ex: kernel). In this case, we can add a collector to
+metrics_collector that will, for example, take measurements and report them
+periodically to metricsd (this is the case for the disk utilization histogram).
+
+
+FAQ
+---
+
+### What should my histogram's |min| and |max| values be set at?
+
+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.
+
+### How many buckets should I use in my histogram?
+
+You should allocate as many buckets as necessary to perform proper analysis
+on the collected data. Note, however, that the memory allocated in metricsd
+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).
+
+### When should I use an enumeration (linear) histogram vs. a regular (exponential) histogram?
+
+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.
+
+### How can I test that my histogram was reported correctly?
+
+* Make sure no error messages appear in logcat when you log a sample.
+* Run `metrics_client -d` to dump the currently aggregated metrics. Your
+  histogram should appear in the list.
+* Make sure that the aggregated metrics were uploaded to the server successfully
+  (check for an OK message from `metricsd` in logcat).
+* After a day, your histogram should be available on the dashboard.
diff --git a/metricsd/collectors/averaged_statistics_collector_test.cc b/metricsd/collectors/averaged_statistics_collector_test.cc
index 9c97f00..68f9f2f 100644
--- a/metricsd/collectors/averaged_statistics_collector_test.cc
+++ b/metricsd/collectors/averaged_statistics_collector_test.cc
@@ -16,11 +16,12 @@
 
 #include "averaged_statistics_collector.h"
 
+#include <memory>
+
 #include <inttypes.h>
 
 #include <base/files/file_util.h>
 #include <base/files/scoped_temp_dir.h>
-#include <base/memory/scoped_ptr.h>
 #include <base/strings/stringprintf.h>
 #include <gtest/gtest.h>
 
@@ -62,7 +63,7 @@
   }
 
   // Collector used for tests.
-  scoped_ptr<AveragedStatisticsCollector> collector_;
+  std::unique_ptr<AveragedStatisticsCollector> collector_;
 
   // Temporary directory used for tests.
   base::ScopedTempDir temp_dir_;
diff --git a/metricsd/collectors/cpu_usage_collector.cc b/metricsd/collectors/cpu_usage_collector.cc
index 05934b4..9b0bb34 100644
--- a/metricsd/collectors/cpu_usage_collector.cc
+++ b/metricsd/collectors/cpu_usage_collector.cc
@@ -104,14 +104,15 @@
                                       uint64_t *user_ticks,
                                       uint64_t *user_nice_ticks,
                                       uint64_t *system_ticks) {
-  std::vector<std::string> proc_stat_lines;
-  base::SplitString(stat_content, '\n', &proc_stat_lines);
+  std::vector<std::string> proc_stat_lines = base::SplitString(
+      stat_content, "\n", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
   if (proc_stat_lines.empty()) {
     LOG(WARNING) << "No lines found in " << kMetricsProcStatFileName;
     return false;
   }
-  std::vector<std::string> proc_stat_totals;
-  base::SplitStringAlongWhitespace(proc_stat_lines[0], &proc_stat_totals);
+  std::vector<std::string> proc_stat_totals =
+      base::SplitString(proc_stat_lines[0], base::kWhitespaceASCII,
+                        base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
 
   if (proc_stat_totals.size() != kMetricsProcStatFirstLineItemsCount ||
       proc_stat_totals[0] != "cpu" ||
diff --git a/metricsd/constants.h b/metricsd/constants.h
index 4815888..b702737 100644
--- a/metricsd/constants.h
+++ b/metricsd/constants.h
@@ -26,6 +26,7 @@
 static const char kMetricsServer[] = "https://clients4.google.com/uma/v2";
 static const char kConsentFileName[] = "enabled";
 static const char kStagedLogName[] = "staged_log";
+static const char kSavedLogName[] = "saved_log";
 static const char kFailedUploadCountName[] = "failed_upload_count";
 static const char kDefaultVersion[] = "0.0.0.0";
 
diff --git a/metricsd/include/metrics/timer.h b/metricsd/include/metrics/timer.h
index b36ffff..c1b8ede 100644
--- a/metricsd/include/metrics/timer.h
+++ b/metricsd/include/metrics/timer.h
@@ -19,10 +19,10 @@
 #ifndef METRICS_TIMER_H_
 #define METRICS_TIMER_H_
 
+#include <memory>
 #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
 
@@ -121,7 +121,7 @@
   TimerState timer_state_;
 
   // Wrapper for the calls to the system clock.
-  scoped_ptr<ClockWrapper> clock_wrapper_;
+  std::unique_ptr<ClockWrapper> clock_wrapper_;
 
   DISALLOW_COPY_AND_ASSIGN(Timer);
 };
diff --git a/metricsd/libmetrics-334380.gyp b/metricsd/libmetrics-369476.gyp
similarity index 72%
rename from metricsd/libmetrics-334380.gyp
rename to metricsd/libmetrics-369476.gyp
index 9771821..b545d35 100644
--- a/metricsd/libmetrics-334380.gyp
+++ b/metricsd/libmetrics-369476.gyp
@@ -1,6 +1,6 @@
 {
   'variables': {
-    'libbase_ver': 334380,
+    'libbase_ver': 369476,
   },
   'includes': [
     'libmetrics.gypi',
diff --git a/metricsd/metrics_collector.cc b/metricsd/metrics_collector.cc
index e9edf2e..ec7e040 100644
--- a/metricsd/metrics_collector.cc
+++ b/metricsd/metrics_collector.cc
@@ -19,6 +19,8 @@
 #include <sysexits.h>
 #include <time.h>
 
+#include <memory>
+
 #include <base/bind.h>
 #include <base/files/file_path.h>
 #include <base/files/file_util.h>
@@ -28,12 +30,13 @@
 #include <base/strings/string_split.h>
 #include <base/strings/string_util.h>
 #include <base/strings/stringprintf.h>
+#include <brillo/binder_watcher.h>
 #include <brillo/osrelease_reader.h>
 #include <dbus/dbus.h>
 #include <dbus/message.h>
 
 #include "constants.h"
-#include "metrics_collector_service_trampoline.h"
+#include "metrics_collector_service_impl.h"
 
 using base::FilePath;
 using base::StringPrintf;
@@ -68,6 +71,7 @@
 const char kVmStatFileName[] = "/proc/vmstat";
 
 const char kWeaveComponent[] = "metrics";
+const char kWeaveTrait[] = "_metrics";
 
 }  // namespace
 
@@ -126,10 +130,18 @@
     version_cumulative_cpu_use_->Set(0);
   }
 
-  // Start metricscollectorservice via trampoline
-  MetricsCollectorServiceTrampoline metricscollectorservice_trampoline(this);
-  metricscollectorservice_trampoline.Run();
+  // Start metricscollectorservice
+  android::sp<BnMetricsCollectorServiceImpl> metrics_collector_service =
+      new BnMetricsCollectorServiceImpl(this);
+  android::status_t status = android::defaultServiceManager()->addService(
+      metrics_collector_service->getInterfaceDescriptor(),
+      metrics_collector_service);
+  CHECK(status == android::OK)
+      << "failed to register service metricscollectorservice";
 
+  // Watch Binder events in the main loop
+  brillo::BinderWatcher binder_watcher;
+  CHECK(binder_watcher.Init()) << "Binder FD watcher init failed";
   return brillo::DBusDaemon::Run();
 }
 
@@ -223,23 +235,15 @@
   bus_->AssertOnDBusThread();
   CHECK(bus_->SetUpAsyncOperations());
 
-  device_ = weaved::Device::CreateInstance(
-      bus_,
-      base::Bind(&MetricsCollector::UpdateWeaveState, base::Unretained(this)));
-  device_->AddComponent(kWeaveComponent, {"_metrics"});
-  device_->AddCommandHandler(
-      kWeaveComponent,
-      "_metrics.enableAnalyticsReporting",
-      base::Bind(&MetricsCollector::OnEnableMetrics, base::Unretained(this)));
-  device_->AddCommandHandler(
-      kWeaveComponent,
-      "_metrics.disableAnalyticsReporting",
-      base::Bind(&MetricsCollector::OnDisableMetrics, base::Unretained(this)));
+  weave_service_subscription_ = weaved::Service::Connect(
+      brillo::MessageLoop::current(),
+      base::Bind(&MetricsCollector::OnWeaveServiceConnected,
+                 weak_ptr_factory_.GetWeakPtr()));
 
   latest_cpu_use_microseconds_ = cpu_usage_collector_->GetCumulativeCpuUse();
   base::MessageLoop::current()->PostDelayedTask(FROM_HERE,
       base::Bind(&MetricsCollector::HandleUpdateStatsTimeout,
-                 base::Unretained(this)),
+                 weak_ptr_factory_.GetWeakPtr()),
       base::TimeDelta::FromMilliseconds(kUpdateStatsIntervalMs));
 
   return EX_OK;
@@ -249,12 +253,28 @@
   brillo::DBusDaemon::OnShutdown(return_code);
 }
 
-void MetricsCollector::OnEnableMetrics(
-    const std::weak_ptr<weaved::Command>& cmd) {
-  auto command = cmd.lock();
-  if (!command)
+void MetricsCollector::OnWeaveServiceConnected(
+    const std::weak_ptr<weaved::Service>& service) {
+  service_ = service;
+  auto weave_service = service_.lock();
+  if (!weave_service)
     return;
 
+  weave_service->AddComponent(kWeaveComponent, {kWeaveTrait}, nullptr);
+  weave_service->AddCommandHandler(
+      kWeaveComponent, kWeaveTrait, "enableAnalyticsReporting",
+      base::Bind(&MetricsCollector::OnEnableMetrics,
+                 weak_ptr_factory_.GetWeakPtr()));
+  weave_service->AddCommandHandler(
+      kWeaveComponent, kWeaveTrait, "disableAnalyticsReporting",
+      base::Bind(&MetricsCollector::OnDisableMetrics,
+                 weak_ptr_factory_.GetWeakPtr()));
+
+  UpdateWeaveState();
+}
+
+void MetricsCollector::OnEnableMetrics(
+    std::unique_ptr<weaved::Command> command) {
   if (base::WriteFile(
           shared_metrics_directory_.Append(metrics::kConsentFileName), "", 0) !=
       0) {
@@ -269,11 +289,7 @@
 }
 
 void MetricsCollector::OnDisableMetrics(
-    const std::weak_ptr<weaved::Command>& cmd) {
-  auto command = cmd.lock();
-  if (!command)
-    return;
-
+    std::unique_ptr<weaved::Command> command) {
   if (!base::DeleteFile(
           shared_metrics_directory_.Append(metrics::kConsentFileName), false)) {
     PLOG(ERROR) << "Could not delete the consent file.";
@@ -287,16 +303,16 @@
 }
 
 void MetricsCollector::UpdateWeaveState() {
-  if (!device_)
+  auto weave_service = service_.lock();
+  if (!weave_service)
     return;
 
   std::string enabled =
       metrics_lib_->AreMetricsEnabled() ? "enabled" : "disabled";
 
-  if (!device_->SetStateProperty(kWeaveComponent,
-                                 "_metrics.analyticsReportingState",
-                                 enabled,
-                                 nullptr)) {
+  if (!weave_service->SetStateProperty(kWeaveComponent, kWeaveTrait,
+                                       "analyticsReportingState", enabled,
+                                       nullptr)) {
     LOG(ERROR) << "failed to update weave's state";
   }
 }
@@ -370,8 +386,8 @@
   }
   base::TimeDelta waitDelta = base::TimeDelta::FromSeconds(wait);
   base::MessageLoop::current()->PostDelayedTask(FROM_HERE,
-      base::Bind(&MetricsCollector::MeminfoCallback, base::Unretained(this),
-                 waitDelta),
+      base::Bind(&MetricsCollector::MeminfoCallback,
+                 weak_ptr_factory_.GetWeakPtr(), waitDelta),
       waitDelta);
 }
 
@@ -385,8 +401,8 @@
   // Make both calls even if the first one fails.
   if (ProcessMeminfo(meminfo_raw)) {
     base::MessageLoop::current()->PostDelayedTask(FROM_HERE,
-        base::Bind(&MetricsCollector::MeminfoCallback, base::Unretained(this),
-                   wait),
+        base::Bind(&MetricsCollector::MeminfoCallback,
+                   weak_ptr_factory_.GetWeakPtr(), wait),
         wait);
   }
 }
@@ -518,18 +534,20 @@
 
 bool MetricsCollector::FillMeminfo(const string& meminfo_raw,
                                     vector<MeminfoRecord>* fields) {
-  vector<string> lines;
-  unsigned int nlines = Tokenize(meminfo_raw, "\n", &lines);
+  vector<std::string> lines =
+      base::SplitString(meminfo_raw, "\n", base::KEEP_WHITESPACE,
+                        base::SPLIT_WANT_NONEMPTY);
 
   // 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();
+  size_t ifield = 0;
+  for (size_t iline = 0;
+       iline < lines.size() && ifield < fields->size();
        iline++) {
-    vector<string> tokens;
-    Tokenize(lines[iline], ": ", &tokens);
+    vector<string> tokens =
+        base::SplitString(lines[iline], ": ", base::KEEP_WHITESPACE,
+                          base::SPLIT_WANT_NONEMPTY);
     if (strcmp((*fields)[ifield].match, tokens[0].c_str()) == 0) {
       // Name matches. Parse value and save.
       if (!base::StringToInt(tokens[1], &(*fields)[ifield].value)) {
@@ -553,7 +571,8 @@
     return;
   }
   base::MessageLoop::current()->PostDelayedTask(FROM_HERE,
-      base::Bind(&MetricsCollector::MemuseCallback, base::Unretained(this)),
+      base::Bind(&MetricsCollector::MemuseCallback,
+                 weak_ptr_factory_.GetWeakPtr()),
       base::TimeDelta::FromSeconds(interval));
 }
 
@@ -665,7 +684,7 @@
 }
 
 void MetricsCollector::SendAndResetDailyUseSample(
-    const scoped_ptr<PersistentInteger>& use) {
+    const unique_ptr<PersistentInteger>& use) {
   SendSample(use->Name(),
              use->GetAndClear(),
              1,                        // value of first bucket
@@ -674,7 +693,7 @@
 }
 
 void MetricsCollector::SendAndResetCrashIntervalSample(
-    const scoped_ptr<PersistentInteger>& interval) {
+    const unique_ptr<PersistentInteger>& interval) {
   SendSample(interval->Name(),
              interval->GetAndClear(),
              1,                        // value of first bucket
@@ -683,7 +702,7 @@
 }
 
 void MetricsCollector::SendAndResetCrashFrequencySample(
-    const scoped_ptr<PersistentInteger>& frequency) {
+    const unique_ptr<PersistentInteger>& frequency) {
   SendSample(frequency->Name(),
              frequency->GetAndClear(),
              1,                        // value of first bucket
@@ -739,6 +758,6 @@
   UpdateStats(TimeTicks::Now(), Time::Now());
   base::MessageLoop::current()->PostDelayedTask(FROM_HERE,
       base::Bind(&MetricsCollector::HandleUpdateStatsTimeout,
-                 base::Unretained(this)),
+                 weak_ptr_factory_.GetWeakPtr()),
       base::TimeDelta::FromMilliseconds(kUpdateStatsIntervalMs));
 }
diff --git a/metricsd/metrics_collector.h b/metricsd/metrics_collector.h
index 422ed7c..ca4ae52 100644
--- a/metricsd/metrics_collector.h
+++ b/metricsd/metrics_collector.h
@@ -20,15 +20,17 @@
 #include <stdint.h>
 
 #include <map>
+#include <memory>
 #include <string>
 #include <vector>
 
 #include <base/files/file_path.h>
-#include <base/memory/scoped_ptr.h>
+#include <base/memory/weak_ptr.h>
 #include <base/time/time.h>
+#include <brillo/binder_watcher.h>
 #include <brillo/daemons/dbus_daemon.h>
 #include <libweaved/command.h>
-#include <libweaved/device.h>
+#include <libweaved/service.h>
 #include <gtest/gtest_prod.h>  // for FRIEND_TEST
 
 #include "collectors/averaged_statistics_collector.h"
@@ -38,6 +40,7 @@
 #include "persistent_integer.h"
 
 using chromeos_metrics::PersistentInteger;
+using std::unique_ptr;
 
 class MetricsCollector : public brillo::DBusDaemon {
  public:
@@ -113,10 +116,10 @@
   };
 
   // Enables metrics reporting.
-  void OnEnableMetrics(const std::weak_ptr<weaved::Command>& cmd);
+  void OnEnableMetrics(std::unique_ptr<weaved::Command> command);
 
   // Disables metrics reporting.
-  void OnDisableMetrics(const std::weak_ptr<weaved::Command>& cmd);
+  void OnDisableMetrics(std::unique_ptr<weaved::Command> command);
 
   // Updates the weave device state.
   void UpdateWeaveState();
@@ -151,18 +154,17 @@
 
   // Sends a sample representing the number of seconds of active use
   // for a 24-hour period and reset |use|.
-  void SendAndResetDailyUseSample(
-      const scoped_ptr<PersistentInteger>& use);
+  void SendAndResetDailyUseSample(const unique_ptr<PersistentInteger>& use);
 
   // Sends a sample representing a time interval between two crashes of the
   // same type and reset |interval|.
   void SendAndResetCrashIntervalSample(
-      const scoped_ptr<PersistentInteger>& interval);
+      const unique_ptr<PersistentInteger>& interval);
 
   // Sends a sample representing a frequency of crashes of some type and reset
   // |frequency|.
   void SendAndResetCrashFrequencySample(
-      const scoped_ptr<PersistentInteger>& frequency);
+      const unique_ptr<PersistentInteger>& frequency);
 
   // Initializes vm and disk stats reporting.
   void StatsReporterInit();
@@ -216,6 +218,10 @@
   // Reads a string from a file and converts it to uint64_t.
   static bool ReadFileToUint64(const base::FilePath& path, uint64_t* value);
 
+  // Callback invoked when a connection to weaved's service is established
+  // over Binder interface.
+  void OnWeaveServiceConnected(const std::weak_ptr<weaved::Service>& service);
+
   // VARIABLES
 
   // Test mode.
@@ -241,38 +247,41 @@
   base::TimeDelta latest_cpu_use_microseconds_;
 
   // Persistent values and accumulators for crash statistics.
-  scoped_ptr<PersistentInteger> daily_cycle_;
-  scoped_ptr<PersistentInteger> weekly_cycle_;
-  scoped_ptr<PersistentInteger> version_cycle_;
+  unique_ptr<PersistentInteger> daily_cycle_;
+  unique_ptr<PersistentInteger> weekly_cycle_;
+  unique_ptr<PersistentInteger> version_cycle_;
 
   // Active use accumulated in a day.
-  scoped_ptr<PersistentInteger> daily_active_use_;
+  unique_ptr<PersistentInteger> daily_active_use_;
   // Active use accumulated since the latest version update.
-  scoped_ptr<PersistentInteger> version_cumulative_active_use_;
+  unique_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_;
+  unique_ptr<PersistentInteger> version_cumulative_cpu_use_;
 
-  scoped_ptr<PersistentInteger> user_crash_interval_;
-  scoped_ptr<PersistentInteger> kernel_crash_interval_;
-  scoped_ptr<PersistentInteger> unclean_shutdown_interval_;
+  unique_ptr<PersistentInteger> user_crash_interval_;
+  unique_ptr<PersistentInteger> kernel_crash_interval_;
+  unique_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_;
+  unique_ptr<PersistentInteger> any_crashes_daily_count_;
+  unique_ptr<PersistentInteger> any_crashes_weekly_count_;
+  unique_ptr<PersistentInteger> user_crashes_daily_count_;
+  unique_ptr<PersistentInteger> user_crashes_weekly_count_;
+  unique_ptr<PersistentInteger> kernel_crashes_daily_count_;
+  unique_ptr<PersistentInteger> kernel_crashes_weekly_count_;
+  unique_ptr<PersistentInteger> kernel_crashes_version_count_;
+  unique_ptr<PersistentInteger> unclean_shutdowns_daily_count_;
+  unique_ptr<PersistentInteger> unclean_shutdowns_weekly_count_;
 
-  scoped_ptr<CpuUsageCollector> cpu_usage_collector_;
-  scoped_ptr<DiskUsageCollector> disk_usage_collector_;
-  scoped_ptr<AveragedStatisticsCollector> averaged_stats_collector_;
+  unique_ptr<CpuUsageCollector> cpu_usage_collector_;
+  unique_ptr<DiskUsageCollector> disk_usage_collector_;
+  unique_ptr<AveragedStatisticsCollector> averaged_stats_collector_;
 
-  std::unique_ptr<weaved::Device> device_;
+  unique_ptr<weaved::Service::Subscription> weave_service_subscription_;
+  std::weak_ptr<weaved::Service> service_;
+
+  base::WeakPtrFactory<MetricsCollector> weak_ptr_factory_{this};
 };
 
 #endif  // METRICS_METRICS_COLLECTOR_H_
diff --git a/metricsd/metrics_collector.rc b/metricsd/metrics_collector.rc
index 2e7e0ae..3dcb2d7 100644
--- a/metricsd/metrics_collector.rc
+++ b/metricsd/metrics_collector.rc
@@ -1,4 +1,4 @@
 service metricscollector /system/bin/metrics_collector --foreground --logtosyslog
     class late_start
-    user system
-    group system dbus
+    user metrics_coll
+    group metrics_coll dbus
diff --git a/metricsd/metrics_collector_main.cc b/metricsd/metrics_collector_main.cc
index d7aaaf5..14bb935 100644
--- a/metricsd/metrics_collector_main.cc
+++ b/metricsd/metrics_collector_main.cc
@@ -41,7 +41,8 @@
   }
   dev_path = dev_path_cstr;
   // Check that rootdev begins with "/dev/block/".
-  if (!base::StartsWithASCII(dev_path, dev_prefix, false)) {
+  if (!base::StartsWith(dev_path, dev_prefix,
+                        base::CompareCase::INSENSITIVE_ASCII)) {
     LOG(WARNING) << "unexpected root device " << dev_path;
     return "";
   }
diff --git a/metricsd/metrics_collector_service_impl.cc b/metricsd/metrics_collector_service_impl.cc
index dbb0578..4d9a05a 100644
--- a/metricsd/metrics_collector_service_impl.cc
+++ b/metricsd/metrics_collector_service_impl.cc
@@ -18,27 +18,18 @@
 
 #include <binder/IServiceManager.h>
 #include <binder/Status.h>
-#include <brillo/binder_watcher.h>
 #include <utils/Errors.h>
 
-#include "metrics_collector_service_trampoline.h"
+#include "metrics_collector.h"
 
 using namespace android;
 
 BnMetricsCollectorServiceImpl::BnMetricsCollectorServiceImpl(
-    MetricsCollectorServiceTrampoline* metrics_collector_service_trampoline) {
-  metrics_collector_service_trampoline_ = metrics_collector_service_trampoline;
-}
-
-void BnMetricsCollectorServiceImpl::Run() {
-  status_t status =
-      defaultServiceManager()->addService(getInterfaceDescriptor(), this);
-  CHECK(status == OK) << "libmetricscollectorservice: failed to add service";
-  binder_watcher_.reset(new ::brillo::BinderWatcher);
-  CHECK(binder_watcher_->Init()) << "Binder FD watcher init failed";
+    MetricsCollector* metrics_collector)
+    : metrics_collector_(metrics_collector) {
 }
 
 android::binder::Status BnMetricsCollectorServiceImpl::notifyUserCrash() {
-  metrics_collector_service_trampoline_->ProcessUserCrash();
+  metrics_collector_->ProcessUserCrash();
   return android::binder::Status::ok();
 }
diff --git a/metricsd/metrics_collector_service_impl.h b/metricsd/metrics_collector_service_impl.h
index bdcab50..8db418a 100644
--- a/metricsd/metrics_collector_service_impl.h
+++ b/metricsd/metrics_collector_service_impl.h
@@ -18,45 +18,31 @@
 #define METRICSD_METRICS_COLLECTOR_SERVICE_IMPL_H_
 
 // metrics_collector binder service implementation.  Constructed by
-// MetricsCollectorServiceTrampoline, which we use to call back into
-// MetricsCollector.  The trampoline isolates us from the -frtti code of
-// metrics_collector / libbrillo.
+// MetricsCollector.
 
 #include "android/brillo/metrics/BnMetricsCollectorService.h"
 
-#include <memory>
-
 #include <binder/Status.h>
-#include <brillo/binder_watcher.h>
 
-class MetricsCollectorServiceTrampoline;
-
-//#include "metrics_collector_service_trampoline.h"
+class MetricsCollector;
 
 class BnMetricsCollectorServiceImpl
     : public android::brillo::metrics::BnMetricsCollectorService {
  public:
-  // Passed a this pointer from the MetricsCollectorServiceTrampoline
-  // object that constructs us.
+  // Passed a this pointer from the MetricsCollector object that constructs us.
   explicit BnMetricsCollectorServiceImpl(
-      MetricsCollectorServiceTrampoline* metrics_collector_service_trampoline);
+      MetricsCollector* metrics_collector_service);
 
   virtual ~BnMetricsCollectorServiceImpl() = default;
 
-  // Starts the binder main loop.
-  void Run();
-
   // Called by crash_reporter to report a userspace crash event.  We relay
-  // this to MetricsCollector using the trampoline.
+  // this to MetricsCollector.
   android::binder::Status notifyUserCrash();
 
  private:
-  // Trampoline object that constructs us, we use this to call MetricsCollector
-  // methods via the trampoline.
-  MetricsCollectorServiceTrampoline* metrics_collector_service_trampoline_;
-
-  // BinderWatcher object we construct for handling Binder traffic
-  std::unique_ptr<brillo::BinderWatcher> binder_watcher_;
+  // MetricsCollector object that constructs us, we use this to call back
+  // to it.
+  MetricsCollector* metrics_collector_;
 };
 
 #endif  // METRICSD_METRICS_COLLECTOR_SERVICE_IMPL_H_
diff --git a/metricsd/metrics_collector_service_trampoline.cc b/metricsd/metrics_collector_service_trampoline.cc
deleted file mode 100644
index 12b80a1..0000000
--- a/metricsd/metrics_collector_service_trampoline.cc
+++ /dev/null
@@ -1,34 +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.
- */
-
-#include "metrics_collector_service_trampoline.h"
-#include "metrics_collector.h"
-#include "metrics_collector_service_impl.h"
-
-MetricsCollectorServiceTrampoline::MetricsCollectorServiceTrampoline(
-    MetricsCollector* metrics_collector) {
-  metrics_collector_ = metrics_collector;
-}
-
-void MetricsCollectorServiceTrampoline::Run() {
-  // Start metricscollectorservice binder service
-  metrics_collector_service.reset(new BnMetricsCollectorServiceImpl(this));
-  metrics_collector_service->Run();
-}
-
-void MetricsCollectorServiceTrampoline::ProcessUserCrash() {
-  metrics_collector_->ProcessUserCrash();
-}
diff --git a/metricsd/metrics_collector_service_trampoline.h b/metricsd/metrics_collector_service_trampoline.h
deleted file mode 100644
index 5da9fa5..0000000
--- a/metricsd/metrics_collector_service_trampoline.h
+++ /dev/null
@@ -1,57 +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.
- */
-
-#ifndef METRICSD_METRICS_COLLECTOR_SERVICE_TRAMPOLINE_H_
-#define METRICSD_METRICS_COLLECTOR_SERVICE_TRAMPOLINE_H_
-
-// Trampoline between the -fno-rtti compile of libmetricsservice and the
-// -frtti compile of metrics_collector.  MetricsCollectorServiceTrampoline
-// is called from MetricsCollector to run the IMetricsCollectorService
-// server, and acts as a go-between for calls from server back to
-// MetricsCollector.
-
-#include <memory>
-
-#include "metrics_collector_service_impl.h"
-
-// Forward declaration of MetricsCollector.  Don't include the header file
-// for the class here, as it pulls in -frtti stuff.
-class MetricsCollector;
-
-class MetricsCollectorServiceTrampoline {
- public:
-  // Constructor take a this pointer from the MetricsCollector class that
-  // constructs these objects.
-  explicit MetricsCollectorServiceTrampoline(
-      MetricsCollector* metrics_collector);
-
-  // Initialize and run the IMetricsCollectorService
-  void Run();
-
-  // Called from IMetricsCollectorService to trampoline into the
-  // MetricsCollector method of the same name.
-  void ProcessUserCrash();
-
- private:
-  // The MetricsCollector object that constructs us, for which we act as
-  // the go-between for MetricsCollectorServiceImpl use.
-  MetricsCollector* metrics_collector_;
-
-  // The IMetricsCollectorService implementation we construct.
-  std::unique_ptr<BnMetricsCollectorServiceImpl> metrics_collector_service;
-};
-
-#endif  // METRICSD_METRICS_COLLECTOR_SERVICE_TRAMPOLINE_H_
diff --git a/metricsd/metricsd.rc b/metricsd/metricsd.rc
index 359d0d1..825c87f 100644
--- a/metricsd/metricsd.rc
+++ b/metricsd/metricsd.rc
@@ -1,9 +1,9 @@
 on post-fs-data
-    mkdir /data/misc/metrics 0770 system system
-    mkdir /data/misc/metricsd 0700 system system
-    mkdir /data/misc/metrics_collector 0700 system system
+    mkdir /data/misc/metrics 0750 metrics_coll system
+    mkdir /data/misc/metricsd 0700 metricsd metricsd
+    mkdir /data/misc/metrics_collector 0700 metrics_coll metrics_coll
 
 service metricsd /system/bin/metricsd --foreground --logtosyslog
     class late_start
-    user system
+    user metricsd
     group system dbus inet
diff --git a/metricsd/metricsd_main.cc b/metricsd/metricsd_main.cc
index f460268..0178342 100644
--- a/metricsd/metricsd_main.cc
+++ b/metricsd/metricsd_main.cc
@@ -14,21 +14,15 @@
  * limitations under the License.
  */
 
-#include <thread>
-
-#include <base/at_exit.h>
 #include <base/command_line.h>
 #include <base/files/file_path.h>
 #include <base/logging.h>
-#include <base/metrics/statistics_recorder.h>
-#include <base/strings/string_util.h>
 #include <base/time/time.h>
 #include <brillo/flag_helper.h>
 #include <brillo/syslog_logging.h>
 
 #include "constants.h"
-#include "uploader/bn_metricsd_impl.h"
-#include "uploader/crash_counters.h"
+#include "uploader/metricsd_service_runner.h"
 #include "uploader/upload_service.h"
 
 int main(int argc, char** argv) {
@@ -39,10 +33,13 @@
 
   // Upload Service flags.
   DEFINE_int32(upload_interval_secs, 1800,
-               "Interval at which metrics_daemon sends the metrics. (needs "
-               "-uploader)");
+               "Interval at which metricsd uploads the metrics.");
+  DEFINE_int32(disk_persistence_interval_secs, 300,
+               "Interval at which metricsd saves the aggregated metrics to "
+               "disk to avoid losing them if metricsd stops in between "
+               "two uploads.");
   DEFINE_string(server, metrics::kMetricsServer,
-                "Server to upload the metrics to. (needs -uploader)");
+                "Server to upload the metrics to.");
   DEFINE_string(private_directory, metrics::kMetricsdDirectory,
                 "Path to the private directory used by metricsd "
                 "(testing only)");
@@ -76,18 +73,11 @@
     return errno;
   }
 
-  std::shared_ptr<CrashCounters> counters(new CrashCounters);
-
   UploadService upload_service(
       FLAGS_server, base::TimeDelta::FromSeconds(FLAGS_upload_interval_secs),
+      base::TimeDelta::FromSeconds(FLAGS_disk_persistence_interval_secs),
       base::FilePath(FLAGS_private_directory),
-      base::FilePath(FLAGS_shared_directory), counters);
+      base::FilePath(FLAGS_shared_directory));
 
-  base::StatisticsRecorder::Initialize();
-
-  // Create and start the binder thread.
-  BnMetricsdImpl binder_service(counters);
-  std::thread binder_thread(&BnMetricsdImpl::Run, &binder_service);
-
-  upload_service.Run();
+  return upload_service.Run();
 }
diff --git a/metricsd/persistent_integer_test.cc b/metricsd/persistent_integer_test.cc
index 55d6cbc..bf76261 100644
--- a/metricsd/persistent_integer_test.cc
+++ b/metricsd/persistent_integer_test.cc
@@ -14,12 +14,13 @@
  * limitations under the License.
  */
 
-#include <gtest/gtest.h>
+#include <memory>
 
 #include <base/compiler_specific.h>
 #include <base/files/file_enumerator.h>
 #include <base/files/file_util.h>
 #include <base/files/scoped_temp_dir.h>
+#include <gtest/gtest.h>
 
 #include "persistent_integer.h"
 
@@ -38,7 +39,7 @@
 };
 
 TEST_F(PersistentIntegerTest, BasicChecks) {
-  scoped_ptr<PersistentInteger> pi(
+  std::unique_ptr<PersistentInteger> pi(
       new PersistentInteger(kBackingFileName, temp_dir_.path()));
 
   // Test initialization.
diff --git a/metricsd/timer.cc b/metricsd/timer.cc
index 0c2c119..06fc336 100644
--- a/metricsd/timer.cc
+++ b/metricsd/timer.cc
@@ -18,8 +18,6 @@
 
 #include <string>
 
-#include <base/memory/scoped_ptr.h>
-
 #include "metrics/metrics_library.h"
 
 namespace chromeos_metrics {
diff --git a/metricsd/timer_test.cc b/metricsd/timer_test.cc
index bc7a2a1..7a67e11 100644
--- a/metricsd/timer_test.cc
+++ b/metricsd/timer_test.cc
@@ -16,9 +16,9 @@
 
 #include <stdint.h>
 
-#include <base/memory/scoped_ptr.h>
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
+#include <memory>
 
 #include "metrics/metrics_library_mock.h"
 #include "metrics/timer.h"
@@ -61,7 +61,7 @@
   virtual void TearDown() {}
 
   Timer timer_;
-  scoped_ptr<ClockWrapperMock> clock_wrapper_mock_;
+  std::unique_ptr<ClockWrapperMock> clock_wrapper_mock_;
   base::TimeTicks stime, etime, stime2, etime2, stime3, etime3;
 };
 
@@ -436,7 +436,7 @@
 
   TimerReporter timer_reporter_;
   MetricsLibraryMock lib_;
-  scoped_ptr<ClockWrapperMock> clock_wrapper_mock_;
+  std::unique_ptr<ClockWrapperMock> clock_wrapper_mock_;
   base::TimeTicks stime, etime;
 };
 
diff --git a/metricsd/uploader/bn_metricsd_impl.cc b/metricsd/uploader/bn_metricsd_impl.cc
index 2cbc2da..219ed60 100644
--- a/metricsd/uploader/bn_metricsd_impl.cc
+++ b/metricsd/uploader/bn_metricsd_impl.cc
@@ -19,8 +19,6 @@
 #include <base/metrics/histogram.h>
 #include <base/metrics/sparse_histogram.h>
 #include <base/metrics/statistics_recorder.h>
-#include <binder/IPCThreadState.h>
-#include <binder/IServiceManager.h>
 #include <utils/Errors.h>
 #include <utils/String16.h>
 #include <utils/String8.h>
@@ -37,16 +35,6 @@
   CHECK(counters_) << "Invalid counters argument to constructor";
 }
 
-void BnMetricsdImpl::Run() {
-  android::status_t status =
-      android::defaultServiceManager()->addService(getInterfaceDescriptor(),
-                                                   this);
-  CHECK(status == android::OK) << "Metricsd service registration failed";
-  android::ProcessState::self()->setThreadPoolMaxThreadCount(0);
-  android::IPCThreadState::self()->disableBackgroundScheduling(true);
-  android::IPCThreadState::self()->joinThreadPool();
-}
-
 Status BnMetricsdImpl::recordHistogram(
     const String16& name, int sample, int min, int max, int nbuckets) {
   base::HistogramBase* histogram = base::Histogram::FactoryGet(
diff --git a/metricsd/uploader/bn_metricsd_impl.h b/metricsd/uploader/bn_metricsd_impl.h
index 016ccb6..bf47e80 100644
--- a/metricsd/uploader/bn_metricsd_impl.h
+++ b/metricsd/uploader/bn_metricsd_impl.h
@@ -25,9 +25,6 @@
   explicit BnMetricsdImpl(const std::shared_ptr<CrashCounters>& counters);
   virtual ~BnMetricsdImpl() = default;
 
-  // Starts the binder main loop.
-  void Run();
-
   // Records a histogram.
   android::binder::Status recordHistogram(const android::String16& name,
                                           int sample,
diff --git a/metricsd/uploader/metrics_log.cc b/metricsd/uploader/metrics_log.cc
index a01b5da..fcaa8c1 100644
--- a/metricsd/uploader/metrics_log.cc
+++ b/metricsd/uploader/metrics_log.cc
@@ -18,6 +18,8 @@
 
 #include <string>
 
+#include <base/files/file_util.h>
+
 #include "uploader/proto/system_profile.pb.h"
 #include "uploader/system_profile_setter.h"
 
@@ -27,6 +29,40 @@
     : MetricsLogBase("", 0, metrics::MetricsLogBase::ONGOING_LOG, "") {
 }
 
+bool MetricsLog::LoadFromFile(const base::FilePath& saved_log) {
+  std::string encoded_log;
+  if (!base::ReadFileToString(saved_log, &encoded_log)) {
+    LOG(ERROR) << "Failed to read the metrics log backup from "
+               << saved_log.value();
+    return false;
+  }
+
+  if (!uma_proto()->ParseFromString(encoded_log)) {
+    LOG(ERROR) << "Failed to parse log from " << saved_log.value()
+               << ", deleting the log";
+    base::DeleteFile(saved_log, false);
+    uma_proto()->Clear();
+    return false;
+  }
+
+  VLOG(1) << uma_proto()->histogram_event_size() << " histograms loaded from "
+          << saved_log.value();
+
+  return true;
+}
+
+bool MetricsLog::SaveToFile(const base::FilePath& path) {
+  std::string encoded_log;
+  GetEncodedLog(&encoded_log);
+
+  if (static_cast<int>(encoded_log.size()) !=
+      base::WriteFile(path, encoded_log.data(), encoded_log.size())) {
+    LOG(ERROR) << "Failed to persist the current log to " << path.value();
+    return false;
+  }
+  return true;
+}
+
 void MetricsLog::IncrementUserCrashCount(unsigned int count) {
   metrics::SystemProfileProto::Stability* stability(
       uma_proto()->mutable_system_profile()->mutable_stability());
@@ -49,5 +85,6 @@
 }
 
 bool MetricsLog::PopulateSystemProfile(SystemProfileSetter* profile_setter) {
+  CHECK(profile_setter);
   return profile_setter->Populate(uma_proto());
 }
diff --git a/metricsd/uploader/metrics_log.h b/metricsd/uploader/metrics_log.h
index b76cd72..9e60b97 100644
--- a/metricsd/uploader/metrics_log.h
+++ b/metricsd/uploader/metrics_log.h
@@ -19,6 +19,7 @@
 
 #include <string>
 
+#include <base/files/file_path.h>
 #include <base/macros.h>
 
 #include "uploader/metrics_log_base.h"
@@ -44,8 +45,15 @@
   // Populate the system profile with system information using setter.
   bool PopulateSystemProfile(SystemProfileSetter* setter);
 
+  // Load the log from |path|.
+  bool LoadFromFile(const base::FilePath& path);
+
+  // Save this log to |path|.
+  bool SaveToFile(const base::FilePath& path);
+
  private:
   friend class UploadServiceTest;
+  FRIEND_TEST(UploadServiceTest, CurrentLogSavedAndResumed);
   FRIEND_TEST(UploadServiceTest, LogContainsAggregatedValues);
   FRIEND_TEST(UploadServiceTest, LogContainsCrashCounts);
   FRIEND_TEST(UploadServiceTest, LogKernelCrash);
diff --git a/metricsd/uploader/metricsd_service_runner.cc b/metricsd/uploader/metricsd_service_runner.cc
new file mode 100644
index 0000000..5a759d3
--- /dev/null
+++ b/metricsd/uploader/metricsd_service_runner.cc
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "uploader/metricsd_service_runner.h"
+
+#include <thread>
+
+#include <binder/IServiceManager.h>
+#include <brillo/binder_watcher.h>
+#include <utils/Errors.h>
+
+#include "uploader/bn_metricsd_impl.h"
+
+MetricsdServiceRunner::MetricsdServiceRunner(
+    std::shared_ptr<CrashCounters> counters)
+    : counters_(counters) {}
+
+void MetricsdServiceRunner::Start() {
+  thread_.reset(new std::thread(&MetricsdServiceRunner::Run, this));
+}
+
+void MetricsdServiceRunner::Run() {
+  android::sp<BnMetricsdImpl> metrics_service(new BnMetricsdImpl(counters_));
+
+  android::status_t status = android::defaultServiceManager()->addService(
+      metrics_service->getInterfaceDescriptor(), metrics_service);
+  CHECK(status == android::OK) << "Metricsd service registration failed";
+
+  message_loop_for_io_.reset(new base::MessageLoopForIO);
+
+  brillo::BinderWatcher watcher;
+  CHECK(watcher.Init()) << "failed to initialize the binder file descriptor "
+                        << "watcher";
+
+  message_loop_for_io_->Run();
+
+  // Delete the message loop here as it needs to be deconstructed in the thread
+  // it is attached to.
+  message_loop_for_io_.reset();
+}
+
+void MetricsdServiceRunner::Stop() {
+  message_loop_for_io_->PostTask(FROM_HERE,
+                                 message_loop_for_io_->QuitWhenIdleClosure());
+
+  thread_->join();
+}
diff --git a/metricsd/uploader/metricsd_service_runner.h b/metricsd/uploader/metricsd_service_runner.h
new file mode 100644
index 0000000..1715de0
--- /dev/null
+++ b/metricsd/uploader/metricsd_service_runner.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef METRICS_UPLOADER_METRISCD_SERVICE_RUNNER_H_
+#define METRICS_UPLOADER_METRISCD_SERVICE_RUNNER_H_
+
+#include <memory>
+#include <thread>
+
+#include <base/message_loop/message_loop.h>
+
+#include "uploader/crash_counters.h"
+
+class MetricsdServiceRunner {
+ public:
+  MetricsdServiceRunner(std::shared_ptr<CrashCounters> counters);
+
+  // Start the Metricsd Binder service in a new thread.
+  void Start();
+
+  // Stop the Metricsd service and wait for its thread to exit.
+  void Stop();
+
+ private:
+  // Creates and run the main loop for metricsd's Binder service.
+  void Run();
+
+  std::unique_ptr<base::MessageLoopForIO> message_loop_for_io_;
+
+  std::unique_ptr<std::thread> thread_;
+  std::shared_ptr<CrashCounters> counters_;
+};
+
+#endif  // METRICS_UPLOADER_METRISCD_SERVICE_RUNNER_H_
diff --git a/metricsd/uploader/system_profile_cache.cc b/metricsd/uploader/system_profile_cache.cc
index 70f6afd..e6f6617 100644
--- a/metricsd/uploader/system_profile_cache.cc
+++ b/metricsd/uploader/system_profile_cache.cc
@@ -80,6 +80,10 @@
   } else {
     reader.Load();
     auto client = update_engine::UpdateEngineClient::CreateInstance();
+    if (!client) {
+      LOG(ERROR) << "failed to create the update engine client";
+      return false;
+    }
     if (!client->GetChannel(&channel)) {
       LOG(ERROR) << "failed to read the current channel from update engine.";
       return false;
diff --git a/metricsd/uploader/system_profile_cache.h b/metricsd/uploader/system_profile_cache.h
index 0a21ad4..f9c484c 100644
--- a/metricsd/uploader/system_profile_cache.h
+++ b/metricsd/uploader/system_profile_cache.h
@@ -19,12 +19,12 @@
 
 #include <stdint.h>
 
+#include <memory>
 #include <string>
 
 #include "base/compiler_specific.h"
 #include "base/files/file_path.h"
 #include "base/gtest_prod_util.h"
-#include "base/memory/scoped_ptr.h"
 #include "persistent_integer.h"
 #include "uploader/proto/system_profile.pb.h"
 #include "uploader/system_profile_setter.h"
@@ -80,7 +80,7 @@
   bool initialized_;
   bool testing_;
   base::FilePath metrics_directory_;
-  scoped_ptr<chromeos_metrics::PersistentInteger> session_id_;
+  std::unique_ptr<chromeos_metrics::PersistentInteger> session_id_;
   SystemProfile profile_;
 };
 
diff --git a/metricsd/uploader/upload_service.cc b/metricsd/uploader/upload_service.cc
index ea8427a..0dc59a4 100644
--- a/metricsd/uploader/upload_service.cc
+++ b/metricsd/uploader/upload_service.cc
@@ -18,6 +18,7 @@
 
 #include <sysexits.h>
 
+#include <memory>
 #include <string>
 
 #include <base/bind.h>
@@ -41,49 +42,89 @@
 
 UploadService::UploadService(const std::string& server,
                              const base::TimeDelta& upload_interval,
+                             const base::TimeDelta& disk_persistence_interval,
                              const base::FilePath& private_metrics_directory,
-                             const base::FilePath& shared_metrics_directory,
-                             const std::shared_ptr<CrashCounters> counters)
-    : histogram_snapshot_manager_(this),
+                             const base::FilePath& shared_metrics_directory)
+    : brillo::Daemon(),
+      histogram_snapshot_manager_(this),
       sender_(new HttpSender(server)),
       failed_upload_count_(metrics::kFailedUploadCountName,
                            private_metrics_directory),
-      counters_(counters),
-      upload_interval_(upload_interval) {
+      counters_(new CrashCounters),
+      upload_interval_(upload_interval),
+      disk_persistence_interval_(disk_persistence_interval),
+      metricsd_service_runner_(counters_) {
   staged_log_path_ = private_metrics_directory.Append(metrics::kStagedLogName);
+  saved_log_path_ = private_metrics_directory.Append(metrics::kSavedLogName);
   consent_file_ = shared_metrics_directory.Append(metrics::kConsentFileName);
 }
 
+void UploadService::LoadSavedLog() {
+  if (base::PathExists(saved_log_path_)) {
+    GetOrCreateCurrentLog()->LoadFromFile(saved_log_path_);
+  }
+}
+
 int UploadService::OnInit() {
+  brillo::Daemon::OnInit();
+
+  base::StatisticsRecorder::Initialize();
+  metricsd_service_runner_.Start();
+
   system_profile_setter_.reset(new SystemProfileCache());
 
-  base::MessageLoop::current()->PostDelayedTask(FROM_HERE,
-      base::Bind(&UploadService::UploadEventCallback,
-                 base::Unretained(this),
-                 upload_interval_),
+  base::MessageLoop::current()->PostDelayedTask(
+      FROM_HERE,
+      base::Bind(&UploadService::UploadEventCallback, base::Unretained(this)),
       upload_interval_);
+
+  base::MessageLoop::current()->PostDelayedTask(
+      FROM_HERE,
+      base::Bind(&UploadService::PersistEventCallback, base::Unretained(this)),
+      disk_persistence_interval_);
+
+  LoadSavedLog();
+
   return EX_OK;
 }
 
+void UploadService::OnShutdown(int* exit_code) {
+  metricsd_service_runner_.Stop();
+  PersistToDisk();
+}
+
 void UploadService::InitForTest(SystemProfileSetter* setter) {
+  LoadSavedLog();
   system_profile_setter_.reset(setter);
 }
 
 void UploadService::StartNewLog() {
-  CHECK(!HasStagedLog()) << "the staged log should be discarded before "
-                         << "starting a new metrics log";
-  MetricsLog* log = new MetricsLog();
-  current_log_.reset(log);
+  current_log_.reset(new MetricsLog());
 }
 
-void UploadService::UploadEventCallback(const base::TimeDelta& interval) {
+void UploadService::UploadEventCallback() {
   UploadEvent();
 
-  base::MessageLoop::current()->PostDelayedTask(FROM_HERE,
-      base::Bind(&UploadService::UploadEventCallback,
-                 base::Unretained(this),
-                 interval),
-      interval);
+  base::MessageLoop::current()->PostDelayedTask(
+      FROM_HERE,
+      base::Bind(&UploadService::UploadEventCallback, base::Unretained(this)),
+      upload_interval_);
+}
+
+void UploadService::PersistEventCallback() {
+  PersistToDisk();
+
+  base::MessageLoop::current()->PostDelayedTask(
+      FROM_HERE,
+      base::Bind(&UploadService::PersistEventCallback, base::Unretained(this)),
+      disk_persistence_interval_);
+}
+
+void UploadService::PersistToDisk() {
+  GatherHistograms();
+  if (current_log_) {
+    current_log_->SaveToFile(saved_log_path_);
+  }
 }
 
 void UploadService::UploadEvent() {
@@ -169,7 +210,7 @@
   if (!current_log_)
     return;
 
-  scoped_ptr<MetricsLog> staged_log;
+  std::unique_ptr<MetricsLog> staged_log;
   staged_log.swap(current_log_);
   staged_log->CloseLog();
   if (!staged_log->PopulateSystemProfile(system_profile_setter_.get())) {
@@ -177,14 +218,16 @@
                  << "log.";
     return;
   }
-  std::string encoded_log;
-  staged_log->GetEncodedLog(&encoded_log);
+
+  if (!base::DeleteFile(saved_log_path_, false)) {
+    // There is a chance that we will upload the same metrics twice but, if we
+    // are lucky, the backup should be overridden before that. In doubt, try not
+    // to lose any metrics.
+    LOG(ERROR) << "failed to delete the last backup of the current log.";
+  }
 
   failed_upload_count_.Set(0);
-  if (static_cast<int>(encoded_log.size()) != base::WriteFile(
-      staged_log_path_, encoded_log.data(), encoded_log.size())) {
-    LOG(ERROR) << "failed to persist to " << staged_log_path_.value();
-  }
+  staged_log->SaveToFile(staged_log_path_);
 }
 
 MetricsLog* UploadService::GetOrCreateCurrentLog() {
diff --git a/metricsd/uploader/upload_service.h b/metricsd/uploader/upload_service.h
index fe064b8..a1d9d3b 100644
--- a/metricsd/uploader/upload_service.h
+++ b/metricsd/uploader/upload_service.h
@@ -17,6 +17,7 @@
 #ifndef METRICS_UPLOADER_UPLOAD_SERVICE_H_
 #define METRICS_UPLOADER_UPLOAD_SERVICE_H_
 
+#include <memory>
 #include <string>
 
 #include <base/metrics/histogram_base.h>
@@ -27,53 +28,60 @@
 #include "persistent_integer.h"
 #include "uploader/crash_counters.h"
 #include "uploader/metrics_log.h"
+#include "uploader/metricsd_service_runner.h"
 #include "uploader/proto/chrome_user_metrics_extension.pb.h"
 #include "uploader/sender.h"
 #include "uploader/system_profile_cache.h"
 
 class SystemProfileSetter;
 
-// Service responsible for uploading the metrics periodically to the server.
-// This service works as a simple 2-state state-machine.
+// Service responsible for backing up the currently aggregated metrics to disk
+// and uploading them periodically to the server.
 //
-// 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,
-// model_manifest_id, etc...).
+// A given metrics sample can be in one of three locations.
+// * in-memory metrics: in memory aggregated metrics, waiting to be staged for
+//   upload.
+// * saved log: protobuf message, written to disk periodically and on shutdown
+//   to make a backup of metrics data for uploading later.
+// * staged log: protobuf message waiting to be uploaded.
 //
-// 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.
+// The service works as follows:
+// On startup, we create the in-memory metrics from the saved log if it exists.
 //
-// * if no staged logs are present:
-//    Take a snapshot of the aggregated metrics, save it to disk and try to send
-//    it:
-//    - if the upload succeeds, we discard the staged log (transitioning back
-//      to the no staged log state)
-//    - if the upload fails, we continue and will retry to upload later.
+// Periodically (every |disk_persistence_interval_| seconds), we take a snapshot
+// of the in-memory metrics and save them to disk.
 //
+// Periodically (every |upload_interval| seconds), we:
+// * take a snapshot of the in-memory metrics and create the staged log
+// * save the staged log to disk to avoid losing it if metricsd or the system
+//   crashes between two uploads.
+// * delete the last saved log: all the metrics contained in it are also in the
+//   newly created staged log.
+//
+// On shutdown (SIGINT or SIGTERM), we save the in-memory metrics to disk.
+//
+// Note: the in-memory metrics can be stored in |current_log_| or
+// base::StatisticsRecorder.
 class UploadService : public base::HistogramFlattener, public brillo::Daemon {
  public:
   UploadService(const std::string& server,
                 const base::TimeDelta& upload_interval,
+                const base::TimeDelta& disk_persistence_interval,
                 const base::FilePath& private_metrics_directory,
-                const base::FilePath& shared_metrics_directory,
-                const std::shared_ptr<CrashCounters> counters);
+                const base::FilePath& shared_metrics_directory);
 
   // Initializes the upload service.
-  int OnInit();
+  int OnInit() override;
+
+  // Cleans up the internal state before exiting.
+  void OnShutdown(int* exit_code) override;
 
   // 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);
+  // Saves the current metrics to a file.
+  void PersistToDisk();
 
   // Triggers an upload event.
   void UploadEvent();
@@ -93,6 +101,8 @@
   friend class UploadServiceTest;
 
   FRIEND_TEST(UploadServiceTest, CanSendMultipleTimes);
+  FRIEND_TEST(UploadServiceTest, CorruptedSavedLog);
+  FRIEND_TEST(UploadServiceTest, CurrentLogSavedAndResumed);
   FRIEND_TEST(UploadServiceTest, DiscardLogsAfterTooManyFailedUpload);
   FRIEND_TEST(UploadServiceTest, EmptyLogsAreNotSent);
   FRIEND_TEST(UploadServiceTest, FailedSendAreRetried);
@@ -104,6 +114,7 @@
   FRIEND_TEST(UploadServiceTest, LogKernelCrash);
   FRIEND_TEST(UploadServiceTest, LogUncleanShutdown);
   FRIEND_TEST(UploadServiceTest, LogUserCrash);
+  FRIEND_TEST(UploadServiceTest, PersistEmptyLog);
   FRIEND_TEST(UploadServiceTest, UnknownCrashIgnored);
   FRIEND_TEST(UploadServiceTest, ValuesInConfigFileAreSent);
 
@@ -114,12 +125,21 @@
   // will be discarded.
   static const int kMaxFailedUpload;
 
+  // Loads the log saved to disk if it exists.
+  void LoadSavedLog();
+
   // Resets the internal state.
   void Reset();
 
   // Returns true iff metrics reporting is enabled.
   bool AreMetricsEnabled();
 
+  // Event callback for handling Upload events.
+  void UploadEventCallback();
+
+  // Event callback for handling Persist events.
+  void PersistEventCallback();
+
   // Aggregates all histogram available in memory and store them in the current
   // log.
   void GatherHistograms();
@@ -141,17 +161,21 @@
   // Returns the current log. If there is no current log, creates it first.
   MetricsLog* GetOrCreateCurrentLog();
 
-  scoped_ptr<SystemProfileSetter> system_profile_setter_;
+  std::unique_ptr<SystemProfileSetter> system_profile_setter_;
   base::HistogramSnapshotManager histogram_snapshot_manager_;
-  scoped_ptr<Sender> sender_;
+  std::unique_ptr<Sender> sender_;
   chromeos_metrics::PersistentInteger failed_upload_count_;
-  scoped_ptr<MetricsLog> current_log_;
+  std::unique_ptr<MetricsLog> current_log_;
   std::shared_ptr<CrashCounters> counters_;
 
   base::TimeDelta upload_interval_;
+  base::TimeDelta disk_persistence_interval_;
+
+  MetricsdServiceRunner metricsd_service_runner_;
 
   base::FilePath consent_file_;
   base::FilePath staged_log_path_;
+  base::FilePath saved_log_path_;
 
   bool testing_;
 };
diff --git a/metricsd/uploader/upload_service_test.cc b/metricsd/uploader/upload_service_test.cc
index 0e2ba8f..70112f4 100644
--- a/metricsd/uploader/upload_service_test.cc
+++ b/metricsd/uploader/upload_service_test.cc
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include <gtest/gtest.h>
+#include <memory>
 
 #include <base/at_exit.h>
 #include <base/files/file_util.h>
@@ -23,6 +23,7 @@
 #include <base/metrics/sparse_histogram.h>
 #include <base/metrics/statistics_recorder.h>
 #include <base/sys_info.h>
+#include <gtest/gtest.h>
 
 #include "constants.h"
 #include "persistent_integer.h"
@@ -44,18 +45,18 @@
     ASSERT_FALSE(base::StatisticsRecorder::IsActive());
     base::StatisticsRecorder::Initialize();
 
-    base::FilePath private_dir = dir_.path().Append("private");
-    base::FilePath shared_dir = dir_.path().Append("shared");
+    private_dir_ = dir_.path().Append("private");
+    shared_dir_ = dir_.path().Append("shared");
 
-    EXPECT_TRUE(base::CreateDirectory(private_dir));
-    EXPECT_TRUE(base::CreateDirectory(shared_dir));
+    EXPECT_TRUE(base::CreateDirectory(private_dir_));
+    EXPECT_TRUE(base::CreateDirectory(shared_dir_));
 
-    ASSERT_EQ(0, base::WriteFile(shared_dir.Append(metrics::kConsentFileName),
+    ASSERT_EQ(0, base::WriteFile(shared_dir_.Append(metrics::kConsentFileName),
                                  "", 0));
-    counters_.reset(new CrashCounters);
 
-    upload_service_.reset(new UploadService("", base::TimeDelta(), private_dir,
-                                            shared_dir, counters_));
+    upload_service_.reset(new UploadService(
+        "", base::TimeDelta(), base::TimeDelta(), private_dir_, shared_dir_));
+    counters_ = upload_service_->counters_;
 
     upload_service_->sender_.reset(new SenderMock);
     upload_service_->InitForTest(new MockSystemProfileSetter);
@@ -80,22 +81,25 @@
     base::FilePath filepath =
         dir_.path().Append("etc/os-release.d").Append(name);
     ASSERT_TRUE(base::CreateDirectory(filepath.DirName()));
-    ASSERT_EQ(
-        value.size(),
-        base::WriteFile(filepath, value.data(), value.size()));
+    ASSERT_EQ(value.size(),
+              base::WriteFile(filepath, value.data(), value.size()));
   }
 
   const metrics::SystemProfileProto_Stability GetCurrentStability() {
-    EXPECT_TRUE(upload_service_->current_log_);
+    EXPECT_TRUE(upload_service_->current_log_.get());
 
-    return upload_service_->current_log_->uma_proto()->system_profile().stability();
+    return upload_service_->current_log_->uma_proto()
+        ->system_profile()
+        .stability();
   }
 
   base::ScopedTempDir dir_;
-  scoped_ptr<UploadService> upload_service_;
+  std::unique_ptr<UploadService> upload_service_;
 
-  scoped_ptr<base::AtExitManager> exit_manager_;
+  std::unique_ptr<base::AtExitManager> exit_manager_;
   std::shared_ptr<CrashCounters> counters_;
+  base::FilePath private_dir_;
+  base::FilePath shared_dir_;
 };
 
 TEST_F(UploadServiceTest, FailedSendAreRetried) {
@@ -148,12 +152,9 @@
 }
 
 TEST_F(UploadServiceTest, LogEmptyByDefault) {
-  UploadService upload_service("", base::TimeDelta(), dir_.path(), dir_.path(),
-                               std::make_shared<CrashCounters>());
-
-  // current_log_ should be initialized later as it needs AtExitManager to exit
+  // current_log_ should be initialized later as it needs AtExitManager to exist
   // in order to gather system information from SysInfo.
-  EXPECT_FALSE(upload_service.current_log_);
+  EXPECT_FALSE(upload_service_->current_log_);
 }
 
 TEST_F(UploadServiceTest, CanSendMultipleTimes) {
@@ -221,10 +222,8 @@
 }
 
 TEST_F(UploadServiceTest, ExtractChannelFromString) {
-  EXPECT_EQ(
-      SystemProfileCache::ProtoChannelFromString(
-          "developer-build"),
-      metrics::SystemProfileProto::CHANNEL_UNKNOWN);
+  EXPECT_EQ(SystemProfileCache::ProtoChannelFromString("developer-build"),
+            metrics::SystemProfileProto::CHANNEL_UNKNOWN);
 
   EXPECT_EQ(metrics::SystemProfileProto::CHANNEL_DEV,
             SystemProfileCache::ProtoChannelFromString("dev-channel"));
@@ -299,3 +298,38 @@
   SetTestingProperty(metrics::kProductId, "hello");
   ASSERT_TRUE(cache.Initialize());
 }
+
+TEST_F(UploadServiceTest, CurrentLogSavedAndResumed) {
+  SendHistogram("hello", 10, 0, 100, 10);
+  upload_service_->PersistToDisk();
+  EXPECT_EQ(
+      1, upload_service_->current_log_->uma_proto()->histogram_event().size());
+  upload_service_.reset(new UploadService(
+      "", base::TimeDelta(), base::TimeDelta(), private_dir_, shared_dir_));
+  upload_service_->InitForTest(nullptr);
+
+  SendHistogram("hello", 10, 0, 100, 10);
+  upload_service_->GatherHistograms();
+  EXPECT_EQ(2, upload_service_->GetOrCreateCurrentLog()
+                   ->uma_proto()
+                   ->histogram_event()
+                   .size());
+}
+
+TEST_F(UploadServiceTest, PersistEmptyLog) {
+  upload_service_->PersistToDisk();
+  EXPECT_FALSE(base::PathExists(upload_service_->saved_log_path_));
+}
+
+TEST_F(UploadServiceTest, CorruptedSavedLog) {
+  // Write a bogus saved log.
+  EXPECT_EQ(5, base::WriteFile(upload_service_->saved_log_path_, "hello", 5));
+
+  upload_service_.reset(new UploadService(
+      "", base::TimeDelta(), base::TimeDelta(), private_dir_, shared_dir_));
+
+  upload_service_->InitForTest(nullptr);
+  // If the log is unreadable, we drop it and continue execution.
+  ASSERT_NE(nullptr, upload_service_->GetOrCreateCurrentLog());
+  ASSERT_FALSE(base::PathExists(upload_service_->saved_log_path_));
+}
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 011defb..e400c85 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -28,6 +28,10 @@
 on init
     sysclktz 0
 
+    # Mix device-specific information into the entropy pool
+    copy /proc/cmdline /dev/urandom
+    copy /default.prop /dev/urandom
+
     # Backward compatibility.
     symlink /system/etc /etc
     symlink /sys/kernel/debug /d
@@ -332,6 +336,7 @@
     chmod 0660 /data/misc/wifi/wpa_supplicant.conf
     mkdir /data/local 0751 root root
     mkdir /data/misc/media 0700 media media
+    mkdir /data/misc/vold 0700 root root
     mkdir /data/misc/boottrace 0771 system shell
     mkdir /data/misc/update_engine 0700 root root
     mkdir /data/misc/trace 0700 root root
@@ -432,8 +437,8 @@
     chown system system /sys/power/autosleep
     chown system system /sys/power/state
     chown system system /sys/power/wakeup_count
-    chown radio system /sys/power/wake_lock
-    chown radio system /sys/power/wake_unlock
+    chown radio wakelock /sys/power/wake_lock
+    chown radio wakelock /sys/power/wake_unlock
     chmod 0660 /sys/power/state
     chmod 0660 /sys/power/wake_lock
     chmod 0660 /sys/power/wake_unlock
@@ -562,7 +567,7 @@
     class core
     critical
     seclabel u:r:healthd:s0
-    group root system
+    group root system wakelock
 
 service console /system/bin/sh
     class core
diff --git a/rootdir/ueventd.rc b/rootdir/ueventd.rc
index b735dc3..6ef491c 100644
--- a/rootdir/ueventd.rc
+++ b/rootdir/ueventd.rc
@@ -1,6 +1,13 @@
 subsystem adf
 	devname uevent_devname
 
+# ueventd can only set permissions on device nodes and their associated
+# sysfs attributes, not on arbitrary paths.
+#
+# format for /dev rules: devname mode uid gid
+# format for /sys rules: nodename attr mode uid gid
+# shortcut: "mtd@NN" expands to "/dev/mtd/mtdNN"
+
 /dev/null                 0666   root       root
 /dev/zero                 0666   root       root
 /dev/full                 0666   root       root
diff --git a/toolbox/ps.c b/toolbox/ps.c
index 3bc540d..ecc1c9f 100644
--- a/toolbox/ps.c
+++ b/toolbox/ps.c
@@ -57,7 +57,7 @@
     int prio, nice, rtprio, sched, psr;
     struct passwd *pw;
 
-    sprintf(statline, "/proc/%d", pid);
+    sprintf(statline, "/proc/%d", tid ? tid : pid);
     stat(statline, &stats);
 
     if(tid) {