Merge "enable integer sanitizer for sdcard service"
diff --git a/debuggerd/backtrace.cpp b/debuggerd/backtrace.cpp
index 32843d8..8f4a53f 100644
--- a/debuggerd/backtrace.cpp
+++ b/debuggerd/backtrace.cpp
@@ -29,6 +29,7 @@
 #include <sys/ptrace.h>
 
 #include <memory>
+#include <string>
 
 #include <backtrace/Backtrace.h>
 
@@ -96,11 +97,11 @@
   }
 }
 
-void dump_backtrace(int fd, int amfd, BacktraceMap* map, pid_t pid, pid_t tid,
-                    const std::set<pid_t>& siblings) {
+void dump_backtrace(int fd, BacktraceMap* map, pid_t pid, pid_t tid,
+                    const std::set<pid_t>& siblings, std::string* amfd_data) {
   log_t log;
   log.tfd = fd;
-  log.amfd = amfd;
+  log.amfd_data = amfd_data;
 
   dump_process_header(&log, pid);
   dump_thread(&log, map, pid, tid);
diff --git a/debuggerd/backtrace.h b/debuggerd/backtrace.h
index 98c433b..acd5eaa 100644
--- a/debuggerd/backtrace.h
+++ b/debuggerd/backtrace.h
@@ -20,6 +20,7 @@
 #include <sys/types.h>
 
 #include <set>
+#include <string>
 
 #include "utility.h"
 
@@ -28,8 +29,8 @@
 
 // Dumps a backtrace using a format similar to what Dalvik uses so that the result
 // can be intermixed in a bug report.
-void dump_backtrace(int fd, int amfd, BacktraceMap* map, pid_t pid, pid_t tid,
-                    const std::set<pid_t>& siblings);
+void dump_backtrace(int fd, BacktraceMap* map, pid_t pid, pid_t tid,
+                    const std::set<pid_t>& siblings, std::string* amfd_data);
 
 /* Dumps the backtrace in the backtrace data structure to the log. */
 void dump_backtrace_to_log(Backtrace* backtrace, log_t* log, const char* prefix);
diff --git a/debuggerd/debuggerd.cpp b/debuggerd/debuggerd.cpp
index 258bd76..d87594c 100644
--- a/debuggerd/debuggerd.cpp
+++ b/debuggerd/debuggerd.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <arpa/inet.h>
 #include <dirent.h>
 #include <elf.h>
 #include <errno.h>
@@ -32,12 +33,15 @@
 #include <sys/un.h>
 #include <time.h>
 
+#include <memory>
 #include <set>
+#include <string>
 
 #include <selinux/android.h>
 
 #include <log/logger.h>
 
+#include <android-base/file.h>
 #include <android-base/unique_fd.h>
 #include <cutils/debugger.h>
 #include <cutils/properties.h>
@@ -287,6 +291,41 @@
   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);
@@ -414,7 +453,7 @@
 
 static bool perform_dump(const debugger_request_t& request, int fd, int tombstone_fd,
                          BacktraceMap* backtrace_map, const std::set<pid_t>& siblings,
-                         int* crash_signal, int amfd) {
+                         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;
@@ -432,10 +471,10 @@
         if (request.action == DEBUGGER_ACTION_DUMP_TOMBSTONE) {
           ALOGV("debuggerd: stopped -- dumping to tombstone");
           engrave_tombstone(tombstone_fd, backtrace_map, request.pid, request.tid, siblings, signal,
-                            request.original_si_code, request.abort_msg_address, amfd);
+                            request.original_si_code, request.abort_msg_address, amfd_data);
         } else if (request.action == DEBUGGER_ACTION_DUMP_BACKTRACE) {
           ALOGV("debuggerd: stopped -- dumping to fd");
-          dump_backtrace(fd, -1, backtrace_map, request.pid, request.tid, siblings);
+          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) {
@@ -458,7 +497,7 @@
         ALOGV("stopped -- fatal signal\n");
         *crash_signal = signal;
         engrave_tombstone(tombstone_fd, backtrace_map, request.pid, request.tid, siblings, signal,
-                          request.original_si_code, request.abort_msg_address, amfd);
+                          request.original_si_code, request.abort_msg_address, amfd_data);
         break;
 
       default:
@@ -472,13 +511,21 @@
 }
 
 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");
+    ALOGE("debuggerd: failed to setresgid: %s", strerror(errno));
     return false;
   }
 
   if (setresuid(AID_DEBUGGERD, AID_DEBUGGERD, AID_DEBUGGERD) != 0) {
-    ALOGE("debuggerd: failed to setresuid");
+    ALOGE("debuggerd: failed to setresuid: %s", strerror(errno));
     return false;
   }
 
@@ -545,9 +592,11 @@
   std::unique_ptr<BacktraceMap> backtrace_map(BacktraceMap::Create(request.pid));
 
   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;
@@ -560,11 +609,11 @@
 
   int crash_signal = SIGKILL;
   succeeded = perform_dump(request, fd, tombstone_fd, backtrace_map.get(), siblings,
-                           &crash_signal, amfd);
+                           &crash_signal, amfd_data.get());
   if (succeeded) {
     if (request.action == DEBUGGER_ACTION_DUMP_TOMBSTONE) {
       if (!tombstone_path.empty()) {
-        write(fd, tombstone_path.c_str(), tombstone_path.length());
+        android::base::WriteFully(fd, tombstone_path.c_str(), tombstone_path.length());
       }
     }
   }
@@ -577,6 +626,13 @@
     }
   }
 
+  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));
   }
@@ -593,9 +649,12 @@
   }
 
   // Wait for gdb, if requested.
-  if (attach_gdb && succeeded) {
+  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));
diff --git a/debuggerd/test/dump_memory_test.cpp b/debuggerd/test/dump_memory_test.cpp
index 2addd5d..49f3690 100644
--- a/debuggerd/test/dump_memory_test.cpp
+++ b/debuggerd/test/dump_memory_test.cpp
@@ -125,7 +125,7 @@
     }
 
     log_.tfd = tombstone_fd;
-    log_.amfd = -1;
+    log_.amfd_data = nullptr;
     log_.crashed_tid = 12;
     log_.current_tid = 12;
     log_.should_retrieve_logcat = false;
diff --git a/debuggerd/test/tombstone_test.cpp b/debuggerd/test/tombstone_test.cpp
index 96b3a7a..58d640e 100644
--- a/debuggerd/test/tombstone_test.cpp
+++ b/debuggerd/test/tombstone_test.cpp
@@ -68,7 +68,8 @@
     }
 
     log_.tfd = tombstone_fd;
-    log_.amfd = -1;
+    amfd_data_.clear();
+    log_.amfd_data = &amfd_data_;
     log_.crashed_tid = 12;
     log_.current_tid = 12;
     log_.should_retrieve_logcat = false;
@@ -90,6 +91,7 @@
   std::unique_ptr<BacktraceMock> backtrace_mock_;
 
   log_t log_;
+  std::string amfd_data_;
 };
 
 TEST_F(TombstoneTest, single_map) {
@@ -117,6 +119,8 @@
 #endif
   ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
 
+  ASSERT_STREQ("", amfd_data_.c_str());
+
   // Verify that the log buf is empty, and no error messages.
   ASSERT_STREQ("", getFakeLogBuf().c_str());
   ASSERT_STREQ("", getFakeLogPrint().c_str());
@@ -150,6 +154,8 @@
 #endif
   ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
 
+  ASSERT_STREQ("", amfd_data_.c_str());
+
   // Verify that the log buf is empty, and no error messages.
   ASSERT_STREQ("", getFakeLogBuf().c_str());
   ASSERT_STREQ("", getFakeLogPrint().c_str());
@@ -189,6 +195,8 @@
 #endif
   ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
 
+  ASSERT_STREQ("", amfd_data_.c_str());
+
   // Verify that the log buf is empty, and no error messages.
   ASSERT_STREQ("", getFakeLogBuf().c_str());
   ASSERT_STREQ("", getFakeLogPrint().c_str());
@@ -251,6 +259,8 @@
 #endif
   ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
 
+  ASSERT_STREQ("", amfd_data_.c_str());
+
   // Verify that the log buf is empty, and no error messages.
   ASSERT_STREQ("", getFakeLogBuf().c_str());
   ASSERT_STREQ("", getFakeLogPrint().c_str());
@@ -305,6 +315,8 @@
 #endif
   ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
 
+  ASSERT_STREQ("", amfd_data_.c_str());
+
   // Verify that the log buf is empty, and no error messages.
   ASSERT_STREQ("", getFakeLogBuf().c_str());
   ASSERT_STREQ("", getFakeLogPrint().c_str());
@@ -359,6 +371,8 @@
 #endif
   ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
 
+  ASSERT_STREQ("", amfd_data_.c_str());
+
   // Verify that the log buf is empty, and no error messages.
   ASSERT_STREQ("", getFakeLogBuf().c_str());
   ASSERT_STREQ("", getFakeLogPrint().c_str());
@@ -411,6 +425,8 @@
 #endif
   ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
 
+  ASSERT_STREQ("", amfd_data_.c_str());
+
   // Verify that the log buf is empty, and no error messages.
   ASSERT_STREQ("", getFakeLogBuf().c_str());
   ASSERT_STREQ("", getFakeLogPrint().c_str());
@@ -469,6 +485,8 @@
 #endif
   ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
 
+  ASSERT_STREQ("", amfd_data_.c_str());
+
   // Verify that the log buf is empty, and no error messages.
   ASSERT_STREQ("", getFakeLogBuf().c_str());
   ASSERT_STREQ("", getFakeLogPrint().c_str());
@@ -501,6 +519,8 @@
 #endif
   ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
 
+  ASSERT_STREQ("", amfd_data_.c_str());
+
   // Verify that the log buf is empty, and no error messages.
   ASSERT_STREQ("", getFakeLogBuf().c_str());
   ASSERT_STREQ("6 DEBUG Cannot get siginfo for 100: Bad address\n\n", getFakeLogPrint().c_str());
@@ -562,6 +582,8 @@
         << "Signal " << si.si_signo << " is not expected to include an address.";
     }
 
+    ASSERT_STREQ("", amfd_data_.c_str());
+
     // Verify that the log buf is empty, and no error messages.
     ASSERT_STREQ("", getFakeLogBuf().c_str());
     ASSERT_STREQ("", getFakeLogPrint().c_str());
@@ -582,6 +604,8 @@
 
   ASSERT_STREQ("", getFakeLogBuf().c_str());
   ASSERT_STREQ("6 DEBUG cannot get siginfo: Bad address\n\n", getFakeLogPrint().c_str());
+
+  ASSERT_STREQ("", amfd_data_.c_str());
 }
 
 TEST_F(TombstoneTest, dump_log_file_error) {
@@ -596,4 +620,14 @@
   ASSERT_STREQ("", getFakeLogBuf().c_str());
   ASSERT_STREQ("6 DEBUG Unable to open /fake/filename: Permission denied\n\n",
                getFakeLogPrint().c_str());
+
+  ASSERT_STREQ("", amfd_data_.c_str());
+}
+
+TEST_F(TombstoneTest, dump_header_info) {
+  dump_header_info(&log_);
+
+  std::string expected = "Build fingerprint: 'unknown'\nRevision: 'unknown'\n";
+  expected += android::base::StringPrintf("ABI: '%s'\n", ABI_STRING);
+  ASSERT_STREQ(expected.c_str(), amfd_data_.c_str());
 }
diff --git a/debuggerd/tombstone.cpp b/debuggerd/tombstone.cpp
index d802c8c..fa983fa 100644
--- a/debuggerd/tombstone.cpp
+++ b/debuggerd/tombstone.cpp
@@ -16,7 +16,6 @@
 
 #define LOG_TAG "DEBUG"
 
-#include <arpa/inet.h>
 #include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
@@ -202,7 +201,7 @@
 static void dump_thread_info(log_t* log, pid_t pid, pid_t tid) {
   char path[64];
   char threadnamebuf[1024];
-  char* threadname = NULL;
+  char* threadname = nullptr;
   FILE *fp;
 
   snprintf(path, sizeof(path), "/proc/%d/comm", tid);
@@ -218,13 +217,13 @@
   }
   // Blacklist logd, logd.reader, logd.writer, logd.auditd, logd.control ...
   static const char logd[] = "logd";
-  if (!strncmp(threadname, logd, sizeof(logd) - 1)
+  if (threadname != nullptr && !strncmp(threadname, logd, sizeof(logd) - 1)
       && (!threadname[sizeof(logd) - 1] || (threadname[sizeof(logd) - 1] == '.'))) {
     log->should_retrieve_logcat = false;
   }
 
   char procnamebuf[1024];
-  char* procname = NULL;
+  char* procname = nullptr;
 
   snprintf(path, sizeof(path), "/proc/%d/cmdline", pid);
   if ((fp = fopen(path, "r"))) {
@@ -613,16 +612,6 @@
   property_get("ro.debuggable", value, "0");
   bool want_logs = (value[0] == '1');
 
-  if (log->amfd >= 0) {
-    // 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);
-    TEMP_FAILURE_RETRY( write(log->amfd, &datum, 4) );
-    datum = htonl(signal);
-    TEMP_FAILURE_RETRY( write(log->amfd, &datum, 4) );
-  }
-
   _LOG(log, logtype::HEADER,
        "*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***\n");
   dump_header_info(log);
@@ -640,17 +629,6 @@
   if (want_logs) {
     dump_logs(log, pid, 0);
   }
-
-  // send EOD to the Activity Manager, then wait for its ack to avoid racing ahead
-  // and killing the target out from under it
-  if (log->amfd >= 0) {
-    uint8_t eodMarker = 0;
-    TEMP_FAILURE_RETRY( write(log->amfd, &eodMarker, 1) );
-    // 3 sec timeout reading the ack; we're fine if that happens
-    TEMP_FAILURE_RETRY( read(log->amfd, &eodMarker, 1) );
-  }
-
-  return;
 }
 
 // open_tombstone - find an available tombstone slot, if any, of the
@@ -708,7 +686,7 @@
 
 void engrave_tombstone(int tombstone_fd, BacktraceMap* map, pid_t pid, pid_t tid,
                        const std::set<pid_t>& siblings, int signal, int original_si_code,
-                       uintptr_t abort_msg_address, int amfd) {
+                       uintptr_t abort_msg_address, std::string* amfd_data) {
   log_t log;
   log.current_tid = tid;
   log.crashed_tid = tid;
@@ -719,8 +697,6 @@
   }
 
   log.tfd = tombstone_fd;
-  // Preserve amfd since it can be modified through the calls below without
-  // being closed.
-  log.amfd = amfd;
+  log.amfd_data = amfd_data;
   dump_crash(&log, map, pid, tid, siblings, signal, original_si_code, abort_msg_address);
 }
diff --git a/debuggerd/tombstone.h b/debuggerd/tombstone.h
index 7f3eebe..487d950 100644
--- a/debuggerd/tombstone.h
+++ b/debuggerd/tombstone.h
@@ -34,6 +34,6 @@
 /* Creates a tombstone file and writes the crash dump to it. */
 void engrave_tombstone(int tombstone_fd, BacktraceMap* map, pid_t pid, pid_t tid,
                        const std::set<pid_t>& siblings, int signal, int original_si_code,
-                       uintptr_t abort_msg_address, int amfd);
+                       uintptr_t abort_msg_address, std::string* amfd_data);
 
 #endif // _DEBUGGERD_TOMBSTONE_H
diff --git a/debuggerd/utility.cpp b/debuggerd/utility.cpp
index cd252ce..bd06095 100644
--- a/debuggerd/utility.cpp
+++ b/debuggerd/utility.cpp
@@ -25,7 +25,8 @@
 #include <sys/ptrace.h>
 #include <sys/wait.h>
 
-#include <android-base/file.h>
+#include <string>
+
 #include <android-base/stringprintf.h>
 #include <backtrace/Backtrace.h>
 #include <log/log.h>
@@ -49,7 +50,6 @@
                       && log->crashed_tid != -1
                       && log->current_tid != -1
                       && (log->crashed_tid == log->current_tid);
-  bool write_to_activitymanager = (log->amfd != -1);
 
   char buf[512];
   va_list ap;
@@ -68,12 +68,8 @@
 
   if (write_to_logcat) {
     __android_log_buf_write(LOG_ID_CRASH, ANDROID_LOG_FATAL, LOG_TAG, buf);
-    if (write_to_activitymanager) {
-      if (!android::base::WriteFully(log->amfd, buf, len)) {
-        // timeout or other failure on write; stop informing the activity manager
-        ALOGE("AM write failed: %s", strerror(errno));
-        log->amfd = -1;
-      }
+    if (log->amfd_data != nullptr) {
+      *log->amfd_data += buf;
     }
   }
 }
diff --git a/debuggerd/utility.h b/debuggerd/utility.h
index ed08ddc..cd01188 100644
--- a/debuggerd/utility.h
+++ b/debuggerd/utility.h
@@ -21,6 +21,8 @@
 #include <stdbool.h>
 #include <sys/types.h>
 
+#include <string>
+
 #include <backtrace/Backtrace.h>
 
 // Figure out the abi based on defined macros.
@@ -42,10 +44,10 @@
 
 
 struct log_t{
-    /* tombstone file descriptor */
+    // Tombstone file descriptor.
     int tfd;
-    /* Activity Manager socket file descriptor */
-    int amfd;
+    // Data to be sent to the Activity Manager.
+    std::string* amfd_data;
     // The tid of the thread that crashed.
     pid_t crashed_tid;
     // The tid of the thread we are currently working with.
@@ -54,7 +56,8 @@
     bool should_retrieve_logcat;
 
     log_t()
-        : tfd(-1), amfd(-1), crashed_tid(-1), current_tid(-1), should_retrieve_logcat(true) {}
+        : tfd(-1), amfd_data(nullptr), crashed_tid(-1), current_tid(-1),
+          should_retrieve_logcat(true) {}
 };
 
 // List of types of logs to simplify the logging decision in _LOG
diff --git a/include/utils/ashmem.h b/include/utils/ashmem.h
deleted file mode 100644
index 0854775..0000000
--- a/include/utils/ashmem.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/* utils/ashmem.h
- **
- ** Copyright 2008 The Android Open Source Project
- **
- ** This file is dual licensed.  It may be redistributed and/or modified
- ** under the terms of the Apache 2.0 License OR version 2 of the GNU
- ** General Public License.
- */
-
-#ifndef _UTILS_ASHMEM_H
-#define _UTILS_ASHMEM_H
-
-#include <linux/limits.h>
-#include <linux/ioctl.h>
-
-#define ASHMEM_NAME_LEN		256
-
-#define ASHMEM_NAME_DEF		"dev/ashmem"
-
-/* Return values from ASHMEM_PIN: Was the mapping purged while unpinned? */
-#define ASHMEM_NOT_REAPED	0
-#define ASHMEM_WAS_REAPED	1
-
-/* Return values from ASHMEM_UNPIN: Is the mapping now pinned or unpinned? */
-#define ASHMEM_NOW_UNPINNED	0
-#define ASHMEM_NOW_PINNED	1
-
-#define __ASHMEMIOC		0x77
-
-#define ASHMEM_SET_NAME		_IOW(__ASHMEMIOC, 1, char[ASHMEM_NAME_LEN])
-#define ASHMEM_GET_NAME		_IOR(__ASHMEMIOC, 2, char[ASHMEM_NAME_LEN])
-#define ASHMEM_SET_SIZE		_IOW(__ASHMEMIOC, 3, size_t)
-#define ASHMEM_GET_SIZE		_IO(__ASHMEMIOC, 4)
-#define ASHMEM_SET_PROT_MASK	_IOW(__ASHMEMIOC, 5, unsigned long)
-#define ASHMEM_GET_PROT_MASK	_IO(__ASHMEMIOC, 6)
-#define ASHMEM_PIN		_IO(__ASHMEMIOC, 7)
-#define ASHMEM_UNPIN		_IO(__ASHMEMIOC, 8)
-#define ASHMEM_ISPINNED		_IO(__ASHMEMIOC, 9)
-#define ASHMEM_PURGE_ALL_CACHES	_IO(__ASHMEMIOC, 10)
-
-#endif	/* _UTILS_ASHMEM_H */
diff --git a/libnativeloader/include/nativeloader/native_loader.h b/libnativeloader/include/nativeloader/native_loader.h
index d606a76..2a6aaec 100644
--- a/libnativeloader/include/nativeloader/native_loader.h
+++ b/libnativeloader/include/nativeloader/native_loader.h
@@ -43,6 +43,9 @@
                         jobject class_loader,
                         jstring library_path);
 
+__attribute__((visibility("default")))
+bool CloseNativeLibrary(void* handle);
+
 #if defined(__ANDROID__)
 // Look up linker namespace by class_loader. Returns nullptr if
 // there is no namespace associated with the class_loader.
diff --git a/libnativeloader/native_loader.cpp b/libnativeloader/native_loader.cpp
index 72152e7..7d9bb56 100644
--- a/libnativeloader/native_loader.cpp
+++ b/libnativeloader/native_loader.cpp
@@ -241,6 +241,10 @@
 #endif
 }
 
+bool CloseNativeLibrary(void* handle) {
+  return dlclose(handle) == 0;
+}
+
 #if defined(__ANDROID__)
 android_namespace_t* FindNamespaceByClassLoader(JNIEnv* env, jobject class_loader) {
   std::lock_guard<std::mutex> guard(g_namespaces_mutex);
diff --git a/toolbox/Android.mk b/toolbox/Android.mk
index 85f9415..d151ad6 100644
--- a/toolbox/Android.mk
+++ b/toolbox/Android.mk
@@ -36,7 +36,6 @@
     log \
     nandread \
     newfs_msdos \
-    ps \
     sendevent \
     start \
     stop \
diff --git a/toolbox/ps.c b/toolbox/ps.c
deleted file mode 100644
index c129110..0000000
--- a/toolbox/ps.c
+++ /dev/null
@@ -1,340 +0,0 @@
-#include <ctype.h>
-#include <dirent.h>
-#include <fcntl.h>
-#include <inttypes.h>
-#include <pwd.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <cutils/sched_policy.h>
-
-static char *nexttoksep(char **strp, char *sep)
-{
-    char *p = strsep(strp,sep);
-    return (p == 0) ? "" : p;
-}
-static char *nexttok(char **strp)
-{
-    return nexttoksep(strp, " ");
-}
-
-#define SHOW_PRIO 1
-#define SHOW_TIME 2
-#define SHOW_POLICY 4
-#define SHOW_CPU  8
-#define SHOW_MACLABEL 16
-#define SHOW_NUMERIC_UID 32
-#define SHOW_ABI 64
-
-#if __LP64__
-#define PC_WIDTH 10 /* Realistically, the top bits will be 0, so don't waste space. */
-#else
-#define PC_WIDTH (2*sizeof(uintptr_t))
-#endif
-
-static int display_flags = 0;
-static int ppid_filter = 0;
-
-static void print_exe_abi(int pid);
-
-static int ps_line(int pid, int tid)
-{
-    char statline[1024];
-    char cmdline[1024];
-    char macline[1024];
-    char user[32];
-    struct stat stats;
-    int r;
-    char *ptr, *name, *state;
-    int ppid;
-    unsigned rss, vss;
-    uintptr_t eip;
-    unsigned utime, stime;
-    int prio, nice, rtprio, sched, psr;
-    struct passwd *pw;
-
-    snprintf(statline, sizeof(statline), "/proc/%d", tid ? tid : pid);
-    stat(statline, &stats);
-
-    if (tid) {
-        snprintf(statline, sizeof(statline), "/proc/%d/task/%d/stat", pid, tid);
-        cmdline[0] = 0;
-        snprintf(macline, sizeof(macline), "/proc/%d/task/%d/attr/current", pid, tid);
-    } else {
-        snprintf(statline, sizeof(statline), "/proc/%d/stat", pid);
-        snprintf(cmdline, sizeof(cmdline), "/proc/%d/cmdline", pid);
-        snprintf(macline, sizeof(macline), "/proc/%d/attr/current", pid);
-        int fd = open(cmdline, O_RDONLY);
-        if (fd == 0) {
-            r = 0;
-        } else {
-            r = read(fd, cmdline, 1023);
-            close(fd);
-            if (r < 0) r = 0;
-        }
-        cmdline[r] = 0;
-    }
-
-    int fd = open(statline, O_RDONLY);
-    if (fd == 0) return -1;
-    r = read(fd, statline, 1023);
-    close(fd);
-    if (r < 0) return -1;
-    statline[r] = 0;
-
-    ptr = statline;
-    nexttok(&ptr); // skip pid
-    ptr++;          // skip "("
-
-    name = ptr;
-    ptr = strrchr(ptr, ')'); // Skip to *last* occurence of ')',
-    *ptr++ = '\0';           // and null-terminate name.
-
-    ptr++;          // skip " "
-    state = nexttok(&ptr);
-    ppid = atoi(nexttok(&ptr));
-    nexttok(&ptr); // pgrp
-    nexttok(&ptr); // sid
-    nexttok(&ptr); // tty
-    nexttok(&ptr); // tpgid
-    nexttok(&ptr); // flags
-    nexttok(&ptr); // minflt
-    nexttok(&ptr); // cminflt
-    nexttok(&ptr); // majflt
-    nexttok(&ptr); // cmajflt
-#if 1
-    utime = atoi(nexttok(&ptr));
-    stime = atoi(nexttok(&ptr));
-#else
-    nexttok(&ptr); // utime
-    nexttok(&ptr); // stime
-#endif
-    nexttok(&ptr); // cutime
-    nexttok(&ptr); // cstime
-    prio = atoi(nexttok(&ptr));
-    nice = atoi(nexttok(&ptr));
-    nexttok(&ptr); // threads
-    nexttok(&ptr); // itrealvalue
-    nexttok(&ptr); // starttime
-    vss = strtoul(nexttok(&ptr), 0, 10); // vsize
-    rss = strtoul(nexttok(&ptr), 0, 10); // rss
-    nexttok(&ptr); // rlim
-    nexttok(&ptr); // startcode
-    nexttok(&ptr); // endcode
-    nexttok(&ptr); // startstack
-    nexttok(&ptr); // kstkesp
-    eip = strtoul(nexttok(&ptr), 0, 10); // kstkeip
-    nexttok(&ptr); // signal
-    nexttok(&ptr); // blocked
-    nexttok(&ptr); // sigignore
-    nexttok(&ptr); // sigcatch
-    nexttok(&ptr); // wchan
-    nexttok(&ptr); // nswap
-    nexttok(&ptr); // cnswap
-    nexttok(&ptr); // exit signal
-    psr = atoi(nexttok(&ptr)); // processor
-    rtprio = atoi(nexttok(&ptr)); // rt_priority
-    sched = atoi(nexttok(&ptr)); // scheduling policy
-
-    nexttok(&ptr); // tty
-
-    if (tid != 0) {
-        ppid = pid;
-        pid = tid;
-    }
-
-    pw = getpwuid(stats.st_uid);
-    if (pw == 0 || (display_flags & SHOW_NUMERIC_UID)) {
-        snprintf(user, sizeof(user), "%d", (int)stats.st_uid);
-    } else {
-        snprintf(user, sizeof(user), "%s", pw->pw_name);
-    }
-
-    if (ppid_filter != 0 && ppid != ppid_filter) {
-        return 0;
-    }
-
-    if (display_flags & SHOW_MACLABEL) {
-        fd = open(macline, O_RDONLY);
-        strcpy(macline, "-");
-        if (fd >= 0) {
-            r = read(fd, macline, sizeof(macline)-1);
-            close(fd);
-            if (r > 0)
-                macline[r] = 0;
-        }
-        printf("%-30s ", macline);
-    }
-
-    printf("%-9s %-5d %-5d %-6d %-5d", user, pid, ppid, vss / 1024, rss * 4);
-    if (display_flags & SHOW_CPU)
-        printf(" %-2d", psr);
-    if (display_flags & SHOW_PRIO)
-        printf(" %-5d %-5d %-5d %-5d", prio, nice, rtprio, sched);
-    if (display_flags & SHOW_POLICY) {
-        SchedPolicy p;
-        if (get_sched_policy(pid, &p) < 0)
-            printf(" un ");
-        else
-            printf(" %.2s ", get_sched_policy_name(p));
-    }
-    char path[PATH_MAX];
-    snprintf(path, sizeof(path), "/proc/%d/wchan", pid);
-    char wchan[10];
-    fd = open(path, O_RDONLY);
-    ssize_t wchan_len = read(fd, wchan, sizeof(wchan));
-    if (wchan_len == -1) {
-        wchan[wchan_len = 0] = '\0';
-    }
-    close(fd);
-    printf(" %10.*s %0*" PRIxPTR " %s ", (int) wchan_len, wchan, (int) PC_WIDTH, eip, state);
-    if (display_flags & SHOW_ABI) {
-        print_exe_abi(pid);
-    }
-    printf("%s", cmdline[0] ? cmdline : name);
-    if (display_flags & SHOW_TIME)
-        printf(" (u:%d, s:%d)", utime, stime);
-
-    printf("\n");
-    return 0;
-}
-
-static void print_exe_abi(int pid)
-{
-    int fd, r;
-    char exeline[1024];
-
-    snprintf(exeline, sizeof(exeline), "/proc/%d/exe", pid);
-    fd = open(exeline, O_RDONLY);
-    if (fd == 0) {
-        printf("    ");
-        return;
-    }
-    r = read(fd, exeline, 5 /* 4 byte ELFMAG + 1 byte EI_CLASS */);
-    close(fd);
-    if (r < 0) {
-        printf("    ");
-        return;
-    }
-    if (memcmp("\177ELF", exeline, 4) != 0) {
-        printf("??  ");
-        return;
-    }
-    switch (exeline[4]) {
-        case 1:
-            printf("32  ");
-            return;
-        case 2:
-            printf("64  ");
-            return;
-        default:
-            printf("??  ");
-            return;
-    }
-}
-
-void ps_threads(int pid)
-{
-    char tmp[128];
-    DIR *d;
-    struct dirent *de;
-
-    snprintf(tmp,sizeof(tmp),"/proc/%d/task",pid);
-    d = opendir(tmp);
-    if (d == 0) return;
-
-    while ((de = readdir(d)) != 0) {
-        if (isdigit(de->d_name[0])) {
-            int tid = atoi(de->d_name);
-            if (tid == pid) continue;
-            ps_line(pid, tid);
-        }
-    }
-    closedir(d);
-}
-
-int ps_main(int argc, char **argv)
-{
-    DIR *d;
-    struct dirent *de;
-    int pidfilter = 0;
-    int threads = 0;
-
-    while (argc > 1) {
-        if (!strcmp(argv[1], "-t")) {
-            threads = 1;
-        } else if (!strcmp(argv[1], "-n")) {
-            display_flags |= SHOW_NUMERIC_UID;
-        } else if (!strcmp(argv[1], "-x")) {
-            display_flags |= SHOW_TIME;
-        } else if (!strcmp(argv[1], "-Z")) {
-            display_flags |= SHOW_MACLABEL;
-        } else if (!strcmp(argv[1], "-P")) {
-            display_flags |= SHOW_POLICY;
-        } else if (!strcmp(argv[1], "-p")) {
-            display_flags |= SHOW_PRIO;
-        } else if (!strcmp(argv[1], "-c")) {
-            display_flags |= SHOW_CPU;
-        } else if (!strcmp(argv[1], "--abi")) {
-            display_flags |= SHOW_ABI;
-        } else if (!strcmp(argv[1], "--ppid")) {
-            if (argc < 3) {
-                /* Bug 26554285: Use printf because some apps require at least
-                 * one line of output to stdout even for errors.
-                 */
-                printf("no ppid\n");
-                return 1;
-            }
-            ppid_filter = atoi(argv[2]);
-            if (ppid_filter == 0) {
-                /* Bug 26554285: Use printf because some apps require at least
-                 * one line of output to stdout even for errors.
-                 */
-                printf("bad ppid '%s'\n", argv[2]);
-                return 1;
-            }
-            argc--;
-            argv++;
-        } else {
-            pidfilter = atoi(argv[1]);
-            if (pidfilter == 0) {
-                /* Bug 26554285: Use printf because some apps require at least
-                 * one line of output to stdout even for errors.
-                 */
-                printf("bad pid '%s'\n", argv[1]);
-                return 1;
-            }
-        }
-        argc--;
-        argv++;
-    }
-
-    if (display_flags & SHOW_MACLABEL) {
-        printf("LABEL                          ");
-    }
-    printf("USER      PID   PPID  VSIZE  RSS  %s%s %sWCHAN      %*s  %sNAME\n",
-           (display_flags&SHOW_CPU)?"CPU ":"",
-           (display_flags&SHOW_PRIO)?"PRIO  NICE  RTPRI SCHED ":"",
-           (display_flags&SHOW_POLICY)?"PCY " : "",
-           (int) PC_WIDTH, "PC",
-           (display_flags&SHOW_ABI)?"ABI " : "");
-
-    d = opendir("/proc");
-    if (d == 0) return -1;
-
-    while ((de = readdir(d)) != 0) {
-        if (isdigit(de->d_name[0])) {
-            int pid = atoi(de->d_name);
-            if (!pidfilter || (pidfilter == pid)) {
-                ps_line(pid, 0);
-                if (threads) ps_threads(pid);
-            }
-        }
-    }
-    closedir(d);
-    return 0;
-}