Merge "Move crypt commands to a different listener in vold" into mnc-dev
diff --git a/debuggerd/Android.mk b/debuggerd/Android.mk
index 1a5f05e..6cfb541 100644
--- a/debuggerd/Android.mk
+++ b/debuggerd/Android.mk
@@ -75,46 +75,55 @@
 
 include $(BUILD_EXECUTABLE)
 
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := \
+debuggerd_test_src_files := \
     utility.cpp \
+    test/dump_maps_test.cpp \
     test/dump_memory_test.cpp \
+    test/elf_fake.cpp \
     test/log_fake.cpp \
+    test/property_fake.cpp \
+    test/ptrace_fake.cpp \
+    test/selinux_fake.cpp \
 
-LOCAL_MODULE := debuggerd_test
-
-LOCAL_SHARED_LIBRARIES := \
+debuggerd_shared_libraries := \
     libbacktrace \
     libbase \
+    libcutils \
 
-LOCAL_C_INCLUDES += $(LOCAL_PATH)/test
-LOCAL_CPPFLAGS := $(common_cppflags)
+debuggerd_c_includes := \
+    $(LOCAL_PATH)/test \
+
+debuggerd_cpp_flags := \
+    $(common_cppflags) \
+    -Wno-missing-field-initializers \
+
+# Only build the host tests on linux.
+ifeq ($(HOST_OS),linux)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := debuggerd_test
+LOCAL_SRC_FILES := $(debuggerd_test_src_files)
+LOCAL_SHARED_LIBRARIES := $(debuggerd_shared_libraries)
+LOCAL_C_INCLUDES := $(debuggerd_c_includes)
+LOCAL_CPPFLAGS := $(debuggerd_cpp_flags)
 
 LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
 LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
 LOCAL_MULTILIB := both
-
 include $(BUILD_HOST_NATIVE_TEST)
 
+endif
+
 include $(CLEAR_VARS)
 
-LOCAL_SRC_FILES := \
-    utility.cpp \
-    test/dump_memory_test.cpp \
-    test/log_fake.cpp \
-
 LOCAL_MODULE := debuggerd_test
-
-LOCAL_SHARED_LIBRARIES := \
-    libbacktrace \
-    libbase \
-
-LOCAL_C_INCLUDES += $(LOCAL_PATH)/test
-LOCAL_CPPFLAGS := $(common_cppflags)
+LOCAL_SRC_FILES := $(debuggerd_test_src_files)
+LOCAL_SHARED_LIBRARIES := $(debuggerd_shared_libraries)
+LOCAL_C_INCLUDES := $(debuggerd_c_includes)
+LOCAL_CPPFLAGS := $(debuggerd_cpp_flags)
 
 LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
 LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
 LOCAL_MULTILIB := both
-
 include $(BUILD_NATIVE_TEST)
diff --git a/debuggerd/test/BacktraceMock.h b/debuggerd/test/BacktraceMock.h
index 05ad12b..5c252ab 100644
--- a/debuggerd/test/BacktraceMock.h
+++ b/debuggerd/test/BacktraceMock.h
@@ -32,6 +32,10 @@
  public:
   BacktraceMapMock() : BacktraceMap(0) {}
   virtual ~BacktraceMapMock() {}
+
+  void AddMap(backtrace_map_t& map) {
+    maps_.push_back(map);
+  }
 };
 
 
diff --git a/debuggerd/test/dump_maps_test.cpp b/debuggerd/test/dump_maps_test.cpp
new file mode 100644
index 0000000..230f4f5
--- /dev/null
+++ b/debuggerd/test/dump_maps_test.cpp
@@ -0,0 +1,571 @@
+/*
+ * 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 <stdlib.h>
+
+#include <memory>
+#include <string>
+
+#include <gtest/gtest.h>
+#include <base/file.h>
+
+#include "utility.h"
+
+#include "BacktraceMock.h"
+#include "elf_fake.h"
+#include "host_signal_fixup.h"
+#include "log_fake.h"
+#include "ptrace_fake.h"
+
+// In order to test this code, we need to include the tombstone.cpp code.
+// Including it, also allows us to override the ptrace function.
+#define ptrace ptrace_fake
+
+#include "tombstone.cpp"
+
+void dump_registers(log_t*, pid_t) {
+}
+
+void dump_memory_and_code(log_t*, Backtrace*) {
+}
+
+void dump_backtrace_to_log(Backtrace*, log_t*, char const*) {
+}
+
+class DumpMapsTest : public ::testing::Test {
+ protected:
+  virtual void SetUp() {
+    map_mock_.reset(new BacktraceMapMock());
+    backtrace_mock_.reset(new BacktraceMock(map_mock_.get()));
+
+    char tmp_file[256];
+    const char data_template[] = "/data/local/tmp/debuggerd_memory_testXXXXXX";
+    memcpy(tmp_file, data_template, sizeof(data_template));
+    int tombstone_fd = mkstemp(tmp_file);
+    if (tombstone_fd == -1) {
+      const char tmp_template[] = "/tmp/debuggerd_memory_testXXXXXX";
+      memcpy(tmp_file, tmp_template, sizeof(tmp_template));
+      tombstone_fd = mkstemp(tmp_file);
+      if (tombstone_fd == -1) {
+        abort();
+      }
+    }
+    if (unlink(tmp_file) == -1) {
+      abort();
+    }
+
+    log_.tfd = tombstone_fd;
+    log_.amfd = -1;
+    log_.crashed_tid = 12;
+    log_.current_tid = 12;
+    log_.should_retrieve_logcat = false;
+
+    resetLogs();
+    elf_set_fake_build_id("");
+    siginfo_t si;
+    si.si_signo = SIGPIPE;
+    ptrace_set_fake_getsiginfo(si);
+  }
+
+  virtual void TearDown() {
+    if (log_.tfd >= 0) {
+      close(log_.tfd);
+    }
+  }
+
+  std::unique_ptr<BacktraceMapMock> map_mock_;
+  std::unique_ptr<BacktraceMock> backtrace_mock_;
+
+  log_t log_;
+};
+
+TEST_F(DumpMapsTest, single_map) {
+  backtrace_map_t map;
+#if defined(__LP64__)
+  map.start = 0x123456789abcd000UL;
+  map.end = 0x123456789abdf000UL;
+#else
+  map.start = 0x1234000;
+  map.end = 0x1235000;
+#endif
+  map_mock_->AddMap(map);
+
+  dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100);
+
+  std::string tombstone_contents;
+  ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+  ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+  const char* expected_dump = \
+"\nmemory map:\n"
+#if defined(__LP64__)
+"    12345678'9abcd000-12345678'9abdefff ---         0     12000\n";
+#else
+"    01234000-01234fff ---         0      1000\n";
+#endif
+  ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
+
+  // Verify that the log buf is empty, and no error messages.
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(DumpMapsTest, single_map_elf_build_id) {
+  backtrace_map_t map;
+#if defined(__LP64__)
+  map.start = 0x123456789abcd000UL;
+  map.end = 0x123456789abdf000UL;
+#else
+  map.start = 0x1234000;
+  map.end = 0x1235000;
+#endif
+  map.flags = PROT_READ;
+  map.name = "/system/lib/libfake.so";
+  map_mock_->AddMap(map);
+
+  elf_set_fake_build_id("abcdef1234567890abcdef1234567890");
+  dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100);
+
+  std::string tombstone_contents;
+  ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+  ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+  const char* expected_dump = \
+"\nmemory map:\n"
+#if defined(__LP64__)
+"    12345678'9abcd000-12345678'9abdefff r--         0     12000  /system/lib/libfake.so (BuildId: abcdef1234567890abcdef1234567890)\n";
+#else
+"    01234000-01234fff r--         0      1000  /system/lib/libfake.so (BuildId: abcdef1234567890abcdef1234567890)\n";
+#endif
+  ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
+
+  // Verify that the log buf is empty, and no error messages.
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+// Even though build id is present, it should not be printed in either of
+// these cases.
+TEST_F(DumpMapsTest, single_map_no_build_id) {
+  backtrace_map_t map;
+#if defined(__LP64__)
+  map.start = 0x123456789abcd000UL;
+  map.end = 0x123456789abdf000UL;
+#else
+  map.start = 0x1234000;
+  map.end = 0x1235000;
+#endif
+  map.flags = PROT_WRITE;
+  map_mock_->AddMap(map);
+
+  map.name = "/system/lib/libfake.so";
+  map_mock_->AddMap(map);
+
+  elf_set_fake_build_id("abcdef1234567890abcdef1234567890");
+  dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100);
+
+  std::string tombstone_contents;
+  ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+  ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+  const char* expected_dump = \
+"\nmemory map:\n"
+#if defined(__LP64__)
+"    12345678'9abcd000-12345678'9abdefff -w-         0     12000\n"
+"    12345678'9abcd000-12345678'9abdefff -w-         0     12000  /system/lib/libfake.so\n";
+#else
+"    01234000-01234fff -w-         0      1000\n"
+"    01234000-01234fff -w-         0      1000  /system/lib/libfake.so\n";
+#endif
+  ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
+
+  // Verify that the log buf is empty, and no error messages.
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(DumpMapsTest, multiple_maps) {
+  backtrace_map_t map;
+
+  map.start = 0xa234000;
+  map.end = 0xa235000;
+  map_mock_->AddMap(map);
+
+  map.start = 0xa334000;
+  map.end = 0xa335000;
+  map.offset = 0xf000;
+  map.flags = PROT_READ;
+  map_mock_->AddMap(map);
+
+  map.start = 0xa434000;
+  map.end = 0xa435000;
+  map.offset = 0x1000;
+  map.load_base = 0xd000;
+  map.flags = PROT_WRITE;
+  map_mock_->AddMap(map);
+
+  map.start = 0xa534000;
+  map.end = 0xa535000;
+  map.offset = 0x3000;
+  map.load_base = 0x2000;
+  map.flags = PROT_EXEC;
+  map_mock_->AddMap(map);
+
+  map.start = 0xa634000;
+  map.end = 0xa635000;
+  map.offset = 0;
+  map.load_base = 0;
+  map.flags = PROT_READ | PROT_WRITE | PROT_EXEC;
+  map.name = "/system/lib/fake.so";
+  map_mock_->AddMap(map);
+
+  dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100);
+
+  std::string tombstone_contents;
+  ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+  ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+  const char* expected_dump = \
+"\nmemory map:\n"
+#if defined(__LP64__)
+"    00000000'0a234000-00000000'0a234fff ---         0      1000\n"
+"    00000000'0a334000-00000000'0a334fff r--      f000      1000\n"
+"    00000000'0a434000-00000000'0a434fff -w-      1000      1000  (load base 0xd000)\n"
+"    00000000'0a534000-00000000'0a534fff --x      3000      1000  (load base 0x2000)\n"
+"    00000000'0a634000-00000000'0a634fff rwx         0      1000  /system/lib/fake.so\n";
+#else
+"    0a234000-0a234fff ---         0      1000\n"
+"    0a334000-0a334fff r--      f000      1000\n"
+"    0a434000-0a434fff -w-      1000      1000  (load base 0xd000)\n"
+"    0a534000-0a534fff --x      3000      1000  (load base 0x2000)\n"
+"    0a634000-0a634fff rwx         0      1000  /system/lib/fake.so\n";
+#endif
+  ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
+
+  // Verify that the log buf is empty, and no error messages.
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(DumpMapsTest, multiple_maps_fault_address_before) {
+  backtrace_map_t map;
+
+  map.start = 0xa434000;
+  map.end = 0xa435000;
+  map.offset = 0x1000;
+  map.load_base = 0xd000;
+  map.flags = PROT_WRITE;
+  map_mock_->AddMap(map);
+
+  map.start = 0xa534000;
+  map.end = 0xa535000;
+  map.offset = 0x3000;
+  map.load_base = 0x2000;
+  map.flags = PROT_EXEC;
+  map_mock_->AddMap(map);
+
+  map.start = 0xa634000;
+  map.end = 0xa635000;
+  map.offset = 0;
+  map.load_base = 0;
+  map.flags = PROT_READ | PROT_WRITE | PROT_EXEC;
+  map.name = "/system/lib/fake.so";
+  map_mock_->AddMap(map);
+
+  siginfo_t si;
+  si.si_signo = SIGBUS;
+  si.si_addr = reinterpret_cast<void*>(0x1000);
+  ptrace_set_fake_getsiginfo(si);
+  dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100);
+
+  std::string tombstone_contents;
+  ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+  ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+  const char* expected_dump = \
+"\nmemory map: (fault address prefixed with --->)\n"
+#if defined(__LP64__)
+"--->Fault address falls at 00000000'00001000 before any mapped regions\n"
+"    00000000'0a434000-00000000'0a434fff -w-      1000      1000  (load base 0xd000)\n"
+"    00000000'0a534000-00000000'0a534fff --x      3000      1000  (load base 0x2000)\n"
+"    00000000'0a634000-00000000'0a634fff rwx         0      1000  /system/lib/fake.so\n";
+#else
+"--->Fault address falls at 00001000 before any mapped regions\n"
+"    0a434000-0a434fff -w-      1000      1000  (load base 0xd000)\n"
+"    0a534000-0a534fff --x      3000      1000  (load base 0x2000)\n"
+"    0a634000-0a634fff rwx         0      1000  /system/lib/fake.so\n";
+#endif
+  ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
+
+  // Verify that the log buf is empty, and no error messages.
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(DumpMapsTest, multiple_maps_fault_address_between) {
+  backtrace_map_t map;
+
+  map.start = 0xa434000;
+  map.end = 0xa435000;
+  map.offset = 0x1000;
+  map.load_base = 0xd000;
+  map.flags = PROT_WRITE;
+  map_mock_->AddMap(map);
+
+  map.start = 0xa534000;
+  map.end = 0xa535000;
+  map.offset = 0x3000;
+  map.load_base = 0x2000;
+  map.flags = PROT_EXEC;
+  map_mock_->AddMap(map);
+
+  map.start = 0xa634000;
+  map.end = 0xa635000;
+  map.offset = 0;
+  map.load_base = 0;
+  map.flags = PROT_READ | PROT_WRITE | PROT_EXEC;
+  map.name = "/system/lib/fake.so";
+  map_mock_->AddMap(map);
+
+  siginfo_t si;
+  si.si_signo = SIGBUS;
+  si.si_addr = reinterpret_cast<void*>(0xa533000);
+  ptrace_set_fake_getsiginfo(si);
+  dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100);
+
+  std::string tombstone_contents;
+  ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+  ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+  const char* expected_dump = \
+"\nmemory map: (fault address prefixed with --->)\n"
+#if defined(__LP64__)
+"    00000000'0a434000-00000000'0a434fff -w-      1000      1000  (load base 0xd000)\n"
+"--->Fault address falls at 00000000'0a533000 between mapped regions\n"
+"    00000000'0a534000-00000000'0a534fff --x      3000      1000  (load base 0x2000)\n"
+"    00000000'0a634000-00000000'0a634fff rwx         0      1000  /system/lib/fake.so\n";
+#else
+"    0a434000-0a434fff -w-      1000      1000  (load base 0xd000)\n"
+"--->Fault address falls at 0a533000 between mapped regions\n"
+"    0a534000-0a534fff --x      3000      1000  (load base 0x2000)\n"
+"    0a634000-0a634fff rwx         0      1000  /system/lib/fake.so\n";
+#endif
+  ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
+
+  // Verify that the log buf is empty, and no error messages.
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(DumpMapsTest, multiple_maps_fault_address_in_map) {
+  backtrace_map_t map;
+
+  map.start = 0xa434000;
+  map.end = 0xa435000;
+  map.offset = 0x1000;
+  map.load_base = 0xd000;
+  map.flags = PROT_WRITE;
+  map_mock_->AddMap(map);
+
+  map.start = 0xa534000;
+  map.end = 0xa535000;
+  map.offset = 0x3000;
+  map.load_base = 0x2000;
+  map.flags = PROT_EXEC;
+  map_mock_->AddMap(map);
+
+  map.start = 0xa634000;
+  map.end = 0xa635000;
+  map.offset = 0;
+  map.load_base = 0;
+  map.flags = PROT_READ | PROT_WRITE | PROT_EXEC;
+  map.name = "/system/lib/fake.so";
+  map_mock_->AddMap(map);
+
+  siginfo_t si;
+  si.si_signo = SIGBUS;
+  si.si_addr = reinterpret_cast<void*>(0xa534040);
+  ptrace_set_fake_getsiginfo(si);
+  dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100);
+
+  std::string tombstone_contents;
+  ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+  ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+  const char* expected_dump = \
+"\nmemory map: (fault address prefixed with --->)\n"
+#if defined(__LP64__)
+"    00000000'0a434000-00000000'0a434fff -w-      1000      1000  (load base 0xd000)\n"
+"--->00000000'0a534000-00000000'0a534fff --x      3000      1000  (load base 0x2000)\n"
+"    00000000'0a634000-00000000'0a634fff rwx         0      1000  /system/lib/fake.so\n";
+#else
+"    0a434000-0a434fff -w-      1000      1000  (load base 0xd000)\n"
+"--->0a534000-0a534fff --x      3000      1000  (load base 0x2000)\n"
+"    0a634000-0a634fff rwx         0      1000  /system/lib/fake.so\n";
+#endif
+  ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
+
+  // Verify that the log buf is empty, and no error messages.
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(DumpMapsTest, multiple_maps_fault_address_after) {
+  backtrace_map_t map;
+
+  map.start = 0xa434000;
+  map.end = 0xa435000;
+  map.offset = 0x1000;
+  map.load_base = 0xd000;
+  map.flags = PROT_WRITE;
+  map_mock_->AddMap(map);
+
+  map.start = 0xa534000;
+  map.end = 0xa535000;
+  map.offset = 0x3000;
+  map.load_base = 0x2000;
+  map.flags = PROT_EXEC;
+  map_mock_->AddMap(map);
+
+  map.start = 0xa634000;
+  map.end = 0xa635000;
+  map.offset = 0;
+  map.load_base = 0;
+  map.flags = PROT_READ | PROT_WRITE | PROT_EXEC;
+  map.name = "/system/lib/fake.so";
+  map_mock_->AddMap(map);
+
+  siginfo_t si;
+  si.si_signo = SIGBUS;
+#if defined(__LP64__)
+  si.si_addr = reinterpret_cast<void*>(0x12345a534040UL);
+#else
+  si.si_addr = reinterpret_cast<void*>(0xf534040UL);
+#endif
+  ptrace_set_fake_getsiginfo(si);
+  dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100);
+
+  std::string tombstone_contents;
+  ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+  ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+  const char* expected_dump = \
+"\nmemory map: (fault address prefixed with --->)\n"
+#if defined(__LP64__)
+"    00000000'0a434000-00000000'0a434fff -w-      1000      1000  (load base 0xd000)\n"
+"    00000000'0a534000-00000000'0a534fff --x      3000      1000  (load base 0x2000)\n"
+"    00000000'0a634000-00000000'0a634fff rwx         0      1000  /system/lib/fake.so\n"
+"--->Fault address falls at 00001234'5a534040 after any mapped regions\n";
+#else
+"    0a434000-0a434fff -w-      1000      1000  (load base 0xd000)\n"
+"    0a534000-0a534fff --x      3000      1000  (load base 0x2000)\n"
+"    0a634000-0a634fff rwx         0      1000  /system/lib/fake.so\n"
+"--->Fault address falls at 0f534040 after any mapped regions\n";
+#endif
+  ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
+
+  // Verify that the log buf is empty, and no error messages.
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(DumpMapsTest, multiple_maps_getsiginfo_fail) {
+  backtrace_map_t map;
+
+  map.start = 0xa434000;
+  map.end = 0xa435000;
+  map.offset = 0x1000;
+  map.load_base = 0xd000;
+  map.flags = PROT_WRITE;
+  map_mock_->AddMap(map);
+
+  siginfo_t si;
+  si.si_signo = 0;
+  ptrace_set_fake_getsiginfo(si);
+  dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100);
+
+  std::string tombstone_contents;
+  ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+  ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+  const char* expected_dump = \
+"Cannot get siginfo for 100: Bad address\n"
+"\nmemory map:\n"
+#if defined(__LP64__)
+"    00000000'0a434000-00000000'0a434fff -w-      1000      1000  (load base 0xd000)\n";
+#else
+"    0a434000-0a434fff -w-      1000      1000  (load base 0xd000)\n";
+#endif
+  ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
+
+  // Verify that the log buf is empty, and no error messages.
+  ASSERT_STREQ("DEBUG Cannot get siginfo for 100: Bad address\n",
+               getFakeLogBuf().c_str());
+  ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(DumpMapsTest, multiple_maps_check_signal_has_si_addr) {
+  backtrace_map_t map;
+
+  map.start = 0xa434000;
+  map.end = 0xa435000;
+  map.flags = PROT_WRITE;
+  map_mock_->AddMap(map);
+
+  for (int i = 1; i < 255; i++) {
+    ASSERT_TRUE(ftruncate(log_.tfd, 0) == 0);
+    ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+
+    siginfo_t si;
+    si.si_signo = i;
+    si.si_addr = reinterpret_cast<void*>(0x1000);
+    ptrace_set_fake_getsiginfo(si);
+    dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100);
+
+    std::string tombstone_contents;
+    ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+    ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+    bool has_addr = false;
+    switch (si.si_signo) {
+    case SIGBUS:
+    case SIGFPE:
+    case SIGILL:
+    case SIGSEGV:
+    case SIGTRAP:
+      has_addr = true;
+      break;
+    }
+
+    const char* expected_addr_dump = \
+"\nmemory map: (fault address prefixed with --->)\n"
+#if defined(__LP64__)
+"--->Fault address falls at 00000000'00001000 before any mapped regions\n"
+"    00000000'0a434000-00000000'0a434fff -w-         0      1000\n";
+#else
+"--->Fault address falls at 00001000 before any mapped regions\n"
+"    0a434000-0a434fff -w-         0      1000\n";
+#endif
+    const char* expected_dump = \
+"\nmemory map:\n"
+#if defined(__LP64__)
+"    00000000'0a434000-00000000'0a434fff -w-         0      1000\n";
+#else
+"    0a434000-0a434fff -w-         0      1000\n";
+#endif
+    if (has_addr) {
+      ASSERT_STREQ(expected_addr_dump, tombstone_contents.c_str())
+        << "Signal " << si.si_signo << " expected to include an address.";
+    } else {
+      ASSERT_STREQ(expected_dump, tombstone_contents.c_str())
+        << "Signal " << si.si_signo << " is not expected to include an address.";
+    }
+
+    // Verify that the log buf is empty, and no error messages.
+    ASSERT_STREQ("", getFakeLogBuf().c_str());
+    ASSERT_STREQ("", getFakeLogPrint().c_str());
+  }
+}
diff --git a/debuggerd/test/elf_fake.cpp b/debuggerd/test/elf_fake.cpp
new file mode 100644
index 0000000..bb52b59
--- /dev/null
+++ b/debuggerd/test/elf_fake.cpp
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+
+#include <string>
+
+class Backtrace;
+
+std::string g_build_id;
+
+void elf_set_fake_build_id(const std::string& build_id) {
+  g_build_id = build_id;
+}
+
+bool elf_get_build_id(Backtrace*, uintptr_t, std::string* build_id) {
+  if (g_build_id != "") {
+    *build_id = g_build_id;
+    return true;
+  }
+  return false;
+}
diff --git a/debuggerd/test/elf_fake.h b/debuggerd/test/elf_fake.h
new file mode 100644
index 0000000..08a8454
--- /dev/null
+++ b/debuggerd/test/elf_fake.h
@@ -0,0 +1,24 @@
+/*
+ * 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 _DEBUGGERD_TEST_ELF_FAKE_H
+#define _DEBUGGERD_TEST_ELF_FAKE_H
+
+#include <string>
+
+void elf_set_fake_build_id(const std::string&);
+
+#endif // _DEBUGGERD_TEST_ELF_FAKE_H
diff --git a/debuggerd/test/host_signal_fixup.h b/debuggerd/test/host_signal_fixup.h
new file mode 100644
index 0000000..c7796ef
--- /dev/null
+++ b/debuggerd/test/host_signal_fixup.h
@@ -0,0 +1,65 @@
+/*
+ * 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 _DEBUGGERD_TEST_HOST_SIGNAL_FIXUP_H
+#define _DEBUGGERD_TEST_HOST_SIGNAL_FIXUP_H
+
+#include <signal.h>
+
+#if !defined(__BIONIC__)
+
+// In order to compile parts of debuggerd for the host, we need to
+// define these values.
+
+#if !defined(NSIGILL)
+#define NSIGILL ILL_BADSTK
+#endif
+
+#if !defined(BUS_MCEERR_AR)
+#define BUS_MCEERR_AR 4
+#endif
+#if !defined(BUS_MCEERR_AO)
+#define BUS_MCEERR_AO 5
+#endif
+#if !defined(NSIGBUS)
+#define NSIGBUS BUS_MCEERR_AO
+#endif
+
+#if !defined(NSIGFPE)
+#define NSIGFPE FPE_FLTSUB
+#endif
+
+#if !defined(NSIGSEGV)
+#define NSIGSEGV SEGV_ACCERR
+#endif
+
+#if !defined(TRAP_BRANCH)
+#define TRAP_BRANCH 3
+#endif
+#if !defined(TRAP_HWBKPT)
+#define TRAP_HWBKPT 4
+#endif
+#if !defined(NSIGTRAP)
+#define NSIGTRAP TRAP_HWBKPT
+#endif
+
+#if !defined(SI_DETHREAD)
+#define SI_DETHREAD -7
+#endif
+
+#endif
+
+#endif // _DEBUGGERD_TEST_HOST_SIGNAL_FIXUP_H
diff --git a/debuggerd/test/log_fake.cpp b/debuggerd/test/log_fake.cpp
index 1161afe..26523ad 100644
--- a/debuggerd/test/log_fake.cpp
+++ b/debuggerd/test/log_fake.cpp
@@ -19,6 +19,13 @@
 #include <string>
 
 #include <base/stringprintf.h>
+#include <log/log.h>
+#include <log/logger.h>
+
+// Forward declarations.
+class Backtrace;
+struct EventTagMap;
+struct AndroidLogEntry;
 
 std::string g_fake_log_buf;
 
@@ -29,6 +36,14 @@
   g_fake_log_print = "";
 }
 
+std::string getFakeLogBuf() {
+  return g_fake_log_buf;
+}
+
+std::string getFakeLogPrint() {
+  return g_fake_log_print;
+}
+
 extern "C" int __android_log_buf_write(int, int, const char* tag, const char* msg) {
   g_fake_log_buf += tag;
   g_fake_log_buf += ' ';
@@ -36,10 +51,6 @@
   return 1;
 }
 
-std::string getFakeLogBuf() {
-  return g_fake_log_buf;
-}
-
 extern "C" int __android_log_print(int, const char* tag, const char* fmt, ...) {
   g_fake_log_print += tag;
   g_fake_log_print += ' ';
@@ -54,6 +65,27 @@
   return 1;
 }
 
-std::string getFakeLogPrint() {
-  return g_fake_log_print;
+extern "C" log_id_t android_name_to_log_id(const char*) {
+  return LOG_ID_SYSTEM;
+}
+
+extern "C" struct logger_list* android_logger_list_open(log_id_t, int, unsigned int, pid_t) {
+  return nullptr;
+}
+
+extern "C" int android_logger_list_read(struct logger_list*, struct log_msg*) {
+  return 0;
+}
+
+extern "C" EventTagMap* android_openEventTagMap(const char*) {
+  return nullptr;
+}
+
+extern "C" int android_log_processBinaryLogBuffer(
+    struct logger_entry*,
+    AndroidLogEntry*, const EventTagMap*, char*, int) {
+  return 0;
+}
+
+extern "C" void android_logger_list_free(struct logger_list*) {
 }
diff --git a/debuggerd/test/property_fake.cpp b/debuggerd/test/property_fake.cpp
new file mode 100644
index 0000000..02069f1
--- /dev/null
+++ b/debuggerd/test/property_fake.cpp
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <string.h>
+
+#include <string>
+#include <unordered_map>
+
+#include <sys/system_properties.h>
+
+std::unordered_map<std::string, std::string> g_properties;
+
+extern "C" int property_set(const char* name, const char* value) {
+  if (g_properties.count(name) != 0) {
+    g_properties.erase(name);
+  }
+  g_properties[name] = value;
+  return 0;
+}
+
+extern "C" int property_get(const char* key, char* value, const char* default_value) {
+  if (g_properties.count(key) == 0) {
+    if (default_value == nullptr) {
+      return 0;
+    }
+    strncpy(value, default_value, PROP_VALUE_MAX-1);
+  } else {
+    strncpy(value, g_properties[key].c_str(), PROP_VALUE_MAX-1);
+  }
+  value[PROP_VALUE_MAX-1] = '\0';
+  return strlen(value);
+}
diff --git a/debuggerd/test/ptrace_fake.cpp b/debuggerd/test/ptrace_fake.cpp
new file mode 100644
index 0000000..f40cbd4
--- /dev/null
+++ b/debuggerd/test/ptrace_fake.cpp
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <sys/ptrace.h>
+
+#include <string>
+
+#include "ptrace_fake.h"
+
+siginfo_t g_fake_si = {.si_signo = 0};
+
+void ptrace_set_fake_getsiginfo(const siginfo_t& si) {
+  g_fake_si = si;
+}
+
+#if !defined(__BIONIC__)
+extern "C" long ptrace_fake(enum __ptrace_request request, ...) {
+#else
+extern "C" long ptrace_fake(int request, ...) {
+#endif
+  if (request == PTRACE_GETSIGINFO) {
+    if (g_fake_si.si_signo == 0) {
+      errno = EFAULT;
+      return -1;
+    }
+
+    va_list ap;
+    va_start(ap, request);
+    va_arg(ap, int);
+    va_arg(ap, int);
+    siginfo_t* si = va_arg(ap, siginfo*);
+    va_end(ap);
+    *si = g_fake_si;
+    return 0;
+  }
+  return -1;
+}
diff --git a/debuggerd/test/ptrace_fake.h b/debuggerd/test/ptrace_fake.h
new file mode 100644
index 0000000..fdbb663
--- /dev/null
+++ b/debuggerd/test/ptrace_fake.h
@@ -0,0 +1,24 @@
+/*
+ * 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 _DEBUGGERD_TEST_PTRACE_FAKE_H
+#define _DEBUGGERD_TEST_PTRACE_FAKE_H
+
+#include <signal.h>
+
+void ptrace_set_fake_getsiginfo(const siginfo_t&);
+
+#endif // _DEBUGGERD_TEST_PTRACE_FAKE_H
diff --git a/debuggerd/test/selinux_fake.cpp b/debuggerd/test/selinux_fake.cpp
new file mode 100644
index 0000000..acdd0a9
--- /dev/null
+++ b/debuggerd/test/selinux_fake.cpp
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+extern "C" int selinux_android_restorecon(const char*, unsigned int) {
+  return 0;
+}
diff --git a/debuggerd/test/sys/system_properties.h b/debuggerd/test/sys/system_properties.h
new file mode 100644
index 0000000..9d44345
--- /dev/null
+++ b/debuggerd/test/sys/system_properties.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _DEBUGGERD_TEST_SYS_SYSTEM_PROPERTIES_H
+#define _DEBUGGERD_TEST_SYS_SYSTEM_PROPERTIES_H
+
+// This is just enough to get the property code to compile on
+// the host.
+
+#define PROP_NAME_MAX   32
+#define PROP_VALUE_MAX  92
+
+#endif // _DEBUGGERD_TEST_SYS_SYSTEM_PROPERTIES_H
diff --git a/debuggerd/tombstone.cpp b/debuggerd/tombstone.cpp
index 614edb6..b0ad274 100644
--- a/debuggerd/tombstone.cpp
+++ b/debuggerd/tombstone.cpp
@@ -317,16 +317,28 @@
   }
 }
 
+static std::string get_addr_string(uintptr_t addr) {
+  std::string addr_str;
+#if defined(__LP64__)
+  addr_str = android::base::StringPrintf("%08x'%08x",
+                                         static_cast<uint32_t>(addr >> 32),
+                                         static_cast<uint32_t>(addr & 0xffffffff));
+#else
+  addr_str = android::base::StringPrintf("%08x", addr);
+#endif
+  return addr_str;
+}
+
 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;
   siginfo_t si;
   memset(&si, 0, sizeof(si));
-  if (ptrace(PTRACE_GETSIGINFO, tid, 0, &si)) {
-    _LOG(log, logtype::ERROR, "cannot get siginfo for %d: %s\n", tid, strerror(errno));
-  } else {
+  if (ptrace(PTRACE_GETSIGINFO, tid, 0, &si) != -1) {
     print_fault_address_marker = signal_has_si_addr(si.si_signo);
     addr = reinterpret_cast<uintptr_t>(si.si_addr);
+  } else {
+    _LOG(log, logtype::ERROR, "Cannot get siginfo for %d: %s\n", tid, strerror(errno));
   }
 
   _LOG(log, logtype::MAPS, "\n");
@@ -335,8 +347,8 @@
   } else {
     _LOG(log, logtype::MAPS, "memory map: (fault address prefixed with --->)\n");
     if (map->begin() != map->end() && addr < map->begin()->start) {
-      _LOG(log, logtype::MAPS, "--->Fault address falls at %" PRIPTR " before any mapped regions\n",
-           addr);
+      _LOG(log, logtype::MAPS, "--->Fault address falls at %s before any mapped regions\n",
+           get_addr_string(addr).c_str());
       print_fault_address_marker = false;
     }
   }
@@ -346,15 +358,15 @@
     line = "    ";
     if (print_fault_address_marker) {
       if (addr < it->start) {
-        _LOG(log, logtype::MAPS, "--->Fault address falls at %" PRIPTR " between mapped regions\n",
-             addr);
+        _LOG(log, logtype::MAPS, "--->Fault address falls at %s between mapped regions\n",
+             get_addr_string(addr).c_str());
         print_fault_address_marker = false;
       } else if (addr >= it->start && addr < it->end) {
         line = "--->";
         print_fault_address_marker = false;
       }
     }
-    line += android::base::StringPrintf("%" PRIPTR "-%" PRIPTR " ", it->start, it->end - 1);
+    line += get_addr_string(it->start) + '-' + get_addr_string(it->end - 1) + ' ';
     if (it->flags & PROT_READ) {
       line += 'r';
     } else {
@@ -372,7 +384,9 @@
     }
     line += android::base::StringPrintf("  %8" PRIxPTR "  %8" PRIxPTR,
                                         it->offset, it->end - it->start);
+    bool space_needed = true;
     if (it->name.length() > 0) {
+      space_needed = false;
       line += "  " + it->name;
       std::string build_id;
       if ((it->flags & PROT_READ) && elf_get_build_id(backtrace, it->start, &build_id)) {
@@ -380,13 +394,16 @@
       }
     }
     if (it->load_base != 0) {
+      if (space_needed) {
+        line += ' ';
+      }
       line += android::base::StringPrintf(" (load base 0x%" PRIxPTR ")", it->load_base);
     }
     _LOG(log, logtype::MAPS, "%s\n", line.c_str());
   }
   if (print_fault_address_marker) {
-    _LOG(log, logtype::MAPS, "--->Fault address falls at %" PRIPTR " after any mapped regions\n",
-        addr);
+    _LOG(log, logtype::MAPS, "--->Fault address falls at %s after any mapped regions\n",
+         get_addr_string(addr).c_str());
   }
 }
 
diff --git a/fastboot/Android.mk b/fastboot/Android.mk
index 0648826..66a470a 100644
--- a/fastboot/Android.mk
+++ b/fastboot/Android.mk
@@ -14,6 +14,8 @@
 
 LOCAL_PATH:= $(call my-dir)
 
+fastboot_version := $(shell git -C $(LOCAL_PATH) rev-parse --short=12 HEAD 2>/dev/null)-android
+
 include $(CLEAR_VARS)
 
 LOCAL_C_INCLUDES := $(LOCAL_PATH)/../mkbootimg \
@@ -25,14 +27,15 @@
 LOCAL_CONLYFLAGS += -std=gnu99
 LOCAL_CFLAGS += -Wall -Wextra -Werror -Wunreachable-code
 
+LOCAL_CFLAGS += -DFASTBOOT_REVISION='"$(fastboot_version)"'
+
 ifeq ($(HOST_OS),linux)
   LOCAL_SRC_FILES += usb_linux.c util_linux.c
 endif
 
 ifeq ($(HOST_OS),darwin)
   LOCAL_SRC_FILES += usb_osx.c util_osx.c
-  LOCAL_LDLIBS += -lpthread -framework CoreFoundation -framework IOKit \
-	-framework Carbon
+  LOCAL_LDLIBS += -lpthread -framework CoreFoundation -framework IOKit -framework Carbon
   LOCAL_CFLAGS += -Wno-unused-parameter
 endif
 
@@ -58,7 +61,8 @@
     libsparse_host \
     libutils \
     liblog \
-    libz
+    libz \
+    libbase
 
 ifneq ($(HOST_OS),windows)
 LOCAL_STATIC_LIBRARIES += libselinux
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 51985af..6724d05 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -59,7 +59,6 @@
 
 char cur_product[FB_RESPONSE_SZ + 1];
 
-static usb_handle *usb = 0;
 static const char *serial = 0;
 static const char *product = 0;
 static const char *cmdline = 0;
@@ -609,7 +608,7 @@
  * erase partitions of type ext4 before flashing a filesystem so no stale
  * inodes are left lying around.  Otherwise, e2fsck gets very upset.
  */
-static int needs_erase(const char *part)
+static int needs_erase(usb_handle* usb, const char *part)
 {
     /* The function fb_format_supported() currently returns the value
      * we want, so just call it.
@@ -725,19 +724,20 @@
 
     setup_requirements(reinterpret_cast<char*>(data), sz);
 
-    for (size_t i = 0; i < ARRAY_SIZE(images); i++) {
+    for (size_t i = 0; i < ARRAY_SIZE(images); ++i) {
         int fd = unzip_to_file(zip, images[i].img_name);
-        if (fd < 0) {
-            if (images[i].is_optional)
+        if (fd == -1) {
+            if (images[i].is_optional) {
                 continue;
+            }
             CloseArchive(zip);
-            die("update package missing %s", images[i].img_name);
+            exit(1); // unzip_to_file already explained why.
         }
         fastboot_buffer buf;
         int rc = load_buf_fd(usb, fd, &buf);
         if (rc) die("cannot load %s from flash", images[i].img_name);
         do_update_signature(zip, images[i].sig_name);
-        if (erase_first && needs_erase(images[i].part_name)) {
+        if (erase_first && needs_erase(usb, images[i].part_name)) {
             fb_queue_erase(images[i].part_name);
         }
         flash_buf(images[i].part_name, &buf);
@@ -792,7 +792,7 @@
             die("could not load %s\n", images[i].img_name);
         }
         do_send_signature(fname);
-        if (erase_first && needs_erase(images[i].part_name)) {
+        if (erase_first && needs_erase(usb, images[i].part_name)) {
             fb_queue_erase(images[i].part_name);
         }
         flash_buf(images[i].part_name, &buf);
@@ -860,7 +860,8 @@
     return num;
 }
 
-void fb_perform_format(const char *partition, int skip_if_not_supported,
+void fb_perform_format(usb_handle* usb,
+                       const char *partition, int skip_if_not_supported,
                        const char *type_override, const char *size_override)
 {
     char pTypeBuff[FB_RESPONSE_SZ + 1], pSizeBuff[FB_RESPONSE_SZ + 1];
@@ -964,8 +965,9 @@
         {"page_size", required_argument, 0, 'n'},
         {"ramdisk_offset", required_argument, 0, 'r'},
         {"tags_offset", required_argument, 0, 't'},
-        {"help", 0, 0, 'h'},
-        {"unbuffered", 0, 0, 0},
+        {"help", no_argument, 0, 'h'},
+        {"unbuffered", no_argument, 0, 0},
+        {"version", no_argument, 0, 0},
         {0, 0, 0, 0}
     };
 
@@ -1037,6 +1039,9 @@
             if (strcmp("unbuffered", longopts[longindex].name) == 0) {
                 setvbuf(stdout, NULL, _IONBF, 0);
                 setvbuf(stderr, NULL, _IONBF, 0);
+            } else if (strcmp("version", longopts[longindex].name) == 0) {
+                fprintf(stdout, "fastboot version %s\n", FASTBOOT_REVISION);
+                return 0;
             }
             break;
         default:
@@ -1063,7 +1068,7 @@
         return 0;
     }
 
-    usb = open_device();
+    usb_handle* usb = open_device();
 
     while (argc > 0) {
         if(!strcmp(*argv, "getvar")) {
@@ -1105,10 +1110,10 @@
             }
             if (type_override && !type_override[0]) type_override = NULL;
             if (size_override && !size_override[0]) size_override = NULL;
-            if (erase_first && needs_erase(argv[1])) {
+            if (erase_first && needs_erase(usb, argv[1])) {
                 fb_queue_erase(argv[1]);
             }
-            fb_perform_format(argv[1], 0, type_override, size_override);
+            fb_perform_format(usb, argv[1], 0, type_override, size_override);
             skip(2);
         } else if(!strcmp(*argv, "signature")) {
             require(2);
@@ -1163,7 +1168,7 @@
                 skip(2);
             }
             if (fname == 0) die("cannot determine image filename for '%s'", pname);
-            if (erase_first && needs_erase(pname)) {
+            if (erase_first && needs_erase(usb, pname)) {
                 fb_queue_erase(pname);
             }
             do_flash(usb, pname, fname);
@@ -1214,9 +1219,9 @@
 
     if (wants_wipe) {
         fb_queue_erase("userdata");
-        fb_perform_format("userdata", 1, NULL, NULL);
+        fb_perform_format(usb, "userdata", 1, NULL, NULL);
         fb_queue_erase("cache");
-        fb_perform_format("cache", 1, NULL, NULL);
+        fb_perform_format(usb, "cache", 1, NULL, NULL);
     }
     if (wants_reboot) {
         fb_queue_reboot();
diff --git a/gatekeeperd/SoftGateKeeper.h b/gatekeeperd/SoftGateKeeper.h
index 1ae45e6..e554411 100644
--- a/gatekeeperd/SoftGateKeeper.h
+++ b/gatekeeperd/SoftGateKeeper.h
@@ -53,14 +53,20 @@
     virtual bool GetAuthTokenKey(const uint8_t **auth_token_key,
             uint32_t *length) const {
         if (auth_token_key == NULL || length == NULL) return false;
-        *auth_token_key = const_cast<const uint8_t *>(key_.get());
+        uint8_t *auth_token_key_copy = new uint8_t[SIGNATURE_LENGTH_BYTES];
+        memcpy(auth_token_key_copy, key_.get(), SIGNATURE_LENGTH_BYTES);
+
+        *auth_token_key = auth_token_key_copy;
         *length = SIGNATURE_LENGTH_BYTES;
         return true;
     }
 
     virtual void GetPasswordKey(const uint8_t **password_key, uint32_t *length) {
         if (password_key == NULL || length == NULL) return;
-        *password_key = const_cast<const uint8_t *>(key_.get());
+        uint8_t *password_key_copy = new uint8_t[SIGNATURE_LENGTH_BYTES];
+        memcpy(password_key_copy, key_.get(), SIGNATURE_LENGTH_BYTES);
+
+        *password_key = password_key_copy;
         *length = SIGNATURE_LENGTH_BYTES;
     }
 
@@ -94,7 +100,8 @@
         return false;
     }
 
-    virtual bool GetFailureRecord(uint32_t uid, secure_id_t user_id, failure_record_t *record) {
+    virtual bool GetFailureRecord(uint32_t uid, secure_id_t user_id, failure_record_t *record,
+            bool /* secure */) {
         failure_record_t *stored = &failure_map_[uid];
         if (user_id != stored->secure_user_id) {
             stored->secure_user_id = user_id;
@@ -105,20 +112,21 @@
         return true;
     }
 
-    virtual void ClearFailureRecord(uint32_t uid, secure_id_t user_id) {
+    virtual bool ClearFailureRecord(uint32_t uid, secure_id_t user_id, bool /* secure */) {
         failure_record_t *stored = &failure_map_[uid];
         stored->secure_user_id = user_id;
         stored->last_checked_timestamp = 0;
         stored->failure_counter = 0;
+        return true;
     }
 
-    virtual bool WriteFailureRecord(uint32_t uid, failure_record_t *record) {
+    virtual bool WriteFailureRecord(uint32_t uid, failure_record_t *record, bool /* secure */) {
         failure_map_[uid] = *record;
         return true;
     }
 
 private:
-    UniquePtr<uint8_t> key_;
+    UniquePtr<uint8_t[]> key_;
     std::unordered_map<uint32_t, failure_record_t> failure_map_;
 };
 }
diff --git a/include/backtrace/BacktraceMap.h b/include/backtrace/BacktraceMap.h
index 784bc03..bb18aa2 100644
--- a/include/backtrace/BacktraceMap.h
+++ b/include/backtrace/BacktraceMap.h
@@ -33,13 +33,11 @@
 #include <string>
 
 struct backtrace_map_t {
-  backtrace_map_t(): start(0), end(0), flags(0) {}
-
-  uintptr_t start;
-  uintptr_t end;
-  uintptr_t offset;
-  uintptr_t load_base;
-  int flags;
+  uintptr_t start = 0;
+  uintptr_t end = 0;
+  uintptr_t offset = 0;
+  uintptr_t load_base = 0;
+  int flags = 0;
   std::string name;
 };
 
diff --git a/include/log/logprint.h b/include/log/logprint.h
index 96249e9..4b812cc 100644
--- a/include/log/logprint.h
+++ b/include/log/logprint.h
@@ -36,9 +36,10 @@
     FORMAT_TIME,
     FORMAT_THREADTIME,
     FORMAT_LONG,
-    /* The following two are modifiers to above formats */
+    /* The following three are modifiers to above formats */
     FORMAT_MODIFIER_COLOR,     /* converts priority to color */
     FORMAT_MODIFIER_TIME_USEC, /* switches from msec to usec time precision */
+    FORMAT_MODIFIER_PRINTABLE, /* converts non-printable to printable escapes */
 } AndroidLogPrintFormat;
 
 typedef struct AndroidLogFormat_t AndroidLogFormat;
diff --git a/init/init.cpp b/init/init.cpp
index 5185f77..85d7909 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -380,7 +380,8 @@
 
     if ((svc->flags & SVC_EXEC) != 0) {
         INFO("SVC_EXEC pid %d (uid %d gid %d+%zu context %s) started; waiting...\n",
-             svc->pid, svc->uid, svc->gid, svc->nr_supp_gids, svc->seclabel);
+             svc->pid, svc->uid, svc->gid, svc->nr_supp_gids,
+             svc->seclabel ? : "default");
         waiting_for_exec = true;
     }
 
diff --git a/init/init_parser.cpp b/init/init_parser.cpp
index d36995d..385b37b 100644
--- a/init/init_parser.cpp
+++ b/init/init_parser.cpp
@@ -666,6 +666,7 @@
 
 service* make_exec_oneshot_service(int nargs, char** args) {
     // Parse the arguments: exec [SECLABEL [UID [GID]*] --] COMMAND ARGS...
+    // SECLABEL can be a - to denote default
     int command_arg = 1;
     for (int i = 1; i < nargs; ++i) {
         if (strcmp(args[i], "--") == 0) {
@@ -691,7 +692,7 @@
         return NULL;
     }
 
-    if (command_arg > 2) {
+    if ((command_arg > 2) && strcmp(args[1], "-")) {
         svc->seclabel = args[1];
     }
     if (command_arg > 3) {
diff --git a/init/readme.txt b/init/readme.txt
index 6b9c42d..c213041 100644
--- a/init/readme.txt
+++ b/init/readme.txt
@@ -180,7 +180,7 @@
    Fork and execute command with the given arguments. The command starts
    after "--" so that an optional security context, user, and supplementary
    groups can be provided. No other commands will be run until this one
-   finishes.
+   finishes. <seclabel> can be a - to denote default.
 
 export <name> <value>
    Set the environment variable <name> equal to <value> in the
diff --git a/liblog/log_time.cpp b/liblog/log_time.cpp
index 50742df..9d5ea0e 100644
--- a/liblog/log_time.cpp
+++ b/liblog/log_time.cpp
@@ -22,7 +22,7 @@
 
 #include <log/log_read.h>
 
-const char log_time::default_format[] = "%m-%d %H:%M:%S.%3q";
+const char log_time::default_format[] = "%m-%d %H:%M:%S.%q";
 const timespec log_time::EPOCH = { 0, 0 };
 
 // Add %#q for fractional seconds to standard strptime function
diff --git a/liblog/logprint.c b/liblog/logprint.c
index a3f1d7e..c2f1545 100644
--- a/liblog/logprint.c
+++ b/liblog/logprint.c
@@ -32,6 +32,9 @@
 #include <log/logd.h>
 #include <log/logprint.h>
 
+/* open coded fragment, prevent circular dependencies */
+#define WEAK static
+
 typedef struct FilterInfo_t {
     char *mTag;
     android_LogPriority mPri;
@@ -44,6 +47,7 @@
     AndroidLogPrintFormat format;
     bool colored_output;
     bool usec_time_output;
+    bool printable_output;
 };
 
 /*
@@ -187,6 +191,7 @@
     p_ret->format = FORMAT_BRIEF;
     p_ret->colored_output = false;
     p_ret->usec_time_output = false;
+    p_ret->printable_output = false;
 
     return p_ret;
 }
@@ -212,13 +217,18 @@
 int android_log_setPrintFormat(AndroidLogFormat *p_format,
         AndroidLogPrintFormat format)
 {
-    if (format == FORMAT_MODIFIER_COLOR) {
+    switch (format) {
+    case FORMAT_MODIFIER_COLOR:
         p_format->colored_output = true;
         return 0;
-    }
-    if (format == FORMAT_MODIFIER_TIME_USEC) {
+    case FORMAT_MODIFIER_TIME_USEC:
         p_format->usec_time_output = true;
         return 0;
+    case FORMAT_MODIFIER_PRINTABLE:
+        p_format->printable_output = true;
+        return 0;
+    default:
+        break;
     }
     p_format->format = format;
     return 1;
@@ -241,6 +251,7 @@
     else if (strcmp(formatString, "long") == 0) format = FORMAT_LONG;
     else if (strcmp(formatString, "color") == 0) format = FORMAT_MODIFIER_COLOR;
     else if (strcmp(formatString, "usec") == 0) format = FORMAT_MODIFIER_TIME_USEC;
+    else if (strcmp(formatString, "printable") == 0) format = FORMAT_MODIFIER_PRINTABLE;
     else format = FORMAT_OFF;
 
     return format;
@@ -276,29 +287,35 @@
     }
 
     if(0 == strncmp("*", filterExpression, tagNameLength)) {
-        // This filter expression refers to the global filter
-        // The default level for this is DEBUG if the priority
-        // is unspecified
+        /*
+         * This filter expression refers to the global filter
+         * The default level for this is DEBUG if the priority
+         * is unspecified
+         */
         if (pri == ANDROID_LOG_DEFAULT) {
             pri = ANDROID_LOG_DEBUG;
         }
 
         p_format->global_pri = pri;
     } else {
-        // for filter expressions that don't refer to the global
-        // filter, the default is verbose if the priority is unspecified
+        /*
+         * for filter expressions that don't refer to the global
+         * filter, the default is verbose if the priority is unspecified
+         */
         if (pri == ANDROID_LOG_DEFAULT) {
             pri = ANDROID_LOG_VERBOSE;
         }
 
         char *tagName;
 
-// Presently HAVE_STRNDUP is never defined, so the second case is always taken
-// Darwin doesn't have strnup, everything else does
+/*
+ * Presently HAVE_STRNDUP is never defined, so the second case is always taken
+ * Darwin doesn't have strnup, everything else does
+ */
 #ifdef HAVE_STRNDUP
         tagName = strndup(filterExpression, tagNameLength);
 #else
-        //a few extra bytes copied...
+        /* a few extra bytes copied... */
         tagName = strdup(filterExpression);
         tagName[tagNameLength] = '\0';
 #endif /*HAVE_STRNDUP*/
@@ -335,9 +352,9 @@
     char *p_ret;
     int err;
 
-    // Yes, I'm using strsep
+    /* Yes, I'm using strsep */
     while (NULL != (p_ret = strsep(&p_cur, " \t,"))) {
-        // ignore whitespace-only entries
+        /* ignore whitespace-only entries */
         if(p_ret[0] != '\0') {
             err = android_log_addFilterRule(p_format, p_ret);
 
@@ -381,8 +398,10 @@
      * When that happens, we must null-terminate the message ourselves.
      */
     if (buf->len < 3) {
-        // An well-formed entry must consist of at least a priority
-        // and two null characters
+        /*
+         * An well-formed entry must consist of at least a priority
+         * and two null characters
+         */
         fprintf(stderr, "+++ LOG: entry too small\n");
         return -1;
     }
@@ -412,7 +431,7 @@
         return -1;
     }
     if (msgEnd == -1) {
-        // incoming message not null-terminated; force it
+        /* incoming message not null-terminated; force it */
         msgEnd = buf->len - 1;
         msg[msgEnd] = '\0';
     }
@@ -473,8 +492,6 @@
     type = *eventData++;
     eventDataLen--;
 
-    //fprintf(stderr, "--- type=%d (rem len=%d)\n", type, eventDataLen);
-
     switch (type) {
     case EVENT_TYPE_INT:
         /* 32-bit signed int */
@@ -735,6 +752,122 @@
     return 0;
 }
 
+/*
+ * One utf8 character at a time
+ *
+ * Returns the length of the utf8 character in the buffer,
+ * or -1 if illegal or truncated
+ *
+ * Open coded from libutils/Unicode.cpp, borrowed from utf8_length(),
+ * can not remove from here because of library circular dependencies.
+ * Expect one-day utf8_character_length with the same signature could
+ * _also_ be part of libutils/Unicode.cpp if its usefullness needs to
+ * propagate globally.
+ */
+WEAK ssize_t utf8_character_length(const char *src, size_t len)
+{
+    const char *cur = src;
+    const char first_char = *cur++;
+    static const uint32_t kUnicodeMaxCodepoint = 0x0010FFFF;
+    int32_t mask, to_ignore_mask;
+    size_t num_to_read;
+    uint32_t utf32;
+
+    if ((first_char & 0x80) == 0) { /* ASCII */
+        return 1;
+    }
+
+    /*
+     * (UTF-8's character must not be like 10xxxxxx,
+     *  but 110xxxxx, 1110xxxx, ... or 1111110x)
+     */
+    if ((first_char & 0x40) == 0) {
+        return -1;
+    }
+
+    for (utf32 = 1, num_to_read = 1, mask = 0x40, to_ignore_mask = 0x80;
+         num_to_read < 5 && (first_char & mask);
+         num_to_read++, to_ignore_mask |= mask, mask >>= 1) {
+        if (num_to_read > len) {
+            return -1;
+        }
+        if ((*cur & 0xC0) != 0x80) { /* can not be 10xxxxxx? */
+            return -1;
+        }
+        utf32 = (utf32 << 6) + (*cur++ & 0b00111111);
+    }
+    /* "first_char" must be (110xxxxx - 11110xxx) */
+    if (num_to_read >= 5) {
+        return -1;
+    }
+    to_ignore_mask |= mask;
+    utf32 |= ((~to_ignore_mask) & first_char) << (6 * (num_to_read - 1));
+    if (utf32 > kUnicodeMaxCodepoint) {
+        return -1;
+    }
+    return num_to_read;
+}
+
+/*
+ * Convert to printable from message to p buffer, return string length. If p is
+ * NULL, do not copy, but still return the expected string length.
+ */
+static size_t convertPrintable(char *p, const char *message, size_t messageLen)
+{
+    char *begin = p;
+    bool print = p != NULL;
+
+    while (messageLen) {
+        char buf[6];
+        ssize_t len = sizeof(buf) - 1;
+        if ((size_t)len > messageLen) {
+            len = messageLen;
+        }
+        len = utf8_character_length(message, len);
+
+        if (len < 0) {
+            snprintf(buf, sizeof(buf),
+                     ((messageLen > 1) && isdigit(message[1]))
+                         ? "\\%03o"
+                         : "\\%o",
+                     *message & 0377);
+            len = 1;
+        } else {
+            buf[0] = '\0';
+            if (len == 1) {
+                if (*message == '\a') {
+                    strcpy(buf, "\\a");
+                } else if (*message == '\b') {
+                    strcpy(buf, "\\b");
+                } else if (*message == '\t') {
+                    strcpy(buf, "\\t");
+                } else if (*message == '\v') {
+                    strcpy(buf, "\\v");
+                } else if (*message == '\f') {
+                    strcpy(buf, "\\f");
+                } else if (*message == '\r') {
+                    strcpy(buf, "\\r");
+                } else if (*message == '\\') {
+                    strcpy(buf, "\\\\");
+                } else if ((*message < ' ') || (*message & 0x80)) {
+                    snprintf(buf, sizeof(buf), "\\%o", *message & 0377);
+                }
+            }
+            if (!buf[0]) {
+                strncpy(buf, message, len);
+                buf[len] = '\0';
+            }
+        }
+        if (print) {
+            strcpy(p, buf);
+        }
+        p += strlen(buf);
+        message += len;
+        messageLen -= len;
+    }
+    return p - begin;
+}
+
 /**
  * Formats a log message into a buffer
  *
@@ -778,7 +911,7 @@
 #else
     ptm = localtime(&(entry->tv_sec));
 #endif
-    //strftime(timeBuf, sizeof(timeBuf), "%Y-%m-%d %H:%M:%S", ptm);
+    /* strftime(timeBuf, sizeof(timeBuf), "%Y-%m-%d %H:%M:%S", ptm); */
     strftime(timeBuf, sizeof(timeBuf), "%m-%d %H:%M:%S", ptm);
     len = strlen(timeBuf);
     if (p_format->usec_time_output) {
@@ -873,24 +1006,34 @@
     const char *pm;
 
     if (prefixSuffixIsHeaderFooter) {
-        // we're just wrapping message with a header/footer
+        /* we're just wrapping message with a header/footer */
         numLines = 1;
     } else {
         pm = entry->message;
         numLines = 0;
 
-        // The line-end finding here must match the line-end finding
-        // in for ( ... numLines...) loop below
+        /*
+         * The line-end finding here must match the line-end finding
+         * in for ( ... numLines...) loop below
+         */
         while (pm < (entry->message + entry->messageLen)) {
             if (*pm++ == '\n') numLines++;
         }
-        // plus one line for anything not newline-terminated at the end
+        /* plus one line for anything not newline-terminated at the end */
         if (pm > entry->message && *(pm-1) != '\n') numLines++;
     }
 
-    // this is an upper bound--newlines in message may be counted
-    // extraneously
-    bufferSize = (numLines * (prefixLen + suffixLen)) + entry->messageLen + 1;
+    /*
+     * this is an upper bound--newlines in message may be counted
+     * extraneously
+     */
+    bufferSize = (numLines * (prefixLen + suffixLen)) + 1;
+    if (p_format->printable_output) {
+        /* Calculate extra length to convert non-printable to printable */
+        bufferSize += convertPrintable(NULL, entry->message, entry->messageLen);
+    } else {
+        bufferSize += entry->messageLen;
+    }
 
     if (defaultBufferSize >= bufferSize) {
         ret = defaultBuffer;
@@ -910,8 +1053,12 @@
     if (prefixSuffixIsHeaderFooter) {
         strcat(p, prefixBuf);
         p += prefixLen;
-        strncat(p, entry->message, entry->messageLen);
-        p += entry->messageLen;
+        if (p_format->printable_output) {
+            p += convertPrintable(p, entry->message, entry->messageLen);
+        } else {
+            strncat(p, entry->message, entry->messageLen);
+            p += entry->messageLen;
+        }
         strcat(p, suffixBuf);
         p += suffixLen;
     } else {
@@ -920,15 +1067,19 @@
             size_t lineLen;
             lineStart = pm;
 
-            // Find the next end-of-line in message
+            /* Find the next end-of-line in message */
             while (pm < (entry->message + entry->messageLen)
                     && *pm != '\n') pm++;
             lineLen = pm - lineStart;
 
             strcat(p, prefixBuf);
             p += prefixLen;
-            strncat(p, lineStart, lineLen);
-            p += lineLen;
+            if (p_format->printable_output) {
+                p += convertPrintable(p, lineStart, lineLen);
+            } else {
+                strncat(p, lineStart, lineLen);
+                p += lineLen;
+            }
             strcat(p, suffixBuf);
             p += suffixLen;
 
diff --git a/libziparchive/zip_archive.cc b/libziparchive/zip_archive.cc
index 8582344..b2a9f88 100644
--- a/libziparchive/zip_archive.cc
+++ b/libziparchive/zip_archive.cc
@@ -30,6 +30,7 @@
 #include <memory>
 #include <vector>
 
+#include "base/file.h"
 #include "base/macros.h"  // TEMP_FAILURE_RETRY may or may not be in unistd
 #include "base/memory.h"
 #include "log/log.h"
@@ -85,7 +86,8 @@
   // Length of the central directory comment.
   uint16_t comment_length;
  private:
-  DISALLOW_IMPLICIT_CONSTRUCTORS(EocdRecord);
+  EocdRecord() = default;
+  DISALLOW_COPY_AND_ASSIGN(EocdRecord);
 } __attribute__((packed));
 
 // A structure representing the fixed length fields for a single
@@ -138,7 +140,8 @@
   // beginning of this archive.
   uint32_t local_file_header_offset;
  private:
-  DISALLOW_IMPLICIT_CONSTRUCTORS(CentralDirectoryRecord);
+  CentralDirectoryRecord() = default;
+  DISALLOW_COPY_AND_ASSIGN(CentralDirectoryRecord);
 } __attribute__((packed));
 
 // The local file header for a given entry. This duplicates information
@@ -175,7 +178,8 @@
   // will appear immediately after the entry file name.
   uint16_t extra_field_length;
  private:
-  DISALLOW_IMPLICIT_CONSTRUCTORS(LocalFileHeader);
+  LocalFileHeader() = default;
+  DISALLOW_COPY_AND_ASSIGN(LocalFileHeader);
 } __attribute__((packed));
 
 struct DataDescriptor {
@@ -189,10 +193,10 @@
   // Uncompressed size of the entry.
   uint32_t uncompressed_size;
  private:
-  DISALLOW_IMPLICIT_CONSTRUCTORS(DataDescriptor);
+  DataDescriptor() = default;
+  DISALLOW_COPY_AND_ASSIGN(DataDescriptor);
 } __attribute__((packed));
 
-#undef DISALLOW_IMPLICIT_CONSTRUCTORS
 
 static const uint32_t kGPBDDFlagMask = 0x0008;         // mask value that signifies that the entry has a DD
 
@@ -265,8 +269,6 @@
 
 static const int32_t kErrorMessageLowerBound = -13;
 
-static const char kTempMappingFileName[] = "zip: ExtractFileToFile";
-
 /*
  * A Read-only Zip archive.
  *
@@ -324,35 +326,6 @@
   }
 };
 
-static int32_t CopyFileToFile(int fd, uint8_t* begin, const uint32_t length, uint64_t *crc_out) {
-  static const uint32_t kBufSize = 32768;
-  uint8_t buf[kBufSize];
-
-  uint32_t count = 0;
-  uint64_t crc = 0;
-  while (count < length) {
-    uint32_t remaining = length - count;
-
-    // Safe conversion because kBufSize is narrow enough for a 32 bit signed
-    // value.
-    ssize_t get_size = (remaining > kBufSize) ? kBufSize : remaining;
-    ssize_t actual = TEMP_FAILURE_RETRY(read(fd, buf, get_size));
-
-    if (actual != get_size) {
-      ALOGW("CopyFileToFile: copy read failed (" ZD " vs " ZD ")", actual, get_size);
-      return kIoError;
-    }
-
-    memcpy(begin + count, buf, get_size);
-    crc = crc32(crc, buf, get_size);
-    count += get_size;
-  }
-
-  *crc_out = crc;
-
-  return 0;
-}
-
 /*
  * Round up to the next highest power of 2.
  *
@@ -972,6 +945,122 @@
   return kIterationEnd;
 }
 
+class Writer {
+ public:
+  virtual bool Append(uint8_t* buf, size_t buf_size) = 0;
+  virtual ~Writer() {}
+ protected:
+  Writer() = default;
+ private:
+  DISALLOW_COPY_AND_ASSIGN(Writer);
+};
+
+// A Writer that writes data to a fixed size memory region.
+// The size of the memory region must be equal to the total size of
+// the data appended to it.
+class MemoryWriter : public Writer {
+ public:
+  MemoryWriter(uint8_t* buf, size_t size) : Writer(),
+      buf_(buf), size_(size), bytes_written_(0) {
+  }
+
+  virtual bool Append(uint8_t* buf, size_t buf_size) override {
+    if (bytes_written_ + buf_size > size_) {
+      ALOGW("Zip: Unexpected size " ZD " (declared) vs " ZD " (actual)",
+            size_, bytes_written_ + buf_size);
+      return false;
+    }
+
+    memcpy(buf_ + bytes_written_, buf, buf_size);
+    bytes_written_ += buf_size;
+    return true;
+  }
+
+ private:
+  uint8_t* const buf_;
+  const size_t size_;
+  size_t bytes_written_;
+};
+
+// A Writer that appends data to a file |fd| at its current position.
+// The file will be truncated to the end of the written data.
+class FileWriter : public Writer {
+ public:
+
+  // Creates a FileWriter for |fd| and prepare to write |entry| to it,
+  // guaranteeing that the file descriptor is valid and that there's enough
+  // space on the volume to write out the entry completely and that the file
+  // is truncated to the correct length.
+  //
+  // Returns a valid FileWriter on success, |nullptr| if an error occurred.
+  static std::unique_ptr<FileWriter> Create(int fd, const ZipEntry* entry) {
+    const uint32_t declared_length = entry->uncompressed_length;
+    const off64_t current_offset = lseek64(fd, 0, SEEK_CUR);
+    if (current_offset == -1) {
+      ALOGW("Zip: unable to seek to current location on fd %d: %s", fd, strerror(errno));
+      return nullptr;
+    }
+
+    int result = 0;
+#if defined(__linux__)
+    if (declared_length > 0) {
+      // Make sure we have enough space on the volume to extract the compressed
+      // entry. Note that the call to ftruncate below will change the file size but
+      // will not allocate space on disk and this call to fallocate will not
+      // change the file size.
+      // Note: fallocate is only supported by the following filesystems -
+      // btrfs, ext4, ocfs2, and xfs. Therefore fallocate might fail with
+      // EOPNOTSUPP error when issued in other filesystems.
+      // Hence, check for the return error code before concluding that the
+      // disk does not have enough space.
+      result = TEMP_FAILURE_RETRY(fallocate(fd, 0, current_offset, declared_length));
+      if (result == -1 && errno == ENOSPC) {
+        ALOGW("Zip: unable to allocate space for file to %" PRId64 ": %s",
+              static_cast<int64_t>(declared_length + current_offset), strerror(errno));
+        return std::unique_ptr<FileWriter>(nullptr);
+      }
+    }
+#endif  // __linux__
+
+    result = TEMP_FAILURE_RETRY(ftruncate(fd, declared_length + current_offset));
+    if (result == -1) {
+      ALOGW("Zip: unable to truncate file to %" PRId64 ": %s",
+            static_cast<int64_t>(declared_length + current_offset), strerror(errno));
+      return std::unique_ptr<FileWriter>(nullptr);
+    }
+
+    return std::unique_ptr<FileWriter>(new FileWriter(fd, declared_length));
+  }
+
+  virtual bool Append(uint8_t* buf, size_t buf_size) override {
+    if (total_bytes_written_ + buf_size > declared_length_) {
+      ALOGW("Zip: Unexpected size " ZD " (declared) vs " ZD " (actual)",
+            declared_length_, total_bytes_written_ + buf_size);
+      return false;
+    }
+
+    const bool result = android::base::WriteFully(fd_, buf, buf_size);
+    if (result) {
+      total_bytes_written_ += buf_size;
+    } else {
+      ALOGW("Zip: unable to write " ZD " bytes to file; %s", buf_size, strerror(errno));
+    }
+
+    return result;
+  }
+ private:
+  FileWriter(const int fd, const size_t declared_length) :
+      Writer(),
+      fd_(fd),
+      declared_length_(declared_length),
+      total_bytes_written_(0) {
+  }
+
+  const int fd_;
+  const size_t declared_length_;
+  size_t total_bytes_written_;
+};
+
 // This method is using libz macros with old-style-casts
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wold-style-cast"
@@ -980,9 +1069,8 @@
 }
 #pragma GCC diagnostic pop
 
-static int32_t InflateToFile(int fd, const ZipEntry* entry,
-                             uint8_t* begin, uint32_t length,
-                             uint64_t* crc_out) {
+static int32_t InflateEntryToWriter(int fd, const ZipEntry* entry,
+                                    Writer* writer, uint64_t* crc_out) {
   const size_t kBufSize = 32768;
   std::vector<uint8_t> read_buf(kBufSize);
   std::vector<uint8_t> write_buf(kBufSize);
@@ -1027,7 +1115,6 @@
   const uint32_t uncompressed_length = entry->uncompressed_length;
 
   uint32_t compressed_length = entry->compressed_length;
-  uint32_t write_count = 0;
   do {
     /* read as much as we can */
     if (zstream.avail_in == 0) {
@@ -1057,12 +1144,10 @@
     if (zstream.avail_out == 0 ||
       (zerr == Z_STREAM_END && zstream.avail_out != kBufSize)) {
       const size_t write_size = zstream.next_out - &write_buf[0];
-      // The file might have declared a bogus length.
-      if (write_size + write_count > length) {
-        return -1;
+      if (!writer->Append(&write_buf[0], write_size)) {
+        // The file might have declared a bogus length.
+        return kInconsistentInformation;
       }
-      memcpy(begin + write_count, &write_buf[0], write_size);
-      write_count += write_size;
 
       zstream.next_out = &write_buf[0];
       zstream.avail_out = kBufSize;
@@ -1083,8 +1168,41 @@
   return 0;
 }
 
-int32_t ExtractToMemory(ZipArchiveHandle handle,
-                        ZipEntry* entry, uint8_t* begin, uint32_t size) {
+static int32_t CopyEntryToWriter(int fd, const ZipEntry* entry, Writer* writer,
+                                 uint64_t *crc_out) {
+  static const uint32_t kBufSize = 32768;
+  std::vector<uint8_t> buf(kBufSize);
+
+  const uint32_t length = entry->uncompressed_length;
+  uint32_t count = 0;
+  uint64_t crc = 0;
+  while (count < length) {
+    uint32_t remaining = length - count;
+
+    // Safe conversion because kBufSize is narrow enough for a 32 bit signed
+    // value.
+    const ssize_t block_size = (remaining > kBufSize) ? kBufSize : remaining;
+    const ssize_t actual = TEMP_FAILURE_RETRY(read(fd, &buf[0], block_size));
+
+    if (actual != block_size) {
+      ALOGW("CopyFileToFile: copy read failed (" ZD " vs " ZD ")", actual, block_size);
+      return kIoError;
+    }
+
+    if (!writer->Append(&buf[0], block_size)) {
+      return kIoError;
+    }
+    crc = crc32(crc, &buf[0], block_size);
+    count += block_size;
+  }
+
+  *crc_out = crc;
+
+  return 0;
+}
+
+int32_t ExtractToWriter(ZipArchiveHandle handle,
+                        ZipEntry* entry, Writer* writer) {
   ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle);
   const uint16_t method = entry->method;
   off64_t data_offset = entry->offset;
@@ -1098,9 +1216,9 @@
   int32_t return_value = -1;
   uint64_t crc = 0;
   if (method == kCompressStored) {
-    return_value = CopyFileToFile(archive->fd, begin, size, &crc);
+    return_value = CopyEntryToWriter(archive->fd, entry, writer, &crc);
   } else if (method == kCompressDeflated) {
-    return_value = InflateToFile(archive->fd, entry, begin, size, &crc);
+    return_value = InflateEntryToWriter(archive->fd, entry, writer, &crc);
   }
 
   if (!return_value && entry->has_data_descriptor) {
@@ -1120,55 +1238,20 @@
   return return_value;
 }
 
+int32_t ExtractToMemory(ZipArchiveHandle handle, ZipEntry* entry,
+                        uint8_t* begin, uint32_t size) {
+  std::unique_ptr<Writer> writer(new MemoryWriter(begin, size));
+  return ExtractToWriter(handle, entry, writer.get());
+}
+
 int32_t ExtractEntryToFile(ZipArchiveHandle handle,
                            ZipEntry* entry, int fd) {
-  const uint32_t declared_length = entry->uncompressed_length;
-
-  const off64_t current_offset = lseek64(fd, 0, SEEK_CUR);
-  if (current_offset == -1) {
-    ALOGW("Zip: unable to seek to current location on fd %d: %s", fd,
-          strerror(errno));
+  std::unique_ptr<Writer> writer(FileWriter::Create(fd, entry));
+  if (writer.get() == nullptr) {
     return kIoError;
   }
 
-  int result = 0;
-#if defined(__linux__)
-  // Make sure we have enough space on the volume to extract the compressed
-  // entry. Note that the call to ftruncate below will change the file size but
-  // will not allocate space on disk.
-  if (declared_length > 0) {
-    result = TEMP_FAILURE_RETRY(fallocate(fd, 0, current_offset, declared_length));
-    if (result == -1) {
-      ALOGW("Zip: unable to allocate space for file to %" PRId64 ": %s",
-            static_cast<int64_t>(declared_length + current_offset), strerror(errno));
-      return kIoError;
-    }
-  }
-#endif  // defined(__linux__)
-
-  result = TEMP_FAILURE_RETRY(ftruncate(fd, declared_length + current_offset));
-  if (result == -1) {
-    ALOGW("Zip: unable to truncate file to %" PRId64 ": %s",
-          static_cast<int64_t>(declared_length + current_offset), strerror(errno));
-    return kIoError;
-  }
-
-  // Don't attempt to map a region of length 0. We still need the
-  // ftruncate() though, since the API guarantees that we will truncate
-  // the file to the end of the uncompressed output.
-  if (declared_length == 0) {
-      return 0;
-  }
-
-  android::FileMap map;
-  if (!map.create(kTempMappingFileName, fd, current_offset, declared_length, false)) {
-    return kMmapFailed;
-  }
-
-  const int32_t error = ExtractToMemory(handle, entry,
-                                        reinterpret_cast<uint8_t*>(map.getDataPtr()),
-                                        map.getDataLength());
-  return error;
+  return ExtractToWriter(handle, entry, writer.get());
 }
 
 const char* ErrorCodeString(int32_t error_code) {
diff --git a/libziparchive/zip_archive_test.cc b/libziparchive/zip_archive_test.cc
index 64faa6d..f8952ce 100644
--- a/libziparchive/zip_archive_test.cc
+++ b/libziparchive/zip_archive_test.cc
@@ -23,6 +23,7 @@
 #include <unistd.h>
 #include <vector>
 
+#include <base/file.h>
 #include <gtest/gtest.h>
 
 static std::string test_data_dir;
@@ -228,6 +229,44 @@
       0x54557478, 0x13030005, 0x7552e25c, 0x01000b78, 0x00428904, 0x13880400,
       0x4b500000, 0x00000605, 0x00010000, 0x004f0001, 0x00430000, 0x00000000 };
 
+// This is a zip file containing a single entry (ab.txt) that contains
+// 90072 repetitions of the string "ab\n" and has an uncompressed length
+// of 270216 bytes.
+static const uint16_t kAbZip[] = {
+  0x4b50, 0x0403, 0x0014, 0x0000, 0x0008, 0x51d2, 0x4698, 0xc4b0,
+  0x2cda, 0x011b, 0x0000, 0x1f88, 0x0004, 0x0006, 0x001c, 0x6261,
+  0x742e, 0x7478, 0x5455, 0x0009, 0x7c03, 0x3a09, 0x7c55, 0x3a09,
+  0x7555, 0x0b78, 0x0100, 0x8904, 0x0042, 0x0400, 0x1388, 0x0000,
+  0xc2ed, 0x0d31, 0x0000, 0x030c, 0x7fa0, 0x3b2e, 0x22ff, 0xa2aa,
+  0x841f, 0x45fc, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+  0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+  0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+  0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+  0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+  0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+  0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+  0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+  0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+  0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+  0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+  0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+  0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+  0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+  0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+  0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+  0x5555, 0x5555, 0x5555, 0x5555, 0xdd55, 0x502c, 0x014b, 0x1e02,
+  0x1403, 0x0000, 0x0800, 0xd200, 0x9851, 0xb046, 0xdac4, 0x1b2c,
+  0x0001, 0x8800, 0x041f, 0x0600, 0x1800, 0x0000, 0x0000, 0x0100,
+  0x0000, 0xa000, 0x0081, 0x0000, 0x6100, 0x2e62, 0x7874, 0x5574,
+  0x0554, 0x0300, 0x097c, 0x553a, 0x7875, 0x000b, 0x0401, 0x4289,
+  0x0000, 0x8804, 0x0013, 0x5000, 0x054b, 0x0006, 0x0000, 0x0100,
+  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 size_t kAbUncompressedSize = 270216;
+
 static int make_temporary_file(const char* file_name_pattern) {
   char full_path[1024];
   // Account for differences between the host and the target.
@@ -275,6 +314,55 @@
   close(output_fd);
 }
 
+TEST(ziparchive, EntryLargerThan32K) {
+  char temp_file_pattern[] = "entry_larger_than_32k_test_XXXXXX";
+  int fd = make_temporary_file(temp_file_pattern);
+  ASSERT_NE(-1, fd);
+  ASSERT_TRUE(android::base::WriteFully(fd, reinterpret_cast<const uint8_t*>(kAbZip),
+                         sizeof(kAbZip) - 1));
+  ZipArchiveHandle handle;
+  ASSERT_EQ(0, OpenArchiveFd(fd, "EntryLargerThan32KTest", &handle));
+
+  ZipEntry entry;
+  ZipEntryName ab_name;
+  ab_name.name = kAbTxtName;
+  ab_name.name_length = kAbTxtNameLength;
+  ASSERT_EQ(0, FindEntry(handle, ab_name, &entry));
+  ASSERT_EQ(kAbUncompressedSize, entry.uncompressed_length);
+
+  // Extract the entry to memory.
+  std::vector<uint8_t> buffer(kAbUncompressedSize);
+  ASSERT_EQ(0, ExtractToMemory(handle, &entry, &buffer[0], buffer.size()));
+
+  // Extract the entry to a file.
+  char output_file_pattern[] = "entry_larger_than_32k_test_output_XXXXXX";
+  int output_fd = make_temporary_file(output_file_pattern);
+  ASSERT_NE(-1, output_fd);
+  ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, output_fd));
+
+  // Make sure the extracted file size is as expected.
+  struct stat stat_buf;
+  ASSERT_EQ(0, fstat(output_fd, &stat_buf));
+  ASSERT_EQ(kAbUncompressedSize, static_cast<size_t>(stat_buf.st_size));
+
+  // Read the file back to a buffer and make sure the contents are
+  // the same as the memory buffer we extracted directly to.
+  std::vector<uint8_t> file_contents(kAbUncompressedSize);
+  ASSERT_EQ(0, lseek64(output_fd, 0, SEEK_SET));
+  ASSERT_TRUE(android::base::ReadFully(output_fd, &file_contents[0], file_contents.size()));
+  ASSERT_EQ(file_contents, buffer);
+
+  for (int i = 0; i < 90072; ++i) {
+    const uint8_t* line = &file_contents[0] + (3 * i);
+    ASSERT_EQ('a', line[0]);
+    ASSERT_EQ('b', line[1]);
+    ASSERT_EQ('\n', line[2]);
+  }
+
+  close(fd);
+  close(output_fd);
+}
+
 TEST(ziparchive, TrailerAfterEOCD) {
   char temp_file_pattern[] = "trailer_after_eocd_test_XXXXXX";
   int fd = make_temporary_file(temp_file_pattern);
diff --git a/logcat/Android.mk b/logcat/Android.mk
index f46a4de..7115f9b 100644
--- a/logcat/Android.mk
+++ b/logcat/Android.mk
@@ -5,7 +5,7 @@
 
 LOCAL_SRC_FILES:= logcat.cpp event.logtags
 
-LOCAL_SHARED_LIBRARIES := liblog
+LOCAL_SHARED_LIBRARIES := liblog libbase libcutils
 
 LOCAL_MODULE := logcat
 
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index 2c2d785..736e02e 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -1,29 +1,38 @@
 // Copyright 2006-2015 The Android Open Source Project
 
+#include <arpa/inet.h>
 #include <assert.h>
 #include <ctype.h>
+#include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <math.h>
+#include <sched.h>
+#include <signal.h>
+#include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <stdarg.h>
 #include <string.h>
-#include <signal.h>
-#include <time.h>
-#include <unistd.h>
 #include <sys/cdefs.h>
 #include <sys/socket.h>
 #include <sys/stat.h>
-#include <arpa/inet.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
 
+#include <memory>
+#include <string>
+
+#include <base/file.h>
+#include <base/strings.h>
+#include <cutils/sched_policy.h>
 #include <cutils/sockets.h>
+#include <log/event_tag_map.h>
 #include <log/log.h>
 #include <log/log_read.h>
-#include <log/logger.h>
 #include <log/logd.h>
+#include <log/logger.h>
 #include <log/logprint.h>
-#include <log/event_tag_map.h>
 
 #define DEFAULT_MAX_ROTATED_LOGS 4
 
@@ -202,7 +211,15 @@
         g_outFD = STDOUT_FILENO;
 
     } else {
-        struct stat statbuf;
+        if (set_sched_policy(0, SP_BACKGROUND) < 0) {
+            fprintf(stderr, "failed to set background scheduling policy\n");
+        }
+
+        struct sched_param param;
+        memset(&param, 0, sizeof(param));
+        if (sched_setscheduler((pid_t) 0, SCHED_BATCH, &param) < 0) {
+            fprintf(stderr, "failed to set to batch scheduler\n");
+        }
 
         g_outFD = openLogFile (g_outputFileName);
 
@@ -210,6 +227,7 @@
             logcat_panic(false, "couldn't open output file");
         }
 
+        struct stat statbuf;
         if (fstat(g_outFD, &statbuf) == -1) {
             close(g_outFD);
             logcat_panic(false, "couldn't get output file stat\n");
@@ -231,11 +249,12 @@
     fprintf(stderr, "options include:\n"
                     "  -s              Set default filter to silent.\n"
                     "                  Like specifying filterspec '*:S'\n"
-                    "  -f <filename>   Log to file. Default to stdout\n"
+                    "  -f <filename>   Log to file. Default is stdout\n"
                     "  -r <kbytes>     Rotate log every kbytes. Requires -f\n"
                     "  -n <count>      Sets max number of rotated logs to <count>, default 4\n"
                     "  -v <format>     Sets the log print format, where <format> is:\n\n"
-                    "                  brief color long process raw tag thread threadtime time usec\n\n"
+                    "                      brief color long printable process raw tag thread\n"
+                    "                      threadtime time usec\n\n"
                     "  -D              print dividers between each log buffer\n"
                     "  -c              clear (flush) the entire log and exit\n"
                     "  -d              dump the log and then exit (don't block)\n"
@@ -352,6 +371,86 @@
     exit(EXIT_FAILURE);
 }
 
+static const char g_defaultTimeFormat[] = "%m-%d %H:%M:%S.%q";
+
+// Find last logged line in gestalt of all matching existing output files
+static log_time lastLogTime(char *outputFileName) {
+    log_time retval(log_time::EPOCH);
+    if (!outputFileName) {
+        return retval;
+    }
+
+    log_time now(CLOCK_REALTIME);
+
+    std::string directory;
+    char *file = strrchr(outputFileName, '/');
+    if (!file) {
+        directory = ".";
+        file = outputFileName;
+    } else {
+        *file = '\0';
+        directory = outputFileName;
+        *file = '/';
+        ++file;
+    }
+    size_t len = strlen(file);
+    log_time modulo(0, NS_PER_SEC);
+    std::unique_ptr<DIR, int(*)(DIR*)>dir(opendir(directory.c_str()), closedir);
+    struct dirent *dp;
+    while ((dp = readdir(dir.get())) != NULL) {
+        if ((dp->d_type != DT_REG)
+                || strncmp(dp->d_name, file, len)
+                || (dp->d_name[len]
+                    && ((dp->d_name[len] != '.')
+                        || !isdigit(dp->d_name[len+1])))) {
+            continue;
+        }
+
+        std::string file_name = directory;
+        file_name += "/";
+        file_name += dp->d_name;
+        std::string file;
+        if (!android::base::ReadFileToString(file_name, &file)) {
+            continue;
+        }
+
+        bool found = false;
+        for (const auto& line : android::base::Split(file, "\n")) {
+            log_time t(log_time::EPOCH);
+            char *ep = t.strptime(line.c_str(), g_defaultTimeFormat);
+            if (!ep || (*ep != ' ')) {
+                continue;
+            }
+            // determine the time precision of the logs (eg: msec or usec)
+            for (unsigned long mod = 1UL; mod < modulo.tv_nsec; mod *= 10) {
+                if (t.tv_nsec % (mod * 10)) {
+                    modulo.tv_nsec = mod;
+                    break;
+                }
+            }
+            // We filter any times later than current as we may not have the
+            // year stored with each log entry. Also, since it is possible for
+            // entries to be recorded out of order (very rare) we select the
+            // maximum we find just in case.
+            if ((t < now) && (t > retval)) {
+                retval = t;
+                found = true;
+            }
+        }
+        // We count on the basename file to be the definitive end, so stop here.
+        if (!dp->d_name[len] && found) {
+            break;
+        }
+    }
+    if (retval == log_time::EPOCH) {
+        return retval;
+    }
+    // tail_time prints matching or higher, round up by the modulo to prevent
+    // a replay of the last entry we have just checked.
+    retval += modulo;
+    return retval;
+}
+
 } /* namespace android */
 
 
@@ -417,12 +516,11 @@
                 /* FALLTHRU */
             case 'T':
                 if (strspn(optarg, "0123456789") != strlen(optarg)) {
-                    char *cp = tail_time.strptime(optarg,
-                                                  log_time::default_format);
+                    char *cp = tail_time.strptime(optarg, g_defaultTimeFormat);
                     if (!cp) {
                         logcat_panic(false,
                                     "-%c \"%s\" not in \"%s\" time format\n",
-                                    ret, optarg, log_time::default_format);
+                                    ret, optarg, g_defaultTimeFormat);
                     }
                     if (*cp) {
                         char c = *cp;
@@ -545,9 +643,11 @@
             break;
 
             case 'f':
+                if ((tail_time == log_time::EPOCH) && (tail_lines != 0)) {
+                    tail_time = lastLogTime(optarg);
+                }
                 // redirect output to a file
                 g_outputFileName = optarg;
-
             break;
 
             case 'r':
diff --git a/logd/Android.mk b/logd/Android.mk
index 73da8dc..615d030 100644
--- a/logd/Android.mk
+++ b/logd/Android.mk
@@ -41,4 +41,15 @@
 
 include $(BUILD_EXECUTABLE)
 
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := logpersist.start
+LOCAL_MODULE_TAGS := debug
+LOCAL_MODULE_CLASS := EXECUTABLES
+LOCAL_MODULE_PATH := $(bin_dir)
+LOCAL_SRC_FILES := logpersist
+ALL_TOOLS := logpersist.start logpersist.stop logpersist.cat
+LOCAL_POST_INSTALL_CMD := $(hide) $(foreach t,$(filter-out $(LOCAL_MODULE),$(ALL_TOOLS)),ln -sf $(LOCAL_MODULE) $(TARGET_OUT)/bin/$(t);)
+include $(BUILD_PREBUILT)
+
 include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp
index 1da52d8..80fc9f5 100644
--- a/logd/LogBuffer.cpp
+++ b/logd/LogBuffer.cpp
@@ -22,6 +22,8 @@
 #include <time.h>
 #include <unistd.h>
 
+#include <unordered_map>
+
 #include <cutils/properties.h>
 #include <log/logger.h>
 
@@ -246,31 +248,21 @@
     uint64_t getKey() { return value; }
 };
 
-class LogBufferElementEntry {
-    const uint64_t key;
-    LogBufferElement *last;
+class LogBufferElementLast {
+
+    typedef std::unordered_map<uint64_t, LogBufferElement *> LogBufferElementMap;
+    LogBufferElementMap map;
 
 public:
-    LogBufferElementEntry(const uint64_t &k, LogBufferElement *e):key(k),last(e) { }
 
-    const uint64_t&getKey() const { return key; }
-
-    LogBufferElement *getLast() { return last; }
-};
-
-class LogBufferElementLast : public android::BasicHashtable<uint64_t, LogBufferElementEntry> {
-
-public:
     bool merge(LogBufferElement *e, unsigned short dropped) {
         LogBufferElementKey key(e->getUid(), e->getPid(), e->getTid());
-        android::hash_t hash = android::hash_type(key.getKey());
-        ssize_t index = find(-1, hash, key.getKey());
-        if (index != -1) {
-            LogBufferElementEntry &entry = editEntryAt(index);
-            LogBufferElement *l = entry.getLast();
+        LogBufferElementMap::iterator it = map.find(key.getKey());
+        if (it != map.end()) {
+            LogBufferElement *l = it->second;
             unsigned short d = l->getDropped();
             if ((dropped + d) > USHRT_MAX) {
-                removeAt(index);
+                map.erase(it);
             } else {
                 l->setDropped(dropped + d);
                 return true;
@@ -279,26 +271,24 @@
         return false;
     }
 
-    size_t add(LogBufferElement *e) {
+    void add(LogBufferElement *e) {
         LogBufferElementKey key(e->getUid(), e->getPid(), e->getTid());
-        android::hash_t hash = android::hash_type(key.getKey());
-        return android::BasicHashtable<uint64_t, LogBufferElementEntry>::
-            add(hash, LogBufferElementEntry(key.getKey(), e));
+        map[key.getKey()] = e;
     }
 
     inline void clear() {
-        android::BasicHashtable<uint64_t, LogBufferElementEntry>::clear();
+        map.clear();
     }
 
     void clear(LogBufferElement *e) {
         uint64_t current = e->getRealTime().nsec() - NS_PER_SEC;
-        ssize_t index = -1;
-        while((index = next(index)) >= 0) {
-            LogBufferElement *l = editEntryAt(index).getLast();
+        for(LogBufferElementMap::iterator it = map.begin(); it != map.end();) {
+            LogBufferElement *l = it->second;
             if ((l->getDropped() >= EXPIRE_THRESHOLD)
                     && (current > l->getRealTime().nsec())) {
-                removeAt(index);
-                index = -1;
+                it = map.erase(it);
+            } else {
+                ++it;
             }
         }
     }
diff --git a/logd/LogStatistics.cpp b/logd/LogStatistics.cpp
index 90c49c0..48c2fe6 100644
--- a/logd/LogStatistics.cpp
+++ b/logd/LogStatistics.cpp
@@ -161,9 +161,8 @@
     }
 
     // report uid -> pid(s) -> pidToName if unique
-    ssize_t index = -1;
-    while ((index = pidTable.next(index)) != -1) {
-        const PidEntry &entry = pidTable.entryAt(index);
+    for(pidTable_t::iterator it = pidTable.begin(); it != pidTable.end(); ++it) {
+        const PidEntry &entry = it->second;
 
         if (entry.getUid() == uid) {
             const char *n = entry.getName();
@@ -520,12 +519,12 @@
 }
 
 uid_t LogStatistics::pidToUid(pid_t pid) {
-    return pidTable.entryAt(pidTable.add(pid)).getUid();
+    return pidTable.add(pid)->second.getUid();
 }
 
 // caller must free character string
 char *LogStatistics::pidToName(pid_t pid) {
-    const char *name = pidTable.entryAt(pidTable.add(pid)).getName();
+    const char *name = pidTable.add(pid)->second.getName();
     if (!name) {
         return NULL;
     }
diff --git a/logd/LogStatistics.h b/logd/LogStatistics.h
index f60f3ed..b9e9650 100644
--- a/logd/LogStatistics.h
+++ b/logd/LogStatistics.h
@@ -21,8 +21,9 @@
 #include <stdlib.h>
 #include <sys/types.h>
 
+#include <unordered_map>
+
 #include <log/log.h>
-#include <utils/BasicHashtable.h>
 
 #include "LogBufferElement.h"
 
@@ -30,8 +31,14 @@
     for (log_id_t i = LOG_ID_MIN; i < LOG_ID_MAX; i = (log_id_t) (i + 1))
 
 template <typename TKey, typename TEntry>
-class LogHashtable : public android::BasicHashtable<TKey, TEntry> {
+class LogHashtable {
+
+    std::unordered_map<TKey, TEntry> map;
+
 public:
+
+    typedef typename std::unordered_map<TKey, TEntry>::iterator iterator;
+
     std::unique_ptr<const TEntry *[]> sort(size_t n) {
         if (!n) {
             std::unique_ptr<const TEntry *[]> sorted(NULL);
@@ -41,9 +48,8 @@
         const TEntry **retval = new const TEntry* [n];
         memset(retval, 0, sizeof(*retval) * n);
 
-        ssize_t index = -1;
-        while ((index = android::BasicHashtable<TKey, TEntry>::next(index)) >= 0) {
-            const TEntry &entry = android::BasicHashtable<TKey, TEntry>::entryAt(index);
+        for(iterator it = map.begin(); it != map.end(); ++it) {
+            const TEntry &entry = it->second;
             size_t s = entry.getSizes();
             ssize_t i = n - 1;
             while ((!retval[i] || (s > retval[i]->getSizes())) && (--i >= 0))
@@ -70,45 +76,43 @@
         return index;
     }
 
-    ssize_t next(ssize_t index) {
-        return android::BasicHashtable<TKey, TEntry>::next(index);
+    inline iterator add(TKey key, LogBufferElement *e) {
+        iterator it = map.find(key);
+        if (it == map.end()) {
+            it = map.insert(std::make_pair(key, TEntry(e))).first;
+        } else {
+            it->second.add(e);
+        }
+        return it;
     }
 
-    size_t add(TKey key, LogBufferElement *e) {
-        android::hash_t hash = android::hash_type(key);
-        ssize_t index = android::BasicHashtable<TKey, TEntry>::find(-1, hash, key);
-        if (index == -1) {
-            return android::BasicHashtable<TKey, TEntry>::add(hash, TEntry(e));
+    inline iterator add(TKey key) {
+        iterator it = map.find(key);
+        if (it == map.end()) {
+            it = map.insert(std::make_pair(key, TEntry(key))).first;
+        } else {
+            it->second.add(key);
         }
-        android::BasicHashtable<TKey, TEntry>::editEntryAt(index).add(e);
-        return index;
-    }
-
-    inline size_t add(TKey key) {
-        android::hash_t hash = android::hash_type(key);
-        ssize_t index = android::BasicHashtable<TKey, TEntry>::find(-1, hash, key);
-        if (index == -1) {
-            return android::BasicHashtable<TKey, TEntry>::add(hash, TEntry(key));
-        }
-        android::BasicHashtable<TKey, TEntry>::editEntryAt(index).add(key);
-        return index;
+        return it;
     }
 
     void subtract(TKey key, LogBufferElement *e) {
-        ssize_t index = android::BasicHashtable<TKey, TEntry>::find(-1, android::hash_type(key), key);
-        if ((index != -1)
-         && android::BasicHashtable<TKey, TEntry>::editEntryAt(index).subtract(e)) {
-            android::BasicHashtable<TKey, TEntry>::removeAt(index);
+        iterator it = map.find(key);
+        if ((it != map.end()) && it->second.subtract(e)) {
+            map.erase(it);
         }
     }
 
     inline void drop(TKey key, LogBufferElement *e) {
-        ssize_t index = android::BasicHashtable<TKey, TEntry>::find(-1, android::hash_type(key), key);
-        if (index != -1) {
-            android::BasicHashtable<TKey, TEntry>::editEntryAt(index).drop(e);
+        iterator it = map.find(key);
+        if (it != map.end()) {
+            it->second.drop(e);
         }
     }
 
+    inline iterator begin() { return map.begin(); }
+    inline iterator end() { return map.end(); }
+
 };
 
 struct EntryBase {
diff --git a/logd/README.property b/logd/README.property
index ad7d0cd..a472efd 100644
--- a/logd/README.property
+++ b/logd/README.property
@@ -10,6 +10,8 @@
                                          default false
 ro.build.type               string       if user, logd.statistics & logd.klogd
                                          default false
+persist.logd.logpersistd    string       Enable logpersist daemon, "logcatd"
+                                         turns on logcat -f in logd context
 persist.logd.size          number 256K   default size of the buffer for all
                                          log ids at initial startup, at runtime
                                          use: logcat -b all -G <value>
diff --git a/logd/logpersist b/logd/logpersist
new file mode 100755
index 0000000..215e1e2
--- /dev/null
+++ b/logd/logpersist
@@ -0,0 +1,36 @@
+#! /system/bin/sh
+# logpersist cat start and stop handlers
+data=/data/misc/logd
+property=persist.logd.logpersistd
+service=logcatd
+progname="${0##*/}"
+if [ X"${1}" = "-h" -o X"${1}" = X"--help" ]; then
+  echo "${progname%.*}.cat            - dump current ${service%d} logs"
+  echo "${progname%.*}.start          - start ${service} service"
+  echo "${progname%.*}.stop [--clear] - stop ${service} service"
+  exit 0
+fi
+case ${progname} in
+*.cat)
+  su 1036 ls "${data}" |
+  tr -d '\r' |
+  sort -ru |
+  sed "s#^#${data}/#" |
+  su 1036 xargs cat
+  ;;
+*.start)
+  su 0 setprop ${property} ${service}
+  getprop ${property}
+  sleep 1
+  ps -t | grep "${data##*/}.*${service%d}"
+  ;;
+*.stop)
+  su 0 stop ${service}
+  su 0 setprop ${property} ""
+  [ X"${1}" != X"-c" -a X"${1}" != X"--clear" ] ||
+  ( sleep 1 ; su 1036,9998 rm -rf "${data}" )
+  ;;
+*)
+  echo "Unexpected command ${0##*/} ${@}" >&2
+  exit 1
+esac
diff --git a/rootdir/init.rc b/rootdir/init.rc
index f1c0a01..2fbac23 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -328,7 +328,7 @@
     restorecon_recursive /data
 
     # Check any timezone data in /data is newer than the copy in /system, delete if not.
-    exec u:r:tzdatacheck:s0 system system -- /system/bin/tzdatacheck /system/usr/share/zoneinfo /data/misc/zoneinfo
+    exec - system system -- /system/bin/tzdatacheck /system/usr/share/zoneinfo /data/misc/zoneinfo
 
     # If there is no fs-post-data action in the init.<device>.rc file, you
     # must uncomment this line, otherwise encrypted filesystems
@@ -669,3 +669,17 @@
     class late_start
     user root
     oneshot
+
+on property:persist.logd.logpersistd=logcatd
+    # all exec/services are called with umask(077), so no gain beyond 0700
+    mkdir /data/misc/logd 0700 logd log
+    # logd for write to /data/misc/logd, log group for read from pstore (-L)
+    exec - logd log -- /system/bin/logcat -L -b all -v threadtime -v usec -v printable -D -f /data/misc/logd/logcat -r 64 -n 256
+    start logcatd
+
+service logcatd /system/bin/logcat -b all -v threadtime -v usec -v printable -D -f /data/misc/logd/logcat -r 64 -n 256
+    class late_start
+    disabled
+    # logd for write to /data/misc/logd, log group for read from log daemon
+    user logd
+    group log
diff --git a/rootdir/init.trace.rc b/rootdir/init.trace.rc
index cd8d350..50944e6 100644
--- a/rootdir/init.trace.rc
+++ b/rootdir/init.trace.rc
@@ -1,6 +1,6 @@
 ## Permissions to allow system-wide tracing to the kernel trace buffer.
 ##
-on early-boot
+on boot
 
 # Allow writing to the kernel trace log.
     chmod 0222 /sys/kernel/debug/tracing/trace_marker