Merge "tombstoned: allow intercepts for java traces."
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index 3a80b50..5565cfd 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -11,6 +11,11 @@
     local_include_dirs: ["include"],
 }
 
+cc_library_headers {
+    name: "libdebuggerd_common_headers",
+    export_include_dirs: ["common/include"]
+}
+
 cc_library_shared {
     name: "libtombstoned_client",
     defaults: ["debuggerd_defaults"],
@@ -19,15 +24,18 @@
         "util.cpp",
     ],
 
+    header_libs: ["libdebuggerd_common_headers"],
+
     static_libs: [
-        "libasync_safe"
+        "libasync_safe",
     ],
 
     shared_libs: [
-        "libcutils",
         "libbase",
+        "libcutils",
     ],
 
+    export_header_lib_headers: ["libdebuggerd_common_headers"],
     export_include_dirs: ["tombstoned/include"]
 }
 
@@ -40,12 +48,15 @@
         "util.cpp",
     ],
 
+    header_libs: ["libdebuggerd_common_headers"],
+
     whole_static_libs: [
         "libasync_safe",
         "libcutils",
         "libbase",
     ],
 
+    export_header_lib_headers: ["libdebuggerd_common_headers"],
     export_include_dirs: ["tombstoned/include"]
 }
 
@@ -55,11 +66,14 @@
     defaults: ["debuggerd_defaults"],
     srcs: ["handler/debuggerd_handler.cpp"],
 
+    header_libs: ["libdebuggerd_common_headers"],
+
     whole_static_libs: [
         "libasync_safe",
         "libdebuggerd",
     ],
 
+    export_header_lib_headers: ["libdebuggerd_common_headers"],
     export_include_dirs: ["include"],
 }
 
@@ -107,11 +121,14 @@
         "util.cpp",
     ],
 
+    header_libs: ["libdebuggerd_common_headers"],
+
     shared_libs: [
         "libbase",
         "libcutils",
     ],
 
+    export_header_lib_headers: ["libdebuggerd_common_headers"],
     export_include_dirs: ["include"],
 }
 
@@ -191,7 +208,8 @@
         "libbase",
         "libcutils",
         "libdebuggerd_client",
-        "liblog"
+        "liblog",
+        "libnativehelper"
     ],
 
     static_libs: [
@@ -271,6 +289,8 @@
     ],
     defaults: ["debuggerd_defaults"],
 
+    header_libs: ["libdebuggerd_common_headers"],
+
     static_libs: [
         "libbase",
         "libcutils",
diff --git a/debuggerd/client/debuggerd_client.cpp b/debuggerd/client/debuggerd_client.cpp
index 4ce038c..c357fd6 100644
--- a/debuggerd/client/debuggerd_client.cpp
+++ b/debuggerd/client/debuggerd_client.cpp
@@ -40,10 +40,12 @@
 
 using android::base::unique_fd;
 
-static bool send_signal(pid_t pid, bool backtrace) {
+static bool send_signal(pid_t pid, const DebuggerdDumpType dump_type) {
+  const int signal = (dump_type == kDebuggerdJavaBacktrace) ? SIGQUIT : DEBUGGER_SIGNAL;
   sigval val;
-  val.sival_int = backtrace;
-  if (sigqueue(pid, DEBUGGER_SIGNAL, val) != 0) {
+  val.sival_int = (dump_type == kDebuggerdNativeBacktrace) ? 1 : 0;
+
+  if (sigqueue(pid, signal, val) != 0) {
     PLOG(ERROR) << "libdebuggerd_client: failed to send signal to pid " << pid;
     return false;
   }
@@ -58,8 +60,8 @@
   tv->tv_usec = static_cast<long>(microseconds.count());
 }
 
-bool debuggerd_trigger_dump(pid_t pid, unique_fd output_fd, DebuggerdDumpType dump_type,
-                            unsigned int timeout_ms) {
+bool debuggerd_trigger_dump(pid_t pid, DebuggerdDumpType dump_type, unsigned int timeout_ms,
+                            unique_fd output_fd) {
   LOG(INFO) << "libdebuggerd_client: started dumping process " << pid;
   unique_fd sockfd;
   const auto end = std::chrono::steady_clock::now() + std::chrono::milliseconds(timeout_ms);
@@ -102,7 +104,7 @@
     return false;
   }
 
-  InterceptRequest req = {.pid = pid };
+  InterceptRequest req = {.pid = pid, .dump_type = dump_type};
   if (!set_timeout(sockfd)) {
     PLOG(ERROR) << "libdebugger_client: failed to set timeout";
     return false;
@@ -140,8 +142,7 @@
     return false;
   }
 
-  bool backtrace = dump_type == kDebuggerdBacktrace;
-  if (!send_signal(pid, backtrace)) {
+  if (!send_signal(pid, dump_type)) {
     return false;
   }
 
@@ -210,15 +211,16 @@
   return true;
 }
 
-int dump_backtrace_to_file(pid_t tid, int fd) {
-  return dump_backtrace_to_file_timeout(tid, fd, 0);
+int dump_backtrace_to_file(pid_t tid, DebuggerdDumpType dump_type, int fd) {
+  return dump_backtrace_to_file_timeout(tid, dump_type, 0, fd);
 }
 
-int dump_backtrace_to_file_timeout(pid_t tid, int fd, int timeout_secs) {
+int dump_backtrace_to_file_timeout(pid_t tid, DebuggerdDumpType dump_type, int timeout_secs,
+                                   int fd) {
   android::base::unique_fd copy(dup(fd));
   if (copy == -1) {
     return -1;
   }
   int timeout_ms = timeout_secs > 0 ? timeout_secs * 1000 : 0;
-  return debuggerd_trigger_dump(tid, std::move(copy), kDebuggerdBacktrace, timeout_ms) ? 0 : -1;
+  return debuggerd_trigger_dump(tid, dump_type, timeout_ms, std::move(copy)) ? 0 : -1;
 }
diff --git a/debuggerd/client/debuggerd_client_test.cpp b/debuggerd/client/debuggerd_client_test.cpp
index 8f97db1..8420f03 100644
--- a/debuggerd/client/debuggerd_client_test.cpp
+++ b/debuggerd/client/debuggerd_client_test.cpp
@@ -67,7 +67,8 @@
   // Wait for a bit to let the child spawn all of its threads.
   std::this_thread::sleep_for(250ms);
 
-  ASSERT_TRUE(debuggerd_trigger_dump(forkpid, std::move(pipe_write), kDebuggerdBacktrace, 10000));
+  ASSERT_TRUE(
+      debuggerd_trigger_dump(forkpid, kDebuggerdNativeBacktrace, 10000, std::move(pipe_write)));
   // Immediately kill the forked child, to make sure that the dump didn't return early.
   ASSERT_EQ(0, kill(forkpid, SIGKILL)) << strerror(errno);
 
@@ -107,5 +108,6 @@
 
   unique_fd output_read, output_write;
   ASSERT_TRUE(Pipe(&output_read, &output_write));
-  ASSERT_TRUE(debuggerd_trigger_dump(forkpid, std::move(output_write), kDebuggerdBacktrace, 0));
+  ASSERT_TRUE(
+      debuggerd_trigger_dump(forkpid, kDebuggerdNativeBacktrace, 0, std::move(output_write)));
 }
diff --git a/debuggerd/common/include/dump_type.h b/debuggerd/common/include/dump_type.h
new file mode 100644
index 0000000..203269e
--- /dev/null
+++ b/debuggerd/common/include/dump_type.h
@@ -0,0 +1,49 @@
+#pragma once
+
+/*
+ * Copyright 2017, 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 <sys/types.h>
+
+#include <ostream>
+
+enum DebuggerdDumpType : uint8_t {
+  kDebuggerdNativeBacktrace,
+  kDebuggerdTombstone,
+  kDebuggerdJavaBacktrace,
+  kDebuggerdAnyIntercept
+};
+
+inline std::ostream& operator<<(std::ostream& stream, const DebuggerdDumpType& rhs) {
+  switch (rhs) {
+    case kDebuggerdNativeBacktrace:
+      stream << "kDebuggerdNativeBacktrace";
+      break;
+    case kDebuggerdTombstone:
+      stream << "kDebuggerdTombstone";
+      break;
+    case kDebuggerdJavaBacktrace:
+      stream << "kDebuggerdJavaBacktrace";
+      break;
+    case kDebuggerdAnyIntercept:
+      stream << "kDebuggerdAnyIntercept";
+      break;
+    default:
+      stream << "[unknown]";
+  }
+
+  return stream;
+}
diff --git a/debuggerd/crash_dump.cpp b/debuggerd/crash_dump.cpp
index be28079..cc78242 100644
--- a/debuggerd/crash_dump.cpp
+++ b/debuggerd/crash_dump.cpp
@@ -150,13 +150,13 @@
   _exit(1);
 }
 
-static void abort_handler(pid_t target, const bool& tombstoned_connected,
+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)) {
+    if (!tombstoned_connect(target, &tombstoned_socket, &output_fd, kDebuggerdAnyIntercept)) {
       // We failed to connect, not much we can do.
       LOG(ERROR) << "failed to connected to tombstoned to report failure";
       _exit(1);
@@ -207,12 +207,14 @@
   action.sa_handler = signal_handler;
   debuggerd_register_handlers(&action);
 
-  if (argc != 3) {
+  if (argc != 4) {
+    LOG(FATAL) << "Wrong number of args: " << argc << " (expected 4)";
     return 1;
   }
 
   pid_t main_tid;
   pid_t pseudothread_tid;
+  int dump_type;
 
   if (!android::base::ParseInt(argv[1], &main_tid, 1, std::numeric_limits<pid_t>::max())) {
     LOG(FATAL) << "invalid main tid: " << argv[1];
@@ -222,6 +224,10 @@
     LOG(FATAL) << "invalid pseudothread tid: " << argv[2];
   }
 
+  if (!android::base::ParseInt(argv[3], &dump_type, 0, 1)) {
+    LOG(FATAL) << "invalid requested dump type: " << argv[3];
+  }
+
   if (target == 1) {
     LOG(FATAL) << "target died before we could attach (received main tid = " << main_tid << ")";
   }
@@ -305,8 +311,9 @@
   // Drop our capabilities now that we've attached to the threads we care about.
   drop_capabilities();
 
-  LOG(INFO) << "obtaining output fd from tombstoned";
-  tombstoned_connected = tombstoned_connect(target, &tombstoned_socket, &output_fd);
+  const DebuggerdDumpType dump_type_enum = static_cast<DebuggerdDumpType>(dump_type);
+  LOG(INFO) << "obtaining output fd from tombstoned, type: " << dump_type_enum;
+  tombstoned_connected = tombstoned_connect(target, &tombstoned_socket, &output_fd, dump_type_enum);
 
   // Write a '\1' to stdout to tell the crashing process to resume.
   // It also restores the value of PR_SET_DUMPABLE at this point.
diff --git a/debuggerd/debuggerd.cpp b/debuggerd/debuggerd.cpp
index 4997dd6..6298ace 100644
--- a/debuggerd/debuggerd.cpp
+++ b/debuggerd/debuggerd.cpp
@@ -72,8 +72,8 @@
   }
 
   std::thread redirect_thread = spawn_redirect_thread(std::move(piperead));
-  if (!debuggerd_trigger_dump(pid, std::move(pipewrite),
-                              backtrace_only ? kDebuggerdBacktrace : kDebuggerdTombstone, 0)) {
+  if (!debuggerd_trigger_dump(pid, backtrace_only ? kDebuggerdNativeBacktrace : kDebuggerdTombstone,
+                              0, std::move(pipewrite))) {
     redirect_thread.join();
     errx(1, "failed to dump process %d", pid);
   }
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index f17724a..8c00b76 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -88,14 +88,15 @@
     }                                                                                       \
   } while (0)
 
-static void tombstoned_intercept(pid_t target_pid, unique_fd* intercept_fd, unique_fd* output_fd) {
+static void tombstoned_intercept(pid_t target_pid, unique_fd* intercept_fd, unique_fd* output_fd,
+                                 DebuggerdDumpType intercept_type) {
   intercept_fd->reset(socket_local_client(kTombstonedInterceptSocketName,
                                           ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_SEQPACKET));
   if (intercept_fd->get() == -1) {
     FAIL() << "failed to contact tombstoned: " << strerror(errno);
   }
 
-  InterceptRequest req = {.pid = target_pid};
+  InterceptRequest req = {.pid = target_pid, .dump_type = intercept_type};
 
   unique_fd output_pipe_write;
   if (!Pipe(output_fd, &output_pipe_write)) {
@@ -146,7 +147,7 @@
   CrasherTest();
   ~CrasherTest();
 
-  void StartIntercept(unique_fd* output_fd);
+  void StartIntercept(unique_fd* output_fd, DebuggerdDumpType intercept_type = kDebuggerdTombstone);
 
   // Returns -1 if we fail to read a response from tombstoned, otherwise the received return code.
   void FinishIntercept(int* result);
@@ -172,12 +173,12 @@
   android::base::SetProperty(kWaitForGdbKey, previous_wait_for_gdb ? "1" : "0");
 }
 
-void CrasherTest::StartIntercept(unique_fd* output_fd) {
+void CrasherTest::StartIntercept(unique_fd* output_fd, DebuggerdDumpType intercept_type) {
   if (crasher_pid == -1) {
     FAIL() << "crasher hasn't been started";
   }
 
-  tombstoned_intercept(crasher_pid, &this->intercept_fd, output_fd);
+  tombstoned_intercept(crasher_pid, &this->intercept_fd, output_fd, intercept_type);
 }
 
 void CrasherTest::FinishIntercept(int* result) {
@@ -428,7 +429,7 @@
   StartProcess([]() {
     abort();
   });
-  StartIntercept(&output_fd);
+  StartIntercept(&output_fd, kDebuggerdNativeBacktrace);
 
   std::this_thread::sleep_for(500ms);
 
@@ -595,11 +596,11 @@
     pid_t pid = 123'456'789 + i;
 
     unique_fd intercept_fd, output_fd;
-    tombstoned_intercept(pid, &intercept_fd, &output_fd);
+    tombstoned_intercept(pid, &intercept_fd, &output_fd, kDebuggerdTombstone);
 
     {
       unique_fd tombstoned_socket, input_fd;
-      ASSERT_TRUE(tombstoned_connect(pid, &tombstoned_socket, &input_fd));
+      ASSERT_TRUE(tombstoned_connect(pid, &tombstoned_socket, &input_fd, kDebuggerdTombstone));
       ASSERT_TRUE(android::base::WriteFully(input_fd.get(), &pid, sizeof(pid)));
     }
 
@@ -627,7 +628,7 @@
       pid_t pid = pid_base + dump;
 
       unique_fd intercept_fd, output_fd;
-      tombstoned_intercept(pid, &intercept_fd, &output_fd);
+      tombstoned_intercept(pid, &intercept_fd, &output_fd, kDebuggerdTombstone);
 
       // Pretend to crash, and then immediately close the socket.
       unique_fd sockfd(socket_local_client(kTombstonedCrashSocketName,
@@ -658,11 +659,11 @@
       pid_t pid = pid_base + dump;
 
       unique_fd intercept_fd, output_fd;
-      tombstoned_intercept(pid, &intercept_fd, &output_fd);
+      tombstoned_intercept(pid, &intercept_fd, &output_fd, kDebuggerdTombstone);
 
       {
         unique_fd tombstoned_socket, input_fd;
-        ASSERT_TRUE(tombstoned_connect(pid, &tombstoned_socket, &input_fd));
+        ASSERT_TRUE(tombstoned_connect(pid, &tombstoned_socket, &input_fd, kDebuggerdTombstone));
         ASSERT_TRUE(android::base::WriteFully(input_fd.get(), &pid, sizeof(pid)));
         tombstoned_notify_completion(tombstoned_socket.get());
       }
diff --git a/debuggerd/handler/debuggerd_fallback.cpp b/debuggerd/handler/debuggerd_fallback.cpp
index a9c9862..43104ec 100644
--- a/debuggerd/handler/debuggerd_fallback.cpp
+++ b/debuggerd/handler/debuggerd_fallback.cpp
@@ -151,7 +151,7 @@
 
   // Fetch output fd from tombstoned.
   unique_fd tombstone_socket, output_fd;
-  if (!tombstoned_connect(getpid(), &tombstone_socket, &output_fd)) {
+  if (!tombstoned_connect(getpid(), &tombstone_socket, &output_fd, kDebuggerdNativeBacktrace)) {
     goto exit;
   }
 
@@ -215,7 +215,8 @@
   }
 
   unique_fd tombstone_socket, output_fd;
-  bool tombstoned_connected = tombstoned_connect(getpid(), &tombstone_socket, &output_fd);
+  bool tombstoned_connected =
+      tombstoned_connect(getpid(), &tombstone_socket, &output_fd, kDebuggerdTombstone);
   debuggerd_fallback_tombstone(output_fd.get(), ucontext, info, abort_message);
   if (tombstoned_connected) {
     tombstoned_notify_completion(tombstone_socket.get());
diff --git a/debuggerd/handler/debuggerd_handler.cpp b/debuggerd/handler/debuggerd_handler.cpp
index 8fd6e11..55cd03e 100644
--- a/debuggerd/handler/debuggerd_handler.cpp
+++ b/debuggerd/handler/debuggerd_handler.cpp
@@ -50,6 +50,8 @@
 
 #include <async_safe/log.h>
 
+#include "dump_type.h"
+
 // see man(2) prctl, specifically the section about PR_GET_NAME
 #define MAX_TASK_NAME_LEN (16)
 
@@ -253,6 +255,14 @@
 // process.
 static void* pseudothread_stack;
 
+static DebuggerdDumpType get_dump_type(const debugger_thread_info* thread_info) {
+  if (thread_info->signal_number == DEBUGGER_SIGNAL && thread_info->info->si_value.sival_int) {
+    return kDebuggerdNativeBacktrace;
+  }
+
+  return kDebuggerdTombstone;
+}
+
 static int debuggerd_dispatch_pseudothread(void* arg) {
   debugger_thread_info* thread_info = static_cast<debugger_thread_info*>(arg);
 
@@ -285,11 +295,15 @@
 
     char main_tid[10];
     char pseudothread_tid[10];
+    char debuggerd_dump_type[10];
     async_safe_format_buffer(main_tid, sizeof(main_tid), "%d", thread_info->crashing_tid);
     async_safe_format_buffer(pseudothread_tid, sizeof(pseudothread_tid), "%d",
                              thread_info->pseudothread_tid);
+    async_safe_format_buffer(debuggerd_dump_type, sizeof(debuggerd_dump_type), "%d",
+                             get_dump_type(thread_info));
 
-    execl(CRASH_DUMP_PATH, CRASH_DUMP_NAME, main_tid, pseudothread_tid, nullptr);
+    execl(CRASH_DUMP_PATH, CRASH_DUMP_NAME, main_tid, pseudothread_tid, debuggerd_dump_type,
+          nullptr);
 
     fatal_errno("exec failed");
   } else {
diff --git a/debuggerd/include/debuggerd/client.h b/debuggerd/include/debuggerd/client.h
index 01de57b..b7284b0 100644
--- a/debuggerd/include/debuggerd/client.h
+++ b/debuggerd/include/debuggerd/client.h
@@ -22,15 +22,13 @@
 
 #include <android-base/unique_fd.h>
 
-enum DebuggerdDumpType {
-  kDebuggerdBacktrace,
-  kDebuggerdTombstone,
-};
+#include "dump_type.h"
 
 // Trigger a dump of specified process to output_fd.
 // output_fd is consumed, timeout of 0 will wait forever.
-bool debuggerd_trigger_dump(pid_t pid, android::base::unique_fd output_fd,
-                            enum DebuggerdDumpType dump_type, unsigned int timeout_ms);
+bool debuggerd_trigger_dump(pid_t pid, enum DebuggerdDumpType dump_type, unsigned int timeout_ms,
+                            android::base::unique_fd output_fd);
 
-int dump_backtrace_to_file(pid_t tid, int fd);
-int dump_backtrace_to_file_timeout(pid_t tid, int fd, int timeout_secs);
+int dump_backtrace_to_file(pid_t tid, enum DebuggerdDumpType dump_type, int output_fd);
+int dump_backtrace_to_file_timeout(pid_t tid, enum DebuggerdDumpType dump_type, int timeout_secs,
+                                   int output_fd);
diff --git a/debuggerd/protocol.h b/debuggerd/protocol.h
index 144efc8..7e1961e 100644
--- a/debuggerd/protocol.h
+++ b/debuggerd/protocol.h
@@ -18,6 +18,8 @@
 
 #include <stdint.h>
 
+#include "dump_type.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";
@@ -40,6 +42,7 @@
 };
 
 struct DumpRequest {
+  DebuggerdDumpType dump_type;
   int32_t pid;
 };
 
@@ -54,10 +57,15 @@
 // Comes with a file descriptor via SCM_RIGHTS.
 // This packet should be sent before an actual dump happens.
 struct InterceptRequest {
+  DebuggerdDumpType dump_type;
   int32_t pid;
 };
 
 enum class InterceptStatus : uint8_t {
+  // Returned when an intercept of a different type has already been
+  // registered (and is active) for a given PID.
+  kFailedAlreadyRegistered,
+  // Returned in all other failure cases.
   kFailed,
   kStarted,
   kRegistered,
diff --git a/debuggerd/tombstoned/include/tombstoned/tombstoned.h b/debuggerd/tombstoned/include/tombstoned/tombstoned.h
index 908517d..6403dbe 100644
--- a/debuggerd/tombstoned/include/tombstoned/tombstoned.h
+++ b/debuggerd/tombstoned/include/tombstoned/tombstoned.h
@@ -20,7 +20,9 @@
 
 #include <android-base/unique_fd.h>
 
+#include "dump_type.h"
+
 bool tombstoned_connect(pid_t pid, android::base::unique_fd* tombstoned_socket,
-                        android::base::unique_fd* output_fd, bool is_native_crash = true);
+                        android::base::unique_fd* output_fd, DebuggerdDumpType dump_type);
 
 bool tombstoned_notify_completion(int tombstoned_socket);
diff --git a/debuggerd/tombstoned/intercept_manager.cpp b/debuggerd/tombstoned/intercept_manager.cpp
index 4d4eb9e..24960bc 100644
--- a/debuggerd/tombstoned/intercept_manager.cpp
+++ b/debuggerd/tombstoned/intercept_manager.cpp
@@ -61,11 +61,24 @@
       reason = "due to input";
     }
 
-    LOG(INFO) << "intercept for pid " << intercept->intercept_pid << " terminated " << reason;
+    LOG(INFO) << "intercept for pid " << intercept->intercept_pid << " and type "
+              << intercept->dump_type << " terminated: " << reason;
     intercept_manager->intercepts.erase(it);
   }
 }
 
+static bool is_intercept_request_valid(const InterceptRequest& request) {
+  if (request.pid <= 0 || request.pid > std::numeric_limits<pid_t>::max()) {
+    return false;
+  }
+
+  if (request.dump_type < 0 || request.dump_type > kDebuggerdJavaBacktrace) {
+    return false;
+  }
+
+  return true;
+}
+
 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;
@@ -103,23 +116,24 @@
     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()) {
+    if (!is_intercept_request_valid(intercept_request)) {
       InterceptResponse response = {};
       response.status = InterceptStatus::kFailed;
-      snprintf(response.error_message, sizeof(response.error_message), "invalid pid %" PRId32,
-               intercept_request.pid);
+      snprintf(response.error_message, sizeof(response.error_message), "invalid intercept request");
       TEMP_FAILURE_RETRY(write(sockfd, &response, sizeof(response)));
       goto fail;
     }
 
     intercept->intercept_pid = intercept_request.pid;
+    intercept->dump_type = intercept_request.dump_type;
 
     // Check if it's already registered.
     if (intercept_manager->intercepts.count(intercept_request.pid) > 0) {
       InterceptResponse response = {};
-      response.status = InterceptStatus::kFailed;
+      response.status = InterceptStatus::kFailedAlreadyRegistered;
       snprintf(response.error_message, sizeof(response.error_message),
-               "pid %" PRId32 " already intercepted", intercept_request.pid);
+               "pid %" PRId32 " already intercepted, type %d", intercept_request.pid,
+               intercept_request.dump_type);
       TEMP_FAILURE_RETRY(write(sockfd, &response, sizeof(response)));
       LOG(WARNING) << response.error_message;
       goto fail;
@@ -138,7 +152,8 @@
     intercept_manager->intercepts[intercept_request.pid] = std::unique_ptr<Intercept>(intercept);
     intercept->registered = true;
 
-    LOG(INFO) << "tombstoned registered intercept for pid " << intercept_request.pid;
+    LOG(INFO) << "registered intercept for pid " << intercept_request.pid << " and type "
+              << intercept_request.dump_type;
 
     // 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).
@@ -174,16 +189,27 @@
                                       intercept_socket);
 }
 
-bool InterceptManager::GetIntercept(pid_t pid, android::base::unique_fd* out_fd) {
+bool InterceptManager::GetIntercept(pid_t pid, DebuggerdDumpType dump_type,
+                                    android::base::unique_fd* out_fd) {
   auto it = this->intercepts.find(pid);
   if (it == this->intercepts.end()) {
     return false;
   }
 
+  if (dump_type == kDebuggerdAnyIntercept) {
+    LOG(INFO) << "found registered intercept of type " << it->second->dump_type
+              << " for requested type kDebuggerdAnyIntercept";
+  } else if (it->second->dump_type != dump_type) {
+    LOG(WARNING) << "found non-matching intercept of type " << it->second->dump_type
+                 << " for requested type: " << dump_type;
+    return false;
+  }
+
   auto intercept = std::move(it->second);
   this->intercepts.erase(it);
 
-  LOG(INFO) << "found intercept fd " << intercept->output_fd.get() << " for pid " << pid;
+  LOG(INFO) << "found intercept fd " << intercept->output_fd.get() << " for pid " << pid
+            << " and type " << intercept->dump_type;
   InterceptResponse response = {};
   response.status = InterceptStatus::kStarted;
   TEMP_FAILURE_RETRY(write(intercept->sockfd, &response, sizeof(response)));
diff --git a/debuggerd/tombstoned/intercept_manager.h b/debuggerd/tombstoned/intercept_manager.h
index cb5db62..a11d565 100644
--- a/debuggerd/tombstoned/intercept_manager.h
+++ b/debuggerd/tombstoned/intercept_manager.h
@@ -25,6 +25,8 @@
 
 #include <android-base/unique_fd.h>
 
+#include "dump_type.h"
+
 struct InterceptManager;
 
 struct Intercept {
@@ -39,6 +41,7 @@
   pid_t intercept_pid = -1;
   android::base::unique_fd output_fd;
   bool registered = false;
+  DebuggerdDumpType dump_type = kDebuggerdNativeBacktrace;
 };
 
 struct InterceptManager {
@@ -50,5 +53,5 @@
   InterceptManager(InterceptManager& copy) = delete;
   InterceptManager(InterceptManager&& move) = delete;
 
-  bool GetIntercept(pid_t pid, android::base::unique_fd* out_fd);
+  bool GetIntercept(pid_t pid, DebuggerdDumpType dump_type, android::base::unique_fd* out_fd);
 };
diff --git a/debuggerd/tombstoned/tombstoned.cpp b/debuggerd/tombstoned/tombstoned.cpp
index 05df9f2..0a9f000 100644
--- a/debuggerd/tombstoned/tombstoned.cpp
+++ b/debuggerd/tombstoned/tombstoned.cpp
@@ -35,6 +35,7 @@
 #include <cutils/sockets.h>
 
 #include "debuggerd/handler.h"
+#include "dump_type.h"
 #include "protocol.h"
 #include "util.h"
 
@@ -52,10 +53,10 @@
 
 struct Crash;
 
-class CrashType {
+class CrashQueue {
  public:
-  CrashType(const std::string& dir_path, const std::string& file_name_prefix, size_t max_artifacts,
-            size_t max_concurrent_dumps)
+  CrashQueue(const std::string& dir_path, const std::string& file_name_prefix, size_t max_artifacts,
+             size_t max_concurrent_dumps)
       : file_name_prefix_(file_name_prefix),
         dir_path_(dir_path),
         dir_fd_(open(dir_path.c_str(), O_DIRECTORY | O_RDONLY | O_CLOEXEC)),
@@ -114,8 +115,8 @@
 
   void on_crash_completed() { --num_concurrent_dumps_; }
 
-  static CrashType* const tombstone;
-  static CrashType* const java_trace;
+  static CrashQueue* const tombstone;
+  static CrashQueue* const java_trace;
 
  private:
   void find_oldest_artifact() {
@@ -158,19 +159,19 @@
 
   std::deque<Crash*> queued_requests_;
 
-  DISALLOW_COPY_AND_ASSIGN(CrashType);
+  DISALLOW_COPY_AND_ASSIGN(CrashQueue);
 };
 
 // Whether java trace dumps are produced via tombstoned.
 static constexpr bool kJavaTraceDumpsEnabled = false;
 
-/* static */ CrashType* const CrashType::tombstone =
-    new CrashType("/data/tombstones", "tombstone_" /* file_name_prefix */, 10 /* max_artifacts */,
-                  1 /* max_concurrent_dumps */);
+/* static */ CrashQueue* const CrashQueue::tombstone =
+    new CrashQueue("/data/tombstones", "tombstone_" /* file_name_prefix */, 10 /* max_artifacts */,
+                   1 /* max_concurrent_dumps */);
 
-/* static */ CrashType* const CrashType::java_trace =
-    (kJavaTraceDumpsEnabled ? new CrashType("/data/anr", "anr_" /* file_name_prefix */,
-                                            64 /* max_artifacts */, 4 /* max_concurrent_dumps */)
+/* static */ CrashQueue* const CrashQueue::java_trace =
+    (kJavaTraceDumpsEnabled ? new CrashQueue("/data/anr", "anr_" /* file_name_prefix */,
+                                             64 /* max_artifacts */, 4 /* max_concurrent_dumps */)
                             : nullptr);
 
 // Ownership of Crash is a bit messy.
@@ -183,10 +184,17 @@
   pid_t crash_pid;
   event* crash_event = nullptr;
 
-  // Not owned by |Crash|.
-  CrashType* crash_type = nullptr;
+  DebuggerdDumpType crash_type;
 };
 
+static CrashQueue* get_crash_queue(const Crash* crash) {
+  if (crash->crash_type == kDebuggerdJavaBacktrace) {
+    return CrashQueue::java_trace;
+  }
+
+  return CrashQueue::tombstone;
+}
+
 // 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);
@@ -194,10 +202,8 @@
 
 static void perform_request(Crash* crash) {
   unique_fd output_fd;
-  // Note that java traces are not interceptible.
-  if ((crash->crash_type == CrashType::java_trace) ||
-      !intercept_manager->GetIntercept(crash->crash_pid, &output_fd)) {
-    output_fd = crash->crash_type->get_output_fd();
+  if (!intercept_manager->GetIntercept(crash->crash_pid, crash->crash_type, &output_fd)) {
+    output_fd = get_crash_queue(crash)->get_output_fd();
   }
 
   TombstonedCrashPacket response = {
@@ -220,7 +226,7 @@
     event_add(crash->crash_event, &timeout);
   }
 
-  crash->crash_type->on_crash_started();
+  get_crash_queue(crash)->on_crash_started();
   return;
 
 fail:
@@ -228,22 +234,22 @@
 }
 
 static void crash_accept_cb(evconnlistener* listener, evutil_socket_t sockfd, sockaddr*, int,
-                            void* crash_type) {
+                            void*) {
   event_base* base = evconnlistener_get_base(listener);
   Crash* crash = new Crash();
 
+  // TODO: Make sure that only java crashes come in on the java socket
+  // and only native crashes on the native socket.
   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;
-  crash->crash_type = static_cast<CrashType*>(crash_type);
   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);
-  CrashType* type = crash->crash_type;
 
   TombstonedCrashPacket request = {};
 
@@ -271,7 +277,13 @@
     goto fail;
   }
 
-  if (type == CrashType::tombstone) {
+  crash->crash_type = request.packet.dump_request.dump_type;
+  if (crash->crash_type < 0 || crash->crash_type > kDebuggerdAnyIntercept) {
+    LOG(WARNING) << "unexpected crash dump type: " << crash->crash_type;
+    goto fail;
+  }
+
+  if (crash->crash_type != kDebuggerdJavaBacktrace) {
     crash->crash_pid = request.packet.dump_request.pid;
   } else {
     // Requests for java traces are sent from untrusted processes, so we
@@ -290,7 +302,7 @@
 
   LOG(INFO) << "received crash request for pid " << crash->crash_pid;
 
-  if (type->maybe_enqueue_crash(crash)) {
+  if (get_crash_queue(crash)->maybe_enqueue_crash(crash)) {
     LOG(INFO) << "enqueueing crash request for pid " << crash->crash_pid;
   } else {
     perform_request(crash);
@@ -307,7 +319,7 @@
   Crash* crash = static_cast<Crash*>(arg);
   TombstonedCrashPacket request = {};
 
-  crash->crash_type->on_crash_completed();
+  get_crash_queue(crash)->on_crash_completed();
 
   if ((ev & EV_READ) == 0) {
     goto fail;
@@ -330,11 +342,11 @@
   }
 
 fail:
-  CrashType* type = crash->crash_type;
+  CrashQueue* queue = get_crash_queue(crash);
   delete crash;
 
   // If there's something queued up, let them proceed.
-  type->maybe_dequeue_crashes(perform_request);
+  queue->maybe_dequeue_crashes(perform_request);
 }
 
 int main(int, char* []) {
@@ -366,7 +378,7 @@
   intercept_manager = new InterceptManager(base, intercept_socket);
 
   evconnlistener* tombstone_listener = evconnlistener_new(
-      base, crash_accept_cb, CrashType::tombstone, -1, LEV_OPT_CLOSE_ON_FREE, crash_socket);
+      base, crash_accept_cb, CrashQueue::tombstone, -1, LEV_OPT_CLOSE_ON_FREE, crash_socket);
   if (!tombstone_listener) {
     LOG(FATAL) << "failed to create evconnlistener for tombstones.";
   }
@@ -379,7 +391,7 @@
 
     evutil_make_socket_nonblocking(java_trace_socket);
     evconnlistener* java_trace_listener = evconnlistener_new(
-        base, crash_accept_cb, CrashType::java_trace, -1, LEV_OPT_CLOSE_ON_FREE, java_trace_socket);
+        base, crash_accept_cb, CrashQueue::java_trace, -1, LEV_OPT_CLOSE_ON_FREE, java_trace_socket);
     if (!java_trace_listener) {
       LOG(FATAL) << "failed to create evconnlistener for java traces.";
     }
diff --git a/debuggerd/tombstoned/tombstoned_client.cpp b/debuggerd/tombstoned/tombstoned_client.cpp
index 39dc6eb..bdb4c1a 100644
--- a/debuggerd/tombstoned/tombstoned_client.cpp
+++ b/debuggerd/tombstoned/tombstoned_client.cpp
@@ -31,10 +31,11 @@
 using android::base::unique_fd;
 
 bool tombstoned_connect(pid_t pid, unique_fd* tombstoned_socket, unique_fd* output_fd,
-                        bool is_native_crash) {
-  unique_fd sockfd(socket_local_client(
-      (is_native_crash ? kTombstonedCrashSocketName : kTombstonedJavaTraceSocketName),
-      ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_SEQPACKET));
+                        DebuggerdDumpType dump_type) {
+  unique_fd sockfd(
+      socket_local_client((dump_type != kDebuggerdJavaBacktrace ? kTombstonedCrashSocketName
+                                                                : kTombstonedJavaTraceSocketName),
+                          ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_SEQPACKET));
   if (sockfd == -1) {
     async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to connect to tombstoned: %s",
                           strerror(errno));
@@ -44,6 +45,7 @@
   TombstonedCrashPacket packet = {};
   packet.packet_type = CrashPacketType::kDumpRequest;
   packet.packet.dump_request.pid = pid;
+  packet.packet.dump_request.dump_type = dump_type;
   if (TEMP_FAILURE_RETRY(write(sockfd, &packet, sizeof(packet))) != sizeof(packet)) {
     async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to write DumpRequest packet: %s",
                           strerror(errno));