Merge "Remove unused klog_get_level."
diff --git a/Android.bp b/Android.bp
index b44c296..c6f6251 100644
--- a/Android.bp
+++ b/Android.bp
@@ -1 +1,6 @@
+filegroup {
+    name: "android_filesystem_config_header",
+    srcs: ["include/private/android_filesystem_config.h"],
+}
+
 subdirs = ["*"]
diff --git a/adb/Android.mk b/adb/Android.mk
index 49e492c..2f5a2ee 100644
--- a/adb/Android.mk
+++ b/adb/Android.mk
@@ -343,6 +343,6 @@
     libcrypto_utils \
     libcrypto \
     libminijail \
-    libdebuggerd_client \
+    libdebuggerd_handler \
 
 include $(BUILD_EXECUTABLE)
diff --git a/adb/adb_auth_host.cpp b/adb/adb_auth_host.cpp
index ec9b1c3..c3f1fe0 100644
--- a/adb/adb_auth_host.cpp
+++ b/adb/adb_auth_host.cpp
@@ -388,7 +388,13 @@
 
 static void adb_auth_inotify_init(const std::set<std::string>& paths) {
     LOG(INFO) << "adb_auth_inotify_init...";
+
     int infd = inotify_init1(IN_CLOEXEC | IN_NONBLOCK);
+    if (infd < 0) {
+        PLOG(ERROR) << "failed to create inotify fd";
+        return;
+    }
+
     for (const std::string& path : paths) {
         int wd = inotify_add_watch(infd, path.c_str(), IN_CREATE | IN_MOVED_TO);
         if (wd < 0) {
diff --git a/adb/commandline.cpp b/adb/commandline.cpp
index a064de2..2befa0c 100644
--- a/adb/commandline.cpp
+++ b/adb/commandline.cpp
@@ -91,9 +91,9 @@
         "global options:\n"
         " -a         listen on all network interfaces, not just localhost\n"
         " -d         use USB device (error if multiple devices connected)\n"
-        " -e         use emulator (error if multiple emulators running)\n"
+        " -e         use TCP/IP device (error if multiple TCP/IP devices available)\n"
         " -s SERIAL\n"
-        "     use device/emulator with given serial number (overrides $ANDROID_SERIAL)\n"
+        "     use device with given serial number (overrides $ANDROID_SERIAL)\n"
         " -p PRODUCT\n"
         "     name or path ('angler'/'out/target/product/angler');\n"
         "     default $ANDROID_PRODUCT_OUT\n"
diff --git a/adb/daemon/main.cpp b/adb/daemon/main.cpp
index 1a39f6a..6382b67 100644
--- a/adb/daemon/main.cpp
+++ b/adb/daemon/main.cpp
@@ -34,9 +34,9 @@
 #include <libminijail.h>
 #include <scoped_minijail.h>
 
-#include "debuggerd/client.h"
 #include <private/android_filesystem_config.h>
 #include <private/android_logger.h>
+#include "debuggerd/handler.h"
 #include "selinux/android.h"
 
 #include "adb.h"
diff --git a/base/include/android-base/strings.h b/base/include/android-base/strings.h
index b8a9289..f5f5c11 100644
--- a/base/include/android-base/strings.h
+++ b/base/include/android-base/strings.h
@@ -64,6 +64,9 @@
 bool EndsWith(const std::string& s, const char* suffix);
 bool EndsWithIgnoreCase(const std::string& s, const char* suffix);
 
+// Tests whether 'lhs' equals 'rhs', ignoring case.
+bool EqualsIgnoreCase(const std::string& lhs, const std::string& rhs);
+
 }  // namespace base
 }  // namespace android
 
diff --git a/base/strings.cpp b/base/strings.cpp
index 7a94ad7..46fe939 100644
--- a/base/strings.cpp
+++ b/base/strings.cpp
@@ -112,5 +112,9 @@
   return EndsWith(s, suffix, false);
 }
 
+bool EqualsIgnoreCase(const std::string& lhs, const std::string& rhs) {
+  return strcasecmp(lhs.c_str(), rhs.c_str()) == 0;
+}
+
 }  // namespace base
 }  // namespace android
diff --git a/base/strings_test.cpp b/base/strings_test.cpp
index 5fb21dd..7a65a00 100644
--- a/base/strings_test.cpp
+++ b/base/strings_test.cpp
@@ -244,3 +244,10 @@
   ASSERT_FALSE(android::base::EndsWithIgnoreCase("foobar", "OBA"));
   ASSERT_FALSE(android::base::EndsWithIgnoreCase("foobar", "FOO"));
 }
+
+TEST(strings, EqualsIgnoreCase) {
+  ASSERT_TRUE(android::base::EqualsIgnoreCase("foo", "FOO"));
+  ASSERT_TRUE(android::base::EqualsIgnoreCase("FOO", "foo"));
+  ASSERT_FALSE(android::base::EqualsIgnoreCase("foo", "bar"));
+  ASSERT_FALSE(android::base::EqualsIgnoreCase("foo", "fool"));
+}
diff --git a/bootstat/boot_event_record_store.cpp b/bootstat/boot_event_record_store.cpp
index 78be944..f902af3 100644
--- a/bootstat/boot_event_record_store.cpp
+++ b/bootstat/boot_event_record_store.cpp
@@ -46,24 +46,6 @@
 
   *uptime = file_stat.st_mtime;
 
-  // The following code (till function exit) is a debug test to ensure the
-  // validity of the file mtime value, i.e., to check that the record file
-  // mtime values are not changed once set.
-  // TODO(jhawkins): Remove this code.
-  std::string content;
-  if (!android::base::ReadFileToString(path, &content)) {
-    PLOG(ERROR) << "Failed to read " << path;
-    return false;
-  }
-
-  // Ignore existing bootstat records (which do not contain file content).
-  if (!content.empty()) {
-    int32_t value;
-    if (android::base::ParseInt(content, &value)) {
-      bootstat::LogHistogram("bootstat_mtime_matches_content", value == *uptime);
-    }
-  }
-
   return true;
 }
 
@@ -89,16 +71,6 @@
     return;
   }
 
-  // Writing the value as content in the record file is a debug measure to
-  // ensure the validity of the file mtime value, i.e., to check that the record
-  // file mtime values are not changed once set.
-  // TODO(jhawkins): Remove this block.
-  if (!android::base::WriteStringToFd(std::to_string(value), record_fd)) {
-    PLOG(ERROR) << "Failed to write value to " << record_path;
-    close(record_fd);
-    return;
-  }
-
   // Fill out the stat structure for |record_path| in order to get the atime to
   // set in the utime() call.
   struct stat file_stat;
diff --git a/bootstat/boot_event_record_store_test.cpp b/bootstat/boot_event_record_store_test.cpp
index 01c2cc1..90f6513 100644
--- a/bootstat/boot_event_record_store_test.cpp
+++ b/bootstat/boot_event_record_store_test.cpp
@@ -45,14 +45,6 @@
     return false;
   }
 
-  // Writing the value as content in the record file is a debug measure to
-  // ensure the validity of the file mtime value, i.e., to check that the record
-  // file mtime values are not changed once set.
-  // TODO(jhawkins): Remove this block.
-  if (!android::base::WriteStringToFd(std::to_string(value), record_fd)) {
-    return false;
-  }
-
   // Set the |mtime| of the file to store the value of the boot event while
   // preserving the |atime|.
   struct timeval atime = {/* tv_sec */ 0, /* tv_usec */ 0};
diff --git a/bootstat/bootstat.rc b/bootstat/bootstat.rc
index c96e996..ca8bea2 100644
--- a/bootstat/bootstat.rc
+++ b/bootstat/bootstat.rc
@@ -11,8 +11,26 @@
 on post-fs-data && property:init.svc.bootanim=running
     exec - root root -- /system/bin/bootstat -r post_decrypt_time_elapsed
 
+# sys.logbootcomplete is a signal to enable the bootstat logging mechanism.
+# This signaling is necessary to prevent logging boot metrics after a soft
+# reboot (e.g., adb shell stop && adb shell start).  /proc/uptime is not reset
+# during a soft reboot, which leads to false boot time metrics being reported.
+#
+# The 'on boot' event occurs once per hard boot (device power on), which
+# switches the flag on. If the device performs a soft reboot, the flag is
+# switched off and cannot be switched on until the device hard boots again.
+
+# Enable bootstat logging on boot.
+on boot
+    setprop sys.logbootcomplete 1
+
+# Disable further bootstat logging on a soft reboot. A soft reboot is
+# signaled by the zygote stopping.
+on property:init.svc.zygote=stopping
+    setprop sys.logbootcomplete 0
+
 # Record boot complete metrics.
-on property:sys.boot_completed=1
+on property:sys.boot_completed=1 && property:sys.logbootcomplete=1
     # Record boot_complete and related stats (decryption, etc).
     exec - root root -- /system/bin/bootstat --record_boot_complete
 
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index f0131b8..6dc6675 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -1,25 +1,197 @@
+cc_defaults {
+    name: "debuggerd_defaults",
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Wno-error",
+        "-Wno-nullability-completeness",
+        "-Os",
+    ],
+
+    local_include_dirs: ["include"],
+}
+
 cc_library_static {
-  name: "libdebuggerd_client",
-  srcs: ["client/debuggerd_client.cpp"],
+    name: "libdebuggerd_handler",
+    defaults: ["debuggerd_defaults"],
+    srcs: ["handler/debuggerd_handler.cpp"],
 
-  cflags: [
-    "-Wall",
-    "-Wextra",
-    "-Werror",
-    "-Os",
-  ],
+    // libdebuggerd_handler gets async signal safe logging via libc_logging,
+    // which defines its interface in bionic private headers.
+    include_dirs: ["bionic/libc"],
+    static_libs: ["libc_logging"],
 
-  target: {
-    android64: {
-      cflags: ["-DTARGET_IS_64_BIT"],
+    export_include_dirs: ["include"],
+}
+
+cc_library {
+    name: "libdebuggerd_client",
+    defaults: ["debuggerd_defaults"],
+    srcs: [
+        "client/debuggerd_client.cpp",
+        "util.cpp",
+    ],
+
+    shared_libs: [
+        "libbase",
+        "libcutils",
+    ],
+    export_include_dirs: ["include"],
+}
+
+cc_library {
+    name: "libdebuggerd",
+    defaults: ["debuggerd_defaults"],
+
+    srcs: [
+        "libdebuggerd/backtrace.cpp",
+        "libdebuggerd/elf_utils.cpp",
+        "libdebuggerd/open_files_list.cpp",
+        "libdebuggerd/tombstone.cpp",
+        "libdebuggerd/utility.cpp",
+    ],
+
+    target: {
+        android_arm: {
+            srcs: ["libdebuggerd/arm/machine.cpp"],
+        },
+        android_arm64: {
+            srcs: ["libdebuggerd/arm64/machine.cpp"],
+        },
+        android_mips: {
+            srcs: ["libdebuggerd/mips/machine.cpp"],
+        },
+        android_mips64: {
+            srcs: ["libdebuggerd/mips64/machine.cpp"],
+        },
+        android_x86: {
+            srcs: ["libdebuggerd/x86/machine.cpp"],
+        },
+        android_x86_64: {
+            srcs: ["libdebuggerd/x86_64/machine.cpp"],
+        },
     },
-  },
 
-  local_include_dirs: ["include"],
-  export_include_dirs: ["include"],
+    local_include_dirs: ["libdebuggerd/include"],
+    export_include_dirs: ["libdebuggerd/include"],
 
-  // libdebuggerd_client gets async signal safe logging via libc_logging,
-  // which defines its interface in bionic private headers.
-  include_dirs: ["bionic/libc"],
-  static_libs: ["libc_logging"],
+    shared_libs: [
+        "libbacktrace",
+        "libbase",
+        "libcutils",
+        "liblog",
+    ],
+}
+
+cc_test {
+    name: "debuggerd_test",
+    defaults: ["debuggerd_defaults"],
+
+    cflags: ["-Wno-missing-field-initializers"],
+    srcs: [
+        "libdebuggerd/test/dump_memory_test.cpp",
+        "libdebuggerd/test/elf_fake.cpp",
+        "libdebuggerd/test/log_fake.cpp",
+        "libdebuggerd/test/open_files_list_test.cpp",
+        "libdebuggerd/test/property_fake.cpp",
+        "libdebuggerd/test/ptrace_fake.cpp",
+        "libdebuggerd/test/tombstone_test.cpp",
+    ],
+
+    target: {
+        android: {
+            srcs: [
+                "debuggerd_test.cpp",
+                "util.cpp"
+            ],
+        },
+    },
+
+    shared_libs: [
+        "libbacktrace",
+        "libbase",
+        "libcutils",
+    ],
+
+    static_libs: [
+        "libdebuggerd"
+    ],
+
+    local_include_dirs: [
+        "libdebuggerd",
+    ],
+
+    compile_multilib: "both",
+    multilib: {
+        lib32: {
+            stem: "debuggerd_test32",
+        },
+        lib64: {
+            stem: "debuggerd_test64",
+        },
+    },
+}
+
+cc_binary {
+    name: "crash_dump",
+    srcs: [
+        "crash_dump.cpp",
+        "util.cpp",
+    ],
+    defaults: ["debuggerd_defaults"],
+
+    compile_multilib: "both",
+    multilib: {
+        lib32: {
+            suffix: "32",
+        },
+        lib64: {
+            suffix: "64",
+        },
+    },
+
+    shared_libs: [
+        "libbacktrace",
+        "libbase",
+        "libdebuggerd",
+        "liblog",
+        "libprocinfo",
+        "libselinux",
+    ],
+}
+
+cc_binary {
+    name: "debuggerd",
+    srcs: [
+        "debuggerd.cpp",
+    ],
+    defaults: ["debuggerd_defaults"],
+
+    shared_libs: [
+        "libbase",
+        "libdebuggerd_client",
+        "liblog",
+        "libselinux",
+    ],
+
+    local_include_dirs: ["include"],
+}
+
+cc_binary {
+    name: "tombstoned",
+    srcs: [
+        "util.cpp",
+        "tombstoned/intercept_manager.cpp",
+        "tombstoned/tombstoned.cpp",
+    ],
+    defaults: ["debuggerd_defaults"],
+
+    static_libs: [
+        "libbase",
+        "libcutils",
+        "libevent",
+        "liblog",
+    ],
+
+    init_rc: ["tombstoned/tombstoned.rc"]
 }
diff --git a/debuggerd/Android.mk b/debuggerd/Android.mk
deleted file mode 100644
index e3bdd43..0000000
--- a/debuggerd/Android.mk
+++ /dev/null
@@ -1,167 +0,0 @@
-LOCAL_PATH := $(call my-dir)
-
-common_cppflags := \
-    -W \
-    -Wall \
-    -Wextra \
-    -Wunused \
-    -Werror \
-
-ifeq ($(TARGET_IS_64_BIT),true)
-common_cppflags += -DTARGET_IS_64_BIT
-endif
-
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
-    backtrace.cpp \
-    debuggerd.cpp \
-    elf_utils.cpp \
-    getevent.cpp \
-    open_files_list.cpp \
-    signal_sender.cpp \
-    tombstone.cpp \
-    utility.cpp \
-
-LOCAL_SRC_FILES_arm    := arm/machine.cpp
-LOCAL_SRC_FILES_arm64  := arm64/machine.cpp
-LOCAL_SRC_FILES_mips   := mips/machine.cpp
-LOCAL_SRC_FILES_mips64 := mips64/machine.cpp
-LOCAL_SRC_FILES_x86    := x86/machine.cpp
-LOCAL_SRC_FILES_x86_64 := x86_64/machine.cpp
-
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-LOCAL_CPPFLAGS := $(common_cppflags)
-
-LOCAL_INIT_RC_32 := debuggerd.rc
-LOCAL_INIT_RC_64 := debuggerd64.rc
-
-LOCAL_SHARED_LIBRARIES := \
-    libbacktrace \
-    libbase \
-    libcutils \
-    liblog \
-    libselinux \
-
-LOCAL_CLANG := true
-
-LOCAL_MODULE := debuggerd
-LOCAL_MODULE_STEM_32 := debuggerd
-LOCAL_MODULE_STEM_64 := debuggerd64
-LOCAL_MULTILIB := both
-
-include $(BUILD_EXECUTABLE)
-
-crasher_cppflags := $(common_cppflags) -O0 -fstack-protector-all -Wno-free-nonheap-object
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := crasher.cpp
-LOCAL_SRC_FILES_arm    := arm/crashglue.S
-LOCAL_SRC_FILES_arm64  := arm64/crashglue.S
-LOCAL_SRC_FILES_mips   := mips/crashglue.S
-LOCAL_SRC_FILES_mips64 := mips64/crashglue.S
-LOCAL_SRC_FILES_x86    := x86/crashglue.S
-LOCAL_SRC_FILES_x86_64 := x86_64/crashglue.S
-LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
-LOCAL_MODULE_TAGS := optional
-LOCAL_CPPFLAGS := $(crasher_cppflags)
-LOCAL_SHARED_LIBRARIES := libbase liblog
-
-# The arm emulator has VFP but not VFPv3-D32.
-ifeq ($(ARCH_ARM_HAVE_VFP_D32),true)
-LOCAL_ASFLAGS_arm += -DHAS_VFP_D32
-endif
-
-LOCAL_MODULE := crasher
-LOCAL_MODULE_STEM_32 := crasher
-LOCAL_MODULE_STEM_64 := crasher64
-LOCAL_MULTILIB := both
-
-include $(BUILD_EXECUTABLE)
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := crasher.cpp
-LOCAL_SRC_FILES_arm    := arm/crashglue.S
-LOCAL_SRC_FILES_arm64  := arm64/crashglue.S
-LOCAL_SRC_FILES_mips   := mips/crashglue.S
-LOCAL_SRC_FILES_mips64 := mips64/crashglue.S
-LOCAL_SRC_FILES_x86    := x86/crashglue.S
-LOCAL_SRC_FILES_x86_64 := x86_64/crashglue.S
-LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
-LOCAL_MODULE_TAGS := optional
-LOCAL_CPPFLAGS := $(crasher_cppflags) -DSTATIC_CRASHER
-LOCAL_FORCE_STATIC_EXECUTABLE := true
-
-# The arm emulator has VFP but not VFPv3-D32.
-ifeq ($(ARCH_ARM_HAVE_VFP_D32),true)
-LOCAL_ASFLAGS_arm += -DHAS_VFP_D32
-endif
-
-LOCAL_MODULE := static_crasher
-LOCAL_MODULE_STEM_32 := static_crasher
-LOCAL_MODULE_STEM_64 := static_crasher64
-LOCAL_MULTILIB := both
-
-LOCAL_STATIC_LIBRARIES := libdebuggerd_client libbase liblog
-
-include $(BUILD_EXECUTABLE)
-
-debuggerd_test_src_files := \
-    utility.cpp \
-    open_files_list.cpp \
-    test/dump_memory_test.cpp \
-    test/elf_fake.cpp \
-    test/log_fake.cpp \
-    test/open_files_list_test.cpp \
-    test/property_fake.cpp \
-    test/ptrace_fake.cpp \
-    test/tombstone_test.cpp \
-    test/selinux_fake.cpp \
-
-debuggerd_shared_libraries := \
-    libbacktrace \
-    libbase \
-    libcutils \
-    liblog
-
-debuggerd_c_includes := \
-    $(LOCAL_PATH)/test \
-
-debuggerd_cpp_flags := \
-    $(common_cppflags) \
-    -Wno-missing-field-initializers \
-    -fno-rtti \
-
-# Bug: http://b/29823425 Disable -Wvarargs for Clang update to r271374
-debuggerd_cpp_flags += -Wno-varargs
-
-# 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_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_NATIVE_TEST)
diff --git a/debuggerd/client/debuggerd_client.cpp b/debuggerd/client/debuggerd_client.cpp
index c67d747..81d70df 100644
--- a/debuggerd/client/debuggerd_client.cpp
+++ b/debuggerd/client/debuggerd_client.cpp
@@ -1,379 +1,189 @@
 /*
- * Copyright (C) 2008 The Android Open Source Project
- * All rights reserved.
+ * Copyright 2016, The Android Open Source Project
  *
- * 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.
+ * 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
  *
- * 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.
+ *     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 "debuggerd/client.h"
+#include <debuggerd/client.h>
 
-#include <errno.h>
-#include <inttypes.h>
-#include <pthread.h>
-#include <sched.h>
+#include <fcntl.h>
 #include <signal.h>
-#include <stddef.h>
-#include <stdio.h>
 #include <stdlib.h>
-#include <string.h>
-#include <sys/mman.h>
-#include <sys/prctl.h>
-#include <sys/socket.h>
-#include <sys/syscall.h>
-#include <sys/un.h>
-#include <sys/wait.h>
+#include <sys/stat.h>
+#include <sys/types.h>
 #include <unistd.h>
 
-#include "private/libc_logging.h"
+#include <chrono>
 
-#if defined(TARGET_IS_64_BIT) && !defined(__LP64__)
-#define SOCKET_NAME "android:debuggerd32"
-#else
-#define SOCKET_NAME "android:debuggerd"
-#endif
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
+#include <cutils/sockets.h>
+#include <debuggerd/handler.h>
+#include <debuggerd/protocol.h>
+#include <debuggerd/util.h>
 
-// see man(2) prctl, specifically the section about PR_GET_NAME
-#define MAX_TASK_NAME_LEN (16)
+using android::base::unique_fd;
 
-static debuggerd_callbacks_t g_callbacks;
-
-// Don't use __libc_fatal because it exits via abort, which might put us back into a signal handler.
-#define fatal(...)                                             \
-  do {                                                         \
-    __libc_format_log(ANDROID_LOG_FATAL, "libc", __VA_ARGS__); \
-    _exit(1);                                                  \
-  } while (0)
-
-static int socket_abstract_client(const char* name, int type) {
-  sockaddr_un addr;
-
-  // Test with length +1 for the *initial* '\0'.
-  size_t namelen = strlen(name);
-  if ((namelen + 1) > sizeof(addr.sun_path)) {
-    errno = EINVAL;
-    return -1;
-  }
-
-  // This is used for abstract socket namespace, we need
-  // an initial '\0' at the start of the Unix socket path.
-  //
-  // Note: The path in this case is *not* supposed to be
-  // '\0'-terminated. ("man 7 unix" for the gory details.)
-  memset(&addr, 0, sizeof(addr));
-  addr.sun_family = AF_LOCAL;
-  addr.sun_path[0] = 0;
-  memcpy(addr.sun_path + 1, name, namelen);
-
-  socklen_t alen = namelen + offsetof(sockaddr_un, sun_path) + 1;
-
-  int s = socket(AF_LOCAL, type, 0);
-  if (s == -1) {
-    return -1;
-  }
-
-  int rc = TEMP_FAILURE_RETRY(connect(s, reinterpret_cast<sockaddr*>(&addr), alen));
-  if (rc == -1) {
-    close(s);
-    return -1;
-  }
-
-  return s;
-}
-
-/*
- * Writes a summary of the signal to the log file.  We do this so that, if
- * for some reason we're not able to contact debuggerd, there is still some
- * indication of the failure in the log.
- *
- * We could be here as a result of native heap corruption, or while a
- * mutex is being held, so we don't want to use any libc functions that
- * could allocate memory or hold a lock.
- */
-static void log_signal_summary(int signum, const siginfo_t* info) {
-  const char* signal_name = "???";
-  bool has_address = false;
-  switch (signum) {
-    case SIGABRT:
-      signal_name = "SIGABRT";
-      break;
-    case SIGBUS:
-      signal_name = "SIGBUS";
-      has_address = true;
-      break;
-    case SIGFPE:
-      signal_name = "SIGFPE";
-      has_address = true;
-      break;
-    case SIGILL:
-      signal_name = "SIGILL";
-      has_address = true;
-      break;
-    case SIGSEGV:
-      signal_name = "SIGSEGV";
-      has_address = true;
-      break;
-#if defined(SIGSTKFLT)
-    case SIGSTKFLT:
-      signal_name = "SIGSTKFLT";
-      break;
-#endif
-    case SIGSYS:
-      signal_name = "SIGSYS";
-      break;
-    case SIGTRAP:
-      signal_name = "SIGTRAP";
-      break;
-  }
-
-  char thread_name[MAX_TASK_NAME_LEN + 1];  // one more for termination
-  if (prctl(PR_GET_NAME, reinterpret_cast<unsigned long>(thread_name), 0, 0, 0) != 0) {
-    strcpy(thread_name, "<name unknown>");
-  } else {
-    // short names are null terminated by prctl, but the man page
-    // implies that 16 byte names are not.
-    thread_name[MAX_TASK_NAME_LEN] = 0;
-  }
-
-  // "info" will be null if the siginfo_t information was not available.
-  // Many signals don't have an address or a code.
-  char code_desc[32];  // ", code -6"
-  char addr_desc[32];  // ", fault addr 0x1234"
-  addr_desc[0] = code_desc[0] = 0;
-  if (info != nullptr) {
-    // For a rethrown signal, this si_code will be right and the one debuggerd shows will
-    // always be SI_TKILL.
-    __libc_format_buffer(code_desc, sizeof(code_desc), ", code %d", info->si_code);
-    if (has_address) {
-      __libc_format_buffer(addr_desc, sizeof(addr_desc), ", fault addr %p", info->si_addr);
-    }
-  }
-  __libc_format_log(ANDROID_LOG_FATAL, "libc", "Fatal signal %d (%s)%s%s in tid %d (%s)", signum,
-                    signal_name, code_desc, addr_desc, gettid(), thread_name);
-}
-
-/*
- * Returns true if the handler for signal "signum" has SA_SIGINFO set.
- */
-static bool have_siginfo(int signum) {
-  struct sigaction old_action, new_action;
-
-  memset(&new_action, 0, sizeof(new_action));
-  new_action.sa_handler = SIG_DFL;
-  new_action.sa_flags = SA_RESTART;
-  sigemptyset(&new_action.sa_mask);
-
-  if (sigaction(signum, &new_action, &old_action) < 0) {
-    __libc_format_log(ANDROID_LOG_WARN, "libc", "Failed testing for SA_SIGINFO: %s",
-                      strerror(errno));
+static bool send_signal(pid_t pid, bool backtrace) {
+  sigval val;
+  val.sival_int = backtrace;
+  if (sigqueue(pid, DEBUGGER_SIGNAL, val) != 0) {
+    PLOG(ERROR) << "libdebuggerd_client: failed to send signal to pid " << pid;
     return false;
   }
-  bool result = (old_action.sa_flags & SA_SIGINFO) != 0;
-
-  if (sigaction(signum, &old_action, nullptr) == -1) {
-    __libc_format_log(ANDROID_LOG_WARN, "libc", "Restore failed in test for SA_SIGINFO: %s",
-                      strerror(errno));
-  }
-  return result;
+  return true;
 }
 
-static void send_debuggerd_packet(pid_t crashing_tid, pid_t pseudothread_tid) {
-  // Mutex to prevent multiple crashing threads from trying to talk
-  // to debuggerd at the same time.
-  static pthread_mutex_t crash_mutex = PTHREAD_MUTEX_INITIALIZER;
-  int ret = pthread_mutex_trylock(&crash_mutex);
-  if (ret != 0) {
-    if (ret == EBUSY) {
-      __libc_format_log(ANDROID_LOG_INFO, "libc",
-                        "Another thread contacted debuggerd first; not contacting debuggerd.");
-      // This will never complete since the lock is never released.
-      pthread_mutex_lock(&crash_mutex);
-    } else {
-      __libc_format_log(ANDROID_LOG_INFO, "libc", "pthread_mutex_trylock failed: %s", strerror(ret));
+static bool check_dumpable(pid_t pid) {
+  // /proc/<pid> is owned by the effective UID of the process.
+  // Ownership of most of the other files in /proc/<pid> varies based on PR_SET_DUMPABLE.
+  // If PR_GET_DUMPABLE would return 0, they're owned by root, instead.
+  std::string proc_pid_path = android::base::StringPrintf("/proc/%d/", pid);
+  std::string proc_pid_status_path = proc_pid_path + "/status";
+
+  unique_fd proc_pid_fd(open(proc_pid_path.c_str(), O_DIRECTORY | O_RDONLY | O_CLOEXEC));
+  if (proc_pid_fd == -1) {
+    return false;
+  }
+  unique_fd proc_pid_status_fd(openat(proc_pid_fd, "status", O_RDONLY | O_CLOEXEC));
+  if (proc_pid_status_fd == -1) {
+    return false;
+  }
+
+  struct stat proc_pid_st;
+  struct stat proc_pid_status_st;
+  if (fstat(proc_pid_fd.get(), &proc_pid_st) != 0 ||
+      fstat(proc_pid_status_fd.get(), &proc_pid_status_st) != 0) {
+    return false;
+  }
+
+  // We can't figure out if a process is dumpable if its effective UID is root, but that's fine
+  // because being root bypasses the PR_SET_DUMPABLE check for ptrace.
+  if (proc_pid_st.st_uid == 0) {
+    return true;
+  }
+
+  if (proc_pid_status_st.st_uid == 0) {
+    return false;
+  }
+
+  return true;
+}
+
+bool debuggerd_trigger_dump(pid_t pid, unique_fd output_fd, DebuggerdDumpType dump_type,
+                            int timeout_ms) {
+  LOG(INFO) << "libdebuggerd_client: started dumping process " << pid;
+  unique_fd sockfd;
+  const auto end = std::chrono::steady_clock::now() + std::chrono::milliseconds(timeout_ms);
+  auto set_timeout = [timeout_ms, &sockfd, &end]() {
+    if (timeout_ms <= 0) {
+      return true;
     }
-    return;
-  }
 
-  int s = socket_abstract_client(SOCKET_NAME, SOCK_STREAM | SOCK_CLOEXEC);
-  if (s == -1) {
-    __libc_format_log(ANDROID_LOG_FATAL, "libc", "Unable to open connection to debuggerd: %s",
-                      strerror(errno));
-    return;
-  }
-
-  // debuggerd knows our pid from the credentials on the
-  // local socket but we need to tell it the tid of the crashing thread.
-  // debuggerd will be paranoid and verify that we sent a tid
-  // that's actually in our process.
-  debugger_msg_t msg;
-  msg.action = DEBUGGER_ACTION_CRASH;
-  msg.tid = crashing_tid;
-  msg.ignore_tid = pseudothread_tid;
-  msg.abort_msg_address = 0;
-
-  if (g_callbacks.get_abort_message) {
-    msg.abort_msg_address = reinterpret_cast<uintptr_t>(g_callbacks.get_abort_message());
-  }
-
-  ret = TEMP_FAILURE_RETRY(write(s, &msg, sizeof(msg)));
-  if (ret == sizeof(msg)) {
-    char debuggerd_ack;
-    ret = TEMP_FAILURE_RETRY(read(s, &debuggerd_ack, 1));
-    if (g_callbacks.post_dump) {
-      g_callbacks.post_dump();
+    auto now = std::chrono::steady_clock::now();
+    if (now > end) {
+      return false;
     }
-  } else {
-    // read or write failed -- broken connection?
-    __libc_format_log(ANDROID_LOG_FATAL, "libc", "Failed while talking to debuggerd: %s",
-                      strerror(errno));
-  }
 
-  close(s);
-}
+    auto time_left = std::chrono::duration_cast<std::chrono::microseconds>(end - now);
+    auto seconds = std::chrono::duration_cast<std::chrono::seconds>(time_left);
+    auto microseconds = std::chrono::duration_cast<std::chrono::microseconds>(time_left - seconds);
+    struct timeval timeout = {
+      .tv_sec = static_cast<long>(seconds.count()),
+      .tv_usec = static_cast<long>(microseconds.count()),
+    };
 
-struct debugger_thread_info {
-  pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
-  pid_t crashing_tid;
-  pid_t pseudothread_tid;
-  int signal_number;
-  siginfo_t* info;
-};
+    if (setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) != 0) {
+      return false;
+    }
+    if (setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)) != 0) {
+      return false;
+    }
 
-// Logging and contacting debuggerd requires free file descriptors, which we might not have.
-// Work around this by spawning a "thread" that shares its parent's address space, but not its file
-// descriptor table, so that we can close random file descriptors without affecting the original
-// process. Note that this doesn't go through pthread_create, so TLS is shared with the spawning
-// process.
-static void* pseudothread_stack;
-static int debuggerd_dispatch_pseudothread(void* arg) {
-  debugger_thread_info* thread_info = static_cast<debugger_thread_info*>(arg);
-
-  for (int i = 3; i < 1024; ++i) {
-    close(i);
-  }
-
-  log_signal_summary(thread_info->signal_number, thread_info->info);
-  send_debuggerd_packet(thread_info->crashing_tid, thread_info->pseudothread_tid);
-  pthread_mutex_unlock(&thread_info->mutex);
-  return 0;
-}
-
-/*
- * Catches fatal signals so we can ask debuggerd to ptrace us before
- * we crash.
- */
-static void debuggerd_signal_handler(int signal_number, siginfo_t* info, void*) {
-  // It's possible somebody cleared the SA_SIGINFO flag, which would mean
-  // our "info" arg holds an undefined value.
-  if (!have_siginfo(signal_number)) {
-    info = nullptr;
-  }
-
-  debugger_thread_info thread_info = {
-    .crashing_tid = gettid(),
-    .signal_number = signal_number,
-    .info = info
+    return true;
   };
 
-  pthread_mutex_lock(&thread_info.mutex);
-  pid_t child_pid = clone(debuggerd_dispatch_pseudothread, pseudothread_stack,
-                          CLONE_THREAD | CLONE_SIGHAND | CLONE_VM | CLONE_CHILD_SETTID,
-                          &thread_info, nullptr, nullptr, &thread_info.pseudothread_tid);
-
-  if (child_pid == -1) {
-    fatal("failed to spawn debuggerd dispatch thread: %s", strerror(errno));
+  if (!check_dumpable(pid)) {
+    dprintf(output_fd.get(), "target pid %d is not dumpable\n", pid);
+    return true;
   }
 
-  // Wait for the child to finish and unlock the mutex.
-  // This relies on bionic behavior that isn't guaranteed by the standard.
-  pthread_mutex_lock(&thread_info.mutex);
-
-
-  // We need to return from the signal handler so that debuggerd can dump the
-  // thread that crashed, but returning here does not guarantee that the signal
-  // will be thrown again, even for SIGSEGV and friends, since the signal could
-  // have been sent manually. Resend the signal with rt_tgsigqueueinfo(2) to
-  // preserve the SA_SIGINFO contents.
-  signal(signal_number, SIG_DFL);
-
-  struct siginfo si;
-  if (!info) {
-    memset(&si, 0, sizeof(si));
-    si.si_code = SI_USER;
-    si.si_pid = getpid();
-    si.si_uid = getuid();
-    info = &si;
-  } else if (info->si_code >= 0 || info->si_code == SI_TKILL) {
-    // rt_tgsigqueueinfo(2)'s documentation appears to be incorrect on kernels
-    // that contain commit 66dd34a (3.9+). The manpage claims to only allow
-    // negative si_code values that are not SI_TKILL, but 66dd34a changed the
-    // check to allow all si_code values in calls coming from inside the house.
+  sockfd.reset(socket(AF_LOCAL, SOCK_SEQPACKET, 0));
+  if (sockfd == -1) {
+    PLOG(ERROR) << "libdebugger_client: failed to create socket";
+    return false;
   }
 
-  int rc = syscall(SYS_rt_tgsigqueueinfo, getpid(), gettid(), signal_number, info);
-  if (rc != 0) {
-    fatal("failed to resend signal during crash: %s", strerror(errno));
+  if (!set_timeout()) {
+    PLOG(ERROR) << "libdebugger_client: failed to set timeout";
+    return false;
   }
+
+  if (socket_local_client_connect(sockfd.get(), kTombstonedInterceptSocketName,
+                                  ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_SEQPACKET) == -1) {
+    PLOG(ERROR) << "libdebuggerd_client: failed to connect to tombstoned";
+    return false;
+  }
+
+  InterceptRequest req = {.pid = pid };
+  if (!set_timeout()) {
+    PLOG(ERROR) << "libdebugger_client: failed to set timeout";
+  }
+
+  if (send_fd(sockfd.get(), &req, sizeof(req), std::move(output_fd)) != sizeof(req)) {
+    PLOG(ERROR) << "libdebuggerd_client: failed to send output fd to tombstoned";
+    return false;
+  }
+
+  bool backtrace = dump_type == kDebuggerdBacktrace;
+  send_signal(pid, backtrace);
+
+  if (!set_timeout()) {
+    PLOG(ERROR) << "libdebugger_client: failed to set timeout";
+  }
+
+  InterceptResponse response;
+  ssize_t rc = TEMP_FAILURE_RETRY(recv(sockfd.get(), &response, sizeof(response), MSG_TRUNC));
+  if (rc == 0) {
+    LOG(ERROR) << "libdebuggerd_client: failed to read response from tombstoned: timeout reached?";
+    return false;
+  } else if (rc != sizeof(response)) {
+    LOG(ERROR)
+      << "libdebuggerd_client: received packet of unexpected length from tombstoned: expected "
+      << sizeof(response) << ", received " << rc;
+    return false;
+  }
+
+  if (response.success != 1) {
+    response.error_message[sizeof(response.error_message) - 1] = '\0';
+    LOG(ERROR) << "libdebuggerd_client: tombstoned reported failure: " << response.error_message;
+  }
+
+  LOG(INFO) << "libdebuggerd_client: done dumping process " << pid;
+
+  return true;
 }
 
-void debuggerd_init(debuggerd_callbacks_t* callbacks) {
-  if (callbacks) {
-    g_callbacks = *callbacks;
+int dump_backtrace_to_file(pid_t tid, int fd) {
+  return dump_backtrace_to_file_timeout(tid, fd, 0);
+}
+
+int dump_backtrace_to_file_timeout(pid_t tid, int fd, int timeout_secs) {
+  android::base::unique_fd copy(dup(fd));
+  if (copy == -1) {
+    return -1;
   }
-
-  void* thread_stack_allocation =
-    mmap(nullptr, PAGE_SIZE * 3, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
-  if (thread_stack_allocation == MAP_FAILED) {
-    fatal("failed to allocate debuggerd thread stack");
-  }
-
-  char* stack = static_cast<char*>(thread_stack_allocation) + PAGE_SIZE;
-  if (mprotect(stack, PAGE_SIZE, PROT_READ | PROT_WRITE) != 0) {
-    fatal("failed to mprotect debuggerd thread stack");
-  }
-
-  // Stack grows negatively, set it to the last byte in the page...
-  stack = (stack + PAGE_SIZE - 1);
-  // and align it.
-  stack -= 15;
-  pseudothread_stack = stack;
-
-  struct sigaction action;
-  memset(&action, 0, sizeof(action));
-  sigemptyset(&action.sa_mask);
-  action.sa_sigaction = debuggerd_signal_handler;
-  action.sa_flags = SA_RESTART | SA_SIGINFO;
-
-  // Use the alternate signal stack if available so we can catch stack overflows.
-  action.sa_flags |= SA_ONSTACK;
-
-  sigaction(SIGABRT, &action, nullptr);
-  sigaction(SIGBUS, &action, nullptr);
-  sigaction(SIGFPE, &action, nullptr);
-  sigaction(SIGILL, &action, nullptr);
-  sigaction(SIGSEGV, &action, nullptr);
-#if defined(SIGSTKFLT)
-  sigaction(SIGSTKFLT, &action, nullptr);
-#endif
-  sigaction(SIGTRAP, &action, nullptr);
+  int timeout_ms = timeout_secs > 0 ? timeout_secs * 1000 : 0;
+  return debuggerd_trigger_dump(tid, std::move(copy), kDebuggerdBacktrace, timeout_ms) ? 0 : -1;
 }
diff --git a/debuggerd/crash_dump.cpp b/debuggerd/crash_dump.cpp
new file mode 100644
index 0000000..e4f88b0
--- /dev/null
+++ b/debuggerd/crash_dump.cpp
@@ -0,0 +1,403 @@
+/*
+ * Copyright 2016, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <arpa/inet.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <syscall.h>
+#include <sys/ptrace.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+#include <limits>
+#include <memory>
+#include <set>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/parseint.h>
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
+#include <cutils/sockets.h>
+#include <log/log.h>
+#include <procinfo/process.h>
+#include <selinux/selinux.h>
+
+#include "backtrace.h"
+#include "tombstone.h"
+#include "utility.h"
+
+#include "debuggerd/handler.h"
+#include "debuggerd/protocol.h"
+#include "debuggerd/util.h"
+
+using android::base::unique_fd;
+using android::base::StringPrintf;
+
+static bool pid_contains_tid(pid_t pid, pid_t tid) {
+  std::string task_path = StringPrintf("/proc/%d/task/%d", pid, tid);
+  return access(task_path.c_str(), F_OK) == 0;
+}
+
+// Attach to a thread, and verify that it's still a member of the given process
+static bool ptrace_seize_thread(pid_t pid, pid_t tid, std::string* error) {
+  if (ptrace(PTRACE_SEIZE, tid, 0, 0) != 0) {
+    *error = StringPrintf("failed to attach to thread %d: %s", tid, strerror(errno));
+    return false;
+  }
+
+  // Make sure that the task we attached to is actually part of the pid we're dumping.
+  if (!pid_contains_tid(pid, tid)) {
+    if (ptrace(PTRACE_DETACH, tid, 0, 0) != 0) {
+      PLOG(FATAL) << "failed to detach from thread " << tid;
+    }
+    *error = StringPrintf("thread %d is not in process %d", tid, pid);
+    return false;
+  }
+
+  // Put the task into ptrace-stop state.
+  if (ptrace(PTRACE_INTERRUPT, tid, 0, 0) != 0) {
+    PLOG(FATAL) << "failed to interrupt thread " << tid;
+  }
+
+  return true;
+}
+
+static bool activity_manager_notify(int pid, int signal, const std::string& amfd_data) {
+  android::base::unique_fd amfd(socket_local_client("/data/system/ndebugsocket", ANDROID_SOCKET_NAMESPACE_FILESYSTEM, SOCK_STREAM));
+  if (amfd.get() == -1) {
+    PLOG(ERROR) << "unable to connect to activity manager";
+    return false;
+  }
+
+  struct timeval tv = {
+    .tv_sec = 1,
+    .tv_usec = 0,
+  };
+  if (setsockopt(amfd.get(), SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) == -1) {
+    PLOG(ERROR) << "failed to set send timeout on activity manager socket";
+    return false;
+  }
+  tv.tv_sec = 3;  // 3 seconds on handshake read
+  if (setsockopt(amfd.get(), SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) == -1) {
+    PLOG(ERROR) << "failed to set receive timeout on activity manager socket";
+    return false;
+  }
+
+  // Activity Manager protocol: binary 32-bit network-byte-order ints for the
+  // pid and signal number, followed by the raw text of the dump, culminating
+  // in a zero byte that marks end-of-data.
+  uint32_t datum = htonl(pid);
+  if (!android::base::WriteFully(amfd, &datum, 4)) {
+    PLOG(ERROR) << "AM pid write failed";
+    return false;
+  }
+  datum = htonl(signal);
+  if (!android::base::WriteFully(amfd, &datum, 4)) {
+    PLOG(ERROR) << "AM signal write failed";
+    return false;
+  }
+  if (!android::base::WriteFully(amfd, amfd_data.c_str(), amfd_data.size() + 1)) {
+    PLOG(ERROR) << "AM data write failed";
+    return false;
+  }
+
+  // 3 sec timeout reading the ack; we're fine if the read fails.
+  char ack;
+  android::base::ReadFully(amfd, &ack, 1);
+  return true;
+}
+
+static bool tombstoned_connect(pid_t pid, unique_fd* tombstoned_socket, unique_fd* output_fd) {
+  unique_fd sockfd(socket_local_client(kTombstonedCrashSocketName,
+                                       ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_SEQPACKET));
+  if (sockfd == -1) {
+    PLOG(ERROR) << "failed to connect to tombstoned";
+    return false;
+  }
+
+  TombstonedCrashPacket packet = {};
+  packet.packet_type = CrashPacketType::kDumpRequest;
+  packet.packet.dump_request.pid = pid;
+  if (TEMP_FAILURE_RETRY(write(sockfd, &packet, sizeof(packet))) != sizeof(packet)) {
+    PLOG(ERROR) << "failed to write DumpRequest packet";
+    return false;
+  }
+
+  unique_fd tmp_output_fd;
+  ssize_t rc = recv_fd(sockfd, &packet, sizeof(packet), &tmp_output_fd);
+  if (rc == -1) {
+    PLOG(ERROR) << "failed to read response to DumpRequest packet";
+    return false;
+  } else if (rc != sizeof(packet)) {
+    LOG(ERROR) << "read DumpRequest response packet of incorrect length (expected "
+               << sizeof(packet) << ", got " << rc << ")";
+    return false;
+  }
+
+  *tombstoned_socket = std::move(sockfd);
+  *output_fd = std::move(tmp_output_fd);
+  return true;
+}
+
+static bool tombstoned_notify_completion(int tombstoned_socket) {
+  TombstonedCrashPacket packet = {};
+  packet.packet_type = CrashPacketType::kCompletedDump;
+  if (TEMP_FAILURE_RETRY(write(tombstoned_socket, &packet, sizeof(packet))) != sizeof(packet)) {
+    return false;
+  }
+  return true;
+}
+
+static void signal_handler(int) {
+  // We can't log easily, because the heap might be corrupt.
+  // Just die and let the surrounding log context explain things.
+  _exit(1);
+}
+
+static void abort_handler(pid_t target, const bool& tombstoned_connected,
+                          unique_fd& tombstoned_socket, unique_fd& output_fd,
+                          const char* abort_msg) {
+  // If we abort before we get an output fd, contact tombstoned to let any
+  // potential listeners know that we failed.
+  if (!tombstoned_connected) {
+    if (!tombstoned_connect(target, &tombstoned_socket, &output_fd)) {
+      // We failed to connect, not much we can do.
+      LOG(ERROR) << "failed to connected to tombstoned to report failure";
+      _exit(1);
+    }
+  }
+
+  dprintf(output_fd.get(), "crash_dump failed to dump process %d: %s\n", target, abort_msg);
+
+  _exit(1);
+}
+
+static void check_process(int proc_fd, pid_t expected_pid) {
+  android::procinfo::ProcessInfo proc_info;
+  if (!android::procinfo::GetProcessInfoFromProcPidFd(proc_fd, &proc_info)) {
+    LOG(FATAL) << "failed to fetch process info";
+  }
+
+  if (proc_info.pid != expected_pid) {
+    LOG(FATAL) << "pid mismatch: expected " << expected_pid << ", actual " << proc_info.ppid;
+  }
+}
+
+int main(int argc, char** argv) {
+  pid_t target = getppid();
+  bool tombstoned_connected = false;
+  unique_fd tombstoned_socket;
+  unique_fd output_fd;
+
+  android::base::InitLogging(argv);
+  android::base::SetAborter([&](const char* abort_msg) {
+    abort_handler(target, tombstoned_connected, tombstoned_socket, output_fd, abort_msg);
+  });
+
+  // Don't try to dump ourselves.
+  struct sigaction action = {};
+  action.sa_handler = signal_handler;
+  debuggerd_register_handlers(&action);
+
+  if (argc != 2) {
+    return 1;
+  }
+
+  pid_t main_tid;
+
+  if (target == 1) {
+    LOG(FATAL) << "target died before we could attach";
+  }
+
+  if (!android::base::ParseInt(argv[1], &main_tid, 1, std::numeric_limits<pid_t>::max())) {
+    LOG(FATAL) << "invalid main tid: " << argv[1];
+  }
+
+  android::procinfo::ProcessInfo target_info;
+  if (!android::procinfo::GetProcessInfo(main_tid, &target_info)) {
+    LOG(FATAL) << "failed to fetch process info for target " << main_tid;
+  }
+
+  if (main_tid != target_info.tid || target != target_info.pid) {
+    LOG(FATAL) << "target info mismatch, expected pid " << target << ", tid " << main_tid
+               << ", received pid " << target_info.pid << ", tid " << target_info.tid;
+  }
+
+  // Open /proc/`getppid()` in the original process, and pass it down to the forked child.
+  std::string target_proc_path = "/proc/" + std::to_string(target);
+  int target_proc_fd = open(target_proc_path.c_str(), O_DIRECTORY | O_RDONLY);
+  if (target_proc_fd == -1) {
+    PLOG(FATAL) << "failed to open " << target_proc_path;
+  }
+
+  // Reparent ourselves to init, so that the signal handler can waitpid on the
+  // original process to avoid leaving a zombie for non-fatal dumps.
+  pid_t forkpid = fork();
+  if (forkpid == -1) {
+    PLOG(FATAL) << "fork failed";
+  } else if (forkpid != 0) {
+    exit(0);
+  }
+
+  // Die if we take too long.
+  alarm(20);
+
+  check_process(target_proc_fd, target);
+
+  std::string attach_error;
+  if (!ptrace_seize_thread(target, main_tid, &attach_error)) {
+    LOG(FATAL) << attach_error;
+  }
+
+  check_process(target_proc_fd, target);
+
+  LOG(INFO) << "obtaining output fd from tombstoned";
+  tombstoned_connected = tombstoned_connect(target, &tombstoned_socket, &output_fd);
+
+  // Write a '\1' to stdout to tell the crashing process to resume.
+  if (TEMP_FAILURE_RETRY(write(STDOUT_FILENO, "\1", 1)) == -1) {
+    PLOG(ERROR) << "failed to communicate to target process";
+  }
+
+  if (tombstoned_connected) {
+    if (TEMP_FAILURE_RETRY(dup2(output_fd.get(), STDOUT_FILENO)) == -1) {
+      PLOG(ERROR) << "failed to dup2 output fd (" << output_fd.get() << ") to STDOUT_FILENO";
+    }
+  } else {
+    unique_fd devnull(TEMP_FAILURE_RETRY(open("/dev/null", O_RDWR)));
+    TEMP_FAILURE_RETRY(dup2(devnull.get(), STDOUT_FILENO));
+  }
+
+  LOG(INFO) << "performing dump of process " << target << " (target tid = " << main_tid << ")";
+
+  // At this point, the thread that made the request has been attached and is
+  // in ptrace-stopped state. After resumption, the triggering signal that has
+  // been queued will be delivered.
+  if (ptrace(PTRACE_CONT, main_tid, 0, 0) != 0) {
+    PLOG(ERROR) << "PTRACE_CONT(" << main_tid << ") failed";
+    exit(1);
+  }
+
+  siginfo_t siginfo = {};
+  if (!wait_for_signal(main_tid, &siginfo)) {
+    printf("failed to wait for signal in tid %d: %s\n", main_tid, strerror(errno));
+    exit(1);
+  }
+
+  int signo = siginfo.si_signo;
+  bool backtrace = false;
+  uintptr_t abort_address = 0;
+
+  // si_value can represent three things:
+  //   0: dump tombstone
+  //   1: dump backtrace
+  //   everything else: abort message address (implies dump tombstone)
+  if (siginfo.si_value.sival_int == 1) {
+    backtrace = true;
+  } else if (siginfo.si_value.sival_ptr != nullptr) {
+    abort_address = reinterpret_cast<uintptr_t>(siginfo.si_value.sival_ptr);
+  }
+
+  // Now that we have the signal that kicked things off, attach all of the
+  // sibling threads, and then proceed.
+  bool fatal_signal = signo != DEBUGGER_SIGNAL;
+  std::set<pid_t> siblings;
+  std::set<pid_t> attached_siblings;
+  if (fatal_signal) {
+    if (!android::procinfo::GetProcessTids(target, &siblings)) {
+      PLOG(FATAL) << "failed to get process siblings";
+    }
+    siblings.erase(main_tid);
+
+    for (pid_t sibling_tid : siblings) {
+      if (!ptrace_seize_thread(target, sibling_tid, &attach_error)) {
+        LOG(WARNING) << attach_error;
+      } else {
+        attached_siblings.insert(sibling_tid);
+      }
+    }
+  }
+
+  check_process(target_proc_fd, target);
+
+  // TODO: Use seccomp to lock ourselves down.
+
+  std::unique_ptr<BacktraceMap> backtrace_map(BacktraceMap::Create(main_tid));
+  std::string amfd_data;
+
+  if (backtrace) {
+    dump_backtrace(output_fd.get(), backtrace_map.get(), target, main_tid, attached_siblings, 0);
+  } else {
+    // Collect the list of open files.
+    OpenFilesList open_files;
+    populate_open_files_list(target, &open_files);
+
+    engrave_tombstone(output_fd.get(), backtrace_map.get(), open_files, target, main_tid,
+                      attached_siblings, abort_address, fatal_signal ? &amfd_data : nullptr);
+  }
+
+  // We don't actually need to PTRACE_DETACH, as long as our tracees aren't in
+  // group-stop state, which is true as long as no stopping signals are sent.
+
+  bool wait_for_gdb = android::base::GetBoolProperty("debug.debuggerd.wait_for_gdb", false);
+  if (!fatal_signal || siginfo.si_code == SI_USER) {
+    // Don't wait_for_gdb when the process didn't actually crash.
+    wait_for_gdb = false;
+  }
+
+  // If the process crashed or we need to send it SIGSTOP for wait_for_gdb,
+  // get it in a state where it can receive signals, and then send the relevant
+  // signal.
+  if (wait_for_gdb || fatal_signal) {
+    if (ptrace(PTRACE_INTERRUPT, main_tid, 0, 0) != 0) {
+      PLOG(ERROR) << "failed to use PTRACE_INTERRUPT on " << main_tid;
+    }
+
+    if (tgkill(target, main_tid, wait_for_gdb ? SIGSTOP : signo) != 0) {
+      PLOG(ERROR) << "failed to resend signal " << signo << " to " << main_tid;
+    }
+  }
+
+  if (wait_for_gdb) {
+    // Use ALOGI to line up with output from engrave_tombstone.
+    ALOGI(
+      "***********************************************************\n"
+      "* Process %d has been suspended while crashing.\n"
+      "* To attach gdbserver and start gdb, run this on the host:\n"
+      "*\n"
+      "*     gdbclient.py -p %d\n"
+      "*\n"
+      "***********************************************************",
+      target, main_tid);
+  }
+
+  if (fatal_signal) {
+    activity_manager_notify(target, signo, amfd_data);
+  }
+
+  // Close stdout before we notify tombstoned of completion.
+  close(STDOUT_FILENO);
+  if (!tombstoned_notify_completion(tombstoned_socket.get())) {
+    LOG(ERROR) << "failed to notify tombstoned of completion";
+  }
+
+  return 0;
+}
diff --git a/debuggerd/crasher/Android.mk b/debuggerd/crasher/Android.mk
new file mode 100644
index 0000000..b8b786b
--- /dev/null
+++ b/debuggerd/crasher/Android.mk
@@ -0,0 +1,66 @@
+LOCAL_PATH := $(call my-dir)
+
+crasher_cppflags := \
+    -std=gnu++14 \
+    -W \
+    -Wall \
+    -Wextra \
+    -Wunused \
+    -Werror \
+    -O0 \
+    -fstack-protector-all \
+    -Wno-free-nonheap-object \
+    -Wno-date-time
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := crasher.cpp
+LOCAL_SRC_FILES_arm    := arm/crashglue.S
+LOCAL_SRC_FILES_arm64  := arm64/crashglue.S
+LOCAL_SRC_FILES_mips   := mips/crashglue.S
+LOCAL_SRC_FILES_mips64 := mips64/crashglue.S
+LOCAL_SRC_FILES_x86    := x86/crashglue.S
+LOCAL_SRC_FILES_x86_64 := x86_64/crashglue.S
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_MODULE_TAGS := optional
+LOCAL_CPPFLAGS := $(crasher_cppflags)
+LOCAL_SHARED_LIBRARIES := libbase liblog
+
+# The arm emulator has VFP but not VFPv3-D32.
+ifeq ($(ARCH_ARM_HAVE_VFP_D32),true)
+LOCAL_ASFLAGS_arm += -DHAS_VFP_D32
+endif
+
+LOCAL_MODULE := crasher
+LOCAL_MODULE_STEM_32 := crasher
+LOCAL_MODULE_STEM_64 := crasher64
+LOCAL_MULTILIB := both
+
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := crasher.cpp
+LOCAL_SRC_FILES_arm    := arm/crashglue.S
+LOCAL_SRC_FILES_arm64  := arm64/crashglue.S
+LOCAL_SRC_FILES_mips   := mips/crashglue.S
+LOCAL_SRC_FILES_mips64 := mips64/crashglue.S
+LOCAL_SRC_FILES_x86    := x86/crashglue.S
+LOCAL_SRC_FILES_x86_64 := x86_64/crashglue.S
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_MODULE_TAGS := optional
+LOCAL_CPPFLAGS := $(crasher_cppflags) -DSTATIC_CRASHER
+LOCAL_FORCE_STATIC_EXECUTABLE := true
+LOCAL_SHARED_LIBRARIES := libbase liblog
+
+# The arm emulator has VFP but not VFPv3-D32.
+ifeq ($(ARCH_ARM_HAVE_VFP_D32),true)
+LOCAL_ASFLAGS_arm += -DHAS_VFP_D32
+endif
+
+LOCAL_MODULE := static_crasher
+LOCAL_MODULE_STEM_32 := static_crasher
+LOCAL_MODULE_STEM_64 := static_crasher64
+LOCAL_MULTILIB := both
+
+LOCAL_STATIC_LIBRARIES := libdebuggerd_handler libbase liblog
+
+include $(BUILD_EXECUTABLE)
diff --git a/debuggerd/arm/crashglue.S b/debuggerd/crasher/arm/crashglue.S
similarity index 100%
rename from debuggerd/arm/crashglue.S
rename to debuggerd/crasher/arm/crashglue.S
diff --git a/debuggerd/arm64/crashglue.S b/debuggerd/crasher/arm64/crashglue.S
similarity index 100%
rename from debuggerd/arm64/crashglue.S
rename to debuggerd/crasher/arm64/crashglue.S
diff --git a/debuggerd/crasher.cpp b/debuggerd/crasher/crasher.cpp
similarity index 99%
rename from debuggerd/crasher.cpp
rename to debuggerd/crasher/crasher.cpp
index 689f4d4..288f116 100644
--- a/debuggerd/crasher.cpp
+++ b/debuggerd/crasher/crasher.cpp
@@ -33,7 +33,7 @@
 #include <log/log.h>
 
 #if defined(STATIC_CRASHER)
-#include "debuggerd/client.h"
+#include "debuggerd/handler.h"
 #endif
 
 #define noinline __attribute__((__noinline__))
diff --git a/debuggerd/mips/crashglue.S b/debuggerd/crasher/mips/crashglue.S
similarity index 100%
rename from debuggerd/mips/crashglue.S
rename to debuggerd/crasher/mips/crashglue.S
diff --git a/debuggerd/mips64/crashglue.S b/debuggerd/crasher/mips64/crashglue.S
similarity index 100%
rename from debuggerd/mips64/crashglue.S
rename to debuggerd/crasher/mips64/crashglue.S
diff --git a/debuggerd/x86/crashglue.S b/debuggerd/crasher/x86/crashglue.S
similarity index 100%
rename from debuggerd/x86/crashglue.S
rename to debuggerd/crasher/x86/crashglue.S
diff --git a/debuggerd/x86_64/crashglue.S b/debuggerd/crasher/x86_64/crashglue.S
similarity index 100%
rename from debuggerd/x86_64/crashglue.S
rename to debuggerd/crasher/x86_64/crashglue.S
diff --git a/debuggerd/debuggerd.cpp b/debuggerd/debuggerd.cpp
index 893adc8..3208230 100644
--- a/debuggerd/debuggerd.cpp
+++ b/debuggerd/debuggerd.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright 2006, The Android Open Source Project
+ * Copyright 2016, The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,937 +14,68 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "debuggerd"
-
-#include <arpa/inet.h>
-#include <dirent.h>
-#include <elf.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <linux/input.h>
-#include <pthread.h>
-#include <signal.h>
-#include <stdarg.h>
+#include <err.h>
 #include <stdio.h>
-#include <sys/poll.h>
-#include <sys/prctl.h>
-#include <sys/ptrace.h>
-#include <sys/socket.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <sys/un.h>
-#include <time.h>
+#include <stdlib.h>
+#include <string.h>
 
-#include <memory>
-#include <set>
-#include <string>
-
-#include <selinux/android.h>
+#include <limits>
+#include <thread>
 
 #include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/parseint.h>
 #include <android-base/unique_fd.h>
-#include <cutils/debugger.h>
-#include <cutils/properties.h>
-#include <cutils/sockets.h>
-#include <log/log.h>
-
-#include <private/android_filesystem_config.h>
-
 #include <debuggerd/client.h>
+#include <debuggerd/util.h>
+#include <selinux/selinux.h>
 
-#include "backtrace.h"
-#include "getevent.h"
-#include "open_files_list.h"
-#include "signal_sender.h"
-#include "tombstone.h"
-#include "utility.h"
+using android::base::unique_fd;
 
-// If the 32 bit executable is compiled on a 64 bit system,
-// use the 32 bit socket name.
-#if defined(TARGET_IS_64_BIT) && !defined(__LP64__)
-#define SOCKET_NAME DEBUGGER32_SOCKET_NAME
-#else
-#define SOCKET_NAME DEBUGGER_SOCKET_NAME
-#endif
+static void usage(int exit_code) {
+  fprintf(stderr, "usage: debuggerd [-b] PID\n");
+  _exit(exit_code);
+}
 
-struct debugger_request_t {
-  debugger_action_t action;
-  pid_t pid, tid;
-  uid_t uid, gid;
-  pid_t ignore_tid;
-  uintptr_t abort_msg_address;
-};
+static std::thread spawn_redirect_thread(unique_fd fd) {
+  return std::thread([fd{ std::move(fd) }]() {
+    while (true) {
+      char buf[BUFSIZ];
+      ssize_t rc = TEMP_FAILURE_RETRY(read(fd.get(), buf, sizeof(buf)));
+      if (rc <= 0) {
+        return;
+      }
 
-static void wait_for_user_action(const debugger_request_t& request) {
-  // Explain how to attach the debugger.
-  ALOGI("***********************************************************\n"
-        "* Process %d has been suspended while crashing.\n"
-        "* To attach gdbserver and start gdb, run this on the host:\n"
-        "*\n"
-        "*     gdbclient.py -p %d\n"
-        "*\n"
-        "* Wait for gdb to start, then press the VOLUME DOWN key\n"
-        "* to let the process continue crashing.\n"
-        "***********************************************************",
-        request.pid, request.tid);
-
-  // Wait for VOLUME DOWN.
-  while (true) {
-    input_event e;
-    if (get_event(&e, -1) == 0) {
-      if (e.type == EV_KEY && e.code == KEY_VOLUMEDOWN && e.value == 0) {
-        break;
+      if (!android::base::WriteFully(STDOUT_FILENO, buf, rc)) {
+        return;
       }
     }
-  }
-
-  ALOGI("debuggerd resuming process %d", request.pid);
+  });
 }
 
-static int get_process_info(pid_t tid, pid_t* out_pid, uid_t* out_uid, uid_t* out_gid) {
-  char path[64];
-  snprintf(path, sizeof(path), "/proc/%d/status", tid);
+int main(int argc, char* argv[]) {
+  if (argc <= 1) usage(0);
+  if (argc > 3) usage(1);
+  if (argc == 3 && strcmp(argv[1], "-b") != 0) usage(1);
 
-  FILE* fp = fopen(path, "r");
-  if (!fp) {
-    return -1;
+  pid_t pid;
+  if (!android::base::ParseInt(argv[argc - 1], &pid, 1, std::numeric_limits<pid_t>::max())) {
+    usage(1);
   }
 
-  int fields = 0;
-  char line[1024];
-  while (fgets(line, sizeof(line), fp)) {
-    size_t len = strlen(line);
-    if (len > 6 && !memcmp(line, "Tgid:\t", 6)) {
-      *out_pid = atoi(line + 6);
-      fields |= 1;
-    } else if (len > 5 && !memcmp(line, "Uid:\t", 5)) {
-      *out_uid = atoi(line + 5);
-      fields |= 2;
-    } else if (len > 5 && !memcmp(line, "Gid:\t", 5)) {
-      *out_gid = atoi(line + 5);
-      fields |= 4;
-    }
-  }
-  fclose(fp);
-  return fields == 7 ? 0 : -1;
-}
-
-/*
- * Corresponds with debugger_action_t enum type in
- * include/cutils/debugger.h.
- */
-static const char *debuggerd_perms[] = {
-  NULL, /* crash is only used on self, no check applied */
-  "dump_tombstone",
-  "dump_backtrace"
-};
-
-static int audit_callback(void* data, security_class_t /* cls */, char* buf, size_t len)
-{
-    struct debugger_request_t* req = reinterpret_cast<debugger_request_t*>(data);
-
-    if (!req) {
-        ALOGE("No debuggerd request audit data");
-        return 0;
-    }
-
-    snprintf(buf, len, "pid=%d uid=%d gid=%d", req->pid, req->uid, req->gid);
-    return 0;
-}
-
-static bool selinux_action_allowed(int s, debugger_request_t* request)
-{
-  char *scon = NULL, *tcon = NULL;
-  const char *tclass = "debuggerd";
-  const char *perm;
-  bool allowed = false;
-
-  if (request->action <= 0 || request->action >= (sizeof(debuggerd_perms)/sizeof(debuggerd_perms[0]))) {
-    ALOGE("SELinux:  No permission defined for debugger action %d", request->action);
-    return false;
+  unique_fd piperead, pipewrite;
+  if (!Pipe(&piperead, &pipewrite)) {
+    err(1, "failed to create pipe");
   }
 
-  perm = debuggerd_perms[request->action];
-
-  if (getpeercon(s, &scon) < 0) {
-    ALOGE("Cannot get peer context from socket\n");
-    goto out;
+  std::thread redirect_thread = spawn_redirect_thread(std::move(piperead));
+  bool backtrace = argc == 3;
+  if (!debuggerd_trigger_dump(pid, std::move(pipewrite),
+                              backtrace ? kDebuggerdBacktrace : kDebuggerdBacktrace, 0)) {
+    redirect_thread.join();
+    errx(1, "failed to dump process %d", pid);
   }
 
-  if (getpidcon(request->tid, &tcon) < 0) {
-    ALOGE("Cannot get context for tid %d\n", request->tid);
-    goto out;
-  }
-
-  allowed = (selinux_check_access(scon, tcon, tclass, perm, reinterpret_cast<void*>(request)) == 0);
-
-out:
-   freecon(scon);
-   freecon(tcon);
-   return allowed;
-}
-
-static bool pid_contains_tid(pid_t pid, pid_t tid) {
-  char task_path[PATH_MAX];
-  if (snprintf(task_path, PATH_MAX, "/proc/%d/task/%d", pid, tid) >= PATH_MAX) {
-    ALOGE("debuggerd: task path overflow (pid = %d, tid = %d)\n", pid, tid);
-    exit(1);
-  }
-
-  return access(task_path, F_OK) == 0;
-}
-
-static int read_request(int fd, debugger_request_t* out_request) {
-  ucred cr;
-  socklen_t len = sizeof(cr);
-  int status = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cr, &len);
-  if (status != 0) {
-    ALOGE("cannot get credentials");
-    return -1;
-  }
-
-  ALOGV("reading tid");
-  pollfd pollfds[1];
-  pollfds[0].fd = fd;
-  pollfds[0].events = POLLIN;
-  pollfds[0].revents = 0;
-  status = TEMP_FAILURE_RETRY(poll(pollfds, 1, 3000));
-  if (status != 1) {
-    ALOGE("timed out reading tid (from pid=%d uid=%d)\n", cr.pid, cr.uid);
-    return -1;
-  }
-
-  debugger_msg_t msg;
-  memset(&msg, 0, sizeof(msg));
-  status = TEMP_FAILURE_RETRY(read(fd, &msg, sizeof(msg)));
-  if (status < 0) {
-    ALOGE("read failure? %s (pid=%d uid=%d)\n", strerror(errno), cr.pid, cr.uid);
-    return -1;
-  }
-  if (status != sizeof(debugger_msg_t)) {
-    ALOGE("invalid crash request of size %d (from pid=%d uid=%d)\n", status, cr.pid, cr.uid);
-    return -1;
-  }
-
-  out_request->action = static_cast<debugger_action_t>(msg.action);
-  out_request->tid = msg.tid;
-  out_request->ignore_tid = msg.ignore_tid;
-  out_request->pid = cr.pid;
-  out_request->uid = cr.uid;
-  out_request->gid = cr.gid;
-  out_request->abort_msg_address = msg.abort_msg_address;
-
-  if (msg.action == DEBUGGER_ACTION_CRASH) {
-    // Ensure that the tid reported by the crashing process is valid.
-    // This check needs to happen again after ptracing the requested thread to prevent a race.
-    if (!pid_contains_tid(out_request->pid, out_request->tid)) {
-      ALOGE("tid %d does not exist in pid %d. ignoring debug request\n", out_request->tid,
-            out_request->pid);
-      return -1;
-    }
-  } else if (cr.uid == 0 || (cr.uid == AID_SYSTEM && msg.action == DEBUGGER_ACTION_DUMP_BACKTRACE)) {
-    // Only root or system can ask us to attach to any process and dump it explicitly.
-    // However, system is only allowed to collect backtraces but cannot dump tombstones.
-    status = get_process_info(out_request->tid, &out_request->pid,
-                              &out_request->uid, &out_request->gid);
-    if (status < 0) {
-      ALOGE("tid %d does not exist. ignoring explicit dump request\n", out_request->tid);
-      return -1;
-    }
-
-    if (!selinux_action_allowed(fd, out_request))
-      return -1;
-  } else {
-    // No one else is allowed to dump arbitrary processes.
-    return -1;
-  }
+  redirect_thread.join();
   return 0;
 }
-
-static int activity_manager_connect() {
-  android::base::unique_fd amfd(socket(PF_UNIX, SOCK_STREAM, 0));
-  if (amfd.get() < -1) {
-    ALOGE("debuggerd: Unable to connect to activity manager (socket failed: %s)", strerror(errno));
-    return -1;
-  }
-
-  struct sockaddr_un address;
-  memset(&address, 0, sizeof(address));
-  address.sun_family = AF_UNIX;
-  // The path used here must match the value defined in NativeCrashListener.java.
-  strncpy(address.sun_path, "/data/system/ndebugsocket", sizeof(address.sun_path));
-  if (TEMP_FAILURE_RETRY(connect(amfd.get(), reinterpret_cast<struct sockaddr*>(&address),
-                                 sizeof(address))) == -1) {
-    ALOGE("debuggerd: Unable to connect to activity manager (connect failed: %s)", strerror(errno));
-    return -1;
-  }
-
-  struct timeval tv;
-  memset(&tv, 0, sizeof(tv));
-  tv.tv_sec = 1;  // tight leash
-  if (setsockopt(amfd.get(), SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) == -1) {
-    ALOGE("debuggerd: Unable to connect to activity manager (setsockopt SO_SNDTIMEO failed: %s)",
-          strerror(errno));
-    return -1;
-  }
-
-  tv.tv_sec = 3;  // 3 seconds on handshake read
-  if (setsockopt(amfd.get(), SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) == -1) {
-    ALOGE("debuggerd: Unable to connect to activity manager (setsockopt SO_RCVTIMEO failed: %s)",
-          strerror(errno));
-    return -1;
-  }
-
-  return amfd.release();
-}
-
-static void activity_manager_write(int pid, int signal, int amfd, const std::string& amfd_data) {
-  if (amfd == -1) {
-    return;
-  }
-
-  // Activity Manager protocol: binary 32-bit network-byte-order ints for the
-  // pid and signal number, followed by the raw text of the dump, culminating
-  // in a zero byte that marks end-of-data.
-  uint32_t datum = htonl(pid);
-  if (!android::base::WriteFully(amfd, &datum, 4)) {
-    ALOGE("AM pid write failed: %s\n", strerror(errno));
-    return;
-  }
-  datum = htonl(signal);
-  if (!android::base::WriteFully(amfd, &datum, 4)) {
-    ALOGE("AM signal write failed: %s\n", strerror(errno));
-    return;
-  }
-
-  if (!android::base::WriteFully(amfd, amfd_data.c_str(), amfd_data.size())) {
-    ALOGE("AM data write failed: %s\n", strerror(errno));
-    return;
-  }
-
-  // Send EOD to the Activity Manager, then wait for its ack to avoid racing
-  // ahead and killing the target out from under it.
-  uint8_t eodMarker = 0;
-  if (!android::base::WriteFully(amfd, &eodMarker, 1)) {
-    ALOGE("AM eod write failed: %s\n", strerror(errno));
-    return;
-  }
-  // 3 sec timeout reading the ack; we're fine if the read fails.
-  android::base::ReadFully(amfd, &eodMarker, 1);
-}
-
-static bool should_attach_gdb(const debugger_request_t& request) {
-  if (request.action == DEBUGGER_ACTION_CRASH) {
-    return property_get_bool("debug.debuggerd.wait_for_gdb", false);
-  }
-  return false;
-}
-
-#if defined(__LP64__)
-static bool is32bit(pid_t tid) {
-  char* exeline;
-  if (asprintf(&exeline, "/proc/%d/exe", tid) == -1) {
-    return false;
-  }
-  int fd = TEMP_FAILURE_RETRY(open(exeline, O_RDONLY | O_CLOEXEC));
-  int saved_errno = errno;
-  free(exeline);
-  if (fd == -1) {
-    ALOGW("Failed to open /proc/%d/exe %s", tid, strerror(saved_errno));
-    return false;
-  }
-
-  char ehdr[EI_NIDENT];
-  ssize_t bytes = TEMP_FAILURE_RETRY(read(fd, &ehdr, sizeof(ehdr)));
-  close(fd);
-  if (bytes != (ssize_t) sizeof(ehdr) || memcmp(ELFMAG, ehdr, SELFMAG) != 0) {
-    return false;
-  }
-  if (ehdr[EI_CLASS] == ELFCLASS32) {
-    return true;
-  }
-  return false;
-}
-
-static void redirect_to_32(int fd, debugger_request_t* request) {
-  debugger_msg_t msg;
-  memset(&msg, 0, sizeof(msg));
-  msg.tid = request->tid;
-  msg.action = request->action;
-
-  int sock_fd = socket_local_client(DEBUGGER32_SOCKET_NAME, ANDROID_SOCKET_NAMESPACE_ABSTRACT,
-                                    SOCK_STREAM | SOCK_CLOEXEC);
-  if (sock_fd < 0) {
-    ALOGE("Failed to connect to debuggerd32: %s", strerror(errno));
-    return;
-  }
-
-  if (TEMP_FAILURE_RETRY(write(sock_fd, &msg, sizeof(msg))) != (ssize_t) sizeof(msg)) {
-    ALOGE("Failed to write request to debuggerd32 socket: %s", strerror(errno));
-    close(sock_fd);
-    return;
-  }
-
-  char ack;
-  if (TEMP_FAILURE_RETRY(read(sock_fd, &ack, 1)) == -1) {
-    ALOGE("Failed to read ack from debuggerd32 socket: %s", strerror(errno));
-    close(sock_fd);
-    return;
-  }
-
-  char buffer[1024];
-  ssize_t bytes_read;
-  while ((bytes_read = TEMP_FAILURE_RETRY(read(sock_fd, buffer, sizeof(buffer)))) > 0) {
-    ssize_t bytes_to_send = bytes_read;
-    ssize_t bytes_written;
-    do {
-      bytes_written = TEMP_FAILURE_RETRY(write(fd, buffer + bytes_read - bytes_to_send,
-                                               bytes_to_send));
-      if (bytes_written == -1) {
-        if (errno == EAGAIN) {
-          // Retry the write.
-          continue;
-        }
-        ALOGE("Error while writing data to fd: %s", strerror(errno));
-        break;
-      }
-      bytes_to_send -= bytes_written;
-    } while (bytes_written != 0 && bytes_to_send > 0);
-    if (bytes_to_send != 0) {
-        ALOGE("Failed to write all data to fd: read %zd, sent %zd", bytes_read, bytes_to_send);
-        break;
-    }
-  }
-  close(sock_fd);
-}
-#endif
-
-// Attach to a thread, and verify that it's still a member of the given process
-static bool ptrace_attach_thread(pid_t pid, pid_t tid) {
-  if (ptrace(PTRACE_ATTACH, tid, 0, 0) != 0) {
-    return false;
-  }
-
-  // Make sure that the task we attached to is actually part of the pid we're dumping.
-  if (!pid_contains_tid(pid, tid)) {
-    if (ptrace(PTRACE_DETACH, tid, 0, 0) != 0) {
-      ALOGE("debuggerd: failed to detach from thread '%d'", tid);
-      exit(1);
-    }
-    return false;
-  }
-
-  return true;
-}
-
-static void ptrace_siblings(pid_t pid, pid_t main_tid, pid_t ignore_tid, std::set<pid_t>& tids) {
-  char task_path[PATH_MAX];
-
-  if (snprintf(task_path, PATH_MAX, "/proc/%d/task", pid) >= PATH_MAX) {
-    ALOGE("debuggerd: task path overflow (pid = %d)\n", pid);
-    abort();
-  }
-
-  std::unique_ptr<DIR, int (*)(DIR*)> d(opendir(task_path), closedir);
-
-  // Bail early if the task directory cannot be opened.
-  if (!d) {
-    ALOGE("debuggerd: failed to open /proc/%d/task: %s", pid, strerror(errno));
-    return;
-  }
-
-  struct dirent* de;
-  while ((de = readdir(d.get())) != NULL) {
-    // Ignore "." and "..".
-    if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) {
-      continue;
-    }
-
-    char* end;
-    pid_t tid = strtoul(de->d_name, &end, 10);
-    if (*end) {
-      continue;
-    }
-
-    if (tid == main_tid || tid == ignore_tid) {
-      continue;
-    }
-
-    if (!ptrace_attach_thread(pid, tid)) {
-      ALOGE("debuggerd: ptrace attach to %d failed: %s", tid, strerror(errno));
-      continue;
-    }
-
-    tids.insert(tid);
-  }
-}
-
-static bool perform_dump(const debugger_request_t& request, int fd, int tombstone_fd,
-                         BacktraceMap* backtrace_map, const OpenFilesList& open_files,
-                         const std::set<pid_t>& siblings,
-                         int* crash_signal, std::string* amfd_data) {
-  if (TEMP_FAILURE_RETRY(write(fd, "\0", 1)) != 1) {
-    ALOGE("debuggerd: failed to respond to client: %s\n", strerror(errno));
-    return false;
-  }
-
-  while (true) {
-    // wait_for_signal waits for forever, but the watchdog process will kill us
-    // if it takes too long.
-    int signal = wait_for_signal(request.tid);
-    switch (signal) {
-      case -1:
-        ALOGE("debuggerd: timed out waiting for signal");
-        return false;
-
-      case SIGSTOP:
-        if (request.action == DEBUGGER_ACTION_DUMP_TOMBSTONE) {
-          ALOGV("debuggerd: stopped -- dumping to tombstone");
-          engrave_tombstone(tombstone_fd, backtrace_map, open_files,
-                            request.pid, request.tid, siblings,
-                            request.abort_msg_address, amfd_data);
-        } else if (request.action == DEBUGGER_ACTION_DUMP_BACKTRACE) {
-          ALOGV("debuggerd: stopped -- dumping to fd");
-          dump_backtrace(fd, backtrace_map, request.pid, request.tid, siblings, nullptr);
-        } else {
-          ALOGV("debuggerd: stopped -- continuing");
-          if (ptrace(PTRACE_CONT, request.tid, 0, 0) != 0) {
-            ALOGE("debuggerd: ptrace continue failed: %s", strerror(errno));
-            return false;
-          }
-          continue;  // loop again
-        }
-        break;
-
-      case SIGABRT:
-      case SIGBUS:
-      case SIGFPE:
-      case SIGILL:
-      case SIGSEGV:
-#ifdef SIGSTKFLT
-      case SIGSTKFLT:
-#endif
-      case SIGSYS:
-      case SIGTRAP:
-        ALOGV("stopped -- fatal signal\n");
-        *crash_signal = signal;
-        engrave_tombstone(tombstone_fd, backtrace_map, open_files,
-                          request.pid, request.tid, siblings,
-                          request.abort_msg_address, amfd_data);
-        break;
-
-      default:
-        ALOGE("debuggerd: process stopped due to unexpected signal %d\n", signal);
-        break;
-    }
-    break;
-  }
-
-  return true;
-}
-
-static bool drop_privileges() {
-  // AID_LOG: for reading the logs data associated with the crashing process.
-  // AID_READPROC: for reading /proc/<PID>/{comm,cmdline}.
-  gid_t groups[] = { AID_DEBUGGERD, AID_LOG, AID_READPROC };
-  if (setgroups(sizeof(groups)/sizeof(groups[0]), groups) != 0) {
-    ALOGE("debuggerd: failed to setgroups: %s", strerror(errno));
-    return false;
-  }
-
-  if (setresgid(AID_DEBUGGERD, AID_DEBUGGERD, AID_DEBUGGERD) != 0) {
-    ALOGE("debuggerd: failed to setresgid: %s", strerror(errno));
-    return false;
-  }
-
-  if (setresuid(AID_DEBUGGERD, AID_DEBUGGERD, AID_DEBUGGERD) != 0) {
-    ALOGE("debuggerd: failed to setresuid: %s", strerror(errno));
-    return false;
-  }
-
-  return true;
-}
-
-static void worker_process(int fd, debugger_request_t& request) {
-  // Open the tombstone file if we need it.
-  std::string tombstone_path;
-  int tombstone_fd = -1;
-  switch (request.action) {
-    case DEBUGGER_ACTION_DUMP_TOMBSTONE:
-    case DEBUGGER_ACTION_CRASH:
-      tombstone_fd = open_tombstone(&tombstone_path);
-      if (tombstone_fd == -1) {
-        ALOGE("debuggerd: failed to open tombstone file: %s\n", strerror(errno));
-        exit(1);
-      }
-      break;
-
-    case DEBUGGER_ACTION_DUMP_BACKTRACE:
-      break;
-
-    default:
-      ALOGE("debuggerd: unexpected request action: %d", request.action);
-      exit(1);
-  }
-
-  // At this point, the thread that made the request is blocked in
-  // a read() call.  If the thread has crashed, then this gives us
-  // time to PTRACE_ATTACH to it before it has a chance to really fault.
-  //
-  // The PTRACE_ATTACH sends a SIGSTOP to the target process, but it
-  // won't necessarily have stopped by the time ptrace() returns.  (We
-  // currently assume it does.)  We write to the file descriptor to
-  // ensure that it can run as soon as we call PTRACE_CONT below.
-  // See details in client/debuggerd_client.cpp, in function
-  // debugger_signal_handler().
-
-  // Attach to the target process.
-  if (!ptrace_attach_thread(request.pid, request.tid)) {
-    ALOGE("debuggerd: ptrace attach failed: %s", strerror(errno));
-    exit(1);
-  }
-
-  // DEBUGGER_ACTION_CRASH requests can come from arbitrary processes and the tid field in the
-  // request is sent from the other side. If an attacker can cause a process to be spawned with the
-  // pid of their process, they could trick debuggerd into dumping that process by exiting after
-  // sending the request. Validate the trusted request.uid/gid to defend against this.
-  if (request.action == DEBUGGER_ACTION_CRASH) {
-    pid_t pid;
-    uid_t uid;
-    gid_t gid;
-    if (get_process_info(request.tid, &pid, &uid, &gid) != 0) {
-      ALOGE("debuggerd: failed to get process info for tid '%d'", request.tid);
-      exit(1);
-    }
-
-    if (pid != request.pid || uid != request.uid || gid != request.gid) {
-      ALOGE(
-        "debuggerd: attached task %d does not match request: "
-        "expected pid=%d,uid=%d,gid=%d, actual pid=%d,uid=%d,gid=%d",
-        request.tid, request.pid, request.uid, request.gid, pid, uid, gid);
-      exit(1);
-    }
-  }
-
-  // Don't attach to the sibling threads if we want to attach gdb.
-  // Supposedly, it makes the process less reliable.
-  bool attach_gdb = should_attach_gdb(request);
-  if (attach_gdb) {
-    // Open all of the input devices we need to listen for VOLUMEDOWN before dropping privileges.
-    if (init_getevent() != 0) {
-      ALOGE("debuggerd: failed to initialize input device, not waiting for gdb");
-      attach_gdb = false;
-    }
-
-  }
-
-  std::set<pid_t> siblings;
-  if (!attach_gdb) {
-    ptrace_siblings(request.pid, request.tid, request.ignore_tid, siblings);
-  }
-
-  // Generate the backtrace map before dropping privileges.
-  std::unique_ptr<BacktraceMap> backtrace_map(BacktraceMap::Create(request.pid));
-
-  // Collect the list of open files before dropping privileges.
-  OpenFilesList open_files;
-  populate_open_files_list(request.pid, &open_files);
-
-  int amfd = -1;
-  std::unique_ptr<std::string> amfd_data;
-  if (request.action == DEBUGGER_ACTION_CRASH) {
-    // Connect to the activity manager before dropping privileges.
-    amfd = activity_manager_connect();
-    amfd_data.reset(new std::string);
-  }
-
-  bool succeeded = false;
-
-  // Now that we've done everything that requires privileges, we can drop them.
-  if (!drop_privileges()) {
-    ALOGE("debuggerd: failed to drop privileges, exiting");
-    _exit(1);
-  }
-
-  int crash_signal = SIGKILL;
-  succeeded = perform_dump(request, fd, tombstone_fd, backtrace_map.get(), open_files,
-                           siblings, &crash_signal, amfd_data.get());
-  if (succeeded) {
-    if (request.action == DEBUGGER_ACTION_DUMP_TOMBSTONE) {
-      if (!tombstone_path.empty()) {
-        android::base::WriteFully(fd, tombstone_path.c_str(), tombstone_path.length());
-      }
-    }
-  }
-
-  if (attach_gdb) {
-    // Tell the signal process to send SIGSTOP to the target.
-    if (!send_signal(request.pid, 0, SIGSTOP)) {
-      ALOGE("debuggerd: failed to stop process for gdb attach: %s", strerror(errno));
-      attach_gdb = false;
-    }
-  }
-
-  if (!attach_gdb) {
-    // Tell the Activity Manager about the crashing process. If we are
-    // waiting for gdb to attach, do not send this or Activity Manager
-    // might kill the process before anyone can attach.
-    activity_manager_write(request.pid, crash_signal, amfd, *amfd_data.get());
-  }
-
-  if (ptrace(PTRACE_DETACH, request.tid, 0, 0) != 0) {
-    ALOGE("debuggerd: ptrace detach from %d failed: %s", request.tid, strerror(errno));
-  }
-
-  for (pid_t sibling : siblings) {
-    ptrace(PTRACE_DETACH, sibling, 0, 0);
-  }
-
-  // Send the signal back to the process if it crashed and we're not waiting for gdb.
-  if (!attach_gdb && request.action == DEBUGGER_ACTION_CRASH) {
-    if (!send_signal(request.pid, request.tid, crash_signal)) {
-      ALOGE("debuggerd: failed to kill process %d: %s", request.pid, strerror(errno));
-    }
-  }
-
-  // Wait for gdb, if requested.
-  if (attach_gdb) {
-    wait_for_user_action(request);
-
-    // Now tell the activity manager about this process.
-    activity_manager_write(request.pid, crash_signal, amfd, *amfd_data.get());
-
-    // Tell the signal process to send SIGCONT to the target.
-    if (!send_signal(request.pid, 0, SIGCONT)) {
-      ALOGE("debuggerd: failed to resume process %d: %s", request.pid, strerror(errno));
-    }
-
-    uninit_getevent();
-  }
-
-  close(amfd);
-
-  exit(!succeeded);
-}
-
-static void monitor_worker_process(int child_pid, const debugger_request_t& request) {
-  struct timespec timeout = {.tv_sec = 10, .tv_nsec = 0 };
-  if (should_attach_gdb(request)) {
-    // If wait_for_gdb is enabled, set the timeout to something large.
-    timeout.tv_sec = INT_MAX;
-  }
-
-  sigset_t signal_set;
-  sigemptyset(&signal_set);
-  sigaddset(&signal_set, SIGCHLD);
-
-  bool kill_worker = false;
-  bool kill_target = false;
-  bool kill_self = false;
-
-  int status;
-  siginfo_t siginfo;
-  int signal = TEMP_FAILURE_RETRY(sigtimedwait(&signal_set, &siginfo, &timeout));
-  if (signal == SIGCHLD) {
-    pid_t rc = waitpid(-1, &status, WNOHANG | WUNTRACED);
-    if (rc != child_pid) {
-      ALOGE("debuggerd: waitpid returned unexpected pid (%d), committing murder-suicide", rc);
-
-      if (WIFEXITED(status)) {
-        ALOGW("debuggerd: pid %d exited with status %d", rc, WEXITSTATUS(status));
-      } else if (WIFSIGNALED(status)) {
-        ALOGW("debuggerd: pid %d received signal %d", rc, WTERMSIG(status));
-      } else if (WIFSTOPPED(status)) {
-        ALOGW("debuggerd: pid %d stopped by signal %d", rc, WSTOPSIG(status));
-      } else if (WIFCONTINUED(status)) {
-        ALOGW("debuggerd: pid %d continued", rc);
-      }
-
-      kill_worker = true;
-      kill_target = true;
-      kill_self = true;
-    } else if (WIFSIGNALED(status)) {
-      ALOGE("debuggerd: worker process %d terminated due to signal %d", child_pid, WTERMSIG(status));
-      kill_worker = false;
-      kill_target = true;
-    } else if (WIFSTOPPED(status)) {
-      ALOGE("debuggerd: worker process %d stopped due to signal %d", child_pid, WSTOPSIG(status));
-      kill_worker = true;
-      kill_target = true;
-    }
-  } else {
-    ALOGE("debuggerd: worker process %d timed out", child_pid);
-    kill_worker = true;
-    kill_target = true;
-  }
-
-  if (kill_worker) {
-    // Something bad happened, kill the worker.
-    if (kill(child_pid, SIGKILL) != 0) {
-      ALOGE("debuggerd: failed to kill worker process %d: %s", child_pid, strerror(errno));
-    } else {
-      waitpid(child_pid, &status, 0);
-    }
-  }
-
-  int exit_signal = SIGCONT;
-  if (kill_target && request.action == DEBUGGER_ACTION_CRASH) {
-    ALOGE("debuggerd: killing target %d", request.pid);
-    exit_signal = SIGKILL;
-  } else {
-    ALOGW("debuggerd: resuming target %d", request.pid);
-  }
-
-  if (kill(request.pid, exit_signal) != 0) {
-    ALOGE("debuggerd: failed to send signal %d to target: %s", exit_signal, strerror(errno));
-  }
-
-  if (kill_self) {
-    stop_signal_sender();
-    _exit(1);
-  }
-}
-
-static void handle_request(int fd) {
-  ALOGV("handle_request(%d)\n", fd);
-
-  android::base::unique_fd closer(fd);
-  debugger_request_t request;
-  memset(&request, 0, sizeof(request));
-  int status = read_request(fd, &request);
-  if (status != 0) {
-    return;
-  }
-
-  ALOGW("debuggerd: handling request: pid=%d uid=%d gid=%d tid=%d\n", request.pid, request.uid,
-        request.gid, request.tid);
-
-#if defined(__LP64__)
-  // On 64 bit systems, requests to dump 32 bit and 64 bit tids come
-  // to the 64 bit debuggerd. If the process is a 32 bit executable,
-  // redirect the request to the 32 bit debuggerd.
-  if (is32bit(request.tid)) {
-    // Only dump backtrace and dump tombstone requests can be redirected.
-    if (request.action == DEBUGGER_ACTION_DUMP_BACKTRACE ||
-        request.action == DEBUGGER_ACTION_DUMP_TOMBSTONE) {
-      redirect_to_32(fd, &request);
-    } else {
-      ALOGE("debuggerd: Not allowed to redirect action %d to 32 bit debuggerd\n", request.action);
-    }
-    return;
-  }
-#endif
-
-  // Fork a child to handle the rest of the request.
-  pid_t fork_pid = fork();
-  if (fork_pid == -1) {
-    ALOGE("debuggerd: failed to fork: %s\n", strerror(errno));
-  } else if (fork_pid == 0) {
-    worker_process(fd, request);
-  } else {
-    monitor_worker_process(fork_pid, request);
-  }
-}
-
-static int do_server() {
-  // debuggerd crashes can't be reported to debuggerd.
-  // Reset all of the crash handlers.
-  signal(SIGABRT, SIG_DFL);
-  signal(SIGBUS, SIG_DFL);
-  signal(SIGFPE, SIG_DFL);
-  signal(SIGILL, SIG_DFL);
-  signal(SIGSEGV, SIG_DFL);
-#ifdef SIGSTKFLT
-  signal(SIGSTKFLT, SIG_DFL);
-#endif
-  signal(SIGTRAP, SIG_DFL);
-
-  // Ignore failed writes to closed sockets
-  signal(SIGPIPE, SIG_IGN);
-
-  // Block SIGCHLD so we can sigtimedwait for it.
-  sigset_t sigchld;
-  sigemptyset(&sigchld);
-  sigaddset(&sigchld, SIGCHLD);
-  sigprocmask(SIG_SETMASK, &sigchld, nullptr);
-
-  int s = socket_local_server(SOCKET_NAME, ANDROID_SOCKET_NAMESPACE_ABSTRACT,
-                              SOCK_STREAM | SOCK_CLOEXEC);
-  if (s == -1) return 1;
-
-  // Fork a process that stays root, and listens on a pipe to pause and resume the target.
-  if (!start_signal_sender()) {
-    ALOGE("debuggerd: failed to fork signal sender");
-    return 1;
-  }
-
-  ALOGI("debuggerd: starting\n");
-
-  for (;;) {
-    ALOGV("waiting for connection\n");
-    int fd = accept4(s, nullptr, nullptr, SOCK_CLOEXEC | SOCK_NONBLOCK);
-    if (fd == -1) {
-      ALOGE("accept failed: %s\n", strerror(errno));
-      continue;
-    }
-
-    handle_request(fd);
-  }
-  return 0;
-}
-
-static int do_explicit_dump(pid_t tid, bool dump_backtrace) {
-  fprintf(stdout, "Sending request to dump task %d...\n", tid);
-  fflush(stdout);
-
-  // TODO: we could have better error reporting if debuggerd sent an error string back.
-  if (dump_backtrace) {
-    if (dump_backtrace_to_file(tid, fileno(stdout)) < 0) {
-      fputs("Error dumping backtrace (check logcat).\n", stderr);
-      return 1;
-    }
-  } else {
-    char tombstone_path[PATH_MAX];
-    if (dump_tombstone(tid, tombstone_path, sizeof(tombstone_path)) < 0) {
-      fputs("Error dumping tombstone (check logcat).\n", stderr);
-      return 1;
-    }
-    fprintf(stderr, "Tombstone written to: %s\n", tombstone_path);
-  }
-  return 0;
-}
-
-static int usage() {
-  fputs("usage: debuggerd [-b] [<tid>]\n"
-        "\n"
-        "Given a thread id, sends a request to debuggerd to dump that thread.\n"
-        "Otherwise, starts the debuggerd server.\n"
-        "\n"
-        "-b\tdump backtrace to console, otherwise generate tombstone\n", stderr);
-  return EXIT_FAILURE;
-}
-
-int main(int argc, char** argv) {
-  union selinux_callback cb;
-  if (argc == 1) {
-    cb.func_audit = audit_callback;
-    selinux_set_callback(SELINUX_CB_AUDIT, cb);
-    cb.func_log = selinux_log_callback;
-    selinux_set_callback(SELINUX_CB_LOG, cb);
-    return do_server();
-  }
-
-  bool dump_backtrace = false;
-  pid_t tid = 0;
-  for (int i = 1; i < argc; i++) {
-    if (!strcmp(argv[i], "-b")) {
-      dump_backtrace = true;
-    } else if (tid != 0 || (tid = atoi(argv[i])) == 0) {
-      // Only one tid is allowed. (And 0 isn't a valid tid.)
-      // atoi(3) returns 0 on failure to parse, so this catches anything else too.
-      return usage();
-    }
-  }
-  return (tid != 0) ? do_explicit_dump(tid, dump_backtrace) : usage();
-}
diff --git a/debuggerd/debuggerd.rc b/debuggerd/debuggerd.rc
deleted file mode 100644
index 1c6b9ff..0000000
--- a/debuggerd/debuggerd.rc
+++ /dev/null
@@ -1,3 +0,0 @@
-service debuggerd /system/bin/debuggerd
-    group root readproc
-    writepid /dev/cpuset/system-background/tasks
diff --git a/debuggerd/debuggerd64.rc b/debuggerd/debuggerd64.rc
deleted file mode 100644
index 3e8847a..0000000
--- a/debuggerd/debuggerd64.rc
+++ /dev/null
@@ -1,3 +0,0 @@
-service debuggerd64 /system/bin/debuggerd64
-    group root readproc
-    writepid /dev/cpuset/system-background/tasks
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
new file mode 100644
index 0000000..e7503e9
--- /dev/null
+++ b/debuggerd/debuggerd_test.cpp
@@ -0,0 +1,407 @@
+/*
+ * Copyright 2016, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <err.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/prctl.h>
+#include <sys/types.h>
+
+#include <chrono>
+#include <regex>
+#include <thread>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/parseint.h>
+#include <android-base/properties.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <cutils/sockets.h>
+#include <debuggerd/handler.h>
+#include <debuggerd/protocol.h>
+#include <debuggerd/util.h>
+#include <gtest/gtest.h>
+
+using namespace std::chrono_literals;
+using android::base::unique_fd;
+
+#if defined(__LP64__)
+#define CRASHER_PATH  "/system/xbin/crasher64"
+#define ARCH_SUFFIX "64"
+#else
+#define CRASHER_PATH "/system/xbin/crasher"
+#define ARCH_SUFFIX ""
+#endif
+
+constexpr char kWaitForGdbKey[] = "debug.debuggerd.wait_for_gdb";
+
+#define TIMEOUT(seconds, expr)                                     \
+  [&]() {                                                          \
+    struct sigaction old_sigaction;                                \
+    struct sigaction new_sigaction = {};                           \
+    new_sigaction.sa_handler = [](int) {};                         \
+    if (sigaction(SIGALRM, &new_sigaction, &new_sigaction) != 0) { \
+      err(1, "sigaction failed");                                  \
+    }                                                              \
+    alarm(seconds);                                                \
+    auto value = expr;                                             \
+    int saved_errno = errno;                                       \
+    if (sigaction(SIGALRM, &old_sigaction, nullptr) != 0) {        \
+      err(1, "sigaction failed");                                  \
+    }                                                              \
+    alarm(0);                                                      \
+    errno = saved_errno;                                           \
+    return value;                                                  \
+  }()
+
+#define ASSERT_MATCH(str, pattern)                                              \
+  do {                                                                          \
+    std::regex r((pattern));                                                    \
+    if (!std::regex_search((str), r)) {                                         \
+      FAIL() << "regex mismatch: expected " << (pattern) << " in: \n" << (str); \
+    }                                                                           \
+  } while (0)
+
+class CrasherTest : public ::testing::Test {
+ public:
+  pid_t crasher_pid = -1;
+  bool previous_wait_for_gdb;
+  unique_fd crasher_pipe;
+  unique_fd intercept_fd;
+
+  CrasherTest();
+  ~CrasherTest();
+
+  void StartIntercept(unique_fd* output_fd);
+
+  // Returns -1 if we fail to read a response from tombstoned, otherwise the received return code.
+  void FinishIntercept(int* result);
+
+  void StartProcess(std::function<void()> function);
+  void StartCrasher(const std::string& crash_type);
+  void FinishCrasher();
+  void AssertDeath(int signo);
+};
+
+CrasherTest::CrasherTest() {
+  previous_wait_for_gdb = android::base::GetBoolProperty(kWaitForGdbKey, false);
+  android::base::SetProperty(kWaitForGdbKey, "0");
+}
+
+CrasherTest::~CrasherTest() {
+  if (crasher_pid != -1) {
+    kill(crasher_pid, SIGKILL);
+    int status;
+    waitpid(crasher_pid, &status, WUNTRACED);
+  }
+
+  android::base::SetProperty(kWaitForGdbKey, previous_wait_for_gdb ? "1" : "0");
+}
+
+void CrasherTest::StartIntercept(unique_fd* output_fd) {
+  if (crasher_pid == -1) {
+    FAIL() << "crasher hasn't been started";
+  }
+
+  intercept_fd.reset(socket_local_client(kTombstonedInterceptSocketName,
+                                         ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_SEQPACKET));
+  if (intercept_fd == -1) {
+    FAIL() << "failed to contact tombstoned: " << strerror(errno);
+  }
+
+  InterceptRequest req = {.pid = crasher_pid };
+
+  unique_fd output_pipe_write;
+  if (!Pipe(output_fd, &output_pipe_write)) {
+    FAIL() << "failed to create output pipe: " << strerror(errno);
+  }
+
+  std::string pipe_size_str;
+  int pipe_buffer_size;
+  if (!android::base::ReadFileToString("/proc/sys/fs/pipe-max-size", &pipe_size_str)) {
+    FAIL() << "failed to read /proc/sys/fs/pipe-max-size: " << strerror(errno);
+  }
+
+  pipe_size_str = android::base::Trim(pipe_size_str);
+
+  if (!android::base::ParseInt(pipe_size_str.c_str(), &pipe_buffer_size, 0)) {
+    FAIL() << "failed to parse pipe max size";
+  }
+
+  if (fcntl(output_fd->get(), F_SETPIPE_SZ, pipe_buffer_size) != pipe_buffer_size) {
+    FAIL() << "failed to set pipe size: " << strerror(errno);
+  }
+
+  if (send_fd(intercept_fd.get(), &req, sizeof(req), std::move(output_pipe_write)) != sizeof(req)) {
+    FAIL() << "failed to send output fd to tombstoned: " << strerror(errno);
+  }
+}
+
+void CrasherTest::FinishIntercept(int* result) {
+  InterceptResponse response;
+
+  // Timeout for tombstoned intercept is 10 seconds.
+  ssize_t rc = TIMEOUT(20, read(intercept_fd.get(), &response, sizeof(response)));
+  if (rc == -1) {
+    FAIL() << "failed to read response from tombstoned: " << strerror(errno);
+  } else if (rc == 0) {
+    *result = -1;
+  } else if (rc != sizeof(response)) {
+    FAIL() << "received packet of unexpected length from tombstoned: expected " << sizeof(response)
+           << ", received " << rc;
+  } else {
+    *result = response.success;
+  }
+}
+
+void CrasherTest::StartProcess(std::function<void()> function) {
+  unique_fd read_pipe;
+  unique_fd crasher_read_pipe;
+  if (!Pipe(&crasher_read_pipe, &crasher_pipe)) {
+    FAIL() << "failed to create pipe: " << strerror(errno);
+  }
+
+  crasher_pid = fork();
+  if (crasher_pid == -1) {
+    FAIL() << "fork failed: " << strerror(errno);
+  } else if (crasher_pid == 0) {
+    unique_fd devnull(open("/dev/null", O_WRONLY));
+    dup2(crasher_read_pipe.get(), STDIN_FILENO);
+    dup2(devnull.get(), STDOUT_FILENO);
+    dup2(devnull.get(), STDERR_FILENO);
+    function();
+    _exit(0);
+  }
+}
+
+void CrasherTest::StartCrasher(const std::string& crash_type) {
+  std::string type = "wait-" + crash_type;
+  StartProcess([type]() {
+    execl(CRASHER_PATH, CRASHER_PATH, type.c_str(), nullptr);
+    err(1, "exec failed");
+  });
+}
+
+void CrasherTest::FinishCrasher() {
+  if (crasher_pipe == -1) {
+    FAIL() << "crasher pipe uninitialized";
+  }
+
+  ssize_t rc = write(crasher_pipe.get(), "\n", 1);
+  if (rc == -1) {
+    FAIL() << "failed to write to crasher pipe: " << strerror(errno);
+  } else if (rc == 0) {
+    FAIL() << "crasher pipe was closed";
+  }
+}
+
+void CrasherTest::AssertDeath(int signo) {
+  int status;
+  pid_t pid = TIMEOUT(5, waitpid(crasher_pid, &status, 0));
+  if (pid != crasher_pid) {
+    FAIL() << "failed to wait for crasher: " << strerror(errno);
+  }
+
+  if (!WIFSIGNALED(status)) {
+    FAIL() << "crasher didn't terminate via a signal";
+  }
+  ASSERT_EQ(signo, WTERMSIG(status));
+  crasher_pid = -1;
+}
+
+static void ConsumeFd(unique_fd fd, std::string* output) {
+  constexpr size_t read_length = PAGE_SIZE;
+  std::string result;
+
+  while (true) {
+    size_t offset = result.size();
+    result.resize(result.size() + PAGE_SIZE);
+    ssize_t rc = TEMP_FAILURE_RETRY(read(fd.get(), &result[offset], read_length));
+    if (rc == -1) {
+      FAIL() << "read failed: " << strerror(errno);
+    } else if (rc == 0) {
+      result.resize(result.size() - PAGE_SIZE);
+      break;
+    }
+
+    result.resize(result.size() - PAGE_SIZE + rc);
+  }
+
+  *output = std::move(result);
+}
+
+TEST_F(CrasherTest, smoke) {
+  int intercept_result;
+  unique_fd output_fd;
+  StartCrasher("SIGSEGV");
+  StartIntercept(&output_fd);
+  FinishCrasher();
+  AssertDeath(SIGSEGV);
+  FinishIntercept(&intercept_result);
+
+  ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+  std::string result;
+  ConsumeFd(std::move(output_fd), &result);
+  ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\), code 1 \(SEGV_MAPERR\), fault addr 0xdead)");
+}
+
+TEST_F(CrasherTest, abort) {
+  int intercept_result;
+  unique_fd output_fd;
+  StartCrasher("abort");
+  StartIntercept(&output_fd);
+  FinishCrasher();
+  AssertDeath(SIGABRT);
+  FinishIntercept(&intercept_result);
+
+  ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+  std::string result;
+  ConsumeFd(std::move(output_fd), &result);
+  ASSERT_MATCH(result, R"(#00 pc [0-9a-f]+\s+ /system/lib)" ARCH_SUFFIX R"(/libc.so \(tgkill)");
+}
+
+TEST_F(CrasherTest, signal) {
+  int intercept_result;
+  unique_fd output_fd;
+  StartCrasher("abort");
+  StartIntercept(&output_fd);
+
+  // Wait for a bit, or we might end up killing the process before the signal
+  // handler even gets a chance to be registered.
+  std::this_thread::sleep_for(100ms);
+  ASSERT_EQ(0, kill(crasher_pid, SIGSEGV));
+
+  AssertDeath(SIGSEGV);
+  FinishIntercept(&intercept_result);
+
+  ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+  std::string result;
+  ConsumeFd(std::move(output_fd), &result);
+  ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\), code 0 \(SI_USER\), fault addr --------)");
+  ASSERT_MATCH(result, R"(backtrace:)");
+}
+
+TEST_F(CrasherTest, abort_message) {
+  int intercept_result;
+  unique_fd output_fd;
+  StartCrasher("smash-stack");
+  StartIntercept(&output_fd);
+  FinishCrasher();
+  AssertDeath(SIGABRT);
+  FinishIntercept(&intercept_result);
+
+  ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+  std::string result;
+  ConsumeFd(std::move(output_fd), &result);
+  ASSERT_MATCH(result, R"(Abort message: 'stack corruption detected \(-fstack-protector\)')");
+}
+
+TEST_F(CrasherTest, intercept_timeout) {
+  int intercept_result;
+  unique_fd output_fd;
+  StartCrasher("abort");
+  StartIntercept(&output_fd);
+
+  // Don't let crasher finish until we timeout.
+  FinishIntercept(&intercept_result);
+
+  ASSERT_NE(1, intercept_result) << "tombstoned reported success? (intercept_result = "
+                                 << intercept_result << ")";
+
+  FinishCrasher();
+  AssertDeath(SIGABRT);
+}
+
+TEST_F(CrasherTest, wait_for_gdb) {
+  if (!android::base::SetProperty(kWaitForGdbKey, "1")) {
+    FAIL() << "failed to enable wait_for_gdb";
+  }
+  sleep(1);
+
+  StartCrasher("abort");
+  FinishCrasher();
+
+  int status;
+  ASSERT_EQ(crasher_pid, waitpid(crasher_pid, &status, WUNTRACED));
+  ASSERT_TRUE(WIFSTOPPED(status));
+  ASSERT_EQ(SIGSTOP, WSTOPSIG(status));
+
+  ASSERT_EQ(0, kill(crasher_pid, SIGCONT));
+
+  AssertDeath(SIGABRT);
+}
+
+// wait_for_gdb shouldn't trigger on manually sent signals.
+TEST_F(CrasherTest, wait_for_gdb_signal) {
+  if (!android::base::SetProperty(kWaitForGdbKey, "1")) {
+    FAIL() << "failed to enable wait_for_gdb";
+  }
+
+  StartCrasher("abort");
+  ASSERT_EQ(0, kill(crasher_pid, SIGSEGV)) << strerror(errno);
+  AssertDeath(SIGSEGV);
+}
+
+TEST_F(CrasherTest, backtrace) {
+  std::string result;
+  int intercept_result;
+  unique_fd output_fd;
+  StartCrasher("abort");
+  StartIntercept(&output_fd);
+
+  std::this_thread::sleep_for(500ms);
+
+  sigval val;
+  val.sival_int = 1;
+  ASSERT_EQ(0, sigqueue(crasher_pid, DEBUGGER_SIGNAL, val)) << strerror(errno);
+  FinishIntercept(&intercept_result);
+  ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+  ConsumeFd(std::move(output_fd), &result);
+  ASSERT_MATCH(result, R"(#00 pc [0-9a-f]+  /system/lib)" ARCH_SUFFIX R"(/libc.so \(read\+)");
+
+  int status;
+  ASSERT_EQ(0, waitpid(crasher_pid, &status, WNOHANG | WUNTRACED));
+
+  StartIntercept(&output_fd);
+  FinishCrasher();
+  AssertDeath(SIGABRT);
+  FinishIntercept(&intercept_result);
+  ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+  ConsumeFd(std::move(output_fd), &result);
+  ASSERT_MATCH(result, R"(#00 pc [0-9a-f]+\s+ /system/lib)" ARCH_SUFFIX R"(/libc.so \(tgkill)");
+}
+
+TEST_F(CrasherTest, PR_SET_DUMPABLE_0_crash) {
+  StartProcess([]() {
+    prctl(PR_SET_DUMPABLE, 0);
+    volatile char* null = static_cast<char*>(nullptr);
+    *null = '\0';
+  });
+  AssertDeath(SIGSEGV);
+}
+
+TEST_F(CrasherTest, PR_SET_DUMPABLE_0_raise) {
+  StartProcess([]() {
+    prctl(PR_SET_DUMPABLE, 0);
+    raise(SIGUSR1);
+  });
+  AssertDeath(SIGUSR1);
+}
diff --git a/debuggerd/getevent.cpp b/debuggerd/getevent.cpp
deleted file mode 100644
index dbb878a..0000000
--- a/debuggerd/getevent.cpp
+++ /dev/null
@@ -1,224 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <dirent.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <linux/input.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/inotify.h>
-#include <sys/ioctl.h>
-#include <sys/limits.h>
-#include <sys/poll.h>
-#include <unistd.h>
-
-#include <memory>
-
-#include <android/log.h>
-
-static struct pollfd* ufds;
-static char** device_names;
-static int nfds;
-
-static int open_device(const char* device) {
-  int version;
-  int fd;
-  struct pollfd* new_ufds;
-  char** new_device_names;
-  char name[80];
-  char location[80];
-  char idstr[80];
-  struct input_id id;
-
-  fd = open(device, O_RDWR);
-  if (fd < 0) {
-    return -1;
-  }
-
-  if (ioctl(fd, EVIOCGVERSION, &version)) {
-    return -1;
-  }
-  if (ioctl(fd, EVIOCGID, &id)) {
-    return -1;
-  }
-  name[sizeof(name) - 1] = '\0';
-  location[sizeof(location) - 1] = '\0';
-  idstr[sizeof(idstr) - 1] = '\0';
-  if (ioctl(fd, EVIOCGNAME(sizeof(name) - 1), &name) < 1) {
-    name[0] = '\0';
-  }
-  if (ioctl(fd, EVIOCGPHYS(sizeof(location) - 1), &location) < 1) {
-    location[0] = '\0';
-  }
-  if (ioctl(fd, EVIOCGUNIQ(sizeof(idstr) - 1), &idstr) < 1) {
-    idstr[0] = '\0';
-  }
-
-  new_ufds = reinterpret_cast<pollfd*>(realloc(ufds, sizeof(ufds[0]) * (nfds + 1)));
-  if (new_ufds == NULL) {
-    fprintf(stderr, "out of memory\n");
-    return -1;
-  }
-  ufds = new_ufds;
-  new_device_names = reinterpret_cast<char**>(realloc(
-      device_names, sizeof(device_names[0]) * (nfds + 1)));
-  if (new_device_names == NULL) {
-    fprintf(stderr, "out of memory\n");
-    return -1;
-  }
-  device_names = new_device_names;
-  ufds[nfds].fd = fd;
-  ufds[nfds].events = POLLIN;
-  device_names[nfds] = strdup(device);
-  nfds++;
-
-  return 0;
-}
-
-int close_device(const char* device) {
-  int i;
-  for (i = 1; i < nfds; i++) {
-    if (strcmp(device_names[i], device) == 0) {
-      int count = nfds - i - 1;
-      free(device_names[i]);
-      memmove(device_names + i, device_names + i + 1, sizeof(device_names[0]) * count);
-      memmove(ufds + i, ufds + i + 1, sizeof(ufds[0]) * count);
-      nfds--;
-      return 0;
-    }
-  }
-  return -1;
-}
-
-static int read_notify(const char* dirname, int nfd) {
-  int res;
-  char devname[PATH_MAX];
-  char* filename;
-  char event_buf[512];
-  int event_size;
-  int event_pos = 0;
-  struct inotify_event *event;
-
-  res = read(nfd, event_buf, sizeof(event_buf));
-  if (res < (int)sizeof(*event)) {
-    if (errno == EINTR)
-      return 0;
-    fprintf(stderr, "could not get event, %s\n", strerror(errno));
-    return 1;
-  }
-
-  strcpy(devname, dirname);
-  filename = devname + strlen(devname);
-  *filename++ = '/';
-
-  while (res >= (int)sizeof(*event)) {
-    event = reinterpret_cast<struct inotify_event*>(event_buf + event_pos);
-    if (event->len) {
-      strcpy(filename, event->name);
-      if (event->mask & IN_CREATE) {
-        open_device(devname);
-      } else {
-        close_device(devname);
-      }
-    }
-    event_size = sizeof(*event) + event->len;
-    res -= event_size;
-    event_pos += event_size;
-  }
-  return 0;
-}
-
-static int scan_dir(const char* dirname) {
-  char devname[PATH_MAX];
-  char* filename;
-  struct dirent* de;
-  std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(dirname), closedir);
-  if (dir == NULL)
-    return -1;
-  strcpy(devname, dirname);
-  filename = devname + strlen(devname);
-  *filename++ = '/';
-  while ((de = readdir(dir.get()))) {
-    if ((de->d_name[0] == '.' && de->d_name[1] == '\0') ||
-        (de->d_name[1] == '.' && de->d_name[2] == '\0'))
-      continue;
-    strcpy(filename, de->d_name);
-    open_device(devname);
-  }
-  return 0;
-}
-
-int init_getevent() {
-  int res;
-  const char* device_path = "/dev/input";
-
-  nfds = 1;
-  ufds = reinterpret_cast<pollfd*>(calloc(1, sizeof(ufds[0])));
-  ufds[0].fd = inotify_init();
-  ufds[0].events = POLLIN;
-
-  res = inotify_add_watch(ufds[0].fd, device_path, IN_DELETE | IN_CREATE);
-  if (res < 0) {
-    return 1;
-  }
-  res = scan_dir(device_path);
-  if (res < 0) {
-    return 1;
-  }
-  return 0;
-}
-
-void uninit_getevent() {
-  int i;
-  for (i = 0; i < nfds; i++) {
-    close(ufds[i].fd);
-  }
-  free(ufds);
-  ufds = 0;
-  nfds = 0;
-}
-
-int get_event(struct input_event* event, int timeout) {
-  int res;
-  int i;
-  int pollres;
-  const char* device_path = "/dev/input";
-  while (1) {
-    pollres = poll(ufds, nfds, timeout);
-    if (pollres == 0) {
-      return 1;
-    }
-    if (ufds[0].revents & POLLIN) {
-      read_notify(device_path, ufds[0].fd);
-    }
-    for (i = 1; i < nfds; i++) {
-      if (ufds[i].revents) {
-        if (ufds[i].revents & POLLIN) {
-          res = read(ufds[i].fd, event, sizeof(*event));
-          if (res < static_cast<int>(sizeof(event))) {
-            fprintf(stderr, "could not get event\n");
-            return -1;
-          }
-          return 0;
-        }
-      }
-    }
-  }
-  return 0;
-}
diff --git a/debuggerd/getevent.h b/debuggerd/getevent.h
deleted file mode 100644
index 426139d..0000000
--- a/debuggerd/getevent.h
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _DEBUGGERD_GETEVENT_H
-#define _DEBUGGERD_GETEVENT_H
-
-int init_getevent();
-void uninit_getevent();
-int get_event(struct input_event* event, int timeout);
-
-#endif // _DEBUGGERD_GETEVENT_H
diff --git a/debuggerd/handler/debuggerd_handler.cpp b/debuggerd/handler/debuggerd_handler.cpp
new file mode 100644
index 0000000..8ea06c0
--- /dev/null
+++ b/debuggerd/handler/debuggerd_handler.cpp
@@ -0,0 +1,383 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "debuggerd/handler.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <linux/futex.h>
+#include <pthread.h>
+#include <sched.h>
+#include <signal.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/prctl.h>
+#include <sys/socket.h>
+#include <sys/syscall.h>
+#include <sys/un.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "private/bionic_futex.h"
+#include "private/libc_logging.h"
+
+// see man(2) prctl, specifically the section about PR_GET_NAME
+#define MAX_TASK_NAME_LEN (16)
+
+#if defined(__LP64__)
+#define CRASH_DUMP_NAME "crash_dump64"
+#else
+#define CRASH_DUMP_NAME "crash_dump32"
+#endif
+
+#define CRASH_DUMP_PATH "/system/bin/" CRASH_DUMP_NAME
+
+static debuggerd_callbacks_t g_callbacks;
+
+// Mutex to ensure only one crashing thread dumps itself.
+static pthread_mutex_t crash_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+// Don't use __libc_fatal because it exits via abort, which might put us back into a signal handler.
+#define fatal(...)                                             \
+  do {                                                         \
+    __libc_format_log(ANDROID_LOG_FATAL, "libc", __VA_ARGS__); \
+    _exit(1);                                                  \
+  } while (0)
+
+/*
+ * Writes a summary of the signal to the log file.  We do this so that, if
+ * for some reason we're not able to contact debuggerd, there is still some
+ * indication of the failure in the log.
+ *
+ * We could be here as a result of native heap corruption, or while a
+ * mutex is being held, so we don't want to use any libc functions that
+ * could allocate memory or hold a lock.
+ */
+static void log_signal_summary(int signum, const siginfo_t* info) {
+  const char* signal_name = "???";
+  bool has_address = false;
+  switch (signum) {
+    case SIGABRT:
+      signal_name = "SIGABRT";
+      break;
+    case SIGBUS:
+      signal_name = "SIGBUS";
+      has_address = true;
+      break;
+    case SIGFPE:
+      signal_name = "SIGFPE";
+      has_address = true;
+      break;
+    case SIGILL:
+      signal_name = "SIGILL";
+      has_address = true;
+      break;
+    case SIGSEGV:
+      signal_name = "SIGSEGV";
+      has_address = true;
+      break;
+#if defined(SIGSTKFLT)
+    case SIGSTKFLT:
+      signal_name = "SIGSTKFLT";
+      break;
+#endif
+    case SIGSYS:
+      signal_name = "SIGSYS";
+      break;
+    case SIGTRAP:
+      signal_name = "SIGTRAP";
+      break;
+  }
+
+  char thread_name[MAX_TASK_NAME_LEN + 1];  // one more for termination
+  if (prctl(PR_GET_NAME, reinterpret_cast<unsigned long>(thread_name), 0, 0, 0) != 0) {
+    strcpy(thread_name, "<name unknown>");
+  } else {
+    // short names are null terminated by prctl, but the man page
+    // implies that 16 byte names are not.
+    thread_name[MAX_TASK_NAME_LEN] = 0;
+  }
+
+  // "info" will be null if the siginfo_t information was not available.
+  // Many signals don't have an address or a code.
+  char code_desc[32];  // ", code -6"
+  char addr_desc[32];  // ", fault addr 0x1234"
+  addr_desc[0] = code_desc[0] = 0;
+  if (info != nullptr) {
+    __libc_format_buffer(code_desc, sizeof(code_desc), ", code %d", info->si_code);
+    if (has_address) {
+      __libc_format_buffer(addr_desc, sizeof(addr_desc), ", fault addr %p", info->si_addr);
+    }
+  }
+  __libc_format_log(ANDROID_LOG_FATAL, "libc", "Fatal signal %d (%s)%s%s in tid %d (%s)", signum,
+                    signal_name, code_desc, addr_desc, gettid(), thread_name);
+}
+
+/*
+ * Returns true if the handler for signal "signum" has SA_SIGINFO set.
+ */
+static bool have_siginfo(int signum) {
+  struct sigaction old_action;
+  if (sigaction(signum, nullptr, &old_action) < 0) {
+    __libc_format_log(ANDROID_LOG_WARN, "libc", "Failed testing for SA_SIGINFO: %s",
+                      strerror(errno));
+    return false;
+  }
+  return (old_action.sa_flags & SA_SIGINFO) != 0;
+}
+
+struct debugger_thread_info {
+  bool crash_dump_started;
+  pid_t crashing_tid;
+  pid_t pseudothread_tid;
+  int signal_number;
+  siginfo_t* info;
+};
+
+// Logging and contacting debuggerd requires free file descriptors, which we might not have.
+// Work around this by spawning a "thread" that shares its parent's address space, but not its file
+// descriptor table, so that we can close random file descriptors without affecting the original
+// process. Note that this doesn't go through pthread_create, so TLS is shared with the spawning
+// process.
+static void* pseudothread_stack;
+
+static int debuggerd_dispatch_pseudothread(void* arg) {
+  debugger_thread_info* thread_info = static_cast<debugger_thread_info*>(arg);
+
+  for (int i = 0; i < 1024; ++i) {
+    close(i);
+  }
+
+  int devnull = TEMP_FAILURE_RETRY(open("/dev/null", O_RDWR));
+
+  // devnull will be 0.
+  TEMP_FAILURE_RETRY(dup2(devnull, STDOUT_FILENO));
+  TEMP_FAILURE_RETRY(dup2(devnull, STDERR_FILENO));
+
+  int pipefds[2];
+  if (pipe(pipefds) != 0) {
+    fatal("failed to create pipe");
+  }
+
+  // Don't use fork(2) to avoid calling pthread_atfork handlers.
+  int forkpid = clone(nullptr, nullptr, SIGCHLD, nullptr);
+  if (forkpid == -1) {
+    __libc_format_log(ANDROID_LOG_FATAL, "libc", "failed to fork in debuggerd signal handler: %s",
+                      strerror(errno));
+  } else if (forkpid == 0) {
+    TEMP_FAILURE_RETRY(dup2(pipefds[1], STDOUT_FILENO));
+    close(pipefds[0]);
+    close(pipefds[1]);
+
+    char buf[10];
+    snprintf(buf, sizeof(buf), "%d", thread_info->crashing_tid);
+    execl(CRASH_DUMP_PATH, CRASH_DUMP_NAME, buf, nullptr);
+
+    fatal("exec failed: %s", strerror(errno));
+  } else {
+    close(pipefds[1]);
+    char buf[4];
+    ssize_t rc = TEMP_FAILURE_RETRY(read(pipefds[0], &buf, sizeof(buf)));
+    if (rc == -1) {
+      __libc_format_log(ANDROID_LOG_FATAL, "libc", "read of IPC pipe failed: %s", strerror(errno));
+    } else if (rc == 0) {
+      __libc_format_log(ANDROID_LOG_FATAL, "libc", "crash_dump helper failed to exec");
+    } else if (rc != 1) {
+      __libc_format_log(ANDROID_LOG_FATAL, "libc",
+                        "read of IPC pipe returned unexpected value: %zd", rc);
+    } else {
+      if (buf[0] != '\1') {
+        __libc_format_log(ANDROID_LOG_FATAL, "libc", "crash_dump helper reported failure");
+      } else {
+        thread_info->crash_dump_started = true;
+      }
+    }
+    close(pipefds[0]);
+
+    // Don't leave a zombie child.
+    siginfo_t child_siginfo;
+    if (TEMP_FAILURE_RETRY(waitid(P_PID, forkpid, &child_siginfo, WEXITED)) != 0) {
+      __libc_format_log(ANDROID_LOG_FATAL, "libc", "failed to wait for crash_dump helper: %s",
+                        strerror(errno));
+      thread_info->crash_dump_started = false;
+    }
+  }
+
+  syscall(__NR_exit, 0);
+  return 0;
+}
+
+static void resend_signal(siginfo_t* info) {
+  // Signals can either be fatal or nonfatal.
+  // For fatal signals, crash_dump will send us the signal we crashed with
+  // before resuming us, so that processes using waitpid on us will see that we
+  // exited with the correct exit status (e.g. so that sh will report
+  // "Segmentation fault" instead of "Killed"). For this to work, we need
+  // to deregister our signal handler for that signal before continuing.
+  if (info->si_signo != DEBUGGER_SIGNAL) {
+    signal(info->si_signo, SIG_DFL);
+  }
+
+  // We need to return from our signal handler so that crash_dump can see the
+  // signal via ptrace and dump the thread that crashed. However, returning
+  // does not guarantee that the signal will be thrown again, even for SIGSEGV
+  // and friends, since the signal could have been sent manually. We blocked
+  // all signals when registering the handler, so resending the signal (using
+  // rt_tgsigqueueinfo(2) to preserve SA_SIGINFO) will cause it to be delivered
+  // when our signal handler returns.
+  int rc = syscall(SYS_rt_tgsigqueueinfo, getpid(), gettid(), info->si_signo, info);
+  if (rc != 0) {
+    fatal("failed to resend signal during crash: %s", strerror(errno));
+  }
+
+  if (info->si_signo == DEBUGGER_SIGNAL) {
+    pthread_mutex_unlock(&crash_mutex);
+  }
+}
+
+// Handler that does crash dumping by forking and doing the processing in the child.
+// Do this by ptracing the relevant thread, and then execing debuggerd to do the actual dump.
+static void debuggerd_signal_handler(int signal_number, siginfo_t* info, void*) {
+  int ret = pthread_mutex_lock(&crash_mutex);
+  if (ret != 0) {
+    __libc_format_log(ANDROID_LOG_INFO, "libc", "pthread_mutex_lock failed: %s", strerror(ret));
+    return;
+  }
+
+  // It's possible somebody cleared the SA_SIGINFO flag, which would mean
+  // our "info" arg holds an undefined value.
+  if (!have_siginfo(signal_number)) {
+    info = nullptr;
+  }
+
+  struct siginfo si = {};
+  if (!info) {
+    memset(&si, 0, sizeof(si));
+    si.si_signo = signal_number;
+    si.si_code = SI_USER;
+    si.si_pid = getpid();
+    si.si_uid = getuid();
+    info = &si;
+  } else if (info->si_code >= 0 || info->si_code == SI_TKILL) {
+    // rt_tgsigqueueinfo(2)'s documentation appears to be incorrect on kernels
+    // that contain commit 66dd34a (3.9+). The manpage claims to only allow
+    // negative si_code values that are not SI_TKILL, but 66dd34a changed the
+    // check to allow all si_code values in calls coming from inside the house.
+  }
+
+  log_signal_summary(signal_number, info);
+  if (prctl(PR_GET_DUMPABLE, 0, 0, 0, 0) == 0) {
+    // The process has disabled core dumps and PTRACE_ATTACH, and does not want to be dumped.
+    __libc_format_log(ANDROID_LOG_INFO, "libc",
+                      "Suppressing debuggerd output because prctl(PR_GET_DUMPABLE)==0");
+
+    resend_signal(info);
+    return;
+  }
+
+  void* abort_message = nullptr;
+  if (g_callbacks.get_abort_message) {
+    abort_message = g_callbacks.get_abort_message();
+  }
+  // Populate si_value with the abort message address, if found.
+  if (abort_message) {
+    info->si_value.sival_ptr = abort_message;
+  }
+
+  debugger_thread_info thread_info = {
+    .crash_dump_started = false,
+    .pseudothread_tid = -1,
+    .crashing_tid = gettid(),
+    .signal_number = signal_number,
+    .info = info
+  };
+
+  // Essentially pthread_create without CLONE_FILES (see debuggerd_dispatch_pseudothread).
+  pid_t child_pid =
+    clone(debuggerd_dispatch_pseudothread, pseudothread_stack,
+          CLONE_THREAD | CLONE_SIGHAND | CLONE_VM | CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID,
+          &thread_info, nullptr, nullptr, &thread_info.pseudothread_tid);
+  if (child_pid == -1) {
+    fatal("failed to spawn debuggerd dispatch thread: %s", strerror(errno));
+  }
+
+  // Wait for the child to start...
+  __futex_wait(&thread_info.pseudothread_tid, -1, nullptr);
+
+  // and then wait for it to finish.
+  __futex_wait(&thread_info.pseudothread_tid, child_pid, nullptr);
+
+  // Signals can either be fatal or nonfatal.
+  // For fatal signals, crash_dump will PTRACE_CONT us with the signal we
+  // crashed with, so that processes using waitpid on us will see that we
+  // exited with the correct exit status (e.g. so that sh will report
+  // "Segmentation fault" instead of "Killed"). For this to work, we need
+  // to deregister our signal handler for that signal before continuing.
+  if (signal_number != DEBUGGER_SIGNAL) {
+    signal(signal_number, SIG_DFL);
+  }
+
+  resend_signal(info);
+}
+
+void debuggerd_init(debuggerd_callbacks_t* callbacks) {
+  if (callbacks) {
+    g_callbacks = *callbacks;
+  }
+
+  void* thread_stack_allocation =
+    mmap(nullptr, PAGE_SIZE * 3, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+  if (thread_stack_allocation == MAP_FAILED) {
+    fatal("failed to allocate debuggerd thread stack");
+  }
+
+  char* stack = static_cast<char*>(thread_stack_allocation) + PAGE_SIZE;
+  if (mprotect(stack, PAGE_SIZE, PROT_READ | PROT_WRITE) != 0) {
+    fatal("failed to mprotect debuggerd thread stack");
+  }
+
+  // Stack grows negatively, set it to the last byte in the page...
+  stack = (stack + PAGE_SIZE - 1);
+  // and align it.
+  stack -= 15;
+  pseudothread_stack = stack;
+
+  struct sigaction action;
+  memset(&action, 0, sizeof(action));
+  sigfillset(&action.sa_mask);
+  action.sa_sigaction = debuggerd_signal_handler;
+  action.sa_flags = SA_RESTART | SA_SIGINFO;
+
+  // Use the alternate signal stack if available so we can catch stack overflows.
+  action.sa_flags |= SA_ONSTACK;
+  debuggerd_register_handlers(&action);
+}
diff --git a/debuggerd/include/debuggerd/client.h b/debuggerd/include/debuggerd/client.h
index aeb723b..91f143b 100644
--- a/debuggerd/include/debuggerd/client.h
+++ b/debuggerd/include/debuggerd/client.h
@@ -1,5 +1,5 @@
 /*
- * Copyright 2016 The Android Open Source Project
+ * Copyright 2016, The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,44 +16,21 @@
 
 #pragma once
 
-#include <stdint.h>
+#include <stdbool.h>
 #include <sys/cdefs.h>
-#include <sys/types.h>
+#include <unistd.h>
 
-// On 32-bit devices, DEBUGGER_SOCKET_NAME is a 32-bit debuggerd.
-// On 64-bit devices, DEBUGGER_SOCKET_NAME is a 64-bit debuggerd.
-#define DEBUGGER_SOCKET_NAME "android:debuggerd"
+#include <android-base/unique_fd.h>
 
-// Used only on 64-bit devices for debuggerd32.
-#define DEBUGGER32_SOCKET_NAME "android:debuggerd32"
+enum DebuggerdDumpType {
+  kDebuggerdBacktrace,
+  kDebuggerdTombstone,
+};
 
-__BEGIN_DECLS
+// Trigger a dump of specified process to output_fd.
+// output_fd is *not* consumed, timeouts <= 0 will wait forever.
+bool debuggerd_trigger_dump(pid_t pid, android::base::unique_fd output_fd,
+                            enum DebuggerdDumpType dump_type, int timeout_ms);
 
-typedef enum {
-  // dump a crash
-  DEBUGGER_ACTION_CRASH,
-  // dump a tombstone file
-  DEBUGGER_ACTION_DUMP_TOMBSTONE,
-  // dump a backtrace only back to the socket
-  DEBUGGER_ACTION_DUMP_BACKTRACE,
-} debugger_action_t;
-
-// Make sure that all values have a fixed size so that this structure
-// is the same for 32 bit and 64 bit processes.
-typedef struct __attribute__((packed)) {
-  int32_t action;
-  pid_t tid;
-  pid_t ignore_tid;
-  uint64_t abort_msg_address;
-} debugger_msg_t;
-
-// These callbacks are called in a signal handler, and thus must be async signal safe.
-// If null, the callbacks will not be called.
-typedef struct {
-  struct abort_msg_t* (*get_abort_message)();
-  void (*post_dump)();
-} debuggerd_callbacks_t;
-
-void debuggerd_init(debuggerd_callbacks_t* callbacks);
-
-__END_DECLS
+int dump_backtrace_to_file(pid_t tid, int fd);
+int dump_backtrace_to_file_timeout(pid_t tid, int fd, int timeout_secs);
diff --git a/debuggerd/include/debuggerd/handler.h b/debuggerd/include/debuggerd/handler.h
new file mode 100644
index 0000000..7196e0a
--- /dev/null
+++ b/debuggerd/include/debuggerd/handler.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <signal.h>
+#include <stdint.h>
+#include <sys/cdefs.h>
+#include <sys/types.h>
+
+__BEGIN_DECLS
+
+// These callbacks are called in a signal handler, and thus must be async signal safe.
+// If null, the callbacks will not be called.
+typedef struct {
+  struct abort_msg_t* (*get_abort_message)();
+  void (*post_dump)();
+} debuggerd_callbacks_t;
+
+void debuggerd_init(debuggerd_callbacks_t* callbacks);
+
+// DEBUGGER_ACTION_DUMP_TOMBSTONE and DEBUGGER_ACTION_DUMP_BACKTRACE are both
+// triggered via DEBUGGER_SIGNAL. The debugger_action_t is sent via si_value
+// using sigqueue(2) or equivalent. If no si_value is specified (e.g. if the
+// signal is sent by kill(2)), the default behavior is to print the backtrace
+// to the log.
+#define DEBUGGER_SIGNAL (__SIGRTMIN + 3)
+
+static void __attribute__((__unused__)) debuggerd_register_handlers(struct sigaction* action) {
+  sigaction(SIGABRT, action, nullptr);
+  sigaction(SIGBUS, action, nullptr);
+  sigaction(SIGFPE, action, nullptr);
+  sigaction(SIGILL, action, nullptr);
+  sigaction(SIGSEGV, action, nullptr);
+#if defined(SIGSTKFLT)
+  sigaction(SIGSTKFLT, action, nullptr);
+#endif
+  sigaction(SIGSYS, action, nullptr);
+  sigaction(SIGTRAP, action, nullptr);
+  sigaction(DEBUGGER_SIGNAL, action, nullptr);
+}
+
+__END_DECLS
diff --git a/debuggerd/include/debuggerd/protocol.h b/debuggerd/include/debuggerd/protocol.h
new file mode 100644
index 0000000..bb2ab0d
--- /dev/null
+++ b/debuggerd/include/debuggerd/protocol.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2016, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+// Sockets in the ANDROID_SOCKET_NAMESPACE_RESERVED namespace.
+// Both sockets are SOCK_SEQPACKET sockets, so no explicit length field is needed.
+constexpr char kTombstonedCrashSocketName[] = "tombstoned_crash";
+constexpr char kTombstonedInterceptSocketName[] = "tombstoned_intercept";
+
+enum class CrashPacketType : uint8_t {
+  // Initial request from crash_dump.
+  kDumpRequest = 0,
+
+  // Notification of a completed crash dump.
+  // Sent after a dump is completed and the process has been untraced, but
+  // before it has been resumed with SIGCONT.
+  kCompletedDump,
+
+  // Responses to kRequest.
+  // kPerformDump sends along an output fd via cmsg(3).
+  kPerformDump = 128,
+  kAbortDump,
+};
+
+struct DumpRequest {
+  int32_t pid;
+};
+
+// The full packet must always be written, regardless of whether the union is used.
+struct TombstonedCrashPacket {
+  CrashPacketType packet_type;
+  union {
+    DumpRequest dump_request;
+  } packet;
+};
+
+// Comes with a file descriptor via SCM_RIGHTS.
+// This packet should be sent before an actual dump happens.
+struct InterceptRequest {
+  int32_t pid;
+};
+
+// Sent either immediately upon failure, or when the intercept has been used.
+struct InterceptResponse {
+  uint8_t success;          // 0 or 1
+  char error_message[127];  // always null-terminated
+};
diff --git a/debuggerd/include/debuggerd/util.h b/debuggerd/include/debuggerd/util.h
new file mode 100644
index 0000000..6051714
--- /dev/null
+++ b/debuggerd/include/debuggerd/util.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2016, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+
+#include <android-base/unique_fd.h>
+
+// *** WARNING ***
+// tombstoned's sockets are SOCK_SEQPACKET sockets.
+// Short reads are treated as errors and short writes are assumed to not happen.
+
+// Sends a packet with an attached fd.
+ssize_t send_fd(int sockfd, const void* _Nonnull data, size_t len, android::base::unique_fd fd);
+
+// Receives a packet and optionally, its attached fd.
+// If out_fd is non-null, packets can optionally have an attached fd.
+// If out_fd is null, received packets must not have an attached fd.
+//
+// Errors:
+//   EOVERFLOW: sockfd is SOCK_DGRAM or SOCK_SEQPACKET and buffer is too small.
+//              The first len bytes of the packet are stored in data, but the
+//              rest of the packet is dropped.
+//   ERANGE:    too many file descriptors were attached to the packet.
+//   ENOMSG:    not enough file descriptors were attached to the packet.
+//
+//   plus any errors returned by the underlying recvmsg.
+ssize_t recv_fd(int sockfd, void* _Nonnull data, size_t len,
+                android::base::unique_fd* _Nullable out_fd);
+
+bool Pipe(android::base::unique_fd* read, android::base::unique_fd* write);
diff --git a/debuggerd/arm/machine.cpp b/debuggerd/libdebuggerd/arm/machine.cpp
similarity index 100%
rename from debuggerd/arm/machine.cpp
rename to debuggerd/libdebuggerd/arm/machine.cpp
diff --git a/debuggerd/arm64/machine.cpp b/debuggerd/libdebuggerd/arm64/machine.cpp
similarity index 100%
rename from debuggerd/arm64/machine.cpp
rename to debuggerd/libdebuggerd/arm64/machine.cpp
diff --git a/debuggerd/backtrace.cpp b/debuggerd/libdebuggerd/backtrace.cpp
similarity index 100%
rename from debuggerd/backtrace.cpp
rename to debuggerd/libdebuggerd/backtrace.cpp
diff --git a/debuggerd/elf_utils.cpp b/debuggerd/libdebuggerd/elf_utils.cpp
similarity index 100%
rename from debuggerd/elf_utils.cpp
rename to debuggerd/libdebuggerd/elf_utils.cpp
diff --git a/debuggerd/backtrace.h b/debuggerd/libdebuggerd/include/backtrace.h
similarity index 100%
rename from debuggerd/backtrace.h
rename to debuggerd/libdebuggerd/include/backtrace.h
diff --git a/debuggerd/elf_utils.h b/debuggerd/libdebuggerd/include/elf_utils.h
similarity index 100%
rename from debuggerd/elf_utils.h
rename to debuggerd/libdebuggerd/include/elf_utils.h
diff --git a/debuggerd/machine.h b/debuggerd/libdebuggerd/include/machine.h
similarity index 100%
rename from debuggerd/machine.h
rename to debuggerd/libdebuggerd/include/machine.h
diff --git a/debuggerd/open_files_list.h b/debuggerd/libdebuggerd/include/open_files_list.h
similarity index 100%
rename from debuggerd/open_files_list.h
rename to debuggerd/libdebuggerd/include/open_files_list.h
diff --git a/debuggerd/tombstone.h b/debuggerd/libdebuggerd/include/tombstone.h
similarity index 97%
rename from debuggerd/tombstone.h
rename to debuggerd/libdebuggerd/include/tombstone.h
index 126f804..4ff24af 100644
--- a/debuggerd/tombstone.h
+++ b/debuggerd/libdebuggerd/include/tombstone.h
@@ -23,6 +23,8 @@
 #include <set>
 #include <string>
 
+#include "open_files_list.h"
+
 class BacktraceMap;
 
 /* Create and open a tombstone file for writing.
diff --git a/debuggerd/utility.h b/debuggerd/libdebuggerd/include/utility.h
similarity index 96%
rename from debuggerd/utility.h
rename to debuggerd/libdebuggerd/include/utility.h
index f7a3f73..bbc4546 100644
--- a/debuggerd/utility.h
+++ b/debuggerd/libdebuggerd/include/utility.h
@@ -18,6 +18,7 @@
 #ifndef _DEBUGGERD_UTILITY_H
 #define _DEBUGGERD_UTILITY_H
 
+#include <signal.h>
 #include <stdbool.h>
 #include <sys/types.h>
 
@@ -78,7 +79,7 @@
 void _LOG(log_t* log, logtype ltype, const char *fmt, ...)
         __attribute__ ((format(printf, 3, 4)));
 
-int wait_for_signal(pid_t tid);
+bool wait_for_signal(pid_t tid, siginfo_t* siginfo);
 
 void dump_memory(log_t* log, Backtrace* backtrace, uintptr_t addr, const char* fmt, ...);
 
diff --git a/debuggerd/mips/machine.cpp b/debuggerd/libdebuggerd/mips/machine.cpp
similarity index 100%
rename from debuggerd/mips/machine.cpp
rename to debuggerd/libdebuggerd/mips/machine.cpp
diff --git a/debuggerd/mips64/machine.cpp b/debuggerd/libdebuggerd/mips64/machine.cpp
similarity index 100%
rename from debuggerd/mips64/machine.cpp
rename to debuggerd/libdebuggerd/mips64/machine.cpp
diff --git a/debuggerd/open_files_list.cpp b/debuggerd/libdebuggerd/open_files_list.cpp
similarity index 100%
rename from debuggerd/open_files_list.cpp
rename to debuggerd/libdebuggerd/open_files_list.cpp
diff --git a/debuggerd/test/BacktraceMock.h b/debuggerd/libdebuggerd/test/BacktraceMock.h
similarity index 100%
rename from debuggerd/test/BacktraceMock.h
rename to debuggerd/libdebuggerd/test/BacktraceMock.h
diff --git a/debuggerd/test/dump_memory_test.cpp b/debuggerd/libdebuggerd/test/dump_memory_test.cpp
similarity index 100%
rename from debuggerd/test/dump_memory_test.cpp
rename to debuggerd/libdebuggerd/test/dump_memory_test.cpp
diff --git a/debuggerd/test/elf_fake.cpp b/debuggerd/libdebuggerd/test/elf_fake.cpp
similarity index 100%
rename from debuggerd/test/elf_fake.cpp
rename to debuggerd/libdebuggerd/test/elf_fake.cpp
diff --git a/debuggerd/test/elf_fake.h b/debuggerd/libdebuggerd/test/elf_fake.h
similarity index 100%
rename from debuggerd/test/elf_fake.h
rename to debuggerd/libdebuggerd/test/elf_fake.h
diff --git a/debuggerd/test/host_signal_fixup.h b/debuggerd/libdebuggerd/test/host_signal_fixup.h
similarity index 100%
rename from debuggerd/test/host_signal_fixup.h
rename to debuggerd/libdebuggerd/test/host_signal_fixup.h
diff --git a/debuggerd/test/log_fake.cpp b/debuggerd/libdebuggerd/test/log_fake.cpp
similarity index 100%
rename from debuggerd/test/log_fake.cpp
rename to debuggerd/libdebuggerd/test/log_fake.cpp
diff --git a/debuggerd/test/log_fake.h b/debuggerd/libdebuggerd/test/log_fake.h
similarity index 100%
rename from debuggerd/test/log_fake.h
rename to debuggerd/libdebuggerd/test/log_fake.h
diff --git a/debuggerd/test/open_files_list_test.cpp b/debuggerd/libdebuggerd/test/open_files_list_test.cpp
similarity index 100%
rename from debuggerd/test/open_files_list_test.cpp
rename to debuggerd/libdebuggerd/test/open_files_list_test.cpp
diff --git a/debuggerd/test/property_fake.cpp b/debuggerd/libdebuggerd/test/property_fake.cpp
similarity index 100%
rename from debuggerd/test/property_fake.cpp
rename to debuggerd/libdebuggerd/test/property_fake.cpp
diff --git a/debuggerd/test/ptrace_fake.cpp b/debuggerd/libdebuggerd/test/ptrace_fake.cpp
similarity index 100%
rename from debuggerd/test/ptrace_fake.cpp
rename to debuggerd/libdebuggerd/test/ptrace_fake.cpp
diff --git a/debuggerd/test/ptrace_fake.h b/debuggerd/libdebuggerd/test/ptrace_fake.h
similarity index 100%
rename from debuggerd/test/ptrace_fake.h
rename to debuggerd/libdebuggerd/test/ptrace_fake.h
diff --git a/debuggerd/test/sys/system_properties.h b/debuggerd/libdebuggerd/test/sys/system_properties.h
similarity index 100%
rename from debuggerd/test/sys/system_properties.h
rename to debuggerd/libdebuggerd/test/sys/system_properties.h
diff --git a/debuggerd/test/tombstone_test.cpp b/debuggerd/libdebuggerd/test/tombstone_test.cpp
similarity index 100%
rename from debuggerd/test/tombstone_test.cpp
rename to debuggerd/libdebuggerd/test/tombstone_test.cpp
diff --git a/debuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
similarity index 98%
rename from debuggerd/tombstone.cpp
rename to debuggerd/libdebuggerd/tombstone.cpp
index e76edb9..01e9cf6 100644
--- a/debuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -32,6 +32,7 @@
 #include <memory>
 #include <string>
 
+#include <android/log.h>
 #include <android-base/stringprintf.h>
 #include <backtrace/Backtrace.h>
 #include <backtrace/BacktraceMap.h>
@@ -39,9 +40,8 @@
 #include <log/log.h>
 #include <log/logprint.h>
 #include <private/android_filesystem_config.h>
-#include <private/android_logger.h>
 
-#include <selinux/android.h>
+#include "debuggerd/handler.h"
 
 #include "backtrace.h"
 #include "elf_utils.h"
@@ -86,6 +86,7 @@
     case SIGSTOP: return "SIGSTOP";
     case SIGSYS: return "SIGSYS";
     case SIGTRAP: return "SIGTRAP";
+    case DEBUGGER_SIGNAL: return "<debuggerd signal>";
     default: return "?";
   }
 }
@@ -625,7 +626,9 @@
                        const OpenFilesList& open_files, pid_t pid, pid_t tid,
                        const std::set<pid_t>& siblings, uintptr_t abort_msg_address) {
   // don't copy log messages to tombstone unless this is a dev device
-  bool want_logs = __android_log_is_debuggable();
+  char value[PROPERTY_VALUE_MAX];
+  property_get("ro.debuggable", value, "0");
+  bool want_logs = (value[0] == '1');
 
   _LOG(log, logtype::HEADER,
        "*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***\n");
diff --git a/debuggerd/utility.cpp b/debuggerd/libdebuggerd/utility.cpp
similarity index 95%
rename from debuggerd/utility.cpp
rename to debuggerd/libdebuggerd/utility.cpp
index 419d36c..57209aa 100644
--- a/debuggerd/utility.cpp
+++ b/debuggerd/libdebuggerd/utility.cpp
@@ -71,21 +71,25 @@
   }
 }
 
-int wait_for_signal(pid_t tid) {
+bool wait_for_signal(pid_t tid, siginfo_t* siginfo) {
   while (true) {
     int status;
     pid_t n = TEMP_FAILURE_RETRY(waitpid(tid, &status, __WALL));
     if (n == -1) {
       ALOGE("waitpid failed: tid %d, %s", tid, strerror(errno));
-      return -1;
+      return false;
     } else if (n == tid) {
       if (WIFSTOPPED(status)) {
-        return WSTOPSIG(status);
+        if (ptrace(PTRACE_GETSIGINFO, tid, nullptr, siginfo) != 0) {
+          ALOGE("PTRACE_GETSIGINFO failed: %s", strerror(errno));
+          return false;
+        }
+        return true;
       } else {
         ALOGE("unexpected waitpid response: n=%d, status=%08x\n", n, status);
         // This is the only circumstance under which we can allow a detach
         // to fail with ESRCH, which indicates the tid has exited.
-        return -1;
+        return false;
       }
     }
   }
diff --git a/debuggerd/x86/machine.cpp b/debuggerd/libdebuggerd/x86/machine.cpp
similarity index 100%
rename from debuggerd/x86/machine.cpp
rename to debuggerd/libdebuggerd/x86/machine.cpp
diff --git a/debuggerd/x86_64/machine.cpp b/debuggerd/libdebuggerd/x86_64/machine.cpp
similarity index 100%
rename from debuggerd/x86_64/machine.cpp
rename to debuggerd/libdebuggerd/x86_64/machine.cpp
diff --git a/debuggerd/signal_sender.h b/debuggerd/signal_sender.h
deleted file mode 100644
index 0443272..0000000
--- a/debuggerd/signal_sender.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _DEBUGGERD_SIGNAL_SENDER_H
-#define _DEBUGGERD_SIGNAL_SENDER_H
-
-#include <sys/types.h>
-
-bool start_signal_sender();
-bool stop_signal_sender();
-
-// Sends a signal to a target process or thread.
-// If tid is greater than zero, this performs tgkill(pid, tid, signal).
-// Otherwise, it performs kill(pid, signal).
-bool send_signal(pid_t pid, pid_t tid, int signal);
-
-#endif
diff --git a/debuggerd/test/selinux_fake.cpp b/debuggerd/test/selinux_fake.cpp
deleted file mode 100644
index acdd0a9..0000000
--- a/debuggerd/test/selinux_fake.cpp
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-extern "C" int selinux_android_restorecon(const char*, unsigned int) {
-  return 0;
-}
diff --git a/debuggerd/tombstoned/intercept_manager.cpp b/debuggerd/tombstoned/intercept_manager.cpp
new file mode 100644
index 0000000..789260d
--- /dev/null
+++ b/debuggerd/tombstoned/intercept_manager.cpp
@@ -0,0 +1,182 @@
+/*
+ * Copyright 2016, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "intercept_manager.h"
+
+#include <inttypes.h>
+#include <sys/types.h>
+
+#include <unordered_map>
+
+#include <event2/event.h>
+#include <event2/listener.h>
+
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+#include <cutils/sockets.h>
+
+#include "debuggerd/protocol.h"
+#include "debuggerd/util.h"
+
+using android::base::unique_fd;
+
+static void intercept_close_cb(evutil_socket_t sockfd, short event, void* arg) {
+  auto intercept = reinterpret_cast<Intercept*>(arg);
+  InterceptManager* intercept_manager = intercept->intercept_manager;
+
+  CHECK_EQ(sockfd, intercept->sockfd.get());
+
+  // If we can read, either we received unexpected data from the other side, or the other side
+  // closed their end of the socket. Either way, kill the intercept.
+
+  // Ownership of intercept differs based on whether we've registered it with InterceptManager.
+  if (!intercept->registered) {
+    delete intercept;
+  } else {
+    auto it = intercept_manager->intercepts.find(intercept->intercept_pid);
+    if (it == intercept_manager->intercepts.end()) {
+      LOG(FATAL) << "intercept close callback called after intercept was already removed?";
+    }
+    if (it->second.get() != intercept) {
+      LOG(FATAL) << "intercept close callback has different Intercept from InterceptManager?";
+    }
+
+    const char* reason;
+    if ((event & EV_TIMEOUT) != 0) {
+      reason = "due to timeout";
+    } else {
+      reason = "due to input";
+    }
+
+    LOG(INFO) << "intercept for pid " << intercept->intercept_pid << " terminated " << reason;
+    intercept_manager->intercepts.erase(it);
+  }
+}
+
+static void intercept_request_cb(evutil_socket_t sockfd, short ev, void* arg) {
+  auto intercept = reinterpret_cast<Intercept*>(arg);
+  InterceptManager* intercept_manager = intercept->intercept_manager;
+
+  CHECK_EQ(sockfd, intercept->sockfd.get());
+
+  if ((ev & EV_TIMEOUT) != 0) {
+    LOG(WARNING) << "tombstoned didn't receive InterceptRequest before timeout";
+    goto fail;
+  } else if ((ev & EV_READ) == 0) {
+    LOG(WARNING) << "tombstoned received unexpected event on intercept socket";
+    goto fail;
+  }
+
+  {
+    unique_fd rcv_fd;
+    InterceptRequest intercept_request;
+    ssize_t result = recv_fd(sockfd, &intercept_request, sizeof(intercept_request), &rcv_fd);
+
+    if (result == -1) {
+      PLOG(WARNING) << "failed to read from intercept socket";
+      goto fail;
+    } else if (result != sizeof(intercept_request)) {
+      LOG(WARNING) << "intercept socket received short read of length " << result << " (expected "
+                   << sizeof(intercept_request) << ")";
+      goto fail;
+    }
+
+    // Move the received FD to the upper half, in order to more easily notice FD leaks.
+    int moved_fd = fcntl(rcv_fd.get(), F_DUPFD, 512);
+    if (moved_fd == -1) {
+      LOG(WARNING) << "failed to move received fd (" << rcv_fd.get() << ")";
+      goto fail;
+    }
+    rcv_fd.reset(moved_fd);
+
+    // We trust the other side, so only do minimal validity checking.
+    if (intercept_request.pid <= 0 || intercept_request.pid > std::numeric_limits<pid_t>::max()) {
+      InterceptResponse response = {};
+      snprintf(response.error_message, sizeof(response.error_message), "invalid pid %" PRId32,
+               intercept_request.pid);
+      TEMP_FAILURE_RETRY(write(sockfd, &response, sizeof(response)));
+      goto fail;
+    }
+
+    intercept->intercept_pid = intercept_request.pid;
+
+    // Register the intercept with the InterceptManager.
+    if (intercept_manager->intercepts.count(intercept_request.pid) > 0) {
+      InterceptResponse response = {};
+      snprintf(response.error_message, sizeof(response.error_message),
+               "pid %" PRId32 " already intercepted", intercept_request.pid);
+      TEMP_FAILURE_RETRY(write(sockfd, &response, sizeof(response)));
+      LOG(WARNING) << response.error_message;
+      goto fail;
+    }
+
+    intercept->output_fd = std::move(rcv_fd);
+    intercept_manager->intercepts[intercept_request.pid] = std::unique_ptr<Intercept>(intercept);
+    intercept->registered = true;
+
+    LOG(INFO) << "tombstoned registered intercept for pid " << intercept_request.pid;
+
+    // Register a different read event on the socket so that we can remove intercepts if the socket
+    // closes (e.g. if a user CTRL-C's the process that requested the intercept).
+    event_assign(intercept->intercept_event, intercept_manager->base, sockfd, EV_READ | EV_TIMEOUT,
+                 intercept_close_cb, arg);
+
+    struct timeval timeout = { .tv_sec = 10, .tv_usec = 0 };
+    event_add(intercept->intercept_event, &timeout);
+  }
+
+  return;
+
+fail:
+  delete intercept;
+}
+
+static void intercept_accept_cb(evconnlistener* listener, evutil_socket_t sockfd, sockaddr*, int,
+                                void* arg) {
+  Intercept* intercept = new Intercept();
+  intercept->intercept_manager = static_cast<InterceptManager*>(arg);
+  intercept->sockfd.reset(sockfd);
+
+  struct timeval timeout = { 1, 0 };
+  event_base* base = evconnlistener_get_base(listener);
+  event* intercept_event =
+    event_new(base, sockfd, EV_TIMEOUT | EV_READ, intercept_request_cb, intercept);
+  intercept->intercept_event = intercept_event;
+  event_add(intercept_event, &timeout);
+}
+
+InterceptManager::InterceptManager(event_base* base, int intercept_socket) : base(base) {
+  this->listener = evconnlistener_new(base, intercept_accept_cb, this, -1, LEV_OPT_CLOSE_ON_FREE,
+                                      intercept_socket);
+}
+
+bool InterceptManager::GetIntercept(pid_t pid, android::base::unique_fd* out_fd) {
+  auto it = this->intercepts.find(pid);
+  if (it == this->intercepts.end()) {
+    return false;
+  }
+
+  auto intercept = std::move(it->second);
+  this->intercepts.erase(it);
+
+  LOG(INFO) << "found intercept fd " << intercept->output_fd.get() << " for pid " << pid;
+  InterceptResponse response = {};
+  response.success = 1;
+  TEMP_FAILURE_RETRY(write(intercept->sockfd, &response, sizeof(response)));
+  *out_fd = std::move(intercept->output_fd);
+
+  return true;
+}
diff --git a/debuggerd/tombstoned/intercept_manager.h b/debuggerd/tombstoned/intercept_manager.h
new file mode 100644
index 0000000..cb5db62
--- /dev/null
+++ b/debuggerd/tombstoned/intercept_manager.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2016, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <sys/types.h>
+
+#include <unordered_map>
+
+#include <event2/event.h>
+#include <event2/listener.h>
+
+#include <android-base/unique_fd.h>
+
+struct InterceptManager;
+
+struct Intercept {
+  ~Intercept() {
+    event_free(intercept_event);
+  }
+
+  InterceptManager* intercept_manager = nullptr;
+  event* intercept_event = nullptr;
+  android::base::unique_fd sockfd;
+
+  pid_t intercept_pid = -1;
+  android::base::unique_fd output_fd;
+  bool registered = false;
+};
+
+struct InterceptManager {
+  event_base* base;
+  std::unordered_map<pid_t, std::unique_ptr<Intercept>> intercepts;
+  evconnlistener* listener = nullptr;
+
+  InterceptManager(event_base* _Nonnull base, int intercept_socket);
+  InterceptManager(InterceptManager& copy) = delete;
+  InterceptManager(InterceptManager&& move) = delete;
+
+  bool GetIntercept(pid_t pid, android::base::unique_fd* out_fd);
+};
diff --git a/debuggerd/tombstoned/tombstoned.cpp b/debuggerd/tombstoned/tombstoned.cpp
new file mode 100644
index 0000000..8705ece
--- /dev/null
+++ b/debuggerd/tombstoned/tombstoned.cpp
@@ -0,0 +1,289 @@
+/*
+ * Copyright 2016, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <array>
+#include <deque>
+#include <unordered_map>
+
+#include <event2/event.h>
+#include <event2/listener.h>
+#include <event2/thread.h>
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
+#include <cutils/sockets.h>
+
+#include "debuggerd/protocol.h"
+#include "debuggerd/util.h"
+
+#include "intercept_manager.h"
+
+using android::base::StringPrintf;
+using android::base::unique_fd;
+
+static InterceptManager* intercept_manager;
+
+enum CrashStatus {
+  kCrashStatusRunning,
+  kCrashStatusQueued,
+};
+
+// Ownership of Crash is a bit messy.
+// It's either owned by an active event that must have a timeout, or owned by
+// queued_requests, in the case that multiple crashes come in at the same time.
+struct Crash {
+  ~Crash() {
+    event_free(crash_event);
+  }
+
+  unique_fd crash_fd;
+  pid_t crash_pid;
+  event* crash_event = nullptr;
+};
+
+static constexpr char kTombstoneDirectory[] = "/data/tombstones/";
+static constexpr size_t kTombstoneCount = 10;
+static int tombstone_directory_fd = -1;
+static int next_tombstone = 0;
+
+static constexpr size_t kMaxConcurrentDumps = 1;
+static size_t num_concurrent_dumps = 0;
+
+static std::deque<Crash*> queued_requests;
+
+// Forward declare the callbacks so they can be placed in a sensible order.
+static void crash_accept_cb(evconnlistener* listener, evutil_socket_t sockfd, sockaddr*, int, void*);
+static void crash_request_cb(evutil_socket_t sockfd, short ev, void* arg);
+static void crash_completed_cb(evutil_socket_t sockfd, short ev, void* arg);
+
+static void find_oldest_tombstone() {
+  size_t oldest_tombstone = 0;
+  time_t oldest_time = std::numeric_limits<time_t>::max();
+
+  for (size_t i = 0; i < kTombstoneCount; ++i) {
+    std::string path = android::base::StringPrintf("%stombstone_%02zu", kTombstoneDirectory, i);
+    struct stat st;
+    if (stat(path.c_str(), &st) != 0) {
+      if (errno == ENOENT) {
+        oldest_tombstone = i;
+        break;
+      } else {
+        PLOG(ERROR) << "failed to stat " << path;
+        continue;
+      }
+    }
+
+    if (st.st_mtime < oldest_time) {
+      oldest_tombstone = i;
+      oldest_time = st.st_mtime;
+    }
+  }
+
+  next_tombstone = oldest_tombstone;
+}
+
+static unique_fd get_tombstone_fd() {
+  // If kMaxConcurrentDumps is greater than 1, then theoretically the same
+  // filename could be handed out to multiple processes. Unlink and create the
+  // file, instead of using O_TRUNC, to avoid two processes interleaving their
+  // output.
+  unique_fd result;
+  char buf[PATH_MAX];
+  snprintf(buf, sizeof(buf), "tombstone_%02d", next_tombstone);
+  if (unlinkat(tombstone_directory_fd, buf, 0) != 0 && errno != ENOENT) {
+    PLOG(FATAL) << "failed to unlink tombstone at " << kTombstoneDirectory << buf;
+  }
+
+  result.reset(
+    openat(tombstone_directory_fd, buf, O_CREAT | O_EXCL | O_WRONLY | O_APPEND | O_CLOEXEC, 0700));
+  if (result == -1) {
+    PLOG(FATAL) << "failed to create tombstone at " << kTombstoneDirectory << buf;
+  }
+
+  next_tombstone = (next_tombstone + 1) % kTombstoneCount;
+  return result;
+}
+
+static void dequeue_request(Crash* crash) {
+  ++num_concurrent_dumps;
+
+  unique_fd output_fd;
+  if (!intercept_manager->GetIntercept(crash->crash_pid, &output_fd)) {
+    output_fd = get_tombstone_fd();
+  }
+
+  TombstonedCrashPacket response = {
+    .packet_type = CrashPacketType::kPerformDump
+  };
+  ssize_t rc = send_fd(crash->crash_fd, &response, sizeof(response), std::move(output_fd));
+  if (rc == -1) {
+    PLOG(WARNING) << "failed to send response to CrashRequest";
+    goto fail;
+  } else if (rc != sizeof(response)) {
+    PLOG(WARNING) << "crash socket write returned short";
+    goto fail;
+  } else {
+    // TODO: Make this configurable by the interceptor?
+    struct timeval timeout = { 10, 0 };
+
+    event_base* base = event_get_base(crash->crash_event);
+    event_assign(crash->crash_event, base, crash->crash_fd, EV_TIMEOUT | EV_READ,
+                 crash_completed_cb, crash);
+    event_add(crash->crash_event, &timeout);
+  }
+  return;
+
+fail:
+  delete crash;
+}
+
+static void crash_accept_cb(evconnlistener* listener, evutil_socket_t sockfd, sockaddr*, int,
+                            void*) {
+  event_base* base = evconnlistener_get_base(listener);
+  Crash* crash = new Crash();
+
+  struct timeval timeout = { 1, 0 };
+  event* crash_event = event_new(base, sockfd, EV_TIMEOUT | EV_READ, crash_request_cb, crash);
+  crash->crash_fd.reset(sockfd);
+  crash->crash_event = crash_event;
+  event_add(crash_event, &timeout);
+}
+
+static void crash_request_cb(evutil_socket_t sockfd, short ev, void* arg) {
+  ssize_t rc;
+  Crash* crash = static_cast<Crash*>(arg);
+  TombstonedCrashPacket request = {};
+
+  if ((ev & EV_TIMEOUT) != 0) {
+    LOG(WARNING) << "crash request timed out";
+    goto fail;
+  } else if ((ev & EV_READ) == 0) {
+    LOG(WARNING) << "tombstoned received unexpected event from crash socket";
+    goto fail;
+  }
+
+  rc = TEMP_FAILURE_RETRY(read(sockfd, &request, sizeof(request)));
+  if (rc == -1) {
+    PLOG(WARNING) << "failed to read from crash socket";
+    goto fail;
+  } else if (rc != sizeof(request)) {
+    LOG(WARNING) << "crash socket received short read of length " << rc << " (expected "
+                 << sizeof(request) << ")";
+    goto fail;
+  }
+
+  if (request.packet_type != CrashPacketType::kDumpRequest) {
+    LOG(WARNING) << "unexpected crash packet type, expected kDumpRequest, received  "
+                 << StringPrintf("%#2hhX", request.packet_type);
+    goto fail;
+  }
+
+  crash->crash_pid = request.packet.dump_request.pid;
+  LOG(INFO) << "received crash request for pid " << crash->crash_pid;
+
+  if (num_concurrent_dumps == kMaxConcurrentDumps) {
+    LOG(INFO) << "enqueueing crash request for pid " << crash->crash_pid;
+    queued_requests.push_back(crash);
+  } else {
+    dequeue_request(crash);
+  }
+
+  return;
+
+fail:
+  delete crash;
+}
+
+static void crash_completed_cb(evutil_socket_t sockfd, short ev, void* arg) {
+  ssize_t rc;
+  Crash* crash = static_cast<Crash*>(arg);
+  TombstonedCrashPacket request = {};
+
+  --num_concurrent_dumps;
+
+  if ((ev & EV_READ) == 0) {
+    goto fail;
+  }
+
+  rc = TEMP_FAILURE_RETRY(read(sockfd, &request, sizeof(request)));
+  if (rc == -1) {
+    PLOG(WARNING) << "failed to read from crash socket";
+    goto fail;
+  } else if (rc != sizeof(request)) {
+    LOG(WARNING) << "crash socket received short read of length " << rc << " (expected "
+                 << sizeof(request) << ")";
+    goto fail;
+  }
+
+  if (request.packet_type != CrashPacketType::kCompletedDump) {
+    LOG(WARNING) << "unexpected crash packet type, expected kCompletedDump, received "
+                 << uint32_t(request.packet_type);
+    goto fail;
+  }
+
+fail:
+  delete crash;
+
+  // If there's something queued up, let them proceed.
+  if (!queued_requests.empty()) {
+    Crash* next_crash = queued_requests.front();
+    queued_requests.pop_front();
+    dequeue_request(next_crash);
+  }
+}
+
+int main(int, char* []) {
+  tombstone_directory_fd = open(kTombstoneDirectory, O_DIRECTORY | O_RDONLY | O_CLOEXEC);
+  if (tombstone_directory_fd == -1) {
+    PLOG(FATAL) << "failed to open tombstone directory";
+  }
+
+  find_oldest_tombstone();
+
+  int intercept_socket = android_get_control_socket(kTombstonedInterceptSocketName);
+  int crash_socket = android_get_control_socket(kTombstonedCrashSocketName);
+
+  if (intercept_socket == -1 || crash_socket == -1) {
+    PLOG(FATAL) << "failed to get socket from init";
+  }
+
+  evutil_make_socket_nonblocking(intercept_socket);
+  evutil_make_socket_nonblocking(crash_socket);
+
+  event_base* base = event_base_new();
+  if (!base) {
+    LOG(FATAL) << "failed to create event_base";
+  }
+
+  intercept_manager = new InterceptManager(base, intercept_socket);
+
+  evconnlistener* listener =
+    evconnlistener_new(base, crash_accept_cb, nullptr, -1, LEV_OPT_CLOSE_ON_FREE, crash_socket);
+  if (!listener) {
+    LOG(FATAL) << "failed to create evconnlistener";
+  }
+
+  LOG(INFO) << "tombstoned successfully initialized";
+  event_base_dispatch(base);
+}
diff --git a/debuggerd/tombstoned/tombstoned.rc b/debuggerd/tombstoned/tombstoned.rc
new file mode 100644
index 0000000..b8345ca
--- /dev/null
+++ b/debuggerd/tombstoned/tombstoned.rc
@@ -0,0 +1,10 @@
+service tombstoned /system/bin/tombstoned
+    user tombstoned
+    group system
+
+    # Don't start tombstoned until after the real /data is mounted.
+    class late_start
+
+    socket tombstoned_crash seqpacket 0666 system system
+    socket tombstoned_intercept seqpacket 0666 system system
+    writepid /dev/cpuset/system-background/tasks
diff --git a/debuggerd/util.cpp b/debuggerd/util.cpp
new file mode 100644
index 0000000..738abdf
--- /dev/null
+++ b/debuggerd/util.cpp
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2016, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "debuggerd/util.h"
+
+#include <sys/socket.h>
+
+#include <utility>
+
+#include <android-base/unique_fd.h>
+#include <cutils/sockets.h>
+
+ssize_t send_fd(int sockfd, const void* data, size_t len, android::base::unique_fd fd) {
+  char cmsg_buf[CMSG_SPACE(sizeof(int))];
+
+  iovec iov = { .iov_base = const_cast<void*>(data), .iov_len = len };
+  msghdr msg = {
+    .msg_iov = &iov, .msg_iovlen = 1, .msg_control = cmsg_buf, .msg_controllen = sizeof(cmsg_buf),
+  };
+  auto cmsg = CMSG_FIRSTHDR(&msg);
+  cmsg->cmsg_level = SOL_SOCKET;
+  cmsg->cmsg_type = SCM_RIGHTS;
+  cmsg->cmsg_len = CMSG_LEN(sizeof(int));
+  *reinterpret_cast<int*>(CMSG_DATA(cmsg)) = fd.get();
+
+  return TEMP_FAILURE_RETRY(sendmsg(sockfd, &msg, 0));
+}
+
+ssize_t recv_fd(int sockfd, void* _Nonnull data, size_t len,
+                android::base::unique_fd* _Nullable out_fd) {
+  char cmsg_buf[CMSG_SPACE(sizeof(int))];
+
+  iovec iov = { .iov_base = const_cast<void*>(data), .iov_len = len };
+  msghdr msg = {
+    .msg_iov = &iov,
+    .msg_iovlen = 1,
+    .msg_control = cmsg_buf,
+    .msg_controllen = sizeof(cmsg_buf),
+    .msg_flags = 0,
+  };
+  auto cmsg = CMSG_FIRSTHDR(&msg);
+  cmsg->cmsg_level = SOL_SOCKET;
+  cmsg->cmsg_type = SCM_RIGHTS;
+  cmsg->cmsg_len = CMSG_LEN(sizeof(int));
+
+  ssize_t result = TEMP_FAILURE_RETRY(recvmsg(sockfd, &msg, 0));
+  if (result == -1) {
+    return -1;
+  }
+
+  android::base::unique_fd fd;
+  bool received_fd = msg.msg_controllen == sizeof(cmsg_buf);
+  if (received_fd) {
+    fd.reset(*reinterpret_cast<int*>(CMSG_DATA(cmsg)));
+  }
+
+  if ((msg.msg_flags & MSG_TRUNC) != 0) {
+    errno = EFBIG;
+    return -1;
+  } else if ((msg.msg_flags & MSG_CTRUNC) != 0) {
+    errno = ERANGE;
+    return -1;
+  }
+
+  if (out_fd) {
+    *out_fd = std::move(fd);
+  } else if (received_fd) {
+    errno = ERANGE;
+    return -1;
+  }
+
+  return result;
+}
+
+bool Pipe(android::base::unique_fd* read, android::base::unique_fd* write) {
+  int pipefds[2];
+  if (pipe(pipefds) != 0) {
+    return false;
+  }
+  read->reset(pipefds[0]);
+  write->reset(pipefds[1]);
+  return true;
+}
diff --git a/fastboot/README.md b/fastboot/README.md
new file mode 100644
index 0000000..022d34b
--- /dev/null
+++ b/fastboot/README.md
@@ -0,0 +1,453 @@
+Fastboot
+--------
+
+The fastboot protocol is a mechanism for communicating with bootloaders
+over USB or ethernet.  It is designed to be very straightforward to implement,
+to allow it to be used across a wide range of devices and from hosts running
+Linux, macOS, or Windows.
+
+
+## Basic Requirements
+
+* USB
+  * Two bulk endpoints (in, out) are required
+  * Max packet size must be 64 bytes for full-speed, 512 bytes for
+    high-speed and 1024 bytes for Super Speed USB.
+  * The protocol is entirely host-driven and synchronous (unlike the
+    multi-channel, bi-directional, asynchronous ADB protocol)
+
+* TCP or UDP
+  * Device must be reachable via IP.
+  * Device will act as the server, fastboot will be the client.
+  * Fastboot data is wrapped in a simple protocol; see below for details.
+
+
+## Transport and Framing
+
+1. Host sends a command, which is an ascii string in a single
+   packet no greater than 64 bytes.
+
+2. Client response with a single packet no greater than 64 bytes.
+   The first four bytes of the response are "OKAY", "FAIL", "DATA",
+   or "INFO".  Additional bytes may contain an (ascii) informative
+   message.
+
+   a. INFO -> the remaining 60 bytes are an informative message
+      (providing progress or diagnostic messages).  They should
+      be displayed and then step #2 repeats
+
+   b. FAIL -> the requested command failed.  The remaining 60 bytes
+      of the response (if present) provide a textual failure message
+      to present to the user.  Stop.
+
+   c. OKAY -> the requested command completed successfully.  Go to #5
+
+   d. DATA -> the requested command is ready for the data phase.
+      A DATA response packet will be 12 bytes long, in the form of
+      DATA00000000 where the 8 digit hexadecimal number represents
+      the total data size to transfer.
+
+3. Data phase.  Depending on the command, the host or client will
+   send the indicated amount of data.  Short packets are always
+   acceptable and zero-length packets are ignored.  This phase continues
+   until the client has sent or received the number of bytes indicated
+   in the "DATA" response above.
+
+4. Client responds with a single packet no greater than 64 bytes.
+   The first four bytes of the response are "OKAY", "FAIL", or "INFO".
+   Similar to #2:
+
+   a. INFO -> display the remaining 60 bytes and return to #4
+
+   b. FAIL -> display the remaining 60 bytes (if present) as a failure
+      reason and consider the command failed.  Stop.
+
+   c. OKAY -> success.  Go to #5
+
+5. Success.  Stop.
+
+
+## Example Session
+
+    Host:    "getvar:version"        request version variable
+
+    Client:  "OKAY0.4"               return version "0.4"
+
+    Host:    "getvar:nonexistant"    request some undefined variable
+
+    Client:  "FAILUnknown variable"  getvar failure; see getvar details below
+
+    Host:    "download:00001234"     request to send 0x1234 bytes of data
+
+    Client:  "DATA00001234"          ready to accept data
+
+    Host:    < 0x1234 bytes >        send data
+
+    Client:  "OKAY"                  success
+
+    Host:    "flash:bootloader"      request to flash the data to the bootloader
+
+    Client:  "INFOerasing flash"     indicate status / progress
+             "INFOwriting flash"
+             "OKAY"                  indicate success
+
+    Host:    "powerdown"             send a command
+
+    Client:  "FAILunknown command"   indicate failure
+
+
+## Command Reference
+
+* Command parameters are indicated by printf-style escape sequences.
+
+* Commands are ascii strings and sent without the quotes (which are
+  for illustration only here) and without a trailing 0 byte.
+
+* Commands that begin with a lowercase letter are reserved for this
+  specification.  OEM-specific commands should not begin with a
+  lowercase letter, to prevent incompatibilities with future specs.
+
+The various currently defined commands are:
+
+    getvar:%s          Read a config/version variable from the bootloader.
+                       The variable contents will be returned after the
+                       OKAY response. If the variable is unknown, the bootloader
+                       should return a FAIL response, optionally with an error
+                       message.
+
+                       Previous versions of this document indicated that getvar
+                       should return an empty OKAY response for unknown
+                       variables, so older devices might exhibit this behavior,
+                       but new implementations should return FAIL instead.
+
+    download:%08x      Write data to memory which will be later used
+                       by "boot", "ramdisk", "flash", etc.  The client
+                       will reply with "DATA%08x" if it has enough
+                       space in RAM or "FAIL" if not.  The size of
+                       the download is remembered.
+
+    verify:%08x        Send a digital signature to verify the downloaded
+                       data.  Required if the bootloader is "secure"
+                       otherwise "flash" and "boot" will be ignored.
+
+    flash:%s           Write the previously downloaded image to the
+                       named partition (if possible).
+
+    erase:%s           Erase the indicated partition (clear to 0xFFs)
+
+    boot               The previously downloaded data is a boot.img
+                       and should be booted according to the normal
+                       procedure for a boot.img
+
+    continue           Continue booting as normal (if possible)
+
+    reboot             Reboot the device.
+
+    reboot-bootloader
+                       Reboot back into the bootloader.
+                       Useful for upgrade processes that require upgrading
+                       the bootloader and then upgrading other partitions
+                       using the new bootloader.
+
+    powerdown          Power off the device.
+
+
+
+## Client Variables
+
+The "getvar:%s" command is used to read client variables which
+represent various information about the device and the software
+on it.
+
+The various currently defined names are:
+
+    version             Version of FastBoot protocol supported.
+                        It should be "0.4" for this document.
+
+    version-bootloader  Version string for the Bootloader.
+
+    version-baseband    Version string of the Baseband Software
+
+    product             Name of the product
+
+    serialno            Product serial number
+
+    secure              If the value is "yes", this is a secure
+                        bootloader requiring a signature before
+                        it will install or boot images.
+
+Names starting with a lowercase character are reserved by this
+specification.  OEM-specific names should not start with lowercase
+characters.
+
+
+## TCP Protocol v1
+
+The TCP protocol is designed to be a simple way to use the fastboot protocol
+over ethernet if USB is not available.
+
+The device will open a TCP server on port 5554 and wait for a fastboot client
+to connect.
+
+### Handshake
+Upon connecting, both sides will send a 4-byte handshake message to ensure they
+are speaking the same protocol. This consists of the ASCII characters "FB"
+followed by a 2-digit base-10 ASCII version number. For example, the version 1
+handshake message will be [FB01].
+
+If either side detects a malformed handshake, it should disconnect.
+
+The protocol version to use must be the minimum of the versions sent by each
+side; if either side cannot speak this protocol version, it should disconnect.
+
+### Fastboot Data
+Once the handshake is complete, fastboot data will be sent as follows:
+
+    [data_size][data]
+
+Where data\_size is an unsigned 8-byte big-endian binary value, and data is the
+fastboot packet. The 8-byte length is intended to provide future-proofing even
+though currently fastboot packets have a 4-byte maximum length.
+
+### Example
+In this example the fastboot host queries the device for two variables,
+"version" and "none".
+
+    Host    <connect to the device on port 5555>
+    Host    FB01
+    Device  FB01
+    Host    [0x00][0x00][0x00][0x00][0x00][0x00][0x00][0x0E]getvar:version
+    Device  [0x00][0x00][0x00][0x00][0x00][0x00][0x00][0x07]OKAY0.4
+    Host    [0x00][0x00][0x00][0x00][0x00][0x00][0x00][0x0B]getvar:none
+    Device  [0x00][0x00][0x00][0x00][0x00][0x00][0x00][0x14]FAILUnknown variable
+    Host    <disconnect>
+
+
+## UDP Protocol v1
+
+The UDP protocol is more complex than TCP since we must implement reliability
+to ensure no packets are lost, but the general concept of wrapping the fastboot
+protocol is the same.
+
+Overview:
+  1. As with TCP, the device will listen on UDP port 5554.
+  2. Maximum UDP packet size is negotiated during initialization.
+  3. The host drives all communication; the device may only send a packet as a
+     response to a host packet.
+  4. If the host does not receive a response in 500ms it will re-transmit.
+
+### UDP Packet format
+
+    +----------+----+-------+-------+--------------------+
+    | Byte #   | 0  |   1   | 2 - 3 |  4+                |
+    +----------+----+-------+-------+--------------------+
+    | Contents | ID | Flags | Seq # | Data               |
+    +----------+----+-------+-------+--------------------+
+
+    ID      Packet ID:
+              0x00: Error.
+              0x01: Query.
+              0x02: Initialization.
+              0x03: Fastboot.
+
+            Packet types are described in more detail below.
+
+    Flags   Packet flags: 0 0 0 0 0 0 0 C
+              C=1 indicates a continuation packet; the data is too large and will
+                  continue in the next packet.
+
+              Remaining bits are reserved for future use and must be set to 0.
+
+    Seq #   2-byte packet sequence number (big-endian). The host will increment
+            this by 1 with each new packet, and the device must provide the
+            corresponding sequence number in the response packets.
+
+    Data    Packet data, not present in all packets.
+
+### Packet Types
+
+    Query
+          The host sends a query packet once on startup to sync with the device.
+          The host will not know the current sequence number, so the device must
+          respond to all query packets regardless of sequence number.
+
+          The response data field should contain a 2-byte big-endian value
+          giving the next expected sequence number.
+
+    Init
+          The host sends an init packet once the query response is returned. The
+          device must abort any in-progress operation and prepare for a new
+          fastboot session. This message is meant to allow recovery if a
+          previous session failed, e.g. due to network error or user Ctrl+C.
+
+          The data field contains two big-endian 2-byte values, a protocol
+          version and the max UDP packet size (including the 4-byte header).
+          Both the host and device will send these values, and in each case
+          the minimum of the sent values must be used.
+
+    Fastboot
+          These packets wrap the fastboot protocol. To write, the host will
+          send a packet with fastboot data, and the device will reply with an
+          empty packet as an ACK. To read, the host will send an empty packet,
+          and the device will reply with fastboot data. The device may not give
+          any data in the ACK packet.
+
+    Error
+          The device may respond to any packet with an error packet to indicate
+          a UDP protocol error. The data field should contain an ASCII string
+          describing the error. This is the only case where a device is allowed
+          to return a packet ID other than the one sent by the host.
+
+### Packet Size
+The maximum packet size is negotiated by the host and device in the Init packet.
+Devices must support at least 512-byte packets, but packet size has a direct
+correlation with download speed, so devices are strongly suggested to support at
+least 1024-byte packets. On a local network with 0.5ms round-trip time this will
+provide transfer rates of ~2MB/s. Over WiFi it will likely be significantly
+less.
+
+Query and Initialization packets, which are sent before size negotiation is
+complete, must always be 512 bytes or less.
+
+### Packet Re-Transmission
+The host will re-transmit any packet that does not receive a response. The
+requirement of exactly one device response packet per host packet is how we
+achieve reliability and in-order delivery of packets.
+
+For simplicity of implementation, there is no windowing of multiple
+unacknowledged packets in this version of the protocol. The host will continue
+to send the same packet until a response is received. Windowing functionality
+may be implemented in future versions if necessary to increase performance.
+
+The first Query packet will only be attempted a small number of times, but
+subsequent packets will attempt to retransmit for at least 1 minute before
+giving up. This means a device may safely ignore host UDP packets for up to 1
+minute during long operations, e.g. writing to flash.
+
+### Continuation Packets
+Any packet may set the continuation flag to indicate that the data is
+incomplete. Large data such as downloading an image may require many
+continuation packets. The receiver should respond to a continuation packet with
+an empty packet to acknowledge receipt. See examples below.
+
+### Summary
+The host starts with a Query packet, then an Initialization packet, after
+which only Fastboot packets are sent. Fastboot packets may contain data from
+the host for writes, or from the device for reads, but not both.
+
+Given a next expected sequence number S and a received packet P, the device
+behavior should be:
+
+    if P is a Query packet:
+      * respond with a Query packet with S in the data field
+    else if P has sequence == S:
+      * process P and take any required action
+      * create a response packet R with the same ID and sequence as P, containing
+        any response data required.
+      * transmit R and save it in case of re-transmission
+      * increment S
+    else if P has sequence == S - 1:
+      * re-transmit the saved response packet R from above
+    else:
+      * ignore the packet
+
+### Examples
+
+In the examples below, S indicates the starting client sequence number.
+
+    Host                                    Client
+    ======================================================================
+    [Initialization, S = 0x55AA]
+    [Host: version 1, 2048-byte packets. Client: version 2, 1024-byte packets.]
+    [Resulting values to use: version = 1, max packet size = 1024]
+    ID   Flag SeqH SeqL Data                ID   Flag SeqH SeqL Data
+    ----------------------------------------------------------------------
+    0x01 0x00 0x00 0x00
+                                            0x01 0x00 0x00 0x00 0x55 0xAA
+    0x02 0x00 0x55 0xAA 0x00 0x01 0x08 0x00
+                                            0x02 0x00 0x55 0xAA 0x00 0x02 0x04 0x00
+
+    ----------------------------------------------------------------------
+    [fastboot "getvar" commands, S = 0x0001]
+    ID    Flags SeqH  SeqL  Data            ID    Flags SeqH  SeqL  Data
+    ----------------------------------------------------------------------
+    0x03  0x00  0x00  0x01  getvar:version
+                                            0x03  0x00  0x00  0x01
+    0x03  0x00  0x00  0x02
+                                            0x03  0x00  0x00  0x02  OKAY0.4
+    0x03  0x00  0x00  0x03  getvar:none
+                                            0x03  0x00  0x00  0x03
+    0x03  0x00  0x00  0x04
+                                            0x03  0x00  0x00  0x04  FAILUnknown var
+
+    ----------------------------------------------------------------------
+    [fastboot "INFO" responses, S = 0x0000]
+    ID    Flags SeqH  SeqL  Data            ID    Flags SeqH  SeqL  Data
+    ----------------------------------------------------------------------
+    0x03  0x00  0x00  0x00  <command>
+                                            0x03  0x00  0x00  0x00
+    0x03  0x00  0x00  0x01
+                                            0x03  0x00  0x00  0x01  INFOWait1
+    0x03  0x00  0x00  0x02
+                                            0x03  0x00  0x00  0x02  INFOWait2
+    0x03  0x00  0x00  0x03
+                                            0x03  0x00  0x00  0x03  OKAY
+
+    ----------------------------------------------------------------------
+    [Chunking 2100 bytes of data, max packet size = 1024, S = 0xFFFF]
+    ID   Flag SeqH SeqL Data                ID   Flag SeqH SeqL Data
+    ----------------------------------------------------------------------
+    0x03 0x00 0xFF 0xFF download:0000834
+                                            0x03 0x00 0xFF 0xFF
+    0x03 0x00 0x00 0x00
+                                            0x03 0x00 0x00 0x00 DATA0000834
+    0x03 0x01 0x00 0x01 <1020 bytes>
+                                            0x03 0x00 0x00 0x01
+    0x03 0x01 0x00 0x02 <1020 bytes>
+                                            0x03 0x00 0x00 0x02
+    0x03 0x00 0x00 0x03 <60 bytes>
+                                            0x03 0x00 0x00 0x03
+    0x03 0x00 0x00 0x04
+                                            0x03 0x00 0x00 0x04 OKAY
+
+    ----------------------------------------------------------------------
+    [Unknown ID error, S = 0x0000]
+    ID    Flags SeqH  SeqL  Data            ID    Flags SeqH  SeqL  Data
+    ----------------------------------------------------------------------
+    0x10  0x00  0x00  0x00
+                                            0x00  0x00  0x00  0x00  <error message>
+
+    ----------------------------------------------------------------------
+    [Host packet loss and retransmission, S = 0x0000]
+    ID    Flags SeqH  SeqL  Data            ID    Flags SeqH  SeqL  Data
+    ----------------------------------------------------------------------
+    0x03  0x00  0x00  0x00  getvar:version [lost]
+    0x03  0x00  0x00  0x00  getvar:version [lost]
+    0x03  0x00  0x00  0x00  getvar:version
+                                            0x03  0x00  0x00  0x00
+    0x03  0x00  0x00  0x01
+                                            0x03  0x00  0x00  0x01  OKAY0.4
+
+    ----------------------------------------------------------------------
+    [Client packet loss and retransmission, S = 0x0000]
+    ID    Flags SeqH  SeqL  Data            ID    Flags SeqH  SeqL  Data
+    ----------------------------------------------------------------------
+    0x03  0x00  0x00  0x00  getvar:version
+                                            0x03  0x00  0x00  0x00 [lost]
+    0x03  0x00  0x00  0x00  getvar:version
+                                            0x03  0x00  0x00  0x00 [lost]
+    0x03  0x00  0x00  0x00  getvar:version
+                                            0x03  0x00  0x00  0x00
+    0x03  0x00  0x00  0x01
+                                            0x03  0x00  0x00  0x01  OKAY0.4
+
+    ----------------------------------------------------------------------
+    [Host packet delayed, S = 0x0000]
+    ID    Flags SeqH  SeqL  Data            ID    Flags SeqH  SeqL  Data
+    ----------------------------------------------------------------------
+    0x03  0x00  0x00  0x00  getvar:version [delayed]
+    0x03  0x00  0x00  0x00  getvar:version
+                                            0x03  0x00  0x00  0x00
+    0x03  0x00  0x00  0x01
+                                            0x03  0x00  0x00  0x01  OKAY0.4
+    0x03  0x00  0x00  0x00  getvar:version [arrives late with old seq#, is ignored]
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 3f8bc8f..f970e68 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -358,7 +358,7 @@
             "  devices [-l]                             List all connected devices [with\n"
             "                                           device paths].\n"
             "  continue                                 Continue with autoboot.\n"
-            "  reboot [bootloader]                      Reboot device [into bootloader].\n"
+            "  reboot [bootloader|emergency]            Reboot device [into bootloader or emergency mode].\n"
             "  reboot-bootloader                        Reboot device into bootloader.\n"
             "  help                                     Show this help message.\n"
             "\n"
@@ -1397,6 +1397,7 @@
     bool wants_wipe = false;
     bool wants_reboot = false;
     bool wants_reboot_bootloader = false;
+    bool wants_reboot_emergency = false;
     bool skip_reboot = false;
     bool wants_set_active = false;
     bool skip_secondary = false;
@@ -1648,6 +1649,11 @@
                     wants_reboot_bootloader = true;
                     skip(1);
                 }
+                if (!strcmp(*argv, "emergency")) {
+                    wants_reboot = false;
+                    wants_reboot_emergency = true;
+                    skip(1);
+                }
             }
             require(0);
         } else if(!strcmp(*argv, "reboot-bootloader")) {
@@ -1807,6 +1813,9 @@
     } else if (wants_reboot_bootloader) {
         fb_queue_command("reboot-bootloader", "rebooting into bootloader");
         fb_queue_wait_for_disconnect();
+    } else if (wants_reboot_emergency) {
+        fb_queue_command("reboot-emergency", "rebooting into emergency download (EDL) mode");
+        fb_queue_wait_for_disconnect();
     }
 
     return fb_execute_queue(transport) ? EXIT_FAILURE : EXIT_SUCCESS;
diff --git a/fastboot/fastboot_protocol.txt b/fastboot/fastboot_protocol.txt
deleted file mode 100644
index b12e420..0000000
--- a/fastboot/fastboot_protocol.txt
+++ /dev/null
@@ -1,449 +0,0 @@
-FastBoot  Version  0.4
-----------------------
-
-The fastboot protocol is a mechanism for communicating with bootloaders
-over USB or ethernet.  It is designed to be very straightforward to implement,
-to allow it to be used across a wide range of devices and from hosts running
-Linux, Windows, or OSX.
-
-
-Basic Requirements
-------------------
-
-* USB
-  * Two bulk endpoints (in, out) are required
-  * Max packet size must be 64 bytes for full-speed, 512 bytes for
-    high-speed and 1024 bytes for Super Speed USB.
-  * The protocol is entirely host-driven and synchronous (unlike the
-    multi-channel, bi-directional, asynchronous ADB protocol)
-
-* TCP or UDP
-  * Device must be reachable via IP.
-  * Device will act as the server, fastboot will be the client.
-  * Fastboot data is wrapped in a simple protocol; see below for details.
-
-
-Transport and Framing
----------------------
-
-1. Host sends a command, which is an ascii string in a single
-   packet no greater than 64 bytes.
-
-2. Client response with a single packet no greater than 64 bytes.
-   The first four bytes of the response are "OKAY", "FAIL", "DATA", 
-   or "INFO".  Additional bytes may contain an (ascii) informative
-   message.
-
-   a. INFO -> the remaining 60 bytes are an informative message
-      (providing progress or diagnostic messages).  They should 
-      be displayed and then step #2 repeats
-
-   b. FAIL -> the requested command failed.  The remaining 60 bytes 
-      of the response (if present) provide a textual failure message 
-      to present to the user.  Stop.
-
-   c. OKAY -> the requested command completed successfully.  Go to #5
-
-   d. DATA -> the requested command is ready for the data phase.
-      A DATA response packet will be 12 bytes long, in the form of
-      DATA00000000 where the 8 digit hexadecimal number represents
-      the total data size to transfer.
-
-3. Data phase.  Depending on the command, the host or client will 
-   send the indicated amount of data.  Short packets are always 
-   acceptable and zero-length packets are ignored.  This phase continues
-   until the client has sent or received the number of bytes indicated
-   in the "DATA" response above.
-
-4. Client responds with a single packet no greater than 64 bytes.  
-   The first four bytes of the response are "OKAY", "FAIL", or "INFO".  
-   Similar to #2:
-
-   a. INFO -> display the remaining 60 bytes and return to #4
-   
-   b. FAIL -> display the remaining 60 bytes (if present) as a failure
-      reason and consider the command failed.  Stop.
-
-   c. OKAY -> success.  Go to #5
-
-5. Success.  Stop.
-
-
-Example Session
----------------
-
-Host:    "getvar:version"        request version variable
-
-Client:  "OKAY0.4"               return version "0.4"
-
-Host:    "getvar:nonexistant"    request some undefined variable
-
-Client:  "FAILUnknown variable"  getvar failure; see getvar details below
-
-Host:    "download:00001234"     request to send 0x1234 bytes of data
-
-Client:  "DATA00001234"          ready to accept data
-
-Host:    < 0x1234 bytes >        send data
-
-Client:  "OKAY"                  success
-
-Host:    "flash:bootloader"      request to flash the data to the bootloader
-
-Client:  "INFOerasing flash"     indicate status / progress
-         "INFOwriting flash"
-         "OKAY"                  indicate success
-
-Host:    "powerdown"             send a command
-
-Client:  "FAILunknown command"   indicate failure
-
-
-Command Reference
------------------
-
-* Command parameters are indicated by printf-style escape sequences.
-
-* Commands are ascii strings and sent without the quotes (which are
-  for illustration only here) and without a trailing 0 byte.
-
-* Commands that begin with a lowercase letter are reserved for this
-  specification.  OEM-specific commands should not begin with a 
-  lowercase letter, to prevent incompatibilities with future specs.
-
- "getvar:%s"           Read a config/version variable from the bootloader.
-                       The variable contents will be returned after the
-                       OKAY response. If the variable is unknown, the bootloader
-                       should return a FAIL response, optionally with an error
-                       message.
-
-                       Previous versions of this document indicated that getvar
-                       should return an empty OKAY response for unknown
-                       variables, so older devices might exhibit this behavior,
-                       but new implementations should return FAIL instead.
-
- "download:%08x"       Write data to memory which will be later used
-                       by "boot", "ramdisk", "flash", etc.  The client
-                       will reply with "DATA%08x" if it has enough 
-                       space in RAM or "FAIL" if not.  The size of
-                       the download is remembered.
-
-  "verify:%08x"        Send a digital signature to verify the downloaded
-                       data.  Required if the bootloader is "secure"
-                       otherwise "flash" and "boot" will be ignored.
-
-  "flash:%s"           Write the previously downloaded image to the
-                       named partition (if possible).
-
-  "erase:%s"           Erase the indicated partition (clear to 0xFFs)
-
-  "boot"               The previously downloaded data is a boot.img
-                       and should be booted according to the normal
-                       procedure for a boot.img
-
-  "continue"           Continue booting as normal (if possible)
-
-  "reboot"             Reboot the device.
-
-  "reboot-bootloader"  Reboot back into the bootloader.
-                       Useful for upgrade processes that require upgrading
-                       the bootloader and then upgrading other partitions
-                       using the new bootloader.
-
-  "powerdown"          Power off the device.
-
-
-
-Client Variables
-----------------
-
-The "getvar:%s" command is used to read client variables which
-represent various information about the device and the software
-on it.
-
-The various currently defined names are:
-
-  version             Version of FastBoot protocol supported.
-                      It should be "0.4" for this document.
-
-  version-bootloader  Version string for the Bootloader.
-
-  version-baseband    Version string of the Baseband Software
-
-  product             Name of the product
-
-  serialno            Product serial number
-
-  secure              If the value is "yes", this is a secure
-                      bootloader requiring a signature before
-                      it will install or boot images.
-
-Names starting with a lowercase character are reserved by this
-specification.  OEM-specific names should not start with lowercase
-characters.
-
-
-TCP Protocol v1
----------------
-
-The TCP protocol is designed to be a simple way to use the fastboot protocol
-over ethernet if USB is not available.
-
-The device will open a TCP server on port 5554 and wait for a fastboot client
-to connect.
-
--- Handshake --
-Upon connecting, both sides will send a 4-byte handshake message to ensure they
-are speaking the same protocol. This consists of the ASCII characters "FB"
-followed by a 2-digit base-10 ASCII version number. For example, the version 1
-handshake message will be [FB01].
-
-If either side detects a malformed handshake, it should disconnect.
-
-The protocol version to use must be the minimum of the versions sent by each
-side; if either side cannot speak this protocol version, it should disconnect.
-
--- Fastboot Data --
-Once the handshake is complete, fastboot data will be sent as follows:
-
-  [data_size][data]
-
-Where data_size is an unsigned 8-byte big-endian binary value, and data is the
-fastboot packet. The 8-byte length is intended to provide future-proofing even
-though currently fastboot packets have a 4-byte maximum length.
-
--- Example --
-In this example the fastboot host queries the device for two variables,
-"version" and "none".
-
-Host    <connect to the device on port 5555>
-Host    FB01
-Device  FB01
-Host    [0x00][0x00][0x00][0x00][0x00][0x00][0x00][0x0E]getvar:version
-Device  [0x00][0x00][0x00][0x00][0x00][0x00][0x00][0x07]OKAY0.4
-Host    [0x00][0x00][0x00][0x00][0x00][0x00][0x00][0x0B]getvar:none
-Device  [0x00][0x00][0x00][0x00][0x00][0x00][0x00][0x14]FAILUnknown variable
-Host    <disconnect>
-
-
-UDP Protocol v1
----------------
-
-The UDP protocol is more complex than TCP since we must implement reliability
-to ensure no packets are lost, but the general concept of wrapping the fastboot
-protocol is the same.
-
-Overview:
-  1. As with TCP, the device will listen on UDP port 5554.
-  2. Maximum UDP packet size is negotiated during initialization.
-  3. The host drives all communication; the device may only send a packet as a
-     response to a host packet.
-  4. If the host does not receive a response in 500ms it will re-transmit.
-
--- UDP Packet format --
-  +----------+----+-------+-------+--------------------+
-  | Byte #   | 0  |   1   | 2 - 3 |  4+                |
-  +----------+----+-------+-------+--------------------+
-  | Contents | ID | Flags | Seq # | Data               |
-  +----------+----+-------+-------+--------------------+
-
-  ID      Packet ID:
-            0x00: Error.
-            0x01: Query.
-            0x02: Initialization.
-            0x03: Fastboot.
-
-          Packet types are described in more detail below.
-
-  Flags   Packet flags: 0 0 0 0 0 0 0 C
-            C=1 indicates a continuation packet; the data is too large and will
-                continue in the next packet.
-
-            Remaining bits are reserved for future use and must be set to 0.
-
-  Seq #   2-byte packet sequence number (big-endian). The host will increment
-          this by 1 with each new packet, and the device must provide the
-          corresponding sequence number in the response packets.
-
-  Data    Packet data, not present in all packets.
-
--- Packet Types --
-Query     The host sends a query packet once on startup to sync with the device.
-          The host will not know the current sequence number, so the device must
-          respond to all query packets regardless of sequence number.
-
-          The response data field should contain a 2-byte big-endian value
-          giving the next expected sequence number.
-
-Init      The host sends an init packet once the query response is returned. The
-          device must abort any in-progress operation and prepare for a new
-          fastboot session. This message is meant to allow recovery if a
-          previous session failed, e.g. due to network error or user Ctrl+C.
-
-          The data field contains two big-endian 2-byte values, a protocol
-          version and the max UDP packet size (including the 4-byte header).
-          Both the host and device will send these values, and in each case
-          the minimum of the sent values must be used.
-
-Fastboot  These packets wrap the fastboot protocol. To write, the host will
-          send a packet with fastboot data, and the device will reply with an
-          empty packet as an ACK. To read, the host will send an empty packet,
-          and the device will reply with fastboot data. The device may not give
-          any data in the ACK packet.
-
-Error     The device may respond to any packet with an error packet to indicate
-          a UDP protocol error. The data field should contain an ASCII string
-          describing the error. This is the only case where a device is allowed
-          to return a packet ID other than the one sent by the host.
-
--- Packet Size --
-The maximum packet size is negotiated by the host and device in the Init packet.
-Devices must support at least 512-byte packets, but packet size has a direct
-correlation with download speed, so devices are strongly suggested to support at
-least 1024-byte packets. On a local network with 0.5ms round-trip time this will
-provide transfer rates of ~2MB/s. Over WiFi it will likely be significantly
-less.
-
-Query and Initialization packets, which are sent before size negotiation is
-complete, must always be 512 bytes or less.
-
--- Packet Re-Transmission --
-The host will re-transmit any packet that does not receive a response. The
-requirement of exactly one device response packet per host packet is how we
-achieve reliability and in-order delivery of packets.
-
-For simplicity of implementation, there is no windowing of multiple
-unacknowledged packets in this version of the protocol. The host will continue
-to send the same packet until a response is received. Windowing functionality
-may be implemented in future versions if necessary to increase performance.
-
-The first Query packet will only be attempted a small number of times, but
-subsequent packets will attempt to retransmit for at least 1 minute before
-giving up. This means a device may safely ignore host UDP packets for up to 1
-minute during long operations, e.g. writing to flash.
-
--- Continuation Packets --
-Any packet may set the continuation flag to indicate that the data is
-incomplete. Large data such as downloading an image may require many
-continuation packets. The receiver should respond to a continuation packet with
-an empty packet to acknowledge receipt. See examples below.
-
--- Summary --
-The host starts with a Query packet, then an Initialization packet, after
-which only Fastboot packets are sent. Fastboot packets may contain data from
-the host for writes, or from the device for reads, but not both.
-
-Given a next expected sequence number S and a received packet P, the device
-behavior should be:
-  if P is a Query packet:
-    * respond with a Query packet with S in the data field
-  else if P has sequence == S:
-    * process P and take any required action
-    * create a response packet R with the same ID and sequence as P, containing
-      any response data required.
-    * transmit R and save it in case of re-transmission
-    * increment S
-  else if P has sequence == S - 1:
-    * re-transmit the saved response packet R from above
-  else:
-    * ignore the packet
-
--- Examples --
-In the examples below, S indicates the starting client sequence number.
-
-Host                                    Client
-======================================================================
-[Initialization, S = 0x55AA]
-[Host: version 1, 2048-byte packets. Client: version 2, 1024-byte packets.]
-[Resulting values to use: version = 1, max packet size = 1024]
-ID   Flag SeqH SeqL Data                ID   Flag SeqH SeqL Data
-----------------------------------------------------------------------
-0x01 0x00 0x00 0x00
-                                        0x01 0x00 0x00 0x00 0x55 0xAA
-0x02 0x00 0x55 0xAA 0x00 0x01 0x08 0x00
-                                        0x02 0x00 0x55 0xAA 0x00 0x02 0x04 0x00
-
-----------------------------------------------------------------------
-[fastboot "getvar" commands, S = 0x0001]
-ID    Flags SeqH  SeqL  Data            ID    Flags SeqH  SeqL  Data
-----------------------------------------------------------------------
-0x03  0x00  0x00  0x01  getvar:version
-                                        0x03  0x00  0x00  0x01
-0x03  0x00  0x00  0x02
-                                        0x03  0x00  0x00  0x02  OKAY0.4
-0x03  0x00  0x00  0x03  getvar:none
-                                        0x03  0x00  0x00  0x03
-0x03  0x00  0x00  0x04
-                                        0x03  0x00  0x00  0x04  FAILUnknown var
-
-----------------------------------------------------------------------
-[fastboot "INFO" responses, S = 0x0000]
-ID    Flags SeqH  SeqL  Data            ID    Flags SeqH  SeqL  Data
-----------------------------------------------------------------------
-0x03  0x00  0x00  0x00  <command>
-                                        0x03  0x00  0x00  0x00
-0x03  0x00  0x00  0x01
-                                        0x03  0x00  0x00  0x01  INFOWait1
-0x03  0x00  0x00  0x02
-                                        0x03  0x00  0x00  0x02  INFOWait2
-0x03  0x00  0x00  0x03
-                                        0x03  0x00  0x00  0x03  OKAY
-
-----------------------------------------------------------------------
-[Chunking 2100 bytes of data, max packet size = 1024, S = 0xFFFF]
-ID   Flag SeqH SeqL Data                ID   Flag SeqH SeqL Data
-----------------------------------------------------------------------
-0x03 0x00 0xFF 0xFF download:0000834
-                                        0x03 0x00 0xFF 0xFF
-0x03 0x00 0x00 0x00
-                                        0x03 0x00 0x00 0x00 DATA0000834
-0x03 0x01 0x00 0x01 <1020 bytes>
-                                        0x03 0x00 0x00 0x01
-0x03 0x01 0x00 0x02 <1020 bytes>
-                                        0x03 0x00 0x00 0x02
-0x03 0x00 0x00 0x03 <60 bytes>
-                                        0x03 0x00 0x00 0x03
-0x03 0x00 0x00 0x04
-                                        0x03 0x00 0x00 0x04 OKAY
-
-----------------------------------------------------------------------
-[Unknown ID error, S = 0x0000]
-ID    Flags SeqH  SeqL  Data            ID    Flags SeqH  SeqL  Data
-----------------------------------------------------------------------
-0x10  0x00  0x00  0x00
-                                        0x00  0x00  0x00  0x00  <error message>
-
-----------------------------------------------------------------------
-[Host packet loss and retransmission, S = 0x0000]
-ID    Flags SeqH  SeqL  Data            ID    Flags SeqH  SeqL  Data
-----------------------------------------------------------------------
-0x03  0x00  0x00  0x00  getvar:version [lost]
-0x03  0x00  0x00  0x00  getvar:version [lost]
-0x03  0x00  0x00  0x00  getvar:version
-                                        0x03  0x00  0x00  0x00
-0x03  0x00  0x00  0x01
-                                        0x03  0x00  0x00  0x01  OKAY0.4
-
-----------------------------------------------------------------------
-[Client packet loss and retransmission, S = 0x0000]
-ID    Flags SeqH  SeqL  Data            ID    Flags SeqH  SeqL  Data
-----------------------------------------------------------------------
-0x03  0x00  0x00  0x00  getvar:version
-                                        0x03  0x00  0x00  0x00 [lost]
-0x03  0x00  0x00  0x00  getvar:version
-                                        0x03  0x00  0x00  0x00 [lost]
-0x03  0x00  0x00  0x00  getvar:version
-                                        0x03  0x00  0x00  0x00
-0x03  0x00  0x00  0x01
-                                        0x03  0x00  0x00  0x01  OKAY0.4
-
-----------------------------------------------------------------------
-[Host packet delayed, S = 0x0000]
-ID    Flags SeqH  SeqL  Data            ID    Flags SeqH  SeqL  Data
-----------------------------------------------------------------------
-0x03  0x00  0x00  0x00  getvar:version [delayed]
-0x03  0x00  0x00  0x00  getvar:version
-                                        0x03  0x00  0x00  0x00
-0x03  0x00  0x00  0x01
-                                        0x03  0x00  0x00  0x01  OKAY0.4
-0x03  0x00  0x00  0x00  getvar:version [arrives late with old seq#, is ignored]
diff --git a/fs_mgr/Android.mk b/fs_mgr/Android.mk
index 051acfa..8d5b51b 100644
--- a/fs_mgr/Android.mk
+++ b/fs_mgr/Android.mk
@@ -27,7 +27,6 @@
     $(LOCAL_PATH)/include \
     system/vold \
     system/extras/ext4_utils \
-    external/openssl/include \
     bootable/recovery
 LOCAL_MODULE:= libfs_mgr
 LOCAL_STATIC_LIBRARIES := $(common_static_libraries)
diff --git a/fs_mgr/fs_mgr.c b/fs_mgr/fs_mgr.c
index 9a53d62..e699b71 100644
--- a/fs_mgr/fs_mgr.c
+++ b/fs_mgr/fs_mgr.c
@@ -407,7 +407,7 @@
     return ret;
 }
 
-static int fs_match(char *in1, char *in2)
+static int fs_match(const char *in1, const char *in2)
 {
     char *n1;
     char *n2;
@@ -837,7 +837,7 @@
  * If multiple fstab entries are to be mounted on "n_name", it will try to mount each one
  * in turn, and stop on 1st success, or no more match.
  */
-int fs_mgr_do_mount(struct fstab *fstab, char *n_name, char *n_blk_device,
+int fs_mgr_do_mount(struct fstab *fstab, const char *n_name, char *n_blk_device,
                     char *tmp_mount_point)
 {
     int i = 0;
diff --git a/fs_mgr/fs_mgr_fstab.c b/fs_mgr/fs_mgr_fstab.c
index 41fb746..5d43b6c 100644
--- a/fs_mgr/fs_mgr_fstab.c
+++ b/fs_mgr/fs_mgr_fstab.c
@@ -35,6 +35,8 @@
     unsigned int zram_size;
     uint64_t reserved_size;
     unsigned int file_encryption_mode;
+    unsigned int erase_blk_size;
+    unsigned int logical_blk_size;
 };
 
 struct flag_list {
@@ -85,6 +87,8 @@
     { "latemount",          MF_LATEMOUNT },
     { "reservedsize=",      MF_RESERVEDSIZE },
     { "quota",              MF_QUOTA },
+    { "eraseblk=",          MF_ERASEBLKSIZE },
+    { "logicalblk=",        MF_LOGICALBLKSIZE },
     { "defaults",           0 },
     { 0,                    0 },
 };
@@ -239,6 +243,22 @@
                      * reserved size of the partition.  Get it and return it.
                      */
                     flag_vals->reserved_size = parse_size(strchr(p, '=') + 1);
+                } else if ((fl[i].flag == MF_ERASEBLKSIZE) && flag_vals) {
+                    /* The erase block size flag is followed by an = and the flash
+                     * erase block size. Get it, check that it is a power of 2 and
+                     * at least 4096, and return it.
+                     */
+                    unsigned int val = strtoul(strchr(p, '=') + 1, NULL, 0);
+                    if (val >= 4096 && (val & (val - 1)) == 0)
+                        flag_vals->erase_blk_size = val;
+                } else if ((fl[i].flag == MF_LOGICALBLKSIZE) && flag_vals) {
+                    /* The logical block size flag is followed by an = and the flash
+                     * logical block size. Get it, check that it is a power of 2 and
+                     * at least 4096, and return it.
+                     */
+                    unsigned int val = strtoul(strchr(p, '=') + 1, NULL, 0);
+                    if (val >= 4096 && (val & (val - 1)) == 0)
+                        flag_vals->logical_blk_size = val;
                 }
                 break;
             }
@@ -385,6 +405,8 @@
         fstab->recs[cnt].zram_size = flag_vals.zram_size;
         fstab->recs[cnt].reserved_size = flag_vals.reserved_size;
         fstab->recs[cnt].file_encryption_mode = flag_vals.file_encryption_mode;
+        fstab->recs[cnt].erase_blk_size = flag_vals.erase_blk_size;
+        fstab->recs[cnt].logical_blk_size = flag_vals.logical_blk_size;
         cnt++;
     }
     /* If an A/B partition, modify block device to be the real block device */
diff --git a/fs_mgr/fs_mgr_main.c b/fs_mgr/fs_mgr_main.c
index 33a7496..4bfe202 100644
--- a/fs_mgr/fs_mgr_main.c
+++ b/fs_mgr/fs_mgr_main.c
@@ -14,12 +14,17 @@
  * limitations under the License.
  */
 
+#define _GNU_SOURCE
+
 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
-#include <libgen.h>
 #include "fs_mgr_priv.h"
 
+#ifdef _LIBGEN_H
+#warning "libgen.h must not be included"
+#endif
+
 char *me = "";
 
 static void usage(void)
@@ -32,10 +37,10 @@
  * and exit the program, do not return to the caller.
  * Return the number of argv[] entries consumed.
  */
-static void parse_options(int argc, char *argv[], int *a_flag, int *u_flag, int *n_flag,
-                     char **n_name, char **n_blk_dev)
+static void parse_options(int argc, char * const argv[], int *a_flag, int *u_flag, int *n_flag,
+                     const char **n_name, const char **n_blk_dev)
 {
-    me = basename(strdup(argv[0]));
+    me = basename(argv[0]);
 
     if (argc <= 1) {
         usage();
@@ -75,14 +80,14 @@
     return;
 }
 
-int main(int argc, char *argv[])
+int main(int argc, char * const argv[])
 {
     int a_flag=0;
     int u_flag=0;
     int n_flag=0;
-    char *n_name=NULL;
-    char *n_blk_dev=NULL;
-    char *fstab_file=NULL;
+    const char *n_name=NULL;
+    const char *n_blk_dev=NULL;
+    const char *fstab_file=NULL;
     struct fstab *fstab=NULL;
 
     klog_set_level(6);
@@ -97,7 +102,7 @@
     if (a_flag) {
         return fs_mgr_mount_all(fstab, MOUNT_MODE_DEFAULT);
     } else if (n_flag) {
-        return fs_mgr_do_mount(fstab, n_name, n_blk_dev, 0);
+        return fs_mgr_do_mount(fstab, n_name, (char *)n_blk_dev, 0);
     } else if (u_flag) {
         return fs_mgr_unmount_all(fstab);
     } else {
diff --git a/fs_mgr/fs_mgr_priv.h b/fs_mgr/fs_mgr_priv.h
index 7f917d9..074cda6 100644
--- a/fs_mgr/fs_mgr_priv.h
+++ b/fs_mgr/fs_mgr_priv.h
@@ -89,6 +89,8 @@
 #define MF_MAX_COMP_STREAMS 0x100000
 #define MF_RESERVEDSIZE     0x200000
 #define MF_QUOTA            0x400000
+#define MF_ERASEBLKSIZE     0x800000
+#define MF_LOGICALBLKSIZE  0X1000000
 
 #define DM_BUF_SIZE 4096
 
diff --git a/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h
index e7a0a1d..d959798 100644
--- a/fs_mgr/include/fs_mgr.h
+++ b/fs_mgr/include/fs_mgr.h
@@ -77,6 +77,8 @@
     unsigned int zram_size;
     uint64_t reserved_size;
     unsigned int file_encryption_mode;
+    unsigned int erase_blk_size;
+    unsigned int logical_blk_size;
 };
 
 // Callback function for verity status
@@ -99,7 +101,7 @@
 #define FS_MGR_DOMNT_FAILED (-1)
 #define FS_MGR_DOMNT_BUSY (-2)
 
-int fs_mgr_do_mount(struct fstab *fstab, char *n_name, char *n_blk_device,
+int fs_mgr_do_mount(struct fstab *fstab, const char *n_name, char *n_blk_device,
                     char *tmp_mount_point);
 int fs_mgr_do_tmpfs_mount(char *n_name);
 int fs_mgr_unmount_all(struct fstab *fstab);
diff --git a/healthd/healthd_mode_charger.cpp b/healthd/healthd_mode_charger.cpp
index 36c4664..2f69372 100644
--- a/healthd/healthd_mode_charger.cpp
+++ b/healthd/healthd_mode_charger.cpp
@@ -30,6 +30,8 @@
 #include <time.h>
 #include <unistd.h>
 
+#include <functional>
+
 #include <android-base/file.h>
 #include <android-base/stringprintf.h>
 
@@ -42,6 +44,7 @@
 #include <cutils/misc.h>
 #include <cutils/uevent.h>
 #include <cutils/properties.h>
+#include <minui/minui.h>
 
 #ifdef CHARGER_ENABLE_SUSPEND
 #include <suspend/autosuspend.h>
@@ -49,7 +52,6 @@
 
 #include "animation.h"
 #include "AnimationParser.h"
-#include "minui/minui.h"
 
 #include <healthd/healthd.h>
 
@@ -563,9 +565,8 @@
     }
 }
 
-static int set_key_callback(int code, int value, void *data)
+static int set_key_callback(struct charger *charger, int code, int value)
 {
-    struct charger *charger = (struct charger *)data;
     int64_t now = curr_time_ms();
     int down = !!value;
 
@@ -600,7 +601,7 @@
 {
     if (ev->type != EV_KEY)
         return;
-    set_key_callback(ev->code, ev->value, charger);
+    set_key_callback(charger, ev->code, ev->value);
 }
 
 static void set_next_key_check(struct charger *charger,
@@ -757,9 +758,8 @@
    return (int)timeout;
 }
 
-static int input_callback(int fd, unsigned int epevents, void *data)
+static int input_callback(struct charger *charger, int fd, unsigned int epevents)
 {
-    struct charger *charger = (struct charger *)data;
     struct input_event ev;
     int ret;
 
@@ -836,7 +836,8 @@
 
     LOGW("--------------- STARTING CHARGER MODE ---------------\n");
 
-    ret = ev_init(input_callback, charger);
+    ret = ev_init(std::bind(&input_callback, charger, std::placeholders::_1,
+                            std::placeholders::_2));
     if (!ret) {
         epollfd = ev_get_epollfd();
         healthd_register_event(epollfd, charger_event_handler);
@@ -875,7 +876,8 @@
             anim->frames[i].surface = scale_frames[i];
         }
     }
-    ev_sync_key_state(set_key_callback, charger);
+    ev_sync_key_state(std::bind(&set_key_callback, charger, std::placeholders::_1,
+                                std::placeholders::_2));
 
     charger->next_screen_transition = -1;
     charger->next_key_check = -1;
diff --git a/include/cutils/debugger.h b/include/cutils/debugger.h
deleted file mode 100644
index 20e8796..0000000
--- a/include/cutils/debugger.h
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __CUTILS_DEBUGGER_H
-#define __CUTILS_DEBUGGER_H
-
-#include <sys/cdefs.h>
-#include <sys/types.h>
-
-#include "debuggerd/client.h"
-
-__BEGIN_DECLS
-
-/* Dumps a process backtrace, registers, and stack to a tombstone file (requires root).
- * Stores the tombstone path in the provided buffer.
- * Returns 0 on success, -1 on error.
- */
-int dump_tombstone(pid_t tid, char* pathbuf, size_t pathlen);
-
-/* Dumps a process backtrace, registers, and stack to a tombstone file (requires root).
- * Stores the tombstone path in the provided buffer.
- * If reading debugger data from debuggerd ever takes longer than timeout_secs
- * seconds, then stop and return an error.
- * Returns 0 on success, -1 on error.
- */
-int dump_tombstone_timeout(pid_t tid, char* pathbuf, size_t pathlen, int timeout_secs);
-
-/* Dumps a process backtrace only to the specified file (requires root).
- * Returns 0 on success, -1 on error.
- */
-int dump_backtrace_to_file(pid_t tid, int fd);
-
-/* Dumps a process backtrace only to the specified file (requires root).
- * If reading debugger data from debuggerd ever takes longer than timeout_secs
- * seconds, then stop and return an error.
- * Returns 0 on success, -1 on error.
- */
-int dump_backtrace_to_file_timeout(pid_t tid, int fd, int timeout_secs);
-
-__END_DECLS
-
-#endif /* __CUTILS_DEBUGGER_H */
diff --git a/include/cutils/multiuser.h b/include/cutils/multiuser.h
index 4f23776..5bd9c7b 100644
--- a/include/cutils/multiuser.h
+++ b/include/cutils/multiuser.h
@@ -32,6 +32,7 @@
 extern uid_t multiuser_get_uid(userid_t user_id, appid_t app_id);
 
 extern gid_t multiuser_get_cache_gid(userid_t user_id, appid_t app_id);
+extern gid_t multiuser_get_ext_gid(userid_t user_id, appid_t app_id);
 extern gid_t multiuser_get_shared_gid(userid_t user_id, appid_t app_id);
 
 /* TODO: switch callers over to multiuser_get_shared_gid() */
diff --git a/include/private/android_filesystem_config.h b/include/private/android_filesystem_config.h
index eb71fb8..8e2bc1c 100644
--- a/include/private/android_filesystem_config.h
+++ b/include/private/android_filesystem_config.h
@@ -126,6 +126,8 @@
 #define AID_MEDIA_AUDIO   1055 /* GID for audio files on internal media storage */
 #define AID_MEDIA_VIDEO   1056 /* GID for video files on internal media storage */
 #define AID_MEDIA_IMAGE   1057 /* GID for image files on internal media storage */
+#define AID_TOMBSTONED    1058  /* tombstoned user */
+#define AID_MEDIA_OBB     1059 /* GID for OBB files on internal media storage */
 /* Changes to this file must be made in AOSP, *not* in internal branches. */
 
 #define AID_SHELL         2000  /* adb and debug shell user */
@@ -164,6 +166,9 @@
 #define AID_CACHE_GID_START  20000 /* start of gids for apps to mark cached data */
 #define AID_CACHE_GID_END    29999 /* end of gids for apps to mark cached data */
 
+#define AID_EXT_GID_START    30000 /* start of gids for apps to mark external data */
+#define AID_EXT_GID_END      39999 /* end of gids for apps to mark external data */
+
 #define AID_SHARED_GID_START 50000 /* start of gids for apps in each user to share */
 #define AID_SHARED_GID_END   59999 /* end of gids for apps in each user to share */
 
diff --git a/init/Android.mk b/init/Android.mk
index 6615692..35e6f4f 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -70,6 +70,7 @@
     init.cpp \
     keychords.cpp \
     property_service.cpp \
+    seccomp.cpp \
     signal_handler.cpp \
     ueventd.cpp \
     ueventd_parser.cpp \
@@ -96,6 +97,7 @@
     libbase \
     libc \
     libselinux \
+    libseccomp_policy \
     liblog \
     libcrypto_utils \
     libcrypto \
diff --git a/init/init.cpp b/init/init.cpp
index ee5add8..1ce3c35 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -62,6 +62,7 @@
 #include "keychords.h"
 #include "log.h"
 #include "property_service.h"
+#include "seccomp.h"
 #include "service.h"
 #include "signal_handler.h"
 #include "ueventd.h"
@@ -262,26 +263,18 @@
     panic();
 }
 
-#define MMAP_RND_PATH "/proc/sys/vm/mmap_rnd_bits"
-#define MMAP_RND_COMPAT_PATH "/proc/sys/vm/mmap_rnd_compat_bits"
-
-/* __attribute__((unused)) due to lack of mips support: see mips block
- * in set_mmap_rnd_bits_action */
-static bool __attribute__((unused)) set_mmap_rnd_bits_min(int start, int min, bool compat) {
-    std::string path;
-    if (compat) {
-        path = MMAP_RND_COMPAT_PATH;
-    } else {
-        path = MMAP_RND_PATH;
-    }
+static bool set_highest_available_option_value(std::string path, int min, int max)
+{
     std::ifstream inf(path, std::fstream::in);
     if (!inf) {
         LOG(ERROR) << "Cannot open for reading: " << path;
         return false;
     }
-    while (start >= min) {
+
+    int current = max;
+    while (current >= min) {
         // try to write out new value
-        std::string str_val = std::to_string(start);
+        std::string str_val = std::to_string(current);
         std::ofstream of(path, std::fstream::out);
         if (!of) {
             LOG(ERROR) << "Cannot open for writing: " << path;
@@ -297,16 +290,33 @@
         if (str_val.compare(str_rec) == 0) {
             break;
         }
-        start--;
+        current--;
     }
     inf.close();
-    if (start < min) {
-        LOG(ERROR) << "Unable to set minimum required entropy " << min << " in " << path;
+
+    if (current < min) {
+        LOG(ERROR) << "Unable to set minimum option value " << min << " in " << path;
         return false;
     }
     return true;
 }
 
+#define MMAP_RND_PATH "/proc/sys/vm/mmap_rnd_bits"
+#define MMAP_RND_COMPAT_PATH "/proc/sys/vm/mmap_rnd_compat_bits"
+
+/* __attribute__((unused)) due to lack of mips support: see mips block
+ * in set_mmap_rnd_bits_action */
+static bool __attribute__((unused)) set_mmap_rnd_bits_min(int start, int min, bool compat) {
+    std::string path;
+    if (compat) {
+        path = MMAP_RND_COMPAT_PATH;
+    } else {
+        path = MMAP_RND_PATH;
+    }
+
+    return set_highest_available_option_value(path, min, start);
+}
+
 /*
  * Set /proc/sys/vm/mmap_rnd_bits and potentially
  * /proc/sys/vm/mmap_rnd_compat_bits to the maximum supported values.
@@ -359,6 +369,25 @@
     return ret;
 }
 
+#define KPTR_RESTRICT_PATH "/proc/sys/kernel/kptr_restrict"
+#define KPTR_RESTRICT_MINVALUE 2
+#define KPTR_RESTRICT_MAXVALUE 4
+
+/* Set kptr_restrict to the highest available level.
+ *
+ * Aborts if unable to set this to an acceptable value.
+ */
+static int set_kptr_restrict_action(const std::vector<std::string>& args)
+{
+    std::string path = KPTR_RESTRICT_PATH;
+
+    if (!set_highest_available_option_value(path, KPTR_RESTRICT_MINVALUE, KPTR_RESTRICT_MAXVALUE)) {
+        LOG(ERROR) << "Unable to set adequate kptr_restrict value!";
+        security_failure();
+    }
+    return 0;
+}
+
 static int keychord_init_action(const std::vector<std::string>& args)
 {
     keychord_init();
@@ -763,6 +792,12 @@
 
         // Now set up SELinux for second stage.
         selinux_initialize(false);
+
+        // Install system-wide seccomp filter
+        if (!set_seccomp_filter()) {
+            LOG(ERROR) << "Failed to set seccomp policy";
+            security_failure();
+        }
     }
 
     // These directories were necessarily created before initial policy load
@@ -800,7 +835,12 @@
     parser.AddSectionParser("service",std::make_unique<ServiceParser>());
     parser.AddSectionParser("on", std::make_unique<ActionParser>());
     parser.AddSectionParser("import", std::make_unique<ImportParser>());
-    parser.ParseConfig("/init.rc");
+    std::string bootscript = property_get("ro.boot.init_rc");
+    if (bootscript.empty()) {
+        parser.ParseConfig("/init.rc");
+    } else {
+        parser.ParseConfig(bootscript);
+    }
 
     ActionManager& am = ActionManager::GetInstance();
 
@@ -811,6 +851,7 @@
     // ... so that we can start queuing up actions that require stuff from /dev.
     am.QueueBuiltinAction(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
     am.QueueBuiltinAction(set_mmap_rnd_bits_action, "set_mmap_rnd_bits");
+    am.QueueBuiltinAction(set_kptr_restrict_action, "set_kptr_restrict");
     am.QueueBuiltinAction(keychord_init_action, "keychord_init");
     am.QueueBuiltinAction(console_init_action, "console_init");
 
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 498a5a1..b2f6411 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -27,6 +27,7 @@
 #include <sys/poll.h>
 
 #include <memory>
+#include <vector>
 
 #include <cutils/misc.h>
 #include <cutils/sockets.h>
@@ -48,6 +49,7 @@
 
 #include <fs_mgr.h>
 #include <android-base/file.h>
+#include <android-base/strings.h>
 #include "bootimg.h"
 
 #include "property_service.h"
@@ -70,30 +72,30 @@
     }
 }
 
-static int check_mac_perms(const char *name, char *sctx, struct ucred *cr)
-{
-    char *tctx = NULL;
-    int result = 0;
+static bool check_mac_perms(const std::string& name, char* sctx, struct ucred* cr) {
+
+    if (!sctx) {
+      return false;
+    }
+
+    if (!sehandle_prop) {
+      return false;
+    }
+
+    char* tctx = nullptr;
+    if (selabel_lookup(sehandle_prop, &tctx, name.c_str(), 1) != 0) {
+      return false;
+    }
+
     property_audit_data audit_data;
 
-    if (!sctx)
-        goto err;
-
-    if (!sehandle_prop)
-        goto err;
-
-    if (selabel_lookup(sehandle_prop, &tctx, name, 1) != 0)
-        goto err;
-
-    audit_data.name = name;
+    audit_data.name = name.c_str();
     audit_data.cr = cr;
 
-    if (selinux_check_access(sctx, tctx, "property_service", "set", reinterpret_cast<void*>(&audit_data)) == 0)
-        result = 1;
+    bool has_access = (selinux_check_access(sctx, tctx, "property_service", "set", &audit_data) == 0);
 
     freecon(tctx);
- err:
-    return result;
+    return has_access;
 }
 
 static int check_control_mac_perms(const char *name, char *sctx, struct ucred *cr)
@@ -142,11 +144,9 @@
     }
 }
 
-bool is_legal_property_name(const std::string &name)
-{
+bool is_legal_property_name(const std::string& name) {
     size_t namelen = name.size();
 
-    if (namelen >= PROP_NAME_MAX) return false;
     if (namelen < 1) return false;
     if (name[0] == '.') return false;
     if (name[namelen - 1] == '.') return false;
@@ -169,58 +169,217 @@
     return true;
 }
 
-int property_set(const char* name, const char* value) {
-    size_t valuelen = strlen(value);
+uint32_t property_set(const std::string& name, const std::string& value) {
+    size_t valuelen = value.size();
 
     if (!is_legal_property_name(name)) {
         LOG(ERROR) << "property_set(\"" << name << "\", \"" << value << "\") failed: bad name";
-        return -1;
+        return PROP_ERROR_INVALID_NAME;
     }
+
     if (valuelen >= PROP_VALUE_MAX) {
         LOG(ERROR) << "property_set(\"" << name << "\", \"" << value << "\") failed: "
                    << "value too long";
-        return -1;
+        return PROP_ERROR_INVALID_VALUE;
     }
 
-    if (strcmp("selinux.restorecon_recursive", name) == 0 && valuelen > 0) {
-        if (restorecon(value, SELINUX_ANDROID_RESTORECON_RECURSE) != 0) {
+    if (name == "selinux.restorecon_recursive" && valuelen > 0) {
+        if (restorecon(value.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE) != 0) {
             LOG(ERROR) << "Failed to restorecon_recursive " << value;
         }
     }
 
-    prop_info* pi = (prop_info*) __system_property_find(name);
+    prop_info* pi = (prop_info*) __system_property_find(name.c_str());
     if (pi != nullptr) {
         // ro.* properties are actually "write-once".
-        if (!strncmp(name, "ro.", 3)) {
+        if (android::base::StartsWith(name, "ro.")) {
             LOG(ERROR) << "property_set(\"" << name << "\", \"" << value << "\") failed: "
                        << "property already set";
-            return -1;
+            return PROP_ERROR_READ_ONLY_PROPERTY;
         }
 
-        __system_property_update(pi, value, valuelen);
+        __system_property_update(pi, value.c_str(), valuelen);
     } else {
-        int rc = __system_property_add(name, strlen(name), value, valuelen);
+        int rc = __system_property_add(name.c_str(), name.size(), value.c_str(), valuelen);
         if (rc < 0) {
             LOG(ERROR) << "property_set(\"" << name << "\", \"" << value << "\") failed: "
                        << "__system_property_add failed";
-            return rc;
+            return PROP_ERROR_SET_FAILED;
         }
     }
 
     // Don't write properties to disk until after we have read all default
     // properties to prevent them from being overwritten by default values.
-    if (persistent_properties_loaded && strncmp("persist.", name, strlen("persist.")) == 0) {
-        write_persistent_property(name, value);
+    if (persistent_properties_loaded && android::base::StartsWith(name, "persist.")) {
+        write_persistent_property(name.c_str(), value.c_str());
     }
-    property_changed(name, value);
-    return 0;
+    property_changed(name.c_str(), value.c_str());
+    return PROP_SUCCESS;
 }
 
-static void handle_property_set_fd()
-{
-    prop_msg msg;
-    int r;
-    char * source_ctx = NULL;
+class SocketConnection {
+ public:
+  SocketConnection(int socket, const struct ucred& cred)
+      : socket_(socket), cred_(cred) {}
+
+  ~SocketConnection() {
+    close(socket_);
+  }
+
+  bool RecvUint32(uint32_t* value, uint32_t* timeout_ms) {
+    return RecvFully(value, sizeof(*value), timeout_ms);
+  }
+
+  bool RecvChars(char* chars, size_t size, uint32_t* timeout_ms) {
+    return RecvFully(chars, size, timeout_ms);
+  }
+
+  bool RecvString(std::string* value, uint32_t* timeout_ms) {
+    uint32_t len = 0;
+    if (!RecvUint32(&len, timeout_ms)) {
+      return false;
+    }
+
+    if (len == 0) {
+      *value = "";
+      return true;
+    }
+
+    std::vector<char> chars(len);
+    if (!RecvChars(&chars[0], len, timeout_ms)) {
+      return false;
+    }
+
+    *value = std::string(&chars[0], len);
+    return true;
+  }
+
+  bool SendUint32(uint32_t value) {
+    int result = TEMP_FAILURE_RETRY(send(socket_, &value, sizeof(value), 0));
+    return result == sizeof(value);
+  }
+
+  int socket() {
+    return socket_;
+  }
+
+  const struct ucred& cred() {
+    return cred_;
+  }
+
+ private:
+  bool PollIn(uint32_t* timeout_ms) {
+    struct pollfd ufds[1];
+    ufds[0].fd = socket_;
+    ufds[0].events = POLLIN;
+    ufds[0].revents = 0;
+    while (*timeout_ms > 0) {
+      Timer timer;
+      int nr = poll(ufds, 1, *timeout_ms);
+      uint64_t millis = timer.duration_ns()/1000000;
+      *timeout_ms = (millis > *timeout_ms) ? 0 : *timeout_ms - millis;
+
+      if (nr > 0) {
+        return true;
+      }
+
+      if (nr == 0) {
+        // Timeout
+        break;
+      }
+
+      if (nr < 0 && errno != EINTR) {
+        PLOG(ERROR) << "sys_prop: error waiting for uid " << cred_.uid << " to send property message";
+        return false;
+      } else { // errno == EINTR
+        // Timer rounds milliseconds down in case of EINTR we want it to be rounded up
+        // to avoid slowing init down by causing EINTR with under millisecond timeout.
+        if (*timeout_ms > 0) {
+          --(*timeout_ms);
+        }
+      }
+    }
+
+    LOG(ERROR) << "sys_prop: timeout waiting for uid " << cred_.uid << " to send property message.";
+    return false;
+  }
+
+  bool RecvFully(void* data_ptr, size_t size, uint32_t* timeout_ms) {
+    size_t bytes_left = size;
+    char* data = static_cast<char*>(data_ptr);
+    while (*timeout_ms > 0 && bytes_left > 0) {
+      if (!PollIn(timeout_ms)) {
+        return false;
+      }
+
+      int result = TEMP_FAILURE_RETRY(recv(socket_, data, bytes_left, MSG_DONTWAIT));
+      if (result <= 0) {
+        return false;
+      }
+
+      bytes_left -= result;
+      data += result;
+    }
+
+    return bytes_left == 0;
+  }
+
+  int socket_;
+  struct ucred cred_;
+
+  DISALLOW_IMPLICIT_CONSTRUCTORS(SocketConnection);
+};
+
+static void handle_property_set(SocketConnection& socket,
+                                const std::string& name,
+                                const std::string& value,
+                                bool legacy_protocol) {
+  const char* cmd_name = legacy_protocol ? "PROP_MSG_SETPROP" : "PROP_MSG_SETPROP2";
+  if (!is_legal_property_name(name)) {
+    LOG(ERROR) << "sys_prop(" << cmd_name << "): illegal property name \"" << name << "\"";
+    socket.SendUint32(PROP_ERROR_INVALID_NAME);
+    return;
+  }
+
+  struct ucred cr = socket.cred();
+  char* source_ctx = nullptr;
+  getpeercon(socket.socket(), &source_ctx);
+
+  if (android::base::StartsWith(name, "ctl.")) {
+    if (check_control_mac_perms(value.c_str(), source_ctx, &cr)) {
+      handle_control_message(name.c_str() + 4, value.c_str());
+      if (!legacy_protocol) {
+        socket.SendUint32(PROP_SUCCESS);
+      }
+    } else {
+      LOG(ERROR) << "sys_prop(" << cmd_name << "): Unable to " << (name.c_str() + 4)
+                 << " service ctl [" << value << "]"
+                 << " uid:" << cr.uid
+                 << " gid:" << cr.gid
+                 << " pid:" << cr.pid;
+      if (!legacy_protocol) {
+        socket.SendUint32(PROP_ERROR_HANDLE_CONTROL_MESSAGE);
+      }
+    }
+  } else {
+    if (check_mac_perms(name, source_ctx, &cr)) {
+      uint32_t result = property_set(name, value);
+      if (!legacy_protocol) {
+        socket.SendUint32(result);
+      }
+    } else {
+      LOG(ERROR) << "sys_prop(" << cmd_name << "): permission denied uid:" << cr.uid << " name:" << name;
+      if (!legacy_protocol) {
+        socket.SendUint32(PROP_ERROR_PERMISSION_DENIED);
+      }
+    }
+  }
+
+  freecon(source_ctx);
+}
+
+static void handle_property_set_fd() {
+    static constexpr uint32_t kDefaultSocketTimeout = 2000; /* ms */
 
     int s = accept(property_set_fd, nullptr, nullptr);
     if (s == -1) {
@@ -236,72 +395,50 @@
         return;
     }
 
-    static constexpr int timeout_ms = 2 * 1000;  /* Default 2 sec timeout for caller to send property. */
-    struct pollfd ufds[1];
-    ufds[0].fd = s;
-    ufds[0].events = POLLIN;
-    ufds[0].revents = 0;
-    int nr = TEMP_FAILURE_RETRY(poll(ufds, 1, timeout_ms));
-    if (nr == 0) {
-        LOG(ERROR) << "sys_prop: timeout waiting for uid " << cr.uid << " to send property message.";
-        close(s);
-        return;
-    } else if (nr < 0) {
-        PLOG(ERROR) << "sys_prop: error waiting for uid " << cr.uid << " to send property message";
-        close(s);
+    SocketConnection socket(s, cr);
+    uint32_t timeout_ms = kDefaultSocketTimeout;
+
+    uint32_t cmd = 0;
+
+    if (!socket.RecvUint32(&cmd, &timeout_ms)) {
+        PLOG(ERROR) << "sys_prop: error while reading command from the socket";
+        socket.SendUint32(PROP_ERROR_READ_CMD);
         return;
     }
 
-    r = TEMP_FAILURE_RETRY(recv(s, &msg, sizeof(msg), MSG_DONTWAIT));
-    if(r != sizeof(prop_msg)) {
-        PLOG(ERROR) << "sys_prop: mis-match msg size received: " << r << " expected: " << sizeof(prop_msg);
-        close(s);
-        return;
-    }
+    switch(cmd) {
+    case PROP_MSG_SETPROP: {
+        char prop_name[PROP_NAME_MAX];
+        char prop_value[PROP_VALUE_MAX];
 
-    switch(msg.cmd) {
-    case PROP_MSG_SETPROP:
-        msg.name[PROP_NAME_MAX-1] = 0;
-        msg.value[PROP_VALUE_MAX-1] = 0;
-
-        if (!is_legal_property_name(msg.name)) {
-            LOG(ERROR) << "sys_prop: illegal property name \"" << msg.name << "\"";
-            close(s);
-            return;
+        if (!socket.RecvChars(prop_name, PROP_NAME_MAX, &timeout_ms) ||
+            !socket.RecvChars(prop_value, PROP_VALUE_MAX, &timeout_ms)) {
+          PLOG(ERROR) << "sys_prop(PROP_MSG_SETPROP): error while reading name/value from the socket";
+          return;
         }
 
-        getpeercon(s, &source_ctx);
+        prop_name[PROP_NAME_MAX-1] = 0;
+        prop_value[PROP_VALUE_MAX-1] = 0;
 
-        if(memcmp(msg.name,"ctl.",4) == 0) {
-            // Keep the old close-socket-early behavior when handling
-            // ctl.* properties.
-            close(s);
-            if (check_control_mac_perms(msg.value, source_ctx, &cr)) {
-                handle_control_message((char*) msg.name + 4, (char*) msg.value);
-            } else {
-                LOG(ERROR) << "sys_prop: Unable to " << (msg.name + 4)
-                           << " service ctl [" << msg.value << "]"
-                           << " uid:" << cr.uid
-                           << " gid:" << cr.gid
-                           << " pid:" << cr.pid;
-            }
-        } else {
-            if (check_mac_perms(msg.name, source_ctx, &cr)) {
-                property_set((char*) msg.name, (char*) msg.value);
-            } else {
-                LOG(ERROR) << "sys_prop: permission denied uid:" << cr.uid << " name:" << msg.name;
-            }
-
-            // Note: bionic's property client code assumes that the
-            // property server will not close the socket until *AFTER*
-            // the property is written to memory.
-            close(s);
-        }
-        freecon(source_ctx);
+        handle_property_set(socket, prop_value, prop_value, true);
         break;
+      }
 
+    case PROP_MSG_SETPROP2: {
+        std::string name;
+        std::string value;
+        if (!socket.RecvString(&name, &timeout_ms) ||
+            !socket.RecvString(&value, &timeout_ms)) {
+          PLOG(ERROR) << "sys_prop(PROP_MSG_SETPROP2): error while reading name/value from the socket";
+          socket.SendUint32(PROP_ERROR_READ_DATA);
+          return;
+        }
+
+        handle_property_set(socket, name, value, false);
+        break;
+      }
     default:
-        close(s);
+        socket.SendUint32(PROP_ERROR_INVALID_CMD);
         break;
     }
 }
@@ -439,6 +576,8 @@
 
 void property_load_boot_defaults() {
     load_properties_from_file(PROP_PATH_RAMDISK_DEFAULT, NULL);
+    load_properties_from_file(PROP_PATH_ODM_DEFAULT, NULL);
+    load_properties_from_file(PROP_PATH_VENDOR_DEFAULT, NULL);
 }
 
 static void load_override_properties() {
@@ -501,12 +640,15 @@
 
 void load_system_props() {
     load_properties_from_file(PROP_PATH_SYSTEM_BUILD, NULL);
+    load_properties_from_file(PROP_PATH_ODM_BUILD, NULL);
     load_properties_from_file(PROP_PATH_VENDOR_BUILD, NULL);
     load_properties_from_file(PROP_PATH_FACTORY, "ro.*");
     load_recovery_id_prop();
 }
 
 void start_property_service() {
+    property_set("ro.property_service.version", "2");
+
     property_set_fd = create_socket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
                                     0666, 0, 0, NULL);
     if (property_set_fd == -1) {
diff --git a/init/property_service.h b/init/property_service.h
index e3a2acb..5d59473 100644
--- a/init/property_service.h
+++ b/init/property_service.h
@@ -27,14 +27,14 @@
     const char* name;
 };
 
-extern void property_init(void);
-extern void property_load_boot_defaults(void);
-extern void load_persist_props(void);
-extern void load_system_props(void);
-extern void start_property_service(void);
+void property_init(void);
+void property_load_boot_defaults(void);
+void load_persist_props(void);
+void load_system_props(void);
+void start_property_service(void);
 std::string property_get(const char* name);
-extern int property_set(const char *name, const char *value);
-extern bool is_legal_property_name(const std::string &name);
+uint32_t property_set(const std::string& name, const std::string& value);
+bool is_legal_property_name(const std::string& name);
 
 
 #endif  /* _INIT_PROPERTY_H */
diff --git a/init/seccomp.cpp b/init/seccomp.cpp
new file mode 100644
index 0000000..d632302
--- /dev/null
+++ b/init/seccomp.cpp
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "seccomp.h"
+
+#include <vector>
+
+#include <sys/prctl.h>
+
+#include <linux/unistd.h>
+#include <linux/audit.h>
+#include <linux/filter.h>
+#include <linux/seccomp.h>
+
+#include "log.h"
+#include "seccomp_policy.h"
+
+#define syscall_nr (offsetof(struct seccomp_data, nr))
+#define arch_nr (offsetof(struct seccomp_data, arch))
+
+#if   defined __arm__
+#define AUDIT_ARCH_NR AUDIT_ARCH_ARM
+#elif defined __aarch64__
+#define AUDIT_ARCH_NR AUDIT_ARCH_AARCH64
+#define AUDIT_ARCH_NR32 AUDIT_ARCH_ARM
+#elif defined __i386__
+#define AUDIT_ARCH_NR AUDIT_ARCH_I386
+#elif defined __x86_64__
+#define AUDIT_ARCH_NR AUDIT_ARCH_X86_64
+#define AUDIT_ARCH_NR32 AUDIT_ARCH_I386
+#elif defined __mips64__
+#define AUDIT_ARCH_NR AUDIT_ARCH_MIPS64
+#define AUDIT_ARCH_NR32 AUDIT_ARCH_MIPS
+#elif defined __mips__ && !defined __mips64__
+#define AUDIT_ARCH_NR AUDIT_ARCH_MIPS
+#else
+#error "Could not determine AUDIT_ARCH_NR for this architecture"
+#endif
+
+typedef std::vector<sock_filter> filter;
+
+// We want to keep the below inline functions for debugging and future
+// development even though they are not used currently.
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-function"
+
+static inline void Kill(filter& f) {
+    f.push_back(BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_KILL));
+}
+
+static inline void Trap(filter& f) {
+    f.push_back(BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_TRAP));
+}
+
+static inline void Error(filter& f, __u16 retcode) {
+    f.push_back(BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ERRNO + retcode));
+}
+
+inline static void Trace(filter& f) {
+    f.push_back(BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_TRACE));
+}
+
+inline static void Allow(filter& f) {
+    f.push_back(BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW));
+}
+
+inline static void AllowSyscall(filter& f, __u32 num) {
+    f.push_back(BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, num, 0, 1));
+    f.push_back(BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW));
+}
+
+inline static void ExamineSyscall(filter& f) {
+    f.push_back(BPF_STMT(BPF_LD|BPF_W|BPF_ABS, syscall_nr));
+}
+
+#ifdef AUDIT_ARCH_NR32
+inline static int SetValidateArchitectureJumpTarget(size_t offset, filter& f) {
+    auto jump_length = f.size() - offset - 1;
+    auto u8_jump_length = (__u8) jump_length;
+    if (u8_jump_length != jump_length) {
+        LOG(ERROR) << "Can't set jump greater than 255 - actual jump is " << jump_length;
+        return -1;
+    }
+    f[offset] = BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, AUDIT_ARCH_NR32, u8_jump_length, 0);
+    return 0;
+}
+#endif
+
+inline static size_t ValidateArchitectureAndJumpIfNeeded(filter& f) {
+    f.push_back(BPF_STMT(BPF_LD|BPF_W|BPF_ABS, arch_nr));
+
+#ifdef AUDIT_ARCH_NR32
+    f.push_back(BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, AUDIT_ARCH_NR, 2, 0));
+    f.push_back(BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, AUDIT_ARCH_NR32, 1, 0));
+    Kill(f);
+    return f.size() - 2;
+#else
+    f.push_back(BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, AUDIT_ARCH_NR, 1, 0));
+    Kill(f);
+    return 0;
+#endif
+}
+
+#pragma clang diagnostic pop
+
+static bool install_filter(filter const& f) {
+    struct sock_fprog prog = {
+        (unsigned short) f.size(),
+        (struct sock_filter*) &f[0],
+    };
+
+    if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog) < 0) {
+        PLOG(ERROR) << "SECCOMP: Could not set seccomp filter";
+        return false;
+    }
+
+    LOG(INFO) << "SECCOMP: Global filter installed";
+    return true;
+}
+
+bool set_seccomp_filter() {
+    filter f;
+
+    // Note that for mixed 64/32 bit architectures, ValidateArchitecture inserts a
+    // jump that must be changed to point to the start of the 32-bit policy
+    // 32 bit syscalls will not hit the policy between here and the call to SetJump
+#ifdef AUDIT_ARCH_NR32
+    auto offset_to_32bit_filter =
+#endif
+        ValidateArchitectureAndJumpIfNeeded(f);
+
+    // Native filter
+    ExamineSyscall(f);
+
+#ifdef __aarch64__
+    // Syscalls needed to boot Android
+    AllowSyscall(f, __NR_pivot_root);
+    AllowSyscall(f, __NR_ioprio_get);
+    AllowSyscall(f, __NR_ioprio_set);
+    AllowSyscall(f, __NR_gettid);
+    AllowSyscall(f, __NR_futex);
+    AllowSyscall(f, __NR_clone);
+    AllowSyscall(f, __NR_rt_sigreturn);
+    AllowSyscall(f, __NR_rt_tgsigqueueinfo);
+    AllowSyscall(f, __NR_add_key);
+    AllowSyscall(f, __NR_request_key);
+    AllowSyscall(f, __NR_keyctl);
+    AllowSyscall(f, __NR_restart_syscall);
+    AllowSyscall(f, __NR_getrandom);
+
+    // Needed for performance tools
+    AllowSyscall(f, __NR_perf_event_open);
+
+    // Needed for treble
+    AllowSyscall(f, __NR_finit_module);
+
+    // Needed for trusty
+    AllowSyscall(f, __NR_syncfs);
+
+    // Needed for strace
+    AllowSyscall(f, __NR_tkill);  // __NR_tkill
+
+    // Needed for kernel to restart syscalls
+    AllowSyscall(f, __NR_restart_syscall);
+
+     // arm64-only filter - autogenerated from bionic syscall usage
+    for (size_t i = 0; i < arm64_filter_size; ++i)
+        f.push_back(arm64_filter[i]);
+#else
+    // Generic policy
+    Allow(f);
+#endif
+
+#ifdef AUDIT_ARCH_NR32
+    if (SetValidateArchitectureJumpTarget(offset_to_32bit_filter, f) != 0)
+        return -1;
+
+    // 32-bit filter for 64-bit platforms
+    ExamineSyscall(f);
+
+#ifdef __aarch64__
+    // Syscalls needed to boot android
+    AllowSyscall(f, 120); // __NR_clone
+    AllowSyscall(f, 240); // __NR_futex
+    AllowSyscall(f, 119); // __NR_sigreturn
+    AllowSyscall(f, 173); // __NR_rt_sigreturn
+    AllowSyscall(f, 363); // __NR_rt_tgsigqueueinfo
+    AllowSyscall(f, 224); // __NR_gettid
+
+    // Syscalls needed to run Chrome
+    AllowSyscall(f, 383); // __NR_seccomp - needed to start Chrome
+    AllowSyscall(f, 384); // __NR_getrandom - needed to start Chrome
+
+    // Syscalls needed to run GFXBenchmark
+    AllowSyscall(f, 190); // __NR_vfork
+
+    // Needed for strace
+    AllowSyscall(f, 238);  // __NR_tkill
+
+    // Needed for kernel to restart syscalls
+    AllowSyscall(f, 0);  // __NR_restart_syscall
+
+    // arm32-on-arm64 only filter - autogenerated from bionic syscall usage
+    for (size_t i = 0; i < arm_filter_size; ++i)
+        f.push_back(arm_filter[i]);
+#else
+    // Generic policy
+    Allow(f);
+#endif
+#endif
+    return install_filter(f);
+}
diff --git a/debuggerd/test/selinux/android.h b/init/seccomp.h
similarity index 81%
rename from debuggerd/test/selinux/android.h
rename to init/seccomp.h
index abed087..cda7a89 100644
--- a/debuggerd/test/selinux/android.h
+++ b/init/seccomp.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2016 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,4 +14,9 @@
  * limitations under the License.
  */
 
-extern "C" int selinux_android_restorecon(const char*, unsigned int);
+#ifndef SECCOMP_H
+#define SECCOMP_H
+
+bool set_seccomp_filter();
+
+#endif
diff --git a/libcutils/Android.bp b/libcutils/Android.bp
index 39f8aba..b96e3ae 100644
--- a/libcutils/Android.bp
+++ b/libcutils/Android.bp
@@ -78,7 +78,6 @@
             srcs: libcutils_nonwindows_sources + [
                 "android_reboot.c",
                 "ashmem-dev.c",
-                "debugger.c",
                 "klog.cpp",
                 "partition_utils.c",
                 "properties.c",
@@ -86,9 +85,6 @@
                 "trace-dev.c",
                 "uevent.c",
             ],
-
-            static_libs: ["libdebuggerd_client"],
-            export_static_lib_headers: ["libdebuggerd_client"],
         },
 
         android_arm: {
diff --git a/libcutils/debugger.c b/libcutils/debugger.c
deleted file mode 100644
index 32fac98..0000000
--- a/libcutils/debugger.c
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "DEBUG"
-
-#include <fcntl.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/socket.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <cutils/debugger.h>
-#include <cutils/sockets.h>
-#include <log/log.h>
-
-static int send_request(int sock_fd, void* msg_ptr, size_t msg_len) {
-  int result = 0;
-  if (TEMP_FAILURE_RETRY(write(sock_fd, msg_ptr, msg_len)) != (ssize_t) msg_len) {
-    result = -1;
-  } else {
-    char ack;
-    if (TEMP_FAILURE_RETRY(read(sock_fd, &ack, 1)) != 1) {
-      result = -1;
-    }
-  }
-  return result;
-}
-
-static int make_dump_request(debugger_action_t action, pid_t tid, int timeout_secs) {
-  debugger_msg_t msg;
-  memset(&msg, 0, sizeof(msg));
-  msg.tid = tid;
-  msg.action = action;
-
-  int sock_fd = socket_local_client(DEBUGGER_SOCKET_NAME, ANDROID_SOCKET_NAMESPACE_ABSTRACT,
-      SOCK_STREAM | SOCK_CLOEXEC);
-  if (sock_fd < 0) {
-    return -1;
-  }
-
-  if (timeout_secs > 0) {
-    struct timeval tm;
-    tm.tv_sec = timeout_secs;
-    tm.tv_usec = 0;
-    if (setsockopt(sock_fd, SOL_SOCKET, SO_RCVTIMEO, &tm, sizeof(tm)) == -1) {
-      ALOGE("WARNING: Cannot set receive timeout value on socket: %s", strerror(errno));
-    }
-
-    if (setsockopt(sock_fd, SOL_SOCKET, SO_SNDTIMEO, &tm, sizeof(tm)) == -1) {
-      ALOGE("WARNING: Cannot set send timeout value on socket: %s", strerror(errno));
-    }
-  }
-
-  if (send_request(sock_fd, &msg, sizeof(msg)) < 0) {
-    close(sock_fd);
-    return -1;
-  }
-
-  return sock_fd;
-}
-
-int dump_backtrace_to_file(pid_t tid, int fd) {
-  return dump_backtrace_to_file_timeout(tid, fd, 0);
-}
-
-int dump_backtrace_to_file_timeout(pid_t tid, int fd, int timeout_secs) {
-  int sock_fd = make_dump_request(DEBUGGER_ACTION_DUMP_BACKTRACE, tid, timeout_secs);
-  if (sock_fd < 0) {
-    return -1;
-  }
-
-  /* Write the data read from the socket to the fd. */
-  int result = 0;
-  char buffer[1024];
-  ssize_t n;
-  while ((n = TEMP_FAILURE_RETRY(read(sock_fd, buffer, sizeof(buffer)))) > 0) {
-    if (TEMP_FAILURE_RETRY(write(fd, buffer, n)) != n) {
-      result = -1;
-      break;
-    }
-  }
-  close(sock_fd);
-  return result;
-}
-
-int dump_tombstone(pid_t tid, char* pathbuf, size_t pathlen) {
-  return dump_tombstone_timeout(tid, pathbuf, pathlen, 0);
-}
-
-int dump_tombstone_timeout(pid_t tid, char* pathbuf, size_t pathlen, int timeout_secs) {
-  int sock_fd = make_dump_request(DEBUGGER_ACTION_DUMP_TOMBSTONE, tid, timeout_secs);
-  if (sock_fd < 0) {
-    return -1;
-  }
-
-  /* Read the tombstone file name. */
-  char buffer[100]; /* This is larger than the largest tombstone path. */
-  int result = 0;
-  ssize_t n = TEMP_FAILURE_RETRY(read(sock_fd, buffer, sizeof(buffer) - 1));
-  if (n <= 0) {
-    result = -1;
-  } else {
-    if (pathbuf && pathlen) {
-      if (n >= (ssize_t) pathlen) {
-        n = pathlen - 1;
-      }
-      buffer[n] = '\0';
-      memcpy(pathbuf, buffer, n + 1);
-    }
-  }
-  close(sock_fd);
-  return result;
-}
diff --git a/libcutils/fs_config.c b/libcutils/fs_config.c
index f43f1e6..b701bba 100644
--- a/libcutils/fs_config.c
+++ b/libcutils/fs_config.c
@@ -177,6 +177,9 @@
                                            CAP_MASK_LONG(CAP_SETPCAP),
                                               "system/bin/webview_zygote64" },
 
+    { 00755, AID_ROOT,      AID_SHELL,     0, "system/bin/crash_dump32" },
+    { 00755, AID_ROOT,      AID_SHELL,     0, "system/bin/crash_dump64" },
+    { 00755, AID_ROOT,      AID_SHELL,     0, "system/bin/debuggerd" },
     { 00750, AID_ROOT,      AID_ROOT,      0, "system/bin/uncrypt" },
     { 00750, AID_ROOT,      AID_ROOT,      0, "system/bin/install-recovery.sh" },
     { 00755, AID_ROOT,      AID_SHELL,     0, "system/bin/*" },
@@ -194,7 +197,10 @@
     { 00640, AID_ROOT,      AID_SHELL,     0, "fstab.*" },
     { 00600, AID_ROOT,      AID_ROOT,      0, "system/build.prop" },
     { 00600, AID_ROOT,      AID_ROOT,      0, "vendor/build.prop" },
+    { 00600, AID_ROOT,      AID_ROOT,      0, "odm/build.prop" },
     { 00600, AID_ROOT,      AID_ROOT,      0, "default.prop" },
+    { 00600, AID_ROOT,      AID_ROOT,      0, "vendor/default.prop" },
+    { 00600, AID_ROOT,      AID_ROOT,      0, "odm/default.prop" },
     { 00644, AID_ROOT,      AID_ROOT,      0, 0 },
 };
 
diff --git a/libcutils/multiuser.c b/libcutils/multiuser.c
index 0ef337d..08d4d6c 100644
--- a/libcutils/multiuser.c
+++ b/libcutils/multiuser.c
@@ -37,6 +37,14 @@
     }
 }
 
+gid_t multiuser_get_ext_gid(userid_t user_id, appid_t app_id) {
+    if (app_id >= AID_APP_START && app_id <= AID_APP_END) {
+        return multiuser_get_uid(user_id, (app_id - AID_APP_START) + AID_EXT_GID_START);
+    } else {
+        return -1;
+    }
+}
+
 gid_t multiuser_get_shared_gid(userid_t user_id, appid_t app_id) {
     if (app_id >= AID_APP_START && app_id <= AID_APP_END) {
         return multiuser_get_uid(user_id, (app_id - AID_APP_START) + AID_SHARED_GID_START);
diff --git a/libcutils/tests/multiuser_test.cpp b/libcutils/tests/multiuser_test.cpp
index 2307ea8..c5f58b4 100644
--- a/libcutils/tests/multiuser_test.cpp
+++ b/libcutils/tests/multiuser_test.cpp
@@ -58,6 +58,14 @@
     EXPECT_EQ(1020000, multiuser_get_cache_gid(10, 10000));
 }
 
+TEST(MultiuserTest, TestExt) {
+    EXPECT_EQ(-1, multiuser_get_ext_gid(0, 0));
+    EXPECT_EQ(-1, multiuser_get_ext_gid(0, 1000));
+    EXPECT_EQ(30000, multiuser_get_ext_gid(0, 10000));
+    EXPECT_EQ(-1, multiuser_get_ext_gid(0, 50000));
+    EXPECT_EQ(1030000, multiuser_get_ext_gid(10, 10000));
+}
+
 TEST(MultiuserTest, TestShared) {
     EXPECT_EQ(-1, multiuser_get_shared_gid(0, 0));
     EXPECT_EQ(-1, multiuser_get_shared_gid(0, 1000));
diff --git a/liblog/Android.bp b/liblog/Android.bp
index bbe7d79..40cdcee 100644
--- a/liblog/Android.bp
+++ b/liblog/Android.bp
@@ -57,7 +57,7 @@
             srcs: liblog_target_sources,
             // AddressSanitizer runtime library depends on liblog.
             sanitize: {
-                never: true,
+                address: false,
             },
         },
         android_arm: {
diff --git a/liblog/logger_write.c b/liblog/logger_write.c
index f19c3ab..1a2d506 100644
--- a/liblog/logger_write.c
+++ b/liblog/logger_write.c
@@ -262,6 +262,8 @@
     }
 
 #if defined(__ANDROID__)
+    clock_gettime(android_log_clockid(), &ts);
+
     if (log_id == LOG_ID_SECURITY) {
         if (vec[0].iov_len < 4) {
             return -EINVAL;
@@ -351,8 +353,6 @@
             return -EPERM;
         }
     }
-
-    clock_gettime(android_log_clockid(), &ts);
 #else
     /* simulate clock_gettime(CLOCK_REALTIME, &ts); */
     {
diff --git a/libsync/include/sync/sync.h b/libsync/include/sync/sync.h
index 2e5d82f..50ed0ac 100644
--- a/libsync/include/sync/sync.h
+++ b/libsync/include/sync/sync.h
@@ -22,9 +22,16 @@
 #include <sys/cdefs.h>
 #include <stdint.h>
 
+#include <linux/types.h>
+
 __BEGIN_DECLS
 
-// XXX: These structs are copied from the header "linux/sync.h".
+struct sync_legacy_merge_data {
+ int32_t fd2;
+ char name[32];
+ int32_t fence;
+};
+
 struct sync_fence_info_data {
  uint32_t len;
  char name[32];
@@ -41,6 +48,108 @@
  uint8_t driver_data[0];
 };
 
+#define SYNC_IOC_MAGIC		'>'
+
+/**
+ * DOC: SYNC_IOC_LEGACY_WAIT - wait for a fence to signal
+ *
+ * pass timeout in milliseconds.  Waits indefinitely timeout < 0.
+ *
+ * This is the legacy version of the Sync API before the de-stage that happened
+ * on Linux kernel 4.7.
+ */
+#define SYNC_IOC_LEGACY_WAIT	_IOW(SYNC_IOC_MAGIC, 0, __s32)
+
+/**
+ * DOC: SYNC_IOC_MERGE - merge two fences
+ *
+ * Takes a struct sync_merge_data.  Creates a new fence containing copies of
+ * the sync_pts in both the calling fd and sync_merge_data.fd2.  Returns the
+ * new fence's fd in sync_merge_data.fence
+ *
+ * This is the legacy version of the Sync API before the de-stage that happened
+ * on Linux kernel 4.7.
+ */
+#define SYNC_IOC_LEGACY_MERGE	_IOWR(SYNC_IOC_MAGIC, 1, \
+	struct sync_legacy_merge_data)
+
+/**
+ * DOC: SYNC_IOC_LEGACY_FENCE_INFO - get detailed information on a fence
+ *
+ * Takes a struct sync_fence_info_data with extra space allocated for pt_info.
+ * Caller should write the size of the buffer into len.  On return, len is
+ * updated to reflect the total size of the sync_fence_info_data including
+ * pt_info.
+ *
+ * pt_info is a buffer containing sync_pt_infos for every sync_pt in the fence.
+ * To iterate over the sync_pt_infos, use the sync_pt_info.len field.
+ *
+ * This is the legacy version of the Sync API before the de-stage that happened
+ * on Linux kernel 4.7.
+ */
+#define SYNC_IOC_LEGACY_FENCE_INFO	_IOWR(SYNC_IOC_MAGIC, 2,\
+	struct sync_fence_info_data)
+
+struct sync_merge_data {
+ char name[32];
+ int32_t fd2;
+ int32_t fence;
+ uint32_t flags;
+ uint32_t pad;
+};
+
+struct sync_file_info {
+ char name[32];
+ int32_t status;
+ uint32_t flags;
+ uint32_t num_fences;
+ uint32_t pad;
+
+ uint64_t sync_fence_info;
+};
+
+struct sync_fence_info {
+ char obj_name[32];
+ char driver_name[32];
+ int32_t status;
+ uint32_t flags;
+ uint64_t timestamp_ns;
+};
+
+/**
+ * Mainline API:
+ *
+ * Opcodes  0, 1 and 2 were burned during a API change to avoid users of the
+ * old API to get weird errors when trying to handling sync_files. The API
+ * change happened during the de-stage of the Sync Framework when there was
+ * no upstream users available.
+ */
+
+/**
+ * DOC: SYNC_IOC_MERGE - merge two fences
+ *
+ * Takes a struct sync_merge_data.  Creates a new fence containing copies of
+ * the sync_pts in both the calling fd and sync_merge_data.fd2.  Returns the
+ * new fence's fd in sync_merge_data.fence
+ *
+ * This is the new version of the Sync API after the de-stage that happened
+ * on Linux kernel 4.7.
+ */
+#define SYNC_IOC_MERGE		_IOWR(SYNC_IOC_MAGIC, 3, struct sync_merge_data)
+
+/**
+ * DOC: SYNC_IOC_FILE_INFO - get detailed information on a sync_file
+ *
+ * Takes a struct sync_file_info. If num_fences is 0, the field is updated
+ * with the actual number of fences. If num_fences is > 0, the system will
+ * use the pointer provided on sync_fence_info to return up to num_fences of
+ * struct sync_fence_info, with detailed fence information.
+ *
+ * This is the new version of the Sync API after the de-stage that happened
+ * on Linux kernel 4.7.
+ */
+#define SYNC_IOC_FILE_INFO	_IOWR(SYNC_IOC_MAGIC, 4, struct sync_file_info)
+
 /* timeout in msecs */
 int sync_wait(int fd, int timeout);
 int sync_merge(const char *name, int fd1, int fd2);
diff --git a/libsync/sync.c b/libsync/sync.c
index 6281b20..9ed03db 100644
--- a/libsync/sync.c
+++ b/libsync/sync.c
@@ -20,6 +20,8 @@
 #include <malloc.h>
 #include <stdint.h>
 #include <string.h>
+#include <errno.h>
+#include <poll.h>
 
 #include <sys/ioctl.h>
 #include <sys/stat.h>
@@ -27,18 +29,6 @@
 
 #include <sync/sync.h>
 
-// The sync code is undergoing a major change. Add enough in to get
-// everything to compile wih the latest uapi headers.
-struct sync_merge_data {
-  int32_t fd2;
-  char name[32];
-  int32_t fence;
-};
-
-#define SYNC_IOC_MAGIC '>'
-#define SYNC_IOC_WAIT _IOW(SYNC_IOC_MAGIC, 0, __s32)
-#define SYNC_IOC_MERGE _IOWR(SYNC_IOC_MAGIC, 1, struct sync_merge_data)
-#define SYNC_IOC_FENCE_INFO _IOWR(SYNC_IOC_MAGIC, 2, struct sync_fence_info_data)
 
 struct sw_sync_create_fence_data {
   __u32 value;
@@ -52,43 +42,133 @@
 
 int sync_wait(int fd, int timeout)
 {
-    __s32 to = timeout;
+    struct pollfd fds;
+    int ret;
 
-    return ioctl(fd, SYNC_IOC_WAIT, &to);
+    if (fd < 0) {
+        errno = EINVAL;
+        return -1;
+    }
+
+    fds.fd = fd;
+    fds.events = POLLIN;
+
+    do {
+        ret = poll(&fds, 1, timeout);
+        if (ret > 0) {
+            if (fds.revents & (POLLERR | POLLNVAL)) {
+                errno = EINVAL;
+                return -1;
+            }
+            return 0;
+        } else if (ret == 0) {
+            errno = ETIME;
+            return -1;
+        }
+    } while (ret == -1 && (errno == EINTR || errno == EAGAIN));
+
+    return ret;
 }
 
 int sync_merge(const char *name, int fd1, int fd2)
 {
+    struct sync_legacy_merge_data legacy_data;
     struct sync_merge_data data;
-    int err;
+    int ret;
 
     data.fd2 = fd2;
     strlcpy(data.name, name, sizeof(data.name));
+    data.flags = 0;
+    data.pad = 0;
 
-    err = ioctl(fd1, SYNC_IOC_MERGE, &data);
-    if (err < 0)
-        return err;
+    ret = ioctl(fd1, SYNC_IOC_MERGE, &data);
+    if (ret < 0 && errno == ENOTTY) {
+        legacy_data.fd2 = fd2;
+        strlcpy(legacy_data.name, name, sizeof(legacy_data.name));
+
+        ret = ioctl(fd1, SYNC_IOC_LEGACY_MERGE, &legacy_data);
+        if (ret < 0)
+            return ret;
+
+        return legacy_data.fence;
+    } else if (ret < 0) {
+        return ret;
+    }
 
     return data.fence;
 }
 
 struct sync_fence_info_data *sync_fence_info(int fd)
 {
-    struct sync_fence_info_data *info;
-    int err;
+    struct sync_fence_info_data *legacy_info;
+    struct sync_pt_info *legacy_pt_info;
+    struct sync_file_info *info;
+    struct sync_fence_info *fence_info;
+    int err, num_fences, i;
 
-    info = malloc(4096);
-    if (info == NULL)
+    legacy_info = malloc(4096);
+    if (legacy_info == NULL)
         return NULL;
 
-    info->len = 4096;
-    err = ioctl(fd, SYNC_IOC_FENCE_INFO, info);
-    if (err < 0) {
-        free(info);
+    legacy_info->len = 4096;
+    err = ioctl(fd, SYNC_IOC_LEGACY_FENCE_INFO, legacy_info);
+    if (err < 0 && errno != ENOTTY) {
+        free(legacy_info);
         return NULL;
+    } else if (err == 0) {
+        return legacy_info;
     }
 
-    return info;
+    info = calloc(1, sizeof(*info));
+    if (info == NULL)
+        goto free;
+
+    err = ioctl(fd, SYNC_IOC_FILE_INFO, info);
+    if (err < 0)
+        goto free;
+
+    num_fences = info->num_fences;
+
+    if (num_fences) {
+        info->flags = 0;
+        info->num_fences = num_fences;
+        info->sync_fence_info = (uint64_t) calloc(num_fences,
+                                        sizeof(struct sync_fence_info));
+        if ((void *)info->sync_fence_info == NULL)
+            goto free;
+
+        err = ioctl(fd, SYNC_IOC_FILE_INFO, info);
+        if (err < 0) {
+            free((void *)info->sync_fence_info);
+            goto free;
+        }
+    }
+
+    legacy_info->len = sizeof(*legacy_info) +
+                        num_fences * sizeof(struct sync_fence_info);
+    strlcpy(legacy_info->name, info->name, sizeof(legacy_info->name));
+    legacy_info->status = info->status;
+
+    legacy_pt_info = (struct sync_pt_info *)legacy_info->pt_info;
+    fence_info = (struct sync_fence_info *)info->sync_fence_info;
+    for (i = 0 ; i < num_fences ; i++) {
+        legacy_pt_info[i].len = sizeof(*legacy_pt_info);
+        strlcpy(legacy_pt_info[i].obj_name, fence_info[i].obj_name,
+                sizeof(legacy_pt_info->obj_name));
+        strlcpy(legacy_pt_info[i].driver_name, fence_info[i].driver_name,
+                sizeof(legacy_pt_info->driver_name));
+        legacy_pt_info[i].status = fence_info[i].status;
+        legacy_pt_info[i].timestamp_ns = fence_info[i].timestamp_ns;
+    }
+
+    free((void *)info->sync_fence_info);
+    free(info);
+    return legacy_info;
+
+free:
+    free(legacy_info);
+    free(info);
+    return NULL;
 }
 
 struct sync_pt_info *sync_pt_info(struct sync_fence_info_data *info,
@@ -113,7 +193,13 @@
 
 int sw_sync_timeline_create(void)
 {
-    return open("/dev/sw_sync", O_RDWR);
+    int ret;
+
+    ret = open("/sys/kernel/debug/sync/sw_sync", O_RDWR);
+    if (ret < 0)
+        ret = open("/dev/sw_sync", O_RDWR);
+
+    return ret;
 }
 
 int sw_sync_timeline_inc(int fd, unsigned count)
diff --git a/libsync/tests/sync_test.cpp b/libsync/tests/sync_test.cpp
index 2c409dc..ff8a300 100644
--- a/libsync/tests/sync_test.cpp
+++ b/libsync/tests/sync_test.cpp
@@ -348,33 +348,6 @@
     ASSERT_EQ(selfMergeFence.getSignaledCount(), 1);
 }
 
-TEST(FenceTest, WaitOnDestroyedTimeline) {
-    SyncTimeline timeline;
-    ASSERT_TRUE(timeline.isValid());
-
-    SyncFence fenceSig(timeline, 100);
-    SyncFence fenceKill(timeline, 200);
-
-    // Spawn a thread to wait on a fence when the timeline is killed.
-    thread waitThread{
-        [&]() {
-            ASSERT_EQ(timeline.inc(100), 0);
-
-            ASSERT_EQ(fenceKill.wait(-1), -1);
-            ASSERT_EQ(errno, ENOENT);
-        }
-    };
-
-    // Wait for the thread to spool up.
-    fenceSig.wait();
-
-    // Kill the timeline.
-    timeline.destroy();
-
-    // wait for the thread to clean up.
-    waitThread.join();
-}
-
 TEST(FenceTest, PollOnDestroyedTimeline) {
     SyncTimeline timeline;
     ASSERT_TRUE(timeline.isValid());
@@ -391,8 +364,7 @@
             struct pollfd fds;
             fds.fd = fenceKill.getFd();
             fds.events = POLLIN | POLLERR;
-            ASSERT_EQ(poll(&fds, 1, -1), 1);
-            ASSERT_TRUE(fds.revents & POLLERR);
+            ASSERT_EQ(poll(&fds, 1, 0), 0);
         }
     };
 
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index c204a16..d9935c3 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -581,24 +581,36 @@
 
 } /* namespace android */
 
+void reportErrorName(const char **current,
+                     const char* name,
+                     bool blockSecurity) {
+    if (*current) {
+       return;
+    }
+    if (blockSecurity && (android_name_to_log_id(name) == LOG_ID_SECURITY)) {
+       return;
+    }
+    *current = name;
+}
 
 int main(int argc, char **argv)
 {
     using namespace android;
     int err;
     int hasSetLogFormat = 0;
-    int clearLog = 0;
-    int getLogSize = 0;
+    bool clearLog = false;
+    bool allSelected = false;
+    bool getLogSize = false;
+    bool getPruneList = false;
+    bool printStatistics = false;
+    bool printDividers = false;
     unsigned long setLogSize = 0;
-    int getPruneList = 0;
     char *setPruneList = NULL;
     char *setId = NULL;
-    int printStatistics = 0;
     int mode = ANDROID_LOG_RDONLY;
     const char *forceFilters = NULL;
     log_device_t* devices = NULL;
     log_device_t* dev;
-    bool printDividers = false;
     struct logger_list *logger_list;
     size_t tail_lines = 0;
     log_time tail_time(log_time::EPOCH);
@@ -710,7 +722,7 @@
             break;
 
             case 'c':
-                clearLog = 1;
+                clearLog = true;
                 mode |= ANDROID_LOG_WRONLY;
             break;
 
@@ -771,7 +783,7 @@
 
             case 'g':
                 if (!optarg) {
-                    getLogSize = 1;
+                    getLogSize = true;
                     break;
                 }
                 // FALLTHRU
@@ -813,7 +825,7 @@
 
             case 'p':
                 if (!optarg) {
-                    getPruneList = 1;
+                    getPruneList = true;
                     break;
                 }
                 // FALLTHRU
@@ -830,6 +842,7 @@
                                   (1 << LOG_ID_SYSTEM) |
                                   (1 << LOG_ID_CRASH);
                     } else if (strcmp(optarg, "all") == 0) {
+                        allSelected = true;
                         idMask = (unsigned)-1;
                     } else {
                         log_id_t log_id = android_name_to_log_id(optarg);
@@ -839,6 +852,7 @@
                             logcat_panic(HELP_TRUE,
                                          "unknown buffer %s\n", optarg);
                         }
+                        if (log_id == LOG_ID_SECURITY) allSelected = false;
                         idMask |= (1 << log_id);
                     }
                     optarg = NULL;
@@ -992,7 +1006,7 @@
                 break;
 
             case 'S':
-                printStatistics = 1;
+                printStatistics = true;
                 break;
 
             case ':':
@@ -1114,7 +1128,7 @@
         dev->logger = android_logger_open(logger_list,
                                           android_name_to_log_id(dev->device));
         if (!dev->logger) {
-            openDeviceFail = openDeviceFail ?: dev->device;
+            reportErrorName(&openDeviceFail, dev->device, allSelected);
             dev = dev->next;
             continue;
         }
@@ -1136,7 +1150,7 @@
 
                     if (file.length() == 0) {
                         perror("while clearing log files");
-                        clearFail = clearFail ?: dev->device;
+                        reportErrorName(&clearFail, dev->device, allSelected);
                         break;
                     }
 
@@ -1144,17 +1158,17 @@
 
                     if (err < 0 && errno != ENOENT && clearFail == NULL) {
                         perror("while clearing log files");
-                        clearFail = dev->device;
+                        reportErrorName(&clearFail, dev->device, allSelected);
                     }
                 }
             } else if (android_logger_clear(dev->logger)) {
-                clearFail = clearFail ?: dev->device;
+                reportErrorName(&clearFail, dev->device, allSelected);
             }
         }
 
         if (setLogSize) {
             if (android_logger_set_log_size(dev->logger, setLogSize)) {
-                setSizeFail = setSizeFail ?: dev->device;
+                reportErrorName(&setSizeFail, dev->device, allSelected);
             }
         }
 
@@ -1163,7 +1177,7 @@
             long readable = android_logger_get_log_readable_size(dev->logger);
 
             if ((size < 0) || (readable < 0)) {
-                getSizeFail = getSizeFail ?: dev->device;
+                reportErrorName(&getSizeFail, dev->device, allSelected);
             } else {
                 printf("%s: ring buffer is %ld%sb (%ld%sb consumed), "
                        "max entry is %db, max payload is %db\n", dev->device,
diff --git a/logcat/tests/Android.mk b/logcat/tests/Android.mk
index 99c2e0a..cb8b061 100644
--- a/logcat/tests/Android.mk
+++ b/logcat/tests/Android.mk
@@ -55,6 +55,6 @@
 LOCAL_MODULE := $(test_module_prefix)unit-tests
 LOCAL_MODULE_TAGS := $(test_tags)
 LOCAL_CFLAGS += $(test_c_flags)
-LOCAL_SHARED_LIBRARIES := liblog
+LOCAL_SHARED_LIBRARIES := liblog libbase
 LOCAL_SRC_FILES := $(test_src_files)
 include $(BUILD_NATIVE_TEST)
diff --git a/logcat/tests/logcat_test.cpp b/logcat/tests/logcat_test.cpp
index 725d76e..337861e 100644
--- a/logcat/tests/logcat_test.cpp
+++ b/logcat/tests/logcat_test.cpp
@@ -23,10 +23,12 @@
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <sys/wait.h>
+#include <unistd.h>
 
 #include <memory>
 #include <string>
 
+#include <android-base/file.h>
 #include <gtest/gtest.h>
 #include <log/log.h>
 #include <log/log_event_list.h>
@@ -53,6 +55,11 @@
 TEST(logcat, buckets) {
     FILE *fp;
 
+#undef LOG_TAG
+#define LOG_TAG "inject"
+    RLOGE("logcat.buckets");
+    sleep(1);
+
     ASSERT_TRUE(NULL != (fp = popen(
       "logcat -b radio -b events -b system -b main -d 2>/dev/null",
       "r")));
@@ -164,13 +171,39 @@
         if (!ep) {
             continue;
         }
-        ep -= 7;
+        static const size_t tag_field_width = 7;
+        ep -= tag_field_width;
         *ep = '\0';
         return cp;
     }
     return NULL;
 }
 
+// If there is not enough background noise in the logs, then spam the logs to
+// permit tail checking so that the tests can progress.
+static size_t inject(ssize_t count) {
+    if (count <= 0) return 0;
+
+    static const size_t retry = 3;
+    size_t errors = retry;
+    size_t num = 0;
+    for(;;) {
+        log_time ts(CLOCK_MONOTONIC);
+        if (__android_log_btwrite(0, EVENT_TYPE_LONG, &ts, sizeof(ts)) >= 0) {
+            if (++num >= (size_t)count) {
+                sleep(1); // let data settle end-to-end
+                return num;
+            }
+            errors = retry;
+            usleep(50);
+        } else if (--errors <= 0) {
+            return num;
+        }
+    }
+    // NOTREACH
+    return num;
+}
+
 TEST(logcat, tz) {
 
     if (android_log_clockid() == CLOCK_MONOTONIC) {
@@ -200,7 +233,7 @@
 
         pclose(fp);
 
-    } while ((count < 3) && --tries && (sleep(1), true));
+    } while ((count < 3) && --tries && inject(3 - count));
 
     ASSERT_EQ(3, count);
 }
@@ -235,8 +268,7 @@
         char buffer[BIG_BUFFER];
 
         snprintf(buffer, sizeof(buffer),
-          "logcat -v long -b radio -b events -b system -b main -t %d 2>/dev/null",
-          num);
+                 "logcat -v long -b all -t %d 2>/dev/null", num);
 
         FILE *fp;
         ASSERT_TRUE(NULL != (fp = popen(buffer, "r")));
@@ -249,7 +281,7 @@
 
         pclose(fp);
 
-    } while ((count < num) && --tries && (sleep(1), true));
+    } while ((count < num) && --tries && inject(num - count));
 
     ASSERT_EQ(num, count);
 }
@@ -272,26 +304,34 @@
 
 TEST(logcat, tail_time) {
     FILE *fp;
-
-    ASSERT_TRUE(NULL != (fp = popen("logcat -v long -b all -t 10 2>&1", "r")));
-
+    int count;
     char buffer[BIG_BUFFER];
     char *last_timestamp = NULL;
     char *first_timestamp = NULL;
-    int count = 0;
-
     char *cp;
-    while ((cp = fgetLongTime(buffer, sizeof(buffer), fp))) {
-        ++count;
-        if (!first_timestamp) {
-            first_timestamp = strdup(cp);
-        }
-        free(last_timestamp);
-        last_timestamp = strdup(cp);
-    }
-    pclose(fp);
 
-    EXPECT_EQ(10, count);
+    int tries = 3; // in case run too soon after system start or buffer clear
+
+    // Do not be tempted to use -v usec because that increases the
+    // chances of an occasional test failure by 1000 (see below).
+    do {
+        ASSERT_TRUE(NULL != (fp = popen("logcat -v long -b all -t 10 2>&1", "r")));
+
+        count = 0;
+
+        while ((cp = fgetLongTime(buffer, sizeof(buffer), fp))) {
+            ++count;
+            if (!first_timestamp) {
+                first_timestamp = strdup(cp);
+            }
+            free(last_timestamp);
+            last_timestamp = strdup(cp);
+        }
+        pclose(fp);
+
+    } while ((count < 10) && --tries && inject(10 - count));
+
+    EXPECT_EQ(10, count); // We want _some_ history, too small, falses below
     EXPECT_TRUE(last_timestamp != NULL);
     EXPECT_TRUE(first_timestamp != NULL);
 
@@ -1412,3 +1452,22 @@
         EXPECT_TRUE(End_to_End(sync.tagStr, ""));
     }
 }
+
+static bool reportedSecurity(const char* command) {
+    FILE* fp = popen(command, "r");
+    if (!fp) return true;
+
+    std::string ret;
+    bool val = android::base::ReadFdToString(fileno(fp), &ret);
+    pclose(fp);
+
+    if (!val) return true;
+    return std::string::npos != ret.find("'security'");
+}
+
+TEST(logcat, security) {
+    EXPECT_FALSE(reportedSecurity("logcat -b all -g 2>&1"));
+    EXPECT_TRUE(reportedSecurity("logcat -b security -g 2>&1"));
+    EXPECT_TRUE(reportedSecurity("logcat -b security -c 2>&1"));
+    EXPECT_TRUE(reportedSecurity("logcat -b security -G 256K 2>&1"));
+}
diff --git a/logd/tests/logd_test.cpp b/logd/tests/logd_test.cpp
index 703c0fb..13a7922 100644
--- a/logd/tests/logd_test.cpp
+++ b/logd/tests/logd_test.cpp
@@ -196,7 +196,9 @@
     EXPECT_TRUE(NULL != main_logs);
 
     char *radio_logs = strstr(cp, "\nChattiest UIDs in radio ");
-    EXPECT_TRUE(NULL != radio_logs);
+    if (!radio_logs) GTEST_LOG_(INFO) << "Value of: NULL != radio_logs\n"
+                                         "Actual: false\n"
+                                         "Expected: false\n";
 
     char *system_logs = strstr(cp, "\nChattiest UIDs in system ");
     EXPECT_TRUE(NULL != system_logs);
@@ -942,8 +944,16 @@
         return 0;
     }
 
-    // Requests dac_read_search, falls back to request dac_override
-    rate /= 2;
+    // The key here is we are root, but we are in u:r:shell:s0,
+    // and the directory does not provide us DAC access
+    // (eg: 0700 system system) so we trigger the pair dac_override
+    // and dac_read_search on every try to get past the message
+    // de-duper.  We will also rotate the file name in the directory
+    // as another measure.
+    static const char file[] = "/data/backup/cannot_access_directory_%u";
+    static const unsigned avc_requests_per_access = 2;
+
+    rate /= avc_requests_per_access;
     useconds_t usec;
     if (rate == 0) {
         rate = 1;
@@ -951,15 +961,12 @@
     } else {
         usec = (1000000 + (rate / 2)) / rate;
     }
-    num = (num + 1) / 2;
+    num = (num + (avc_requests_per_access / 2)) / avc_requests_per_access;
 
     if (usec < 2) usec = 2;
 
     while (num > 0) {
-        if (access(android::base::StringPrintf(
-                       "/data/misc/logd/cannot_access_directory_%u",
-                       num).c_str(),
-                   F_OK) == 0) {
+        if (access(android::base::StringPrintf(file, num).c_str(), F_OK) == 0) {
             _exit(-1);
             // NOTREACHED
             return 0;
@@ -1002,7 +1009,7 @@
 
         // int len = get4LE(eventData + 4 + 1);
         log_msg.buf[LOGGER_ENTRY_MAX_LEN] = '\0';
-        const char *cp = strstr(eventData + 4 + 1 + 4, "): avc: ");
+        const char *cp = strstr(eventData + 4 + 1 + 4, "): avc: denied");
         if (!cp) continue;
 
         ++count;
@@ -1055,8 +1062,7 @@
     // give logd another 3 seconds to react to the burst before checking
     sepolicy_rate(rate, rate * 3);
     // maximum period at double the maximum burst rate (spam filter kicked in)
-    EXPECT_GE(((AUDIT_RATE_LIMIT_MAX * AUDIT_RATE_LIMIT_BURST_DURATION) * 130) /
-                                        100, // +30% margin
+    EXPECT_GE(threshold * 2,
               count_avc(sepolicy_rate(rate,
                                       rate * AUDIT_RATE_LIMIT_BURST_DURATION)));
     // cool down, and check unspammy rate still works
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 791d67f..d6ae6f0 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -118,7 +118,6 @@
     write /proc/sys/kernel/sched_child_runs_first 0
 
     write /proc/sys/kernel/randomize_va_space 2
-    write /proc/sys/kernel/kptr_restrict 2
     write /proc/sys/vm/mmap_min_addr 32768
     write /proc/sys/net/ipv4/ping_group_range "0 2147483647"
     write /proc/sys/net/unix/max_dgram_qlen 600
@@ -356,10 +355,6 @@
     # We restorecon /data in case the userdata partition has been reset.
     restorecon /data
 
-    # start debuggerd to make debugging early-boot crashes easier.
-    start debuggerd
-    start debuggerd64
-
     # Make sure we have the device encryption key.
     start vold
     installkey /data
diff --git a/rootdir/init.usb.configfs.rc b/rootdir/init.usb.configfs.rc
index dc875b4..32f0198 100644
--- a/rootdir/init.usb.configfs.rc
+++ b/rootdir/init.usb.configfs.rc
@@ -70,7 +70,7 @@
 
 on property:sys.usb.config=audio_source && property:sys.usb.configfs=1
     write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "audiosource"
-    symlink /config/usb_gadget/g1/functions/audio_source.gs2 /config/usb_gadget/g1/configs/b.1/f1
+    symlink /config/usb_gadget/g1/functions/audio_source.gs3 /config/usb_gadget/g1/configs/b.1/f1
     write /config/usb_gadget/g1/UDC ${sys.usb.controller}
     setprop sys.usb.state ${sys.usb.config}
 
@@ -79,7 +79,7 @@
 
 on property:sys.usb.ffs.ready=1 && property:sys.usb.config=audio_source,adb && property:sys.usb.configfs=1
     write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "audiosource_adb"
-    symlink /config/usb_gadget/g1/functions/audio_source.gs2 /config/usb_gadget/g1/configs/b.1/f1
+    symlink /config/usb_gadget/g1/functions/audio_source.gs3 /config/usb_gadget/g1/configs/b.1/f1
     symlink /config/usb_gadget/g1/functions/ffs.adb /config/usb_gadget/g1/configs/b.1/f2
     write /config/usb_gadget/g1/UDC ${sys.usb.controller}
     setprop sys.usb.state ${sys.usb.config}