Merge "crash_reporter: Add README.md"
diff --git a/adb/Android.mk b/adb/Android.mk
index 9c8ab6d..d629223 100644
--- a/adb/Android.mk
+++ b/adb/Android.mk
@@ -349,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..484e561 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -857,8 +857,7 @@
 #if ADB_HOST
         SendOkay(reply_fd);
 #endif
-        SendProtocolString(reply_fd, listeners);
-        return 1;
+        return SendProtocolString(reply_fd, listeners);
     }
 
     if (!strcmp(service, "killforward-all")) {
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/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/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/services.cpp b/adb/services.cpp
index 20166ce..cd33e7b 100644
--- a/adb/services.cpp
+++ b/adb/services.cpp
@@ -368,8 +368,9 @@
     while (true) {
         bool is_ambiguous = false;
         std::string error = "unknown error";
-        atransport* t = acquire_one_transport(sinfo->transport_type, sinfo->serial.c_str(),
-                                              &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;
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/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/crash_reporter/Android.mk b/crash_reporter/Android.mk
index bc023b0..4feb72a 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
 
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/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/fastboot.cpp b/fastboot/fastboot.cpp
index 5b66366..bd17485 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -282,8 +282,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 +318,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"
@@ -739,12 +738,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 +1426,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/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/private/android_filesystem_config.h b/include/private/android_filesystem_config.h
index bbe4486..9876e34 100644
--- a/include/private/android_filesystem_config.h
+++ b/include/private/android_filesystem_config.h
@@ -83,6 +83,10 @@
 #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_SHELL         2000  /* adb and debug shell user */
 #define AID_CACHE         2001  /* cache access */
@@ -184,6 +188,10 @@
     { "tlsdate",       AID_TLSDATE, },
     { "mediaex",       AID_MEDIA_EX, },
     { "audioserver",   AID_AUDIOSERVER, },
+    { "metrics_coll",  AID_METRICS_COLL },
+    { "metricsd",      AID_METRICSD },
+    { "webserv",       AID_WEBSERV },
+    { "debuggerd",     AID_DEBUGGERD, },
 
     { "shell",         AID_SHELL, },
     { "cache",         AID_CACHE, },
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/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..d2291bb 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)) {
@@ -608,6 +612,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);
 }
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/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/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/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/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/Android.mk b/liblog/Android.mk
index 4ab5006..a183db8 100644
--- a/liblog/Android.mk
+++ b/liblog/Android.mk
@@ -22,7 +22,7 @@
 #       's/^\([0-9]*\)[ \t]*liblog[ \t].*/-DLIBLOG_LOG_TAG=\1/p' \
 #       $(LOCAL_PATH)/event.logtags)
 # so make sure we do not regret hard-coding it as follows:
-liblog_cflags := -DLIBLOG_LOG_TAG=1005 -DSNET_EVENT_LOG_TAG=1397638484
+liblog_cflags := -DLIBLOG_LOG_TAG=1005
 
 liblog_host_sources := logd_write.c log_event_write.c fake_log_device.c event.logtags
 liblog_target_sources := logd_write.c log_event_write.c event_tag_map.c log_time.cpp log_is_loggable.c
diff --git a/liblog/logd_write.c b/liblog/logd_write.c
index ec86e6b..55b965b 100644
--- a/liblog/logd_write.c
+++ b/liblog/logd_write.c
@@ -221,48 +221,50 @@
             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;
         }
-        if (((uint32_t *)vec[0].iov_base)[0] != htole32(SNET_EVENT_LOG_TAG)) {
-            static atomic_uintptr_t map;
-            int ret;
-            const char *tag = NULL;
-            EventTagMap *m, *f = NULL;
 
-            m = (EventTagMap *)atomic_load(&map);
+        tag = NULL;
+        f = NULL;
+        m = (EventTagMap *)atomic_load(&map);
+
+        if (!m) {
+            ret = trylock();
+            m = (EventTagMap *)atomic_load(&map); /* trylock flush cache */
             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, 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);
+                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;
                     }
-                }
-                if (!ret) { /* trylock succeeded, unlock */
-                    unlock();
+                    atomic_store(&map, (uintptr_t)m);
                 }
             }
-            if (m && (m != (EventTagMap *)(uintptr_t)-1LL)) {
-                tag = android_lookupEventTag(
+            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;
-            }
+        }
+        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 */
@@ -666,3 +668,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/libnativeloader/native_loader.cpp b/libnativeloader/native_loader.cpp
index 403a4f4..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 {
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/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/logd/Android.mk b/logd/Android.mk
index d19c255..feca8d5 100644
--- a/logd/Android.mk
+++ b/logd/Android.mk
@@ -38,9 +38,7 @@
 #  event_flag := $(call event_logtags,auditd)
 #  event_flag += $(call event_logtags,logd)
 # so make sure we do not regret hard-coding it as follows:
-event_flag := -DAUDITD_LOG_TAG=1003 \
-              -DLOGD_LOG_TAG=1004 \
-              -DSNET_EVENT_LOG_TAG=1397638484
+event_flag := -DAUDITD_LOG_TAG=1003 -DLOGD_LOG_TAG=1004
 
 LOCAL_CFLAGS := -Werror $(event_flag)
 
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 1b829c6..cdf5d08 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)
@@ -109,6 +109,9 @@
     }
 
     log_id_for_each(i) {
+        mLastSet[i] = false;
+        mLast[i] = mLogElements.begin();
+
         char key[PROP_NAME_MAX];
 
         snprintf(key, sizeof(key), "%s.%s",
@@ -205,20 +208,16 @@
 
     LogBufferElement *elem = new LogBufferElement(log_id, realtime,
                                                   uid, pid, tid, msg, len);
-    if (log_id != LOG_ID_SECURITY) { // whitelist LOG_ID_SECURITY
+    if (log_id != LOG_ID_SECURITY) {
         int prio = ANDROID_LOG_INFO;
-        const char *tag = (const char *)-1;
+        const char *tag = NULL;
         if (log_id == LOG_ID_EVENTS) {
-            // whitelist "snet_event_log"
-            if (elem->getTag() != SNET_EVENT_LOG_TAG) {
-                tag = android::tagToName(elem->getTag());
-            }
+            tag = android::tagToName(elem->getTag());
         } else {
             prio = *msg;
             tag = msg + 1;
         }
-        if ((tag != (const char *)-1) &&
-                !__android_log_is_loggable(prio, tag, ANDROID_LOG_VERBOSE)) {
+        if (!__android_log_is_loggable(prio, tag, ANDROID_LOG_VERBOSE)) {
             // Log traffic received to total
             pthread_mutex_lock(&mLogElementsLock);
             stats.add(elem);
@@ -333,7 +332,15 @@
         }
     }
 
+    bool setLast = mLastSet[id] && (it == mLast[id]);
     it = mLogElements.erase(it);
+    if (setLast) {
+        if (it == mLogElements.end()) { // unlikely
+            mLastSet[id] = false;
+        } else {
+            mLast[id] = it;
+        }
+    }
     if (coalesce) {
         stats.erase(element);
     } else {
@@ -494,7 +501,8 @@
 
     if (caller_uid != AID_ROOT) {
         // Only here if clearAll condition (pruneRows == ULONG_MAX)
-        for(it = mLogElements.begin(); it != mLogElements.end();) {
+        it = mLastSet[id] ? mLast[id] : mLogElements.begin();
+        while (it != mLogElements.end()) {
             LogBufferElement *element = *it;
 
             if ((element->getLogId() != id) || (element->getUid() != caller_uid)) {
@@ -502,6 +510,11 @@
                 continue;
             }
 
+            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) {
@@ -570,7 +583,7 @@
 
         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
@@ -619,6 +632,11 @@
                 continue;
             }
 
+            if (leading && (!mLastSet[id] || ((*mLast[id])->getLogId() != id))) {
+                mLast[id] = it;
+                mLastSet[id] = true;
+            }
+
             unsigned short dropped = element->getDropped();
 
             // remove any leading drops
@@ -729,7 +747,7 @@
 
     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 *element = *it;
 
@@ -738,6 +756,11 @@
             continue;
         }
 
+        if (!mLastSet[id] || ((*mLast[id])->getLogId() != id)) {
+            mLast[id] = it;
+            mLastSet[id] = true;
+        }
+
         if (oldest && (oldest->mStart <= element->getSequence())) {
             busy = true;
             if (whitelist) {
@@ -768,7 +791,7 @@
 
     // 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 *element = *it;
 
@@ -777,6 +800,11 @@
                 continue;
             }
 
+            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))) {
diff --git a/logd/LogBuffer.h b/logd/LogBuffer.h
index 2667e78..03739c7 100644
--- a/logd/LogBuffer.h
+++ b/logd/LogBuffer.h
@@ -82,6 +82,9 @@
     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>
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/README.property b/logd/README.property
index 75d9eb0..22f86b9 100644
--- a/logd/README.property
+++ b/logd/README.property
@@ -15,7 +15,11 @@
 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>
-ro.logd.size               number svelte default for persist.logd.size
+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
diff --git a/metricsd/Android.mk b/metricsd/Android.mk
index 2cd1f7b..ed3fcbb 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,13 +104,13 @@
 # ==========================================================
 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
 include $(BUILD_STATIC_LIBRARY)
 
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/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/metrics_collector.cc b/metricsd/metrics_collector.cc
index a5daab5..2cf2338 100644
--- a/metricsd/metrics_collector.cc
+++ b/metricsd/metrics_collector.cc
@@ -30,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;
@@ -70,6 +71,7 @@
 const char kVmStatFileName[] = "/proc/vmstat";
 
 const char kWeaveComponent[] = "metrics";
+const char kWeaveTrait[] = "_metrics";
 
 }  // namespace
 
@@ -128,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();
 }
 
@@ -225,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;
@@ -251,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) {
@@ -271,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.";
@@ -289,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";
   }
 }
@@ -372,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);
 }
 
@@ -387,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);
   }
 }
@@ -555,7 +569,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));
 }
 
@@ -741,6 +756,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 45ef63d..ca4ae52 100644
--- a/metricsd/metrics_collector.h
+++ b/metricsd/metrics_collector.h
@@ -25,10 +25,12 @@
 #include <vector>
 
 #include <base/files/file_path.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"
@@ -114,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();
@@ -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.
@@ -272,7 +278,10 @@
   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_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/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..39655e6 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());
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..2834977
--- /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_->QuitClosure());
+
+  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/upload_service.cc b/metricsd/uploader/upload_service.cc
index 2fb30c3..ab44b28 100644
--- a/metricsd/uploader/upload_service.cc
+++ b/metricsd/uploader/upload_service.cc
@@ -42,49 +42,88 @@
 
 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();
+}
+
 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() {
@@ -178,14 +217,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 1d36121..a1d9d3b 100644
--- a/metricsd/uploader/upload_service.h
+++ b/metricsd/uploader/upload_service.h
@@ -28,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();
@@ -94,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);
@@ -105,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);
 
@@ -115,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();
@@ -150,9 +169,13 @@
   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 ec507e8..70112f4 100644
--- a/metricsd/uploader/upload_service_test.cc
+++ b/metricsd/uploader/upload_service_test.cc
@@ -45,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);
@@ -81,15 +81,16 @@
     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_.get());
 
-    return upload_service_->current_log_->uma_proto()->system_profile().stability();
+    return upload_service_->current_log_->uma_proto()
+        ->system_profile()
+        .stability();
   }
 
   base::ScopedTempDir dir_;
@@ -97,6 +98,8 @@
 
   std::unique_ptr<base::AtExitManager> exit_manager_;
   std::shared_ptr<CrashCounters> counters_;
+  base::FilePath private_dir_;
+  base::FilePath shared_dir_;
 };
 
 TEST_F(UploadServiceTest, FailedSendAreRetried) {
@@ -149,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) {
@@ -222,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"));
@@ -300,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 a52d87d..d322402 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -332,6 +332,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
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) {