Merge "trusty: Add nvram-wipe utility."
diff --git a/CleanSpec.mk b/CleanSpec.mk
index 3cad427..31e60ca 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -58,3 +58,4 @@
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/grep $(PRODUCT_OUT)/system/bin/toolbox)
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/hw/gatekeeper.$(TARGET_DEVICE).so)
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib64/hw/gatekeeper.$(TARGET_DEVICE).so)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/vendor)
diff --git a/adb/Android.mk b/adb/Android.mk
index 6188184..0babf1d 100644
--- a/adb/Android.mk
+++ b/adb/Android.mk
@@ -319,6 +319,7 @@
 LOCAL_C_INCLUDES += system/extras/ext4_utils
 
 LOCAL_SANITIZE := $(adb_target_sanitize)
+LOCAL_STRIP_MODULE := keep_symbols
 LOCAL_STATIC_LIBRARIES := \
     libadbd \
     libbase \
@@ -333,6 +334,7 @@
     libbase \
     libcrypto_utils_static \
     libcrypto_static \
-    libminijail
+    libminijail \
+    libdebuggerd_client \
 
 include $(BUILD_EXECUTABLE)
diff --git a/adb/adb.cpp b/adb/adb.cpp
index 3f14f1a..725aa91 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -46,8 +46,6 @@
 #include "adb_utils.h"
 #include "transport.h"
 
-#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
-
 #if !ADB_HOST
 #include <cutils/properties.h>
 #include <sys/capability.h>
@@ -329,8 +327,6 @@
 
 void handle_packet(apacket *p, atransport *t)
 {
-    asocket *s;
-
     D("handle_packet() %c%c%c%c", ((char*) (&(p->msg.command)))[0],
             ((char*) (&(p->msg.command)))[1],
             ((char*) (&(p->msg.command)))[2],
@@ -339,7 +335,7 @@
 
     switch(p->msg.command){
     case A_SYNC:
-        if(p->msg.arg0){
+        if (p->msg.arg0){
             send_packet(p, t);
 #if ADB_HOST
             send_connect(t);
@@ -384,8 +380,8 @@
         if (t->online && p->msg.arg0 != 0 && p->msg.arg1 == 0) {
             char *name = (char*) p->data;
             name[p->msg.data_length > 0 ? p->msg.data_length - 1 : 0] = 0;
-            s = create_local_service_socket(name, t);
-            if(s == 0) {
+            asocket* s = create_local_service_socket(name, t);
+            if (s == nullptr) {
                 send_close(0, p->msg.arg0, t);
             } else {
                 s->peer = create_remote_socket(p->msg.arg0, t);
@@ -398,7 +394,8 @@
 
     case A_OKAY: /* READY(local-id, remote-id, "") */
         if (t->online && p->msg.arg0 != 0 && p->msg.arg1 != 0) {
-            if((s = find_local_socket(p->msg.arg1, 0))) {
+            asocket* s = find_local_socket(p->msg.arg1, 0);
+            if (s) {
                 if(s->peer == 0) {
                     /* On first READY message, create the connection. */
                     s->peer = create_remote_socket(p->msg.arg0, t);
@@ -422,7 +419,8 @@
 
     case A_CLSE: /* CLOSE(local-id, remote-id, "") or CLOSE(0, remote-id, "") */
         if (t->online && p->msg.arg1 != 0) {
-            if((s = find_local_socket(p->msg.arg1, p->msg.arg0))) {
+            asocket* s = find_local_socket(p->msg.arg1, p->msg.arg0);
+            if (s) {
                 /* According to protocol.txt, p->msg.arg0 might be 0 to indicate
                  * a failed OPEN only. However, due to a bug in previous ADB
                  * versions, CLOSE(0, remote-id, "") was also used for normal
@@ -445,11 +443,12 @@
 
     case A_WRTE: /* WRITE(local-id, remote-id, <data>) */
         if (t->online && p->msg.arg0 != 0 && p->msg.arg1 != 0) {
-            if((s = find_local_socket(p->msg.arg1, p->msg.arg0))) {
+            asocket* s = find_local_socket(p->msg.arg1, p->msg.arg0);
+            if (s) {
                 unsigned rid = p->msg.arg0;
                 p->len = p->msg.data_length;
 
-                if(s->enqueue(s, p) == 0) {
+                if (s->enqueue(s, p) == 0) {
                     D("Enqueue the socket");
                     send_ready(s->id, rid, t);
                 }
diff --git a/adb/adb_auth_host.cpp b/adb/adb_auth_host.cpp
index 03cebe9..dff874f 100644
--- a/adb/adb_auth_host.cpp
+++ b/adb/adb_auth_host.cpp
@@ -33,16 +33,13 @@
 #include <crypto_utils/android_pubkey.h>
 #include <cutils/list.h>
 
+#include <openssl/base64.h>
 #include <openssl/evp.h>
 #include <openssl/objects.h>
 #include <openssl/pem.h>
 #include <openssl/rsa.h>
 #include <openssl/sha.h>
 
-#if defined(OPENSSL_IS_BORINGSSL)
-#include <openssl/base64.h>
-#endif
-
 #define ANDROID_PATH   ".android"
 #define ADB_KEY_FILE   "adbkey"
 
@@ -54,108 +51,52 @@
 static struct listnode key_list;
 
 
-static void get_user_info(char *buf, size_t len)
-{
-    char hostname[1024], username[1024];
-    int ret = -1;
-
-    if (getenv("HOSTNAME") != NULL) {
-        strncpy(hostname, getenv("HOSTNAME"), sizeof(hostname));
-        hostname[sizeof(hostname)-1] = '\0';
-        ret = 0;
-    }
-
-#ifndef _WIN32
-    if (ret < 0)
-        ret = gethostname(hostname, sizeof(hostname));
+static std::string get_user_info() {
+    std::string hostname;
+    if (getenv("HOSTNAME")) hostname = getenv("HOSTNAME");
+#if !defined(_WIN32)
+    char buf[64];
+    if (hostname.empty() && gethostname(buf, sizeof(buf)) != -1) hostname = buf;
 #endif
-    if (ret < 0)
-        strcpy(hostname, "unknown");
+    if (hostname.empty()) hostname = "unknown";
 
-    ret = -1;
-
-    if (getenv("LOGNAME") != NULL) {
-        strncpy(username, getenv("LOGNAME"), sizeof(username));
-        username[sizeof(username)-1] = '\0';
-        ret = 0;
-    }
-
+    std::string username;
+    if (getenv("LOGNAME")) username = getenv("LOGNAME");
 #if !defined _WIN32 && !defined ADB_HOST_ON_TARGET
-    if (ret < 0)
-        ret = getlogin_r(username, sizeof(username));
+    if (username.empty() && getlogin()) username = getlogin();
 #endif
-    if (ret < 0)
-        strcpy(username, "unknown");
+    if (username.empty()) hostname = "unknown";
 
-    ret = snprintf(buf, len, " %s@%s", username, hostname);
-    if (ret >= (signed)len)
-        buf[len - 1] = '\0';
+    return " " + username + "@" + hostname;
 }
 
-static int write_public_keyfile(RSA *private_key, const char *private_key_path)
-{
+static bool write_public_keyfile(RSA* private_key, const std::string& private_key_path) {
     uint8_t binary_key_data[ANDROID_PUBKEY_ENCODED_SIZE];
-    uint8_t* base64_key_data = nullptr;
-    size_t base64_key_length = 0;
-    FILE *outfile = NULL;
-    char path[PATH_MAX], info[MAX_PAYLOAD_V1];
-    int ret = 0;
-
-    if (!android_pubkey_encode(private_key, binary_key_data,
-                               sizeof(binary_key_data))) {
-        D("Failed to convert to publickey");
-        goto out;
+    if (!android_pubkey_encode(private_key, binary_key_data, sizeof(binary_key_data))) {
+        LOG(ERROR) << "Failed to convert to public key";
+        return false;
     }
 
-    D("Writing public key to '%s'", path);
-
-#if defined(OPENSSL_IS_BORINGSSL)
+    size_t base64_key_length;
     if (!EVP_EncodedLength(&base64_key_length, sizeof(binary_key_data))) {
-        D("Public key too large to base64 encode");
-        goto out;
-    }
-#else
-    /* While we switch from OpenSSL to BoringSSL we have to implement
-     * |EVP_EncodedLength| here. */
-    base64_key_length = 1 + ((sizeof(binary_key_data) + 2) / 3 * 4);
-#endif
-
-    base64_key_data = new uint8_t[base64_key_length];
-    if (base64_key_data == nullptr) {
-        D("Allocation failure");
-        goto out;
+        LOG(ERROR) << "Public key too large to base64 encode";
+        return false;
     }
 
-    base64_key_length = EVP_EncodeBlock(base64_key_data, binary_key_data,
+    std::string content;
+    content.resize(base64_key_length);
+    base64_key_length = EVP_EncodeBlock(reinterpret_cast<uint8_t*>(&content[0]), binary_key_data,
                                         sizeof(binary_key_data));
-    get_user_info(info, sizeof(info));
 
-    if (snprintf(path, sizeof(path), "%s.pub", private_key_path) >=
-        (int)sizeof(path)) {
-        D("Path too long while writing public key");
-        goto out;
+    content += get_user_info();
+
+    std::string path(private_key_path + ".pub");
+    if (!android::base::WriteStringToFile(content, path)) {
+        PLOG(ERROR) << "Failed to write public key to '" << path << "'";
+        return false;
     }
 
-    outfile = fopen(path, "w");
-    if (!outfile) {
-        D("Failed to open '%s'", path);
-        goto out;
-    }
-
-    if (fwrite(base64_key_data, base64_key_length, 1, outfile) != 1 ||
-        fwrite(info, strlen(info), 1, outfile) != 1) {
-        D("Write error while writing public key");
-        goto out;
-    }
-
-    ret = 1;
-
- out:
-    if (outfile != NULL) {
-        fclose(outfile);
-    }
-    delete[] base64_key_data;
-    return ret;
+    return true;
 }
 
 static int generate_key(const char *file)
diff --git a/adb/client/main.cpp b/adb/client/main.cpp
index 65640ad..d2ca44e 100644
--- a/adb/client/main.cpp
+++ b/adb/client/main.cpp
@@ -23,11 +23,6 @@
 #include <stdlib.h>
 #include <unistd.h>
 
-// We only build the affinity WAR code for Linux.
-#if defined(__linux__)
-#include <sched.h>
-#endif
-
 #include <android-base/errors.h>
 #include <android-base/file.h>
 #include <android-base/logging.h>
@@ -39,21 +34,14 @@
 #include "adb_utils.h"
 #include "transport.h"
 
-#if defined(_WIN32)
-static BOOL WINAPI ctrlc_handler(DWORD type) {
-    // TODO: Consider trying to kill a starting up adb server (if we're in
-    // launch_server) by calling GenerateConsoleCtrlEvent().
-    exit(STATUS_CONTROL_C_EXIT);
-    return TRUE;
-}
-
 static std::string GetLogFilePath() {
+#if defined(_WIN32)
     const char log_name[] = "adb.log";
     WCHAR temp_path[MAX_PATH];
 
     // https://msdn.microsoft.com/en-us/library/windows/desktop/aa364992%28v=vs.85%29.aspx
     DWORD nchars = GetTempPathW(arraysize(temp_path), temp_path);
-    if ((nchars >= arraysize(temp_path)) || (nchars == 0)) {
+    if (nchars >= arraysize(temp_path) || nchars == 0) {
         // If string truncation or some other error.
         fatal("cannot retrieve temporary file path: %s\n",
               android::base::SystemErrorCodeToString(GetLastError()).c_str());
@@ -65,12 +53,12 @@
     }
 
     return temp_path_utf8 + log_name;
-}
 #else
-static std::string GetLogFilePath() {
-    return std::string("/tmp/adb.log");
-}
+    const char* tmp_dir = getenv("TMPDIR");
+    if (tmp_dir == nullptr) tmp_dir = "/tmp";
+    return android::base::StringPrintf("%s/adb.%u.log", tmp_dir, getuid());
 #endif
+}
 
 static void setup_daemon_logging(void) {
     const std::string log_file_path(GetLogFilePath());
@@ -90,6 +78,15 @@
     LOG(INFO) << adb_version();
 }
 
+#if defined(_WIN32)
+static BOOL WINAPI ctrlc_handler(DWORD type) {
+    // TODO: Consider trying to kill a starting up adb server (if we're in
+    // launch_server) by calling GenerateConsoleCtrlEvent().
+    exit(STATUS_CONTROL_C_EXIT);
+    return TRUE;
+}
+#endif
+
 int adb_server_main(int is_daemon, int server_port, int ack_reply_fd) {
 #if defined(_WIN32)
     // adb start-server starts us up with stdout and stderr hooked up to
diff --git a/adb/commandline.cpp b/adb/commandline.cpp
index 82fa19a..477edc1 100644
--- a/adb/commandline.cpp
+++ b/adb/commandline.cpp
@@ -872,27 +872,26 @@
  *   we hang up.
  */
 static int adb_sideload_host(const char* fn) {
-    printf("loading: '%s'", fn);
-    fflush(stdout);
+    fprintf(stderr, "loading: '%s'...\n", fn);
 
     std::string content;
     if (!android::base::ReadFileToString(fn, &content)) {
-        printf("\n");
-        fprintf(stderr, "* cannot read '%s' *\n", fn);
+        fprintf(stderr, "failed: %s\n", strerror(errno));
         return -1;
     }
 
     const uint8_t* data = reinterpret_cast<const uint8_t*>(content.data());
     unsigned sz = content.size();
 
+    fprintf(stderr, "connecting...\n");
     std::string service =
             android::base::StringPrintf("sideload-host:%d:%d", sz, SIDELOAD_HOST_BLOCK_SIZE);
     std::string error;
     unique_fd fd(adb_connect(service, &error));
-    if (fd >= 0) {
+    if (fd < 0) {
         // Try falling back to the older sideload method.  Maybe this
         // is an older device that doesn't support sideload-host.
-        printf("\n");
+        fprintf(stderr, "falling back to older sideload method...\n");
         return adb_download_buffer("sideload", fn, data, sz, true);
     }
 
@@ -1093,12 +1092,10 @@
         return true;
     }
 
-    // Give adbd 500ms to kill itself, then wait-for-device for it to come back up.
-    adb_sleep_ms(500);
-    TransportType type;
-    const char* serial;
-    adb_get_transport(&type, &serial);
-    return wait_for_device("wait-for-any", type, serial);
+    // Give adbd some time to kill itself and come back up.
+    // We can't use wait-for-device because devices (e.g. adb over network) might not come back.
+    adb_sleep_ms(3000);
+    return true;
 }
 
 // Connects to the device "shell" service with |command| and prints the
diff --git a/adb/daemon/main.cpp b/adb/daemon/main.cpp
index 916bedf..b54243e 100644
--- a/adb/daemon/main.cpp
+++ b/adb/daemon/main.cpp
@@ -28,10 +28,13 @@
 #include <memory>
 
 #include <android-base/logging.h>
+#include <android-base/macros.h>
 #include <android-base/stringprintf.h>
 #include <libminijail.h>
+#include <scoped_minijail.h>
 
 #include "cutils/properties.h"
+#include "debuggerd/client.h"
 #include "private/android_filesystem_config.h"
 #include "selinux/android.h"
 
@@ -98,8 +101,7 @@
 }
 
 static void drop_privileges(int server_port) {
-    std::unique_ptr<minijail, void (*)(minijail*)> jail(minijail_new(),
-                                                        &minijail_destroy);
+    ScopedMinijail jail(minijail_new());
 
     // Add extra groups:
     // AID_ADB to access the USB driver
@@ -115,9 +117,7 @@
                       AID_INET,     AID_NET_BT,    AID_NET_BT_ADMIN,
                       AID_SDCARD_R, AID_SDCARD_RW, AID_NET_BW_STATS,
                       AID_READPROC};
-    minijail_set_supplementary_gids(jail.get(),
-                                    sizeof(groups) / sizeof(groups[0]),
-                                    groups);
+    minijail_set_supplementary_gids(jail.get(), arraysize(groups), groups);
 
     // Don't listen on a port (default 5037) if running in secure mode.
     // Don't run as root if running in secure mode.
@@ -247,6 +247,7 @@
 
     close_stdin();
 
+    debuggerd_init(nullptr);
     adb_trace_init(argv);
 
     D("Handling main()");
diff --git a/adb/file_sync_service.cpp b/adb/file_sync_service.cpp
index 926dbcf..14c26cb 100644
--- a/adb/file_sync_service.cpp
+++ b/adb/file_sync_service.cpp
@@ -21,13 +21,13 @@
 
 #include <dirent.h>
 #include <errno.h>
-#include <log/log.h>
-#include <selinux/android.h>
+#include <linux/xattr.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/stat.h>
 #include <sys/types.h>
+#include <sys/xattr.h>
 #include <unistd.h>
 #include <utime.h>
 
@@ -39,6 +39,8 @@
 
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
+#include <log/log.h>
+#include <selinux/android.h>
 
 static bool should_use_fs_config(const std::string& path) {
     // TODO: use fs_config to configure permissions on /data.
@@ -47,11 +49,27 @@
            android::base::StartsWith(path, "/oem/");
 }
 
+static bool update_capabilities(const char* path, uint64_t capabilities) {
+    if (capabilities == 0) {
+        // Ensure we clean up in case the capabilities weren't 0 in the past.
+        removexattr(path, XATTR_NAME_CAPS);
+        return true;
+    }
+
+    vfs_cap_data cap_data = {};
+    cap_data.magic_etc = VFS_CAP_REVISION | VFS_CAP_FLAGS_EFFECTIVE;
+    cap_data.data[0].permitted = (capabilities & 0xffffffff);
+    cap_data.data[0].inheritable = 0;
+    cap_data.data[1].permitted = (capabilities >> 32);
+    cap_data.data[1].inheritable = 0;
+    return setxattr(path, XATTR_NAME_CAPS, &cap_data, sizeof(cap_data), 0) != -1;
+}
+
 static bool secure_mkdirs(const std::string& path) {
     uid_t uid = -1;
     gid_t gid = -1;
     unsigned int mode = 0775;
-    uint64_t cap = 0;
+    uint64_t capabilities = 0;
 
     if (path[0] != '/') return false;
 
@@ -62,18 +80,19 @@
         partial_path += path_component;
 
         if (should_use_fs_config(partial_path)) {
-            fs_config(partial_path.c_str(), 1, nullptr, &uid, &gid, &mode, &cap);
+            fs_config(partial_path.c_str(), 1, nullptr, &uid, &gid, &mode, &capabilities);
         }
         if (adb_mkdir(partial_path.c_str(), mode) == -1) {
             if (errno != EEXIST) {
                 return false;
             }
         } else {
-            if (chown(partial_path.c_str(), uid, gid) == -1) {
-                return false;
-            }
+            if (chown(partial_path.c_str(), uid, gid) == -1) return false;
+
             // Not all filesystems support setting SELinux labels. http://b/23530370.
             selinux_android_restorecon(partial_path.c_str(), 0);
+
+            if (!update_capabilities(partial_path.c_str(), capabilities)) return false;
         }
     }
     return true;
@@ -83,8 +102,7 @@
     syncmsg msg;
     msg.stat.id = ID_STAT;
 
-    struct stat st;
-    memset(&st, 0, sizeof(st));
+    struct stat st = {};
     // TODO: add a way to report that the stat failed!
     lstat(path, &st);
     msg.stat.mode = st.st_mode;
@@ -146,8 +164,8 @@
     return SendSyncFail(fd, android::base::StringPrintf("%s: %s", reason.c_str(), strerror(errno)));
 }
 
-static bool handle_send_file(int s, const char* path, uid_t uid,
-                             gid_t gid, mode_t mode, std::vector<char>& buffer, bool do_unlink) {
+static bool handle_send_file(int s, const char* path, uid_t uid, gid_t gid, uint64_t capabilities,
+                             mode_t mode, std::vector<char>& buffer, bool do_unlink) {
     syncmsg msg;
     unsigned int timestamp = 0;
 
@@ -178,8 +196,13 @@
 
         // fchown clears the setuid bit - restore it if present.
         // Ignore the result of calling fchmod. It's not supported
-        // by all filesystems. b/12441485
+        // by all filesystems, so we don't check for success. b/12441485
         fchmod(fd, mode);
+
+        if (!update_capabilities(path, capabilities)) {
+            SendSyncFailErrno(s, "update_capabilities failed");
+            goto fail;
+        }
     }
 
     while (true) {
@@ -338,13 +361,13 @@
 
     uid_t uid = -1;
     gid_t gid = -1;
-    uint64_t cap = 0;
+    uint64_t capabilities = 0;
     if (should_use_fs_config(path)) {
         unsigned int broken_api_hack = mode;
-        fs_config(path.c_str(), 0, nullptr, &uid, &gid, &broken_api_hack, &cap);
+        fs_config(path.c_str(), 0, nullptr, &uid, &gid, &broken_api_hack, &capabilities);
         mode = broken_api_hack;
     }
-    return handle_send_file(s, path.c_str(), uid, gid, mode, buffer, do_unlink);
+    return handle_send_file(s, path.c_str(), uid, gid, capabilities, mode, buffer, do_unlink);
 }
 
 static bool do_recv(int s, const char* path, std::vector<char>& buffer) {
diff --git a/adb/jdwp_service.cpp b/adb/jdwp_service.cpp
index 3c812cc..7a44801 100644
--- a/adb/jdwp_service.cpp
+++ b/adb/jdwp_service.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-/* implement the "debug-ports" and "track-debug-ports" device services */
+#if !ADB_HOST
 
 #define TRACE_TAG JDWP
 
@@ -24,22 +24,29 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sys/socket.h>
+#include <sys/un.h>
 #include <unistd.h>
 
+#include <list>
+#include <memory>
+#include <vector>
+
 #include "adb.h"
+#include "adb_io.h"
 #include "adb_utils.h"
 
 /* here's how these things work.
 
    when adbd starts, it creates a unix server socket
-   named @vm-debug-control (@ is a shortcut for "first byte is zero"
+   named @jdwp-control (@ is a shortcut for "first byte is zero"
    to use the private namespace instead of the file system)
 
    when a new JDWP daemon thread starts in a new VM process, it creates
-   a connection to @vm-debug-control to announce its availability.
+   a connection to @jdwp-control to announce its availability.
 
 
-     JDWP thread                             @vm-debug-control
+     JDWP thread                             @jdwp-control
          |                                         |
          |------------------------------->         |
          | hello I'm in process <pid>              |
@@ -72,7 +79,7 @@
         to the JDWP process with the help of sendmsg()
 
 
-     JDWP thread                             @vm-debug-control
+     JDWP thread                             @jdwp-control
          |                                         |
          |                  <----------------------|
          |           OK, try this file descriptor  |
@@ -116,249 +123,191 @@
  ** for each JDWP process, we record its pid and its connected socket
  **/
 
-#define  MAX_OUT_FDS   4
+// PIDs are transmitted as 4 hex digits in ascii.
+static constexpr size_t PID_LEN = 4;
 
-#if !ADB_HOST
+static void jdwp_process_event(int socket, unsigned events, void* _proc);
+static void jdwp_process_list_updated(void);
 
-#include <sys/socket.h>
-#include <sys/un.h>
+struct JdwpProcess;
+static std::list<std::unique_ptr<JdwpProcess>> _jdwp_list;
 
 struct JdwpProcess {
-    JdwpProcess*  next;
-    JdwpProcess*  prev;
-    int           pid;
-    int           socket;
-    fdevent*      fde;
+    explicit JdwpProcess(int socket) {
+        this->socket = socket;
+        this->fde = fdevent_create(socket, jdwp_process_event, this);
 
-    char          in_buff[4];  /* input character to read PID */
-    int           in_len;      /* number from JDWP process    */
+        if (!this->fde) {
+            fatal("could not create fdevent for new JDWP process");
+        }
 
-    int           out_fds[MAX_OUT_FDS]; /* output array of file descriptors */
-    int           out_count;            /* to send to the JDWP process      */
+        this->fde->state |= FDE_DONT_CLOSE;
+
+        /* start by waiting for the PID */
+        fdevent_add(this->fde, FDE_READ);
+    }
+
+    ~JdwpProcess() {
+        if (this->socket >= 0) {
+            adb_shutdown(this->socket);
+            adb_close(this->socket);
+            this->socket = -1;
+        }
+
+        if (this->fde) {
+            fdevent_destroy(this->fde);
+            this->fde = nullptr;
+        }
+
+        out_fds.clear();
+    }
+
+    void RemoveFromList() {
+        if (this->pid >= 0) {
+            D("removing pid %d from jdwp process list", this->pid);
+        } else {
+            D("removing transient JdwpProcess from list");
+        }
+
+        auto pred = [this](const auto& proc) { return proc.get() == this; };
+        _jdwp_list.remove_if(pred);
+    }
+
+    int pid = -1;
+    int socket = -1;
+    fdevent* fde = nullptr;
+
+    std::vector<unique_fd> out_fds;
+    char in_buf[PID_LEN + 1];
+    ssize_t in_len = 0;
 };
 
-static JdwpProcess  _jdwp_list;
+static size_t jdwp_process_list(char* buffer, size_t bufferlen) {
+    std::string temp;
 
-static int
-jdwp_process_list( char*  buffer, int  bufferlen )
-{
-    char*         end  = buffer + bufferlen;
-    char*         p    = buffer;
-    JdwpProcess*  proc = _jdwp_list.next;
-
-    for ( ; proc != &_jdwp_list; proc = proc->next ) {
-        int  len;
-
+    for (auto& proc : _jdwp_list) {
         /* skip transient connections */
-        if (proc->pid < 0)
+        if (proc->pid < 0) {
             continue;
+        }
 
-        len = snprintf(p, end-p, "%d\n", proc->pid);
-        if (p + len >= end)
+        std::string next = std::to_string(proc->pid) + "\n";
+        if (temp.length() + next.length() > bufferlen) {
+            D("truncating JDWP process list (max len = %zu)", bufferlen);
             break;
-        p += len;
-    }
-    p[0] = 0;
-    return (p - buffer);
-}
-
-
-static int
-jdwp_process_list_msg( char*  buffer, int  bufferlen )
-{
-    char  head[5];
-    int   len = jdwp_process_list( buffer+4, bufferlen-4 );
-    snprintf(head, sizeof head, "%04x", len);
-    memcpy(buffer, head, 4);
-    return len + 4;
-}
-
-
-static void  jdwp_process_list_updated(void);
-
-static void
-jdwp_process_free( JdwpProcess*  proc )
-{
-    if (proc) {
-        int  n;
-
-        proc->prev->next = proc->next;
-        proc->next->prev = proc->prev;
-
-        if (proc->socket >= 0) {
-            adb_shutdown(proc->socket);
-            adb_close(proc->socket);
-            proc->socket = -1;
         }
-
-        if (proc->fde != NULL) {
-            fdevent_destroy(proc->fde);
-            proc->fde = NULL;
-        }
-        proc->pid = -1;
-
-        for (n = 0; n < proc->out_count; n++) {
-            adb_close(proc->out_fds[n]);
-        }
-        proc->out_count = 0;
-
-        free(proc);
-
-        jdwp_process_list_updated();
+        temp.append(next);
     }
+
+    memcpy(buffer, temp.data(), temp.length());
+    return temp.length();
 }
 
-
-static void  jdwp_process_event(int, unsigned, void*);  /* forward */
-
-
-static JdwpProcess*
-jdwp_process_alloc( int  socket )
-{
-    JdwpProcess* proc = reinterpret_cast<JdwpProcess*>(
-        calloc(1, sizeof(*proc)));
-
-    if (proc == NULL) {
-        D("not enough memory to create new JDWP process");
-        return NULL;
+static size_t jdwp_process_list_msg(char* buffer, size_t bufferlen) {
+    // Message is length-prefixed with 4 hex digits in ASCII.
+    static constexpr size_t header_len = 4;
+    if (bufferlen < header_len) {
+        fatal("invalid JDWP process list buffer size: %zu", bufferlen);
     }
 
-    proc->socket = socket;
-    proc->pid    = -1;
-    proc->next   = proc;
-    proc->prev   = proc;
-
-    proc->fde = fdevent_create( socket, jdwp_process_event, proc );
-    if (proc->fde == NULL) {
-        D("could not create fdevent for new JDWP process" );
-        free(proc);
-        return NULL;
-    }
-
-    proc->fde->state |= FDE_DONT_CLOSE;
-    proc->in_len      = 0;
-    proc->out_count   = 0;
-
-    /* append to list */
-    proc->next = &_jdwp_list;
-    proc->prev = proc->next->prev;
-
-    proc->prev->next = proc;
-    proc->next->prev = proc;
-
-    /* start by waiting for the PID */
-    fdevent_add(proc->fde, FDE_READ);
-
-    return proc;
+    char head[header_len + 1];
+    size_t len = jdwp_process_list(buffer + header_len, bufferlen - header_len);
+    snprintf(head, sizeof head, "%04zx", len);
+    memcpy(buffer, head, header_len);
+    return len + header_len;
 }
 
-
-static void
-jdwp_process_event( int  socket, unsigned  events, void*  _proc )
-{
-    JdwpProcess*  proc = reinterpret_cast<JdwpProcess*>(_proc);
+static void jdwp_process_event(int socket, unsigned events, void* _proc) {
+    JdwpProcess* proc = reinterpret_cast<JdwpProcess*>(_proc);
 
     if (events & FDE_READ) {
         if (proc->pid < 0) {
             /* read the PID as a 4-hexchar string */
-            char*  p    = proc->in_buff + proc->in_len;
-            int    size = 4 - proc->in_len;
-            char   temp[5];
-            while (size > 0) {
-                int  len = recv( socket, p, size, 0 );
-                if (len < 0) {
-                    if (errno == EINTR)
-                        continue;
-                    if (errno == EAGAIN)
-                        return;
-                    /* this can fail here if the JDWP process crashes very fast */
-                    D("weird unknown JDWP process failure: %s",
-                      strerror(errno));
-
-                    goto CloseProcess;
-                }
-                if (len == 0) {  /* end of stream ? */
-                    D("weird end-of-stream from unknown JDWP process");
-                    goto CloseProcess;
-                }
-                p            += len;
-                proc->in_len += len;
-                size         -= len;
+            if (proc->in_len < 0) {
+                fatal("attempting to read JDWP pid again?");
             }
-            /* we have read 4 characters, now decode the pid */
-            memcpy(temp, proc->in_buff, 4);
-            temp[4] = 0;
 
-            if (sscanf( temp, "%04x", &proc->pid ) != 1) {
-                D("could not decode JDWP %p PID number: '%s'", proc, temp);
+            char* p = proc->in_buf + proc->in_len;
+            size_t size = PID_LEN - proc->in_len;
+
+            ssize_t rc = TEMP_FAILURE_RETRY(recv(socket, p, size, 0));
+            if (rc < 0) {
+                if (errno == EAGAIN) {
+                    return;
+                }
+
+                D("failed to read jdwp pid: %s", strerror(errno));
+                goto CloseProcess;
+            }
+
+            proc->in_len += rc;
+            if (proc->in_len != PID_LEN) {
+                return;
+            }
+
+            proc->in_buf[PID_LEN] = '\0';
+            proc->in_len = -1;
+
+            if (sscanf(proc->in_buf, "%04x", &proc->pid) != 1) {
+                D("could not decode JDWP %p PID number: '%s'", proc, p);
                 goto CloseProcess;
             }
 
             /* all is well, keep reading to detect connection closure */
             D("Adding pid %d to jdwp process list", proc->pid);
             jdwp_process_list_updated();
-        }
-        else
-        {
+        } else {
             /* the pid was read, if we get there it's probably because the connection
              * was closed (e.g. the JDWP process exited or crashed) */
-            char  buf[32];
+            char buf[32];
 
-            for (;;) {
-                int  len = recv(socket, buf, sizeof(buf), 0);
+            while (true) {
+                int len = TEMP_FAILURE_RETRY(recv(socket, buf, sizeof(buf), 0));
 
-                if (len <= 0) {
-                    if (len < 0 && errno == EINTR)
-                        continue;
-                    if (len < 0 && errno == EAGAIN)
+                if (len == 0) {
+                    D("terminating JDWP %d connection: EOF", proc->pid);
+                    break;
+                } else if (len < 0) {
+                    if (len < 0 && errno == EAGAIN) {
                         return;
-                    else {
-                        D("terminating JDWP %d connection: %s", proc->pid,
-                          strerror(errno));
-                        break;
                     }
-                }
-                else {
-                    D( "ignoring unexpected JDWP %d control socket activity (%d bytes)",
-                       proc->pid, len );
+
+                    D("terminating JDWP %d connection: EOF", proc->pid);
+                    break;
+                } else {
+                    D("ignoring unexpected JDWP %d control socket activity (%d bytes)", proc->pid,
+                      len);
                 }
             }
 
-        CloseProcess:
-            if (proc->pid >= 0) {
-                D( "remove pid %d to jdwp process list", proc->pid );
-            }
-            jdwp_process_free(proc);
-            return;
+            goto CloseProcess;
         }
     }
 
     if (events & FDE_WRITE) {
-        D("trying to write to JDWP pid controli (count=%d first=%d) %d",
-          proc->pid, proc->out_count, proc->out_fds[0]);
-        if (proc->out_count > 0) {
-            int  fd = proc->out_fds[0];
-            int  n, ret;
-            struct cmsghdr*  cmsg;
-            struct msghdr    msg;
-            struct iovec     iov;
-            char             dummy = '!';
-            char             buffer[sizeof(struct cmsghdr) + sizeof(int)];
+        D("trying to send fd to JDWP process (count = %zu)", proc->out_fds.size());
+        if (!proc->out_fds.empty()) {
+            int fd = proc->out_fds.back().get();
+            struct cmsghdr* cmsg;
+            struct msghdr msg;
+            struct iovec iov;
+            char dummy = '!';
+            char buffer[sizeof(struct cmsghdr) + sizeof(int)];
 
-            iov.iov_base       = &dummy;
-            iov.iov_len        = 1;
-            msg.msg_name       = NULL;
-            msg.msg_namelen    = 0;
-            msg.msg_iov        = &iov;
-            msg.msg_iovlen     = 1;
-            msg.msg_flags      = 0;
-            msg.msg_control    = buffer;
+            iov.iov_base = &dummy;
+            iov.iov_len = 1;
+            msg.msg_name = NULL;
+            msg.msg_namelen = 0;
+            msg.msg_iov = &iov;
+            msg.msg_iovlen = 1;
+            msg.msg_flags = 0;
+            msg.msg_control = buffer;
             msg.msg_controllen = sizeof(buffer);
 
             cmsg = CMSG_FIRSTHDR(&msg);
-            cmsg->cmsg_len   = msg.msg_controllen;
+            cmsg->cmsg_len = msg.msg_controllen;
             cmsg->cmsg_level = SOL_SOCKET;
-            cmsg->cmsg_type  = SCM_RIGHTS;
+            cmsg->cmsg_type = SCM_RIGHTS;
             ((int*)CMSG_DATA(cmsg))[0] = fd;
 
             if (!set_file_block_mode(proc->socket, true)) {
@@ -366,74 +315,59 @@
                 goto CloseProcess;
             }
 
-            for (;;) {
-                ret = sendmsg(proc->socket, &msg, 0);
-                if (ret >= 0) {
-                    adb_close(fd);
-                    break;
-                }
-                if (errno == EINTR)
-                    continue;
-                D("sending new file descriptor to JDWP %d failed: %s",
-                  proc->pid, strerror(errno));
+            int ret = TEMP_FAILURE_RETRY(sendmsg(proc->socket, &msg, 0));
+            if (ret < 0) {
+                D("sending new file descriptor to JDWP %d failed: %s", proc->pid, strerror(errno));
                 goto CloseProcess;
             }
 
-            D("sent file descriptor %d to JDWP process %d",
-              fd, proc->pid);
+            adb_close(fd);
 
-            for (n = 1; n < proc->out_count; n++)
-                proc->out_fds[n-1] = proc->out_fds[n];
+            D("sent file descriptor %d to JDWP process %d", fd, proc->pid);
+
+            proc->out_fds.pop_back();
 
             if (!set_file_block_mode(proc->socket, false)) {
                 VLOG(JDWP) << "failed to set non-blocking mode for fd " << proc->socket;
                 goto CloseProcess;
             }
 
-            if (--proc->out_count == 0)
-                fdevent_del( proc->fde, FDE_WRITE );
+            if (proc->out_fds.empty()) {
+                fdevent_del(proc->fde, FDE_WRITE);
+            }
         }
     }
+
+    return;
+
+CloseProcess:
+    proc->RemoveFromList();
+    jdwp_process_list_updated();
 }
 
-
-int
-create_jdwp_connection_fd(int  pid)
-{
-    JdwpProcess*  proc = _jdwp_list.next;
-
+int create_jdwp_connection_fd(int pid) {
     D("looking for pid %d in JDWP process list", pid);
-    for ( ; proc != &_jdwp_list; proc = proc->next ) {
+
+    for (auto& proc : _jdwp_list) {
         if (proc->pid == pid) {
-            goto FoundIt;
+            int fds[2];
+
+            if (adb_socketpair(fds) < 0) {
+                D("%s: socket pair creation failed: %s", __FUNCTION__, strerror(errno));
+                return -1;
+            }
+            D("socketpair: (%d,%d)", fds[0], fds[1]);
+
+            proc->out_fds.emplace_back(fds[1]);
+            if (proc->out_fds.size() == 1) {
+                fdevent_add(proc->fde, FDE_WRITE);
+            }
+
+            return fds[0];
         }
     }
     D("search failed !!");
     return -1;
-
-FoundIt:
-    {
-        int  fds[2];
-
-        if (proc->out_count >= MAX_OUT_FDS) {
-            D("%s: too many pending JDWP connection for pid %d",
-              __FUNCTION__, pid);
-            return -1;
-        }
-
-        if (adb_socketpair(fds) < 0) {
-            D("%s: socket pair creation failed: %s",
-              __FUNCTION__, strerror(errno));
-            return -1;
-        }
-        D("socketpair: (%d,%d)", fds[0], fds[1]);
-
-        proc->out_fds[ proc->out_count ] = fds[1];
-        if (++proc->out_count == 1)
-            fdevent_add( proc->fde, FDE_WRITE );
-
-        return fds[0];
-    }
 }
 
 /**  VM DEBUG CONTROL SOCKET
@@ -442,33 +376,27 @@
  **/
 
 /* name of the debug control Unix socket */
-#define  JDWP_CONTROL_NAME      "\0jdwp-control"
-#define  JDWP_CONTROL_NAME_LEN  (sizeof(JDWP_CONTROL_NAME)-1)
+#define JDWP_CONTROL_NAME "\0jdwp-control"
+#define JDWP_CONTROL_NAME_LEN (sizeof(JDWP_CONTROL_NAME) - 1)
 
 struct JdwpControl {
-    int       listen_socket;
-    fdevent*  fde;
+    int listen_socket;
+    fdevent* fde;
 };
 
+static JdwpControl _jdwp_control;
 
-static void
-jdwp_control_event(int  s, unsigned events, void*  user);
+static void jdwp_control_event(int s, unsigned events, void* user);
 
-
-static int
-jdwp_control_init( JdwpControl*  control,
-                   const char*   sockname,
-                   int           socknamelen )
-{
-    sockaddr_un   addr;
-    socklen_t     addrlen;
-    int           s;
-    int           maxpath = sizeof(addr.sun_path);
-    int           pathlen = socknamelen;
+static int jdwp_control_init(JdwpControl* control, const char* sockname, int socknamelen) {
+    sockaddr_un addr;
+    socklen_t addrlen;
+    int s;
+    int maxpath = sizeof(addr.sun_path);
+    int pathlen = socknamelen;
 
     if (pathlen >= maxpath) {
-        D( "vm debug control socket name too long (%d extra chars)",
-           pathlen+1-maxpath );
+        D("vm debug control socket name too long (%d extra chars)", pathlen + 1 - maxpath);
         return -1;
     }
 
@@ -476,25 +404,22 @@
     addr.sun_family = AF_UNIX;
     memcpy(addr.sun_path, sockname, socknamelen);
 
-    s = socket( AF_UNIX, SOCK_STREAM, 0 );
+    s = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
     if (s < 0) {
-        D( "could not create vm debug control socket. %d: %s",
-           errno, strerror(errno));
+        D("could not create vm debug control socket. %d: %s", errno, strerror(errno));
         return -1;
     }
 
-    addrlen = (pathlen + sizeof(addr.sun_family));
+    addrlen = pathlen + sizeof(addr.sun_family);
 
     if (bind(s, reinterpret_cast<sockaddr*>(&addr), addrlen) < 0) {
-        D( "could not bind vm debug control socket: %d: %s",
-           errno, strerror(errno) );
+        D("could not bind vm debug control socket: %d: %s", errno, strerror(errno));
         adb_close(s);
         return -1;
     }
 
-    if ( listen(s, 4) < 0 ) {
-        D("listen failed in jdwp control socket: %d: %s",
-          errno, strerror(errno));
+    if (listen(s, 4) < 0) {
+        D("listen failed in jdwp control socket: %d: %s", errno, strerror(errno));
         adb_close(s);
         return -1;
     }
@@ -503,128 +428,109 @@
 
     control->fde = fdevent_create(s, jdwp_control_event, control);
     if (control->fde == NULL) {
-        D( "could not create fdevent for jdwp control socket" );
+        D("could not create fdevent for jdwp control socket");
         adb_close(s);
         return -1;
     }
 
     /* only wait for incoming connections */
     fdevent_add(control->fde, FDE_READ);
-    close_on_exec(s);
 
     D("jdwp control socket started (%d)", control->listen_socket);
     return 0;
 }
 
-
-static void
-jdwp_control_event( int  s, unsigned  events, void*  _control )
-{
-    JdwpControl*  control = (JdwpControl*) _control;
+static void jdwp_control_event(int s, unsigned events, void* _control) {
+    JdwpControl* control = (JdwpControl*)_control;
 
     if (events & FDE_READ) {
-        sockaddr_storage   ss;
-        sockaddr*          addrp = reinterpret_cast<sockaddr*>(&ss);
-        socklen_t          addrlen = sizeof(ss);
-        int                s = -1;
-        JdwpProcess*       proc;
-
-        do {
-            s = adb_socket_accept(control->listen_socket, addrp, &addrlen);
-            if (s < 0) {
-                if (errno == EINTR)
-                    continue;
-                if (errno == ECONNABORTED) {
-                    /* oops, the JDWP process died really quick */
-                    D("oops, the JDWP process died really quick");
-                    return;
-                }
+        sockaddr_storage ss;
+        sockaddr* addrp = reinterpret_cast<sockaddr*>(&ss);
+        socklen_t addrlen = sizeof(ss);
+        int s = adb_socket_accept(control->listen_socket, addrp, &addrlen);
+        if (s < 0) {
+            if (errno == ECONNABORTED) {
+                /* oops, the JDWP process died really quick */
+                D("oops, the JDWP process died really quick");
+                return;
+            } else {
                 /* the socket is probably closed ? */
-                D( "weird accept() failed on jdwp control socket: %s",
-                   strerror(errno) );
+                D("weird accept() failed on jdwp control socket: %s", strerror(errno));
                 return;
             }
         }
-        while (s < 0);
 
-        proc = jdwp_process_alloc( s );
-        if (proc == NULL)
-            return;
+        auto proc = std::make_unique<JdwpProcess>(s);
+        if (!proc) {
+            fatal("failed to allocate JdwpProcess");
+        }
+
+        _jdwp_list.emplace_back(std::move(proc));
     }
 }
 
-
-static JdwpControl   _jdwp_control;
-
 /** "jdwp" local service implementation
  ** this simply returns the list of known JDWP process pids
  **/
 
-struct JdwpSocket {
-    asocket  socket;
-    int      pass;
+struct JdwpSocket : public asocket {
+    bool pass;
 };
 
-static void
-jdwp_socket_close( asocket*  s )
-{
-    asocket*  peer = s->peer;
+static void jdwp_socket_close(asocket* s) {
+    D("LS(%d): closing jdwp socket", s->id);
+
+    if (s->peer) {
+        D("LS(%d) peer->close()ing peer->id=%d peer->fd=%d", s->id, s->peer->id, s->peer->fd);
+        s->peer->peer = nullptr;
+        s->peer->close(s->peer);
+        s->peer = nullptr;
+    }
 
     remove_socket(s);
-
-    if (peer) {
-        peer->peer = NULL;
-        peer->close(peer);
-    }
     free(s);
 }
 
-static int
-jdwp_socket_enqueue( asocket*  s, apacket*  p )
-{
+static int jdwp_socket_enqueue(asocket* s, apacket* p) {
     /* you can't write to this asocket */
+    D("LS(%d): JDWP socket received data?", s->id);
     put_apacket(p);
     s->peer->close(s->peer);
     return -1;
 }
 
+static void jdwp_socket_ready(asocket* s) {
+    JdwpSocket* jdwp = (JdwpSocket*)s;
+    asocket* peer = jdwp->peer;
 
-static void
-jdwp_socket_ready( asocket*  s )
-{
-    JdwpSocket*  jdwp = (JdwpSocket*)s;
-    asocket*     peer = jdwp->socket.peer;
-
-   /* on the first call, send the list of pids,
-    * on the second one, close the connection
-    */
-    if (jdwp->pass == 0) {
-        apacket*  p = get_apacket();
+    /* on the first call, send the list of pids,
+     * on the second one, close the connection
+     */
+    if (!jdwp->pass) {
+        apacket* p = get_apacket();
         p->len = jdwp_process_list((char*)p->data, s->get_max_payload());
         peer->enqueue(peer, p);
-        jdwp->pass = 1;
-    }
-    else {
+        jdwp->pass = true;
+    } else {
         peer->close(peer);
     }
 }
 
-asocket*
-create_jdwp_service_socket( void )
-{
+asocket* create_jdwp_service_socket(void) {
     JdwpSocket* s = reinterpret_cast<JdwpSocket*>(calloc(sizeof(*s), 1));
 
-    if (s == NULL)
-        return NULL;
+    if (!s) {
+        fatal("failed to allocate JdwpSocket");
+    }
 
-    install_local_socket(&s->socket);
+    install_local_socket(s);
 
-    s->socket.ready   = jdwp_socket_ready;
-    s->socket.enqueue = jdwp_socket_enqueue;
-    s->socket.close   = jdwp_socket_close;
-    s->pass           = 0;
+    s->ready = jdwp_socket_ready;
+    s->enqueue = jdwp_socket_enqueue;
+    s->close = jdwp_socket_close;
+    s->pass = false;
 
-    return &s->socket;
+    return s;
 }
 
 /** "track-jdwp" local service implementation
@@ -632,113 +538,88 @@
  ** to the client...
  **/
 
-struct JdwpTracker {
-    asocket       socket;
-    JdwpTracker*  next;
-    JdwpTracker*  prev;
-    int           need_update;
+struct JdwpTracker : public asocket {
+    bool need_initial;
 };
 
-static JdwpTracker   _jdwp_trackers_list;
+static std::vector<std::unique_ptr<JdwpTracker>> _jdwp_trackers;
 
+static void jdwp_process_list_updated(void) {
+    char buffer[1024];
+    int len = jdwp_process_list_msg(buffer, sizeof(buffer));
 
-static void
-jdwp_process_list_updated(void)
-{
-    char             buffer[1024];
-    int              len;
-    JdwpTracker*  t = _jdwp_trackers_list.next;
-
-    len = jdwp_process_list_msg(buffer, sizeof(buffer));
-
-    for ( ; t != &_jdwp_trackers_list; t = t->next ) {
-        apacket*  p    = get_apacket();
-        asocket*  peer = t->socket.peer;
+    for (auto& t : _jdwp_trackers) {
+        apacket* p = get_apacket();
         memcpy(p->data, buffer, len);
         p->len = len;
-        peer->enqueue( peer, p );
+
+        if (t->peer) {
+            // The tracker might not have been connected yet.
+            t->peer->enqueue(t->peer, p);
+        }
     }
 }
 
-static void
-jdwp_tracker_close( asocket*  s )
-{
-    JdwpTracker*  tracker = (JdwpTracker*) s;
-    asocket*      peer    = s->peer;
+static void jdwp_tracker_close(asocket* s) {
+    D("LS(%d): destroying jdwp tracker service", s->id);
 
-    if (peer) {
-        peer->peer = NULL;
-        peer->close(peer);
+    if (s->peer) {
+        D("LS(%d) peer->close()ing peer->id=%d peer->fd=%d", s->id, s->peer->id, s->peer->fd);
+        s->peer->peer = nullptr;
+        s->peer->close(s->peer);
+        s->peer = nullptr;
     }
 
     remove_socket(s);
 
-    tracker->prev->next = tracker->next;
-    tracker->next->prev = tracker->prev;
-
-    free(s);
+    auto pred = [s](const auto& tracker) { return tracker.get() == s; };
+    std::remove_if(_jdwp_trackers.begin(), _jdwp_trackers.end(), pred);
 }
 
-static void
-jdwp_tracker_ready( asocket*  s )
-{
-    JdwpTracker*  t = (JdwpTracker*) s;
+static void jdwp_tracker_ready(asocket* s) {
+    JdwpTracker* t = (JdwpTracker*)s;
 
-    if (t->need_update) {
-        apacket*  p = get_apacket();
-        t->need_update = 0;
+    if (t->need_initial) {
+        apacket* p = get_apacket();
+        t->need_initial = false;
         p->len = jdwp_process_list_msg((char*)p->data, s->get_max_payload());
         s->peer->enqueue(s->peer, p);
     }
 }
 
-static int
-jdwp_tracker_enqueue( asocket*  s, apacket*  p )
-{
+static int jdwp_tracker_enqueue(asocket* s, apacket* p) {
     /* you can't write to this socket */
+    D("LS(%d): JDWP tracker received data?", s->id);
     put_apacket(p);
     s->peer->close(s->peer);
     return -1;
 }
 
+asocket* create_jdwp_tracker_service_socket(void) {
+    auto t = std::make_unique<JdwpTracker>();
+    if (!t) {
+        fatal("failed to allocate JdwpTracker");
+    }
 
-asocket*
-create_jdwp_tracker_service_socket( void )
-{
-    JdwpTracker* t = reinterpret_cast<JdwpTracker*>(calloc(sizeof(*t), 1));
+    memset(t.get(), 0, sizeof(asocket));
 
-    if (t == NULL)
-        return NULL;
+    install_local_socket(t.get());
+    D("LS(%d): created new jdwp tracker service", t->id);
 
-    t->next = &_jdwp_trackers_list;
-    t->prev = t->next->prev;
+    t->ready = jdwp_tracker_ready;
+    t->enqueue = jdwp_tracker_enqueue;
+    t->close = jdwp_tracker_close;
+    t->need_initial = true;
 
-    t->next->prev = t;
-    t->prev->next = t;
+    asocket* result = t.get();
 
-    install_local_socket(&t->socket);
+    _jdwp_trackers.emplace_back(std::move(t));
 
-    t->socket.ready   = jdwp_tracker_ready;
-    t->socket.enqueue = jdwp_tracker_enqueue;
-    t->socket.close   = jdwp_tracker_close;
-    t->need_update    = 1;
-
-    return &t->socket;
+    return result;
 }
 
-
-int
-init_jdwp(void)
-{
-    _jdwp_list.next = &_jdwp_list;
-    _jdwp_list.prev = &_jdwp_list;
-
-    _jdwp_trackers_list.next = &_jdwp_trackers_list;
-    _jdwp_trackers_list.prev = &_jdwp_trackers_list;
-
-    return jdwp_control_init( &_jdwp_control,
-                              JDWP_CONTROL_NAME,
-                              JDWP_CONTROL_NAME_LEN );
+int init_jdwp(void) {
+    return jdwp_control_init(&_jdwp_control, JDWP_CONTROL_NAME, JDWP_CONTROL_NAME_LEN);
 }
 
 #endif /* !ADB_HOST */
diff --git a/adb/shell_service.cpp b/adb/shell_service.cpp
index b038fda..d83622c 100644
--- a/adb/shell_service.cpp
+++ b/adb/shell_service.cpp
@@ -155,14 +155,19 @@
 
     const std::string& command() const { return command_; }
 
-    int local_socket_fd() const { return local_socket_sfd_; }
+    int ReleaseLocalSocket() { return local_socket_sfd_.release(); }
 
     pid_t pid() const { return pid_; }
 
     // Sets up FDs, forks a subprocess, starts the subprocess manager thread,
-    // and exec's the child. Returns false on failure.
+    // and exec's the child. Returns false and sets error on failure.
     bool ForkAndExec(std::string* _Nonnull error);
 
+    // Start the subprocess manager thread. Consumes the subprocess, regardless of success.
+    // Returns false and sets error on failure.
+    static bool StartThread(std::unique_ptr<Subprocess> subprocess,
+                            std::string* _Nonnull error);
+
   private:
     // Opens the file at |pts_name|.
     int OpenPtyChildFd(const char* pts_name, unique_fd* error_sfd);
@@ -390,14 +395,19 @@
         }
     }
 
-    if (!adb_thread_create(ThreadHandler, this)) {
+    D("subprocess parent: completed");
+    return true;
+}
+
+bool Subprocess::StartThread(std::unique_ptr<Subprocess> subprocess, std::string* error) {
+    Subprocess* raw = subprocess.release();
+    if (!adb_thread_create(ThreadHandler, raw)) {
         *error =
             android::base::StringPrintf("failed to create subprocess thread: %s", strerror(errno));
-        kill(pid_, SIGKILL);
+        kill(raw->pid_, SIGKILL);
         return false;
     }
 
-    D("subprocess parent: completed");
     return true;
 }
 
@@ -439,8 +449,9 @@
     Subprocess* subprocess = reinterpret_cast<Subprocess*>(userdata);
 
     adb_thread_setname(android::base::StringPrintf(
-            "shell srvc %d", subprocess->local_socket_fd()));
+            "shell srvc %d", subprocess->pid()));
 
+    D("passing data streams for PID %d", subprocess->pid());
     subprocess->PassDataStreams();
 
     D("deleting Subprocess for PID %d", subprocess->pid());
@@ -733,7 +744,7 @@
       protocol == SubprocessProtocol::kNone ? "none" : "shell",
       terminal_type, name);
 
-    Subprocess* subprocess = new Subprocess(name, terminal_type, type, protocol);
+    auto subprocess = std::make_unique<Subprocess>(name, terminal_type, type, protocol);
     if (!subprocess) {
         LOG(ERROR) << "failed to allocate new subprocess";
         return ReportError(protocol, "failed to allocate new subprocess");
@@ -742,11 +753,17 @@
     std::string error;
     if (!subprocess->ForkAndExec(&error)) {
         LOG(ERROR) << "failed to start subprocess: " << error;
-        delete subprocess;
         return ReportError(protocol, error);
     }
 
-    D("subprocess creation successful: local_socket_fd=%d, pid=%d",
-      subprocess->local_socket_fd(), subprocess->pid());
-    return subprocess->local_socket_fd();
+    unique_fd local_socket(subprocess->ReleaseLocalSocket());
+    D("subprocess creation successful: local_socket_fd=%d, pid=%d", local_socket.get(),
+      subprocess->pid());
+
+    if (!Subprocess::StartThread(std::move(subprocess), &error)) {
+        LOG(ERROR) << "failed to start subprocess management thread: " << error;
+        return ReportError(protocol, error);
+    }
+
+    return local_socket.release();
 }
diff --git a/adb/sockets.cpp b/adb/sockets.cpp
index b2555d0..4ed1c45 100644
--- a/adb/sockets.cpp
+++ b/adb/sockets.cpp
@@ -410,7 +410,7 @@
 #endif
     int fd = service_to_fd(name, transport);
     if (fd < 0) {
-        return 0;
+        return nullptr;
     }
 
     asocket* s = create_local_socket(fd);
diff --git a/adb/test_device.py b/adb/test_device.py
index cdc57c6..2efac9d 100644
--- a/adb/test_device.py
+++ b/adb/test_device.py
@@ -31,6 +31,7 @@
 import subprocess
 import sys
 import tempfile
+import time
 import unittest
 
 import mock
@@ -495,6 +496,36 @@
             self.assertEqual(input.splitlines(), stdout.splitlines())
             self.assertEqual('', stderr)
 
+    def test_sighup(self):
+        """Ensure that SIGHUP gets sent upon non-interactive ctrl-c"""
+        log_path = "/data/local/tmp/adb_signal_test.log"
+
+        # Clear the output file.
+        self.device.shell_nocheck(["echo", ">", log_path])
+
+        script = """
+            trap "echo SIGINT > {path}; exit 0" SIGINT
+            trap "echo SIGHUP > {path}; exit 0" SIGHUP
+            echo Waiting
+            while true; do sleep 100; done
+        """.format(path=log_path)
+
+        script = ";".join([x.strip() for x in script.strip().splitlines()])
+
+        process = self.device.shell_popen(
+            ["sh", "-c", "'{}'".format(script)], kill_atexit=False, stdout=subprocess.PIPE)
+
+        self.assertEqual("Waiting\n", process.stdout.readline())
+        process.send_signal(signal.SIGINT)
+        process.wait()
+
+        # Waiting for the local adb to finish is insufficient, since it hangs
+        # up immediately.
+        time.sleep(0.25)
+
+        stdout, _ = self.device.shell(["cat", log_path])
+        self.assertEqual(stdout.strip(), "SIGHUP")
+
 
 class ArgumentEscapingTest(DeviceTest):
     def test_shell_escaping(self):
diff --git a/adb/transport_local.cpp b/adb/transport_local.cpp
index 31b5ad6..d918cc7 100644
--- a/adb/transport_local.cpp
+++ b/adb/transport_local.cpp
@@ -235,16 +235,20 @@
 /* This is relevant only for ADB daemon running inside the emulator. */
 /*
  * Redefine open and write for qemu_pipe.h that contains inlined references
- * to those routines. We will redifine them back after qemu_pipe.h inclusion.
+ * to those routines. We will redefine them back after qemu_pipe.h inclusion.
  */
 #undef open
+#undef read
 #undef write
 #define open    adb_open
+#define read    adb_read
 #define write   adb_write
-#include <hardware/qemu_pipe.h>
+#include <system/qemu_pipe.h>
 #undef open
+#undef read
 #undef write
 #define open    ___xxx_open
+#define read    ___xxx_read
 #define write   ___xxx_write
 
 /* A worker thread that monitors host connections, and registers a transport for
@@ -292,7 +296,7 @@
     D("transport: qemu_socket_thread() starting");
 
     /* adb QEMUD service connection request. */
-    snprintf(con_name, sizeof(con_name), "qemud:adb:%d", port);
+    snprintf(con_name, sizeof(con_name), "pipe:qemud:adb:%d", port);
 
     /* Connect to the adb QEMUD service. */
     fd = qemu_pipe_open(con_name);
diff --git a/adf/Android.bp b/adf/Android.bp
new file mode 100644
index 0000000..b44c296
--- /dev/null
+++ b/adf/Android.bp
@@ -0,0 +1 @@
+subdirs = ["*"]
diff --git a/adf/Android.mk b/adf/Android.mk
deleted file mode 100644
index 64d486e..0000000
--- a/adf/Android.mk
+++ /dev/null
@@ -1,18 +0,0 @@
-#
-# Copyright (C) 2013 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.
-#
-LOCAL_PATH := $(my-dir)
-
-include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/adf/libadf/Android.bp b/adf/libadf/Android.bp
new file mode 100644
index 0000000..2b5461e
--- /dev/null
+++ b/adf/libadf/Android.bp
@@ -0,0 +1,21 @@
+// Copyright (C) 2013 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.
+
+cc_library_static {
+    name: "libadf",
+    srcs: ["adf.c"],
+    cflags: ["-Werror"],
+    local_include_dirs: ["include"],
+    export_include_dirs: ["include"],
+}
diff --git a/adf/libadf/Android.mk b/adf/libadf/Android.mk
deleted file mode 100644
index 7df354b..0000000
--- a/adf/libadf/Android.mk
+++ /dev/null
@@ -1,24 +0,0 @@
-# Copyright (C) 2013 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.
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := adf.c
-LOCAL_MODULE := libadf
-LOCAL_MODULE_TAGS := optional
-LOCAL_CFLAGS += -Werror
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_C_INCLUDES += $(LOCAL_EXPORT_C_INCLUDE_DIRS)
-include $(BUILD_STATIC_LIBRARY)
diff --git a/adf/libadf/tests/Android.bp b/adf/libadf/tests/Android.bp
new file mode 100644
index 0000000..7b33300
--- /dev/null
+++ b/adf/libadf/tests/Android.bp
@@ -0,0 +1,22 @@
+//
+// Copyright (C) 2013 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.
+//
+
+cc_test {
+    name: "adf-unit-tests",
+    srcs: ["adf_test.cpp"],
+    static_libs: ["libadf"],
+    cflags: ["-Werror"],
+}
diff --git a/adf/libadf/tests/Android.mk b/adf/libadf/tests/Android.mk
deleted file mode 100644
index 68e5817..0000000
--- a/adf/libadf/tests/Android.mk
+++ /dev/null
@@ -1,23 +0,0 @@
-#
-# Copyright (C) 2013 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.
-#
-LOCAL_PATH := $(my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := adf_test.cpp
-LOCAL_MODULE := adf-unit-tests
-LOCAL_STATIC_LIBRARIES := libadf
-LOCAL_CFLAGS += -Werror
-include $(BUILD_NATIVE_TEST)
diff --git a/adf/libadfhwc/Android.bp b/adf/libadfhwc/Android.bp
new file mode 100644
index 0000000..86f0c9c
--- /dev/null
+++ b/adf/libadfhwc/Android.bp
@@ -0,0 +1,29 @@
+// Copyright (C) 2013 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.
+
+cc_library_static {
+    name: "libadfhwc",
+    srcs: ["adfhwc.cpp"],
+    static_libs: [
+        "libadf",
+        "liblog",
+        "libutils",
+    ],
+    cflags: [
+        "-DLOG_TAG=\\\"adfhwc\\\"",
+        "-Werror",
+    ],
+    local_include_dirs: ["include"],
+    export_include_dirs: ["include"],
+}
diff --git a/adf/libadfhwc/Android.mk b/adf/libadfhwc/Android.mk
deleted file mode 100644
index 898f9c9..0000000
--- a/adf/libadfhwc/Android.mk
+++ /dev/null
@@ -1,25 +0,0 @@
-# Copyright (C) 2013 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.
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := adfhwc.cpp
-LOCAL_MODULE := libadfhwc
-LOCAL_MODULE_TAGS := optional
-LOCAL_STATIC_LIBRARIES := libadf liblog libutils
-LOCAL_CFLAGS += -DLOG_TAG=\"adfhwc\" -Werror
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_C_INCLUDES += $(LOCAL_EXPORT_C_INCLUDE_DIRS)
-include $(BUILD_STATIC_LIBRARY)
diff --git a/base/Android.bp b/base/Android.bp
new file mode 100644
index 0000000..7bf4c79
--- /dev/null
+++ b/base/Android.bp
@@ -0,0 +1,96 @@
+//
+// 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.
+//
+
+libbase_cppflags = [
+    "-Wall",
+    "-Wextra",
+    "-Werror",
+]
+
+cc_library {
+    name: "libbase",
+    clang: true,
+    host_supported: true,
+    srcs: [
+        "file.cpp",
+        "logging.cpp",
+        "parsenetaddress.cpp",
+        "stringprintf.cpp",
+        "strings.cpp",
+        "test_utils.cpp",
+    ],
+    local_include_dirs: ["include"],
+    cppflags: libbase_cppflags,
+    export_include_dirs: ["include"],
+    shared_libs: ["liblog"],
+    target: {
+        android: {
+            srcs: ["errors_unix.cpp"],
+            cppflags: ["-Wexit-time-destructors"],
+        },
+        darwin: {
+            srcs: ["errors_unix.cpp"],
+            cppflags: ["-Wexit-time-destructors"],
+        },
+        linux: {
+            srcs: ["errors_unix.cpp"],
+            cppflags: ["-Wexit-time-destructors"],
+        },
+        windows: {
+            srcs: [
+                "errors_windows.cpp",
+                "utf8.cpp",
+            ],
+            enabled: true,
+        },
+    },
+}
+
+// Tests
+// ------------------------------------------------------------------------------
+cc_test {
+    name: "libbase_test",
+    host_supported: true,
+    clang: true,
+    srcs: [
+        "errors_test.cpp",
+        "file_test.cpp",
+        "logging_test.cpp",
+        "parseint_test.cpp",
+        "parsenetaddress_test.cpp",
+        "stringprintf_test.cpp",
+        "strings_test.cpp",
+        "test_main.cpp",
+    ],
+    target: {
+        windows: {
+            srcs: ["utf8_test.cpp"],
+            enabled: true,
+        },
+    },
+    local_include_dirs: ["."],
+    cppflags: libbase_cppflags,
+    shared_libs: ["libbase"],
+    compile_multilib: "both",
+    multilib: {
+        lib32: {
+            suffix: "32",
+        },
+        lib64: {
+            suffix: "64",
+        },
+    },
+}
diff --git a/base/Android.mk b/base/Android.mk
deleted file mode 100644
index 1693e74..0000000
--- a/base/Android.mk
+++ /dev/null
@@ -1,140 +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.
-#
-
-LOCAL_PATH := $(call my-dir)
-
-libbase_src_files := \
-    file.cpp \
-    logging.cpp \
-    parsenetaddress.cpp \
-    stringprintf.cpp \
-    strings.cpp \
-    test_utils.cpp \
-
-libbase_linux_src_files := \
-    errors_unix.cpp \
-
-libbase_darwin_src_files := \
-    errors_unix.cpp \
-
-libbase_windows_src_files := \
-    errors_windows.cpp \
-    utf8.cpp \
-
-libbase_test_src_files := \
-    errors_test.cpp \
-    file_test.cpp \
-    logging_test.cpp \
-    parseint_test.cpp \
-    parsenetaddress_test.cpp \
-    stringprintf_test.cpp \
-    strings_test.cpp \
-    test_main.cpp \
-
-libbase_test_windows_src_files := \
-    utf8_test.cpp \
-
-libbase_cppflags := \
-    -Wall \
-    -Wextra \
-    -Werror \
-
-libbase_linux_cppflags := \
-    -Wexit-time-destructors \
-
-libbase_darwin_cppflags := \
-    -Wexit-time-destructors \
-
-# Device
-# ------------------------------------------------------------------------------
-include $(CLEAR_VARS)
-LOCAL_MODULE := libbase
-LOCAL_CLANG := true
-LOCAL_SRC_FILES := $(libbase_src_files) $(libbase_linux_src_files)
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-LOCAL_CPPFLAGS := $(libbase_cppflags) $(libbase_linux_cppflags)
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_STATIC_LIBRARIES := liblog
-LOCAL_MULTILIB := both
-include $(BUILD_STATIC_LIBRARY)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := libbase
-LOCAL_CLANG := true
-LOCAL_WHOLE_STATIC_LIBRARIES := libbase
-LOCAL_SHARED_LIBRARIES := liblog
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_MULTILIB := both
-include $(BUILD_SHARED_LIBRARY)
-
-# Host
-# ------------------------------------------------------------------------------
-include $(CLEAR_VARS)
-LOCAL_MODULE := libbase
-LOCAL_SRC_FILES := $(libbase_src_files)
-LOCAL_SRC_FILES_darwin := $(libbase_darwin_src_files)
-LOCAL_SRC_FILES_linux := $(libbase_linux_src_files)
-LOCAL_SRC_FILES_windows := $(libbase_windows_src_files)
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-LOCAL_CPPFLAGS := $(libbase_cppflags)
-LOCAL_CPPFLAGS_darwin := $(libbase_darwin_cppflags)
-LOCAL_CPPFLAGS_linux := $(libbase_linux_cppflags)
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_STATIC_LIBRARIES := liblog
-LOCAL_MULTILIB := both
-LOCAL_MODULE_HOST_OS := darwin linux windows
-include $(BUILD_HOST_STATIC_LIBRARY)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := libbase
-LOCAL_WHOLE_STATIC_LIBRARIES := libbase
-LOCAL_SHARED_LIBRARIES := liblog
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_MULTILIB := both
-LOCAL_MODULE_HOST_OS := darwin linux windows
-include $(BUILD_HOST_SHARED_LIBRARY)
-
-# Tests
-# ------------------------------------------------------------------------------
-include $(CLEAR_VARS)
-LOCAL_MODULE := libbase_test
-LOCAL_CLANG := true
-LOCAL_SRC_FILES := $(libbase_test_src_files)
-LOCAL_SRC_FILES_darwin := $(libbase_test_darwin_src_files)
-LOCAL_SRC_FILES_linux := $(libbase_test_linux_src_files)
-LOCAL_SRC_FILES_windows := $(libbase_test_windows_src_files)
-LOCAL_C_INCLUDES := $(LOCAL_PATH)
-LOCAL_CPPFLAGS := $(libbase_cppflags)
-LOCAL_SHARED_LIBRARIES := libbase
-LOCAL_MULTILIB := both
-LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
-LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
-include $(BUILD_NATIVE_TEST)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := libbase_test
-LOCAL_MODULE_HOST_OS := darwin linux windows
-LOCAL_SRC_FILES := $(libbase_test_src_files)
-LOCAL_SRC_FILES_darwin := $(libbase_test_darwin_src_files)
-LOCAL_SRC_FILES_linux := $(libbase_test_linux_src_files)
-LOCAL_SRC_FILES_windows := $(libbase_test_windows_src_files)
-LOCAL_C_INCLUDES := $(LOCAL_PATH)
-LOCAL_CPPFLAGS := $(libbase_cppflags)
-LOCAL_SHARED_LIBRARIES := libbase
-LOCAL_MULTILIB := both
-LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
-LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
-include $(BUILD_HOST_NATIVE_TEST)
diff --git a/base/logging.cpp b/base/logging.cpp
index 1741871..769c266 100644
--- a/base/logging.cpp
+++ b/base/logging.cpp
@@ -21,6 +21,7 @@
 #include "android-base/logging.h"
 
 #include <libgen.h>
+#include <time.h>
 
 // For getprogname(3) or program_invocation_short_name.
 #if defined(__ANDROID__) || defined(__APPLE__)
@@ -186,12 +187,24 @@
 
 void StderrLogger(LogId, LogSeverity severity, const char*, const char* file,
                   unsigned int line, const char* message) {
+  struct tm now;
+  time_t t = time(nullptr);
+
+#if defined(_WIN32)
+  localtime_s(&now, &t);
+#else
+  localtime_r(&t, &now);
+#endif
+
+  char timestamp[32];
+  strftime(timestamp, sizeof(timestamp), "%m-%d %H:%M:%S", &now);
+
   static const char log_characters[] = "VDIWEF";
   static_assert(arraysize(log_characters) - 1 == FATAL + 1,
                 "Mismatch in size of log_characters and values in LogSeverity");
   char severity_char = log_characters[severity];
-  fprintf(stderr, "%s %c %5d %5d %s:%u] %s\n", ProgramInvocationName(),
-          severity_char, getpid(), GetThreadId(), file, line, message);
+  fprintf(stderr, "%s %c %s %5d %5d %s:%u] %s\n", ProgramInvocationName(),
+          severity_char, timestamp, getpid(), GetThreadId(), file, line, message);
 }
 
 
diff --git a/bootstat/boot_event_record_store.cpp b/bootstat/boot_event_record_store.cpp
index ef4f68e..346eada 100644
--- a/bootstat/boot_event_record_store.cpp
+++ b/bootstat/boot_event_record_store.cpp
@@ -25,6 +25,7 @@
 #include <utility>
 #include <android-base/file.h>
 #include <android-base/logging.h>
+#include <android-base/parseint.h>
 #include "histogram_logger.h"
 #include "uptime_parser.h"
 
@@ -57,8 +58,10 @@
 
   // Ignore existing bootstat records (which do not contain file content).
   if (!content.empty()) {
-    int32_t value = std::stoi(content);
-    bootstat::LogHistogram("bootstat_mtime_matches_content", value == *uptime);
+    int32_t value;
+    if (android::base::ParseInt(content.c_str(), &value)) {
+      bootstat::LogHistogram("bootstat_mtime_matches_content", value == *uptime);
+    }
   }
 
   return true;
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index 7c1b7f3..71a5a39 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -28,6 +28,7 @@
 #include <memory>
 #include <string>
 #include <android-base/logging.h>
+#include <android-base/parseint.h>
 #include <cutils/properties.h>
 #include <log/log.h>
 #include "boot_event_record_store.h"
@@ -56,8 +57,9 @@
   BootEventRecordStore boot_event_store;
   if (!value_str.empty()) {
     int32_t value = 0;
-    value = std::stoi(value_str);
-    boot_event_store.AddBootEventWithValue(event, value);
+    if (android::base::ParseInt(value_str.c_str(), &value)) {
+      boot_event_store.AddBootEventWithValue(event, value);
+    }
   } else {
     boot_event_store.AddBootEvent(event);
   }
@@ -187,7 +189,10 @@
   std::string boot_complete_prefix = "boot_complete";
 
   std::string build_date_str = GetProperty("ro.build.date.utc");
-  int32_t build_date = std::stoi(build_date_str);
+  int32_t build_date;
+  if (!android::base::ParseInt(build_date_str.c_str(), &build_date)) {
+    return std::string();
+  }
 
   BootEventRecordStore boot_event_store;
   BootEventRecordStore::BootEventRecord record;
@@ -223,6 +228,10 @@
   // ota_boot_complete.  The latter signifies that the device is booting after
   // a system update.
   std::string boot_complete_prefix = CalculateBootCompletePrefix();
+  if (boot_complete_prefix.empty()) {
+    // The system is hosed because the build date property could not be read.
+    return;
+  }
 
   // post_decrypt_time_elapsed is only logged on encrypted devices.
   if (boot_event_store.GetBootEvent("post_decrypt_time_elapsed", &record)) {
diff --git a/crash_reporter/crash_collector.cc b/crash_reporter/crash_collector.cc
index d993576..6e81c51 100644
--- a/crash_reporter/crash_collector.cc
+++ b/crash_reporter/crash_collector.cc
@@ -317,7 +317,7 @@
       continue;
 
     std::string filename(ent->d_name);
-    size_t last_dot = filename.rfind(".");
+    size_t last_dot = filename.rfind('.');
     std::string basename;
     // If there is a valid looking extension, use the base part of the
     // name.  If the only dot is the first byte (aka a dot file), treat
diff --git a/crash_reporter/kernel_collector_test.cc b/crash_reporter/kernel_collector_test.cc
index 60fd832..0f6b930 100644
--- a/crash_reporter/kernel_collector_test.cc
+++ b/crash_reporter/kernel_collector_test.cc
@@ -283,7 +283,7 @@
   pos += strlen(kNamePrefix);
   std::string filename = log.substr(pos, std::string::npos);
   // Take the name up until \n
-  size_t end_pos = filename.find_first_of("\n");
+  size_t end_pos = filename.find_first_of('\n');
   ASSERT_NE(std::string::npos, end_pos);
   filename = filename.substr(0, end_pos);
   ASSERT_EQ(0U, filename.find(test_crash_directory().value()));
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
new file mode 100644
index 0000000..8a63f3f
--- /dev/null
+++ b/debuggerd/Android.bp
@@ -0,0 +1,19 @@
+cc_library_static {
+  name: "libdebuggerd_client",
+  srcs: ["client/debuggerd_client.cpp"],
+
+  cflags: [
+    "-Wall",
+    "-Wextra",
+    "-Werror",
+    "-Os",
+  ],
+
+  local_include_dirs: ["include"],
+  export_include_dirs: ["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"],
+}
diff --git a/debuggerd/Android.mk b/debuggerd/Android.mk
index 9ce94c5..fdedb76 100644
--- a/debuggerd/Android.mk
+++ b/debuggerd/Android.mk
@@ -8,6 +8,10 @@
     -Wunused \
     -Werror \
 
+ifeq ($(TARGET_IS_64_BIT),true)
+common_cppflags += -DTARGET_IS_64_BIT
+endif
+
 include $(CLEAR_VARS)
 
 LOCAL_SRC_FILES:= \
@@ -26,15 +30,12 @@
 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
 
-ifeq ($(TARGET_IS_64_BIT),true)
-LOCAL_CPPFLAGS += -DTARGET_IS_64_BIT
-endif
-
 LOCAL_SHARED_LIBRARIES := \
     libbacktrace \
     libbase \
@@ -51,7 +52,7 @@
 
 include $(BUILD_EXECUTABLE)
 
-
+crasher_cppflags := $(common_cppflags) -fstack-protector-all -Wno-free-nonheap-object -Wno-date-time
 
 include $(CLEAR_VARS)
 LOCAL_SRC_FILES := crasher.cpp
@@ -63,8 +64,7 @@
 LOCAL_SRC_FILES_x86_64 := x86_64/crashglue.S
 LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
 LOCAL_MODULE_TAGS := optional
-LOCAL_CPPFLAGS := $(common_cppflags) -fstack-protector-all -Wno-free-nonheap-object -Wno-date-time
-#LOCAL_FORCE_STATIC_EXECUTABLE := true
+LOCAL_CPPFLAGS := $(crasher_cppflags)
 LOCAL_SHARED_LIBRARIES := libcutils liblog
 
 # The arm emulator has VFP but not VFPv3-D32.
@@ -79,6 +79,34 @@
 
 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 := libcutils 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_client libcutils liblog
+
+include $(BUILD_EXECUTABLE)
+
 debuggerd_test_src_files := \
     utility.cpp \
     test/dump_memory_test.cpp \
@@ -102,6 +130,9 @@
     -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)
 
diff --git a/debuggerd/client/debuggerd_client.cpp b/debuggerd/client/debuggerd_client.cpp
new file mode 100644
index 0000000..44e92fe
--- /dev/null
+++ b/debuggerd/client/debuggerd_client.cpp
@@ -0,0 +1,311 @@
+/*
+ * 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/client.h"
+
+#include <errno.h>
+#include <inttypes.h>
+#include <pthread.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 <unistd.h>
+
+#include "private/libc_logging.h"
+
+#if defined(TARGET_IS_64_BIT) && !defined(__LP64__)
+#define SOCKET_NAME "android:debuggerd32"
+#else
+#define SOCKET_NAME "android:debuggerd"
+#endif
+
+// see man(2) prctl, specifically the section about PR_GET_NAME
+#define MAX_TASK_NAME_LEN (16)
+
+static debuggerd_callbacks_t g_callbacks;
+
+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 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));
+    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;
+}
+
+static void send_debuggerd_packet(siginfo_t* info) {
+  // 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));
+    }
+    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 = gettid();
+  msg.abort_msg_address = 0;
+
+  if (g_callbacks.get_abort_message) {
+    msg.abort_msg_address = reinterpret_cast<uintptr_t>(g_callbacks.get_abort_message());
+  }
+
+  msg.original_si_code = (info != nullptr) ? info->si_code : 0;
+  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));
+    int saved_errno = errno;
+    if (g_callbacks.post_dump) {
+      g_callbacks.post_dump();
+    }
+    errno = saved_errno;
+  } else {
+    // read or write failed -- broken connection?
+    __libc_format_log(ANDROID_LOG_FATAL, "libc", "Failed while talking to debuggerd: %s",
+                      strerror(errno));
+  }
+
+  close(s);
+}
+
+/*
+ * 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;
+  }
+
+  log_signal_summary(signal_number, info);
+
+  send_debuggerd_packet(info);
+
+  // 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.
+  }
+
+  int rc = syscall(SYS_rt_tgsigqueueinfo, getpid(), gettid(), signal_number, info);
+  if (rc != 0) {
+    __libc_format_log(ANDROID_LOG_FATAL, "libc", "failed to resend signal during crash: %s",
+                      strerror(errno));
+    _exit(0);
+  }
+}
+
+void debuggerd_init(debuggerd_callbacks_t* callbacks) {
+  if (callbacks) {
+    g_callbacks = *callbacks;
+  }
+
+  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);
+}
diff --git a/debuggerd/crasher.cpp b/debuggerd/crasher.cpp
index bdeaf0b..a37df33 100644
--- a/debuggerd/crasher.cpp
+++ b/debuggerd/crasher.cpp
@@ -16,6 +16,10 @@
 #include <cutils/sockets.h>
 #include <log/log.h>
 
+#if defined(STATIC_CRASHER)
+#include "debuggerd/client.h"
+#endif
+
 #ifndef __unused
 #define __unused __attribute__((__unused__))
 #endif
@@ -43,7 +47,7 @@
 // Assign local array address to global variable to force stack guards.
 // Use another noinline function to corrupt the stack.
 __attribute__ ((noinline)) static int smash_stack(volatile int* plen) {
-    printf("crasher: deliberately corrupting stack...\n");
+    printf("%s: deliberately corrupting stack...\n", __progname);
 
     char buf[128];
     smash_stack_dummy_buf = buf;
@@ -135,7 +139,7 @@
 
 static int do_action(const char* arg)
 {
-    fprintf(stderr,"crasher: init pid=%d tid=%d\n", getpid(), gettid());
+    fprintf(stderr, "%s: init pid=%d tid=%d\n", __progname, getpid(), gettid());
 
     if (!strncmp(arg, "thread-", strlen("thread-"))) {
         return do_action_on_thread(arg + strlen("thread-"));
@@ -209,9 +213,26 @@
 
 int main(int argc, char **argv)
 {
-    fprintf(stderr,"crasher: built at " __TIME__ "!@\n");
+    fprintf(stderr, "%s: built at " __TIME__ "!@\n", __progname);
 
-    if(argc > 1) {
+#if defined(STATIC_CRASHER)
+    debuggerd_callbacks_t callbacks = {
+      .get_abort_message = []() {
+        static struct {
+          size_t size;
+          char msg[32];
+        } msg;
+
+        msg.size = strlen("dummy abort message");
+        memcpy(msg.msg, "dummy abort message", strlen("dummy abort message"));
+        return reinterpret_cast<abort_msg_t*>(&msg);
+      },
+      .post_dump = nullptr
+    };
+    debuggerd_init(&callbacks);
+#endif
+
+    if (argc > 1) {
         return do_action(argv[1]);
     } else {
         crash1();
diff --git a/debuggerd/debuggerd.cpp b/debuggerd/debuggerd.cpp
index d87594c..a82fd07 100644
--- a/debuggerd/debuggerd.cpp
+++ b/debuggerd/debuggerd.cpp
@@ -562,7 +562,7 @@
   // 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 bionic/libc/linker/debugger.c, in function
+  // See details in client/debuggerd_client.cpp, in function
   // debugger_signal_handler().
 
   // Attach to the target process.
diff --git a/debuggerd/include/debuggerd/client.h b/debuggerd/include/debuggerd/client.h
new file mode 100644
index 0000000..41b7b3a
--- /dev/null
+++ b/debuggerd/include/debuggerd/client.h
@@ -0,0 +1,59 @@
+/*
+ * 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>
+#include <sys/cdefs.h>
+#include <sys/types.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"
+
+// Used only on 64-bit devices for debuggerd32.
+#define DEBUGGER32_SOCKET_NAME "android:debuggerd32"
+
+__BEGIN_DECLS
+
+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;
+  uint64_t abort_msg_address;
+  int32_t original_si_code;
+} 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
diff --git a/debuggerd/tombstone.cpp b/debuggerd/tombstone.cpp
index fa983fa..dfdf29c 100644
--- a/debuggerd/tombstone.cpp
+++ b/debuggerd/tombstone.cpp
@@ -368,6 +368,7 @@
     ALOGE("Cannot get siginfo for %d: %s\n", tid, strerror(errno));
   }
 
+  ScopedBacktraceMapIteratorLock lock(map);
   _LOG(log, logtype::MAPS, "\n");
   if (!print_fault_address_marker) {
     _LOG(log, logtype::MAPS, "memory map:\n");
diff --git a/fastboot/engine.cpp b/fastboot/engine.cpp
index db5d0e0..728dcb8 100644
--- a/fastboot/engine.cpp
+++ b/fastboot/engine.cpp
@@ -38,8 +38,6 @@
 #include <sys/types.h>
 #include <unistd.h>
 
-#define ARRAY_SIZE(x)           (sizeof(x)/sizeof(x[0]))
-
 #define OP_DOWNLOAD   1
 #define OP_COMMAND    2
 #define OP_QUERY      3
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 1839d25..665b9d0 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -47,6 +47,7 @@
 #include <utility>
 #include <vector>
 
+#include <android-base/macros.h>
 #include <android-base/parseint.h>
 #include <android-base/parsenetaddress.h>
 #include <android-base/stringprintf.h>
@@ -67,8 +68,6 @@
 #define O_BINARY 0
 #endif
 
-#define ARRAY_SIZE(a) (sizeof(a)/sizeof(*(a)))
-
 char cur_product[FB_RESPONSE_SZ + 1];
 
 static const char* serial = nullptr;
@@ -926,7 +925,7 @@
 
     setup_requirements(reinterpret_cast<char*>(data), sz);
 
-    for (size_t i = 0; i < ARRAY_SIZE(images); ++i) {
+    for (size_t i = 0; i < arraysize(images); ++i) {
         int fd = unzip_to_file(zip, images[i].img_name);
         if (fd == -1) {
             if (images[i].is_optional) {
@@ -988,7 +987,7 @@
 
     setup_requirements(reinterpret_cast<char*>(data), sz);
 
-    for (size_t i = 0; i < ARRAY_SIZE(images); i++) {
+    for (size_t i = 0; i < arraysize(images); i++) {
         fname = find_item(images[i].part_name, product);
         fastboot_buffer buf;
         if (!load_buf(transport, fname.c_str(), &buf)) {
diff --git a/fs_mgr/fs_mgr.c b/fs_mgr/fs_mgr.c
index b3e65cb..1b3893f 100644
--- a/fs_mgr/fs_mgr.c
+++ b/fs_mgr/fs_mgr.c
@@ -586,6 +586,7 @@
 
         /* mount(2) returned an error, handle the encryptable/formattable case */
         bool wiped = partition_wiped(fstab->recs[top_idx].blk_device);
+        bool crypt_footer = false;
         if (mret && mount_errno != EBUSY && mount_errno != EACCES &&
             fs_mgr_is_formattable(&fstab->recs[top_idx]) && wiped) {
             /* top_idx and attempted_idx point at the same partition, but sometimes
@@ -606,8 +607,11 @@
                     ERROR("%s(): %s wouldn't open (%s)\n", __func__,
                           fstab->recs[top_idx].key_loc, strerror(errno));
                 }
+            } else if (fs_mgr_is_encryptable(&fstab->recs[top_idx]) &&
+                !strcmp(fstab->recs[top_idx].key_loc, KEY_IN_FOOTER)) {
+                crypt_footer = true;
             }
-            if (fs_mgr_do_format(&fstab->recs[top_idx]) == 0) {
+            if (fs_mgr_do_format(&fstab->recs[top_idx], crypt_footer) == 0) {
                 /* Let's replay the mount actions. */
                 i = top_idx - 1;
                 continue;
diff --git a/fs_mgr/fs_mgr_format.c b/fs_mgr/fs_mgr_format.c
index 6c5b1eb..5011b08 100644
--- a/fs_mgr/fs_mgr_format.c
+++ b/fs_mgr/fs_mgr_format.c
@@ -32,11 +32,12 @@
 #include "ext4.h"
 #include "make_ext4fs.h"
 #include "fs_mgr_priv.h"
+#include "cryptfs.h"
 
 extern struct fs_info info;     /* magic global from ext4_utils */
 extern void reset_ext4fs_info();
 
-static int format_ext4(char *fs_blkdev, char *fs_mnt_point)
+static int format_ext4(char *fs_blkdev, char *fs_mnt_point, bool crypt_footer)
 {
     uint64_t dev_sz;
     int fd, rc = 0;
@@ -63,6 +64,9 @@
     /* Format the partition using the calculated length */
     reset_ext4fs_info();
     info.len = (off64_t)dev_sz;
+    if (crypt_footer) {
+        info.len -= CRYPT_FOOTER_OFFSET;
+    }
 
     /* Use make_ext4fs_internal to avoid wiping an already-wiped partition. */
     rc = make_ext4fs_internal(fd, NULL, NULL, fs_mnt_point, 0, 0, 0, 0, 0, 0, sehandle, 0, 0, NULL);
@@ -118,7 +122,7 @@
     return rc;
 }
 
-int fs_mgr_do_format(struct fstab_rec *fstab)
+int fs_mgr_do_format(struct fstab_rec *fstab, bool crypt_footer)
 {
     int rc = -EINVAL;
 
@@ -127,7 +131,7 @@
     if (!strncmp(fstab->fs_type, "f2fs", 4)) {
         rc = format_f2fs(fstab->blk_device);
     } else if (!strncmp(fstab->fs_type, "ext4", 4)) {
-        rc = format_ext4(fstab->blk_device, fstab->mount_point);
+        rc = format_ext4(fstab->blk_device, fstab->mount_point, crypt_footer);
     } else {
         ERROR("File system type '%s' is not supported\n", fstab->fs_type);
     }
diff --git a/fs_mgr/fs_mgr_main.c b/fs_mgr/fs_mgr_main.c
index e5a00d5..b3f131e 100644
--- a/fs_mgr/fs_mgr_main.c
+++ b/fs_mgr/fs_mgr_main.c
@@ -85,7 +85,6 @@
     char *fstab_file=NULL;
     struct fstab *fstab=NULL;
 
-    klog_init();
     klog_set_level(6);
 
     parse_options(argc, argv, &a_flag, &u_flag, &n_flag, &n_name, &n_blk_dev);
@@ -111,4 +110,3 @@
     /* Should not get here */
     exit(1);
 }
-
diff --git a/fs_mgr/fs_mgr_slotselect.c b/fs_mgr/fs_mgr_slotselect.c
index ca07b18..0f59115 100644
--- a/fs_mgr/fs_mgr_slotselect.c
+++ b/fs_mgr/fs_mgr_slotselect.c
@@ -41,7 +41,7 @@
     int n;
     int misc_fd;
     ssize_t num_read;
-    struct bootloader_message msg;
+    struct bootloader_message_ab msg;
 
     misc_fd = -1;
     for (n = 0; n < fstab->num_entries; n++) {
diff --git a/fs_mgr/fs_mgr_verity.cpp b/fs_mgr/fs_mgr_verity.cpp
index 72554a8..67104cc 100644
--- a/fs_mgr/fs_mgr_verity.cpp
+++ b/fs_mgr/fs_mgr_verity.cpp
@@ -91,14 +91,12 @@
     FILE* f = fopen(path, "r");
     if (!f) {
         ERROR("Can't open '%s'\n", path);
-        free(key_data);
         return NULL;
     }
 
     if (!fread(key_data, sizeof(key_data), 1, f)) {
         ERROR("Could not read key!\n");
         fclose(f);
-        free(key_data);
         return NULL;
     }
 
@@ -107,7 +105,6 @@
     RSA* key = NULL;
     if (!android_pubkey_decode(key_data, sizeof(key_data), &key)) {
         ERROR("Could not parse key!\n");
-        free(key_data);
         return NULL;
     }
 
@@ -145,6 +142,18 @@
     return retval;
 }
 
+static int verify_verity_signature(const struct fec_verity_metadata& verity)
+{
+    if (verify_table(verity.signature, sizeof(verity.signature),
+            verity.table, verity.table_length) == 0 ||
+        verify_table(verity.ecc_signature, sizeof(verity.ecc_signature),
+            verity.table, verity.table_length) == 0) {
+        return 0;
+    }
+
+    return -1;
+}
+
 static int invalidate_table(char *table, size_t table_length)
 {
     size_t n = 0;
@@ -849,7 +858,7 @@
     std::string result, word;
     auto tokens = android::base::Split(*table, " ");
 
-    for (const auto token : tokens) {
+    for (const auto& token : tokens) {
         if (android::base::StartsWith(token, "/dev/block/") &&
             android::base::StartsWith(blk_device, token.c_str())) {
             word = blk_device;
@@ -950,8 +959,7 @@
     }
 
     // verify the signature on the table
-    if (verify_table(verity.signature, sizeof(verity.signature), params.table,
-            verity.table_length) < 0) {
+    if (verify_verity_signature(verity) < 0) {
         if (params.mode == VERITY_MODE_LOGGING) {
             // the user has been warned, allow mounting without dm-verity
             retval = FS_MGR_SETUP_VERITY_SUCCESS;
diff --git a/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h
index b498618..3c27ede 100644
--- a/fs_mgr/include/fs_mgr.h
+++ b/fs_mgr/include/fs_mgr.h
@@ -18,6 +18,7 @@
 #define __CORE_FS_MGR_H
 
 #include <stdint.h>
+#include <stdbool.h>
 #include <linux/dm-ioctl.h>
 
 // Magic number at start of verity metadata
@@ -108,7 +109,7 @@
 int fs_mgr_is_nofail(struct fstab_rec *fstab);
 int fs_mgr_swapon_all(struct fstab *fstab);
 
-int fs_mgr_do_format(struct fstab_rec *fstab);
+int fs_mgr_do_format(struct fstab_rec *fstab, bool reserve_footer);
 
 #ifdef __cplusplus
 }
diff --git a/healthd/Android.mk b/healthd/Android.mk
index 127f39e..deebed5 100644
--- a/healthd/Android.mk
+++ b/healthd/Android.mk
@@ -17,7 +17,7 @@
 LOCAL_MODULE := libbatterymonitor
 LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
 LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_STATIC_LIBRARIES := libutils libbinder
+LOCAL_STATIC_LIBRARIES := libutils libbase libbinder
 include $(BUILD_STATIC_LIBRARY)
 
 include $(CLEAR_VARS)
@@ -60,7 +60,7 @@
 
 LOCAL_C_INCLUDES := bootable/recovery $(LOCAL_PATH)/include
 
-LOCAL_STATIC_LIBRARIES := libbatterymonitor libbatteryservice libbinder
+LOCAL_STATIC_LIBRARIES := libbatterymonitor libbatteryservice libbinder libbase
 
 ifneq ($(strip $(LOCAL_CHARGER_NO_UI)),true)
 LOCAL_STATIC_LIBRARIES += libminui libpng libz
diff --git a/healthd/BatteryMonitor.cpp b/healthd/BatteryMonitor.cpp
index 69647de..d1c547d 100644
--- a/healthd/BatteryMonitor.cpp
+++ b/healthd/BatteryMonitor.cpp
@@ -28,6 +28,8 @@
 #include <unistd.h>
 #include <memory>
 
+#include <android-base/file.h>
+#include <android-base/strings.h>
 #include <batteryservice/BatteryService.h>
 #include <cutils/klog.h>
 #include <cutils/properties.h>
@@ -121,34 +123,15 @@
     return ret;
 }
 
-int BatteryMonitor::readFromFile(const String8& path, char* buf, size_t size) {
-    char *cp = NULL;
-
-    if (path.isEmpty())
-        return -1;
-    int fd = open(path.string(), O_RDONLY, 0);
-    if (fd == -1) {
-        KLOG_ERROR(LOG_TAG, "Could not open '%s'\n", path.string());
-        return -1;
+int BatteryMonitor::readFromFile(const String8& path, std::string* buf) {
+    if (android::base::ReadFileToString(String8::std_string(path), buf)) {
+        *buf = android::base::Trim(*buf);
     }
-
-    ssize_t count = TEMP_FAILURE_RETRY(read(fd, buf, size));
-    if (count > 0)
-            cp = (char *)memrchr(buf, '\n', count);
-
-    if (cp)
-        *cp = '\0';
-    else
-        buf[0] = '\0';
-
-    close(fd);
-    return count;
+    return buf->length();
 }
 
 BatteryMonitor::PowerSupplyType BatteryMonitor::readPowerSupplyType(const String8& path) {
-    const int SIZE = 128;
-    char buf[SIZE];
-    int length = readFromFile(path, buf, SIZE);
+    std::string buf;
     BatteryMonitor::PowerSupplyType ret;
     struct sysfsStringEnumMap supplyTypeMap[] = {
             { "Unknown", ANDROID_POWER_SUPPLY_TYPE_UNKNOWN },
@@ -167,12 +150,12 @@
             { NULL, 0 },
     };
 
-    if (length <= 0)
+    if (readFromFile(path, &buf) <= 0)
         return ANDROID_POWER_SUPPLY_TYPE_UNKNOWN;
 
-    ret = (BatteryMonitor::PowerSupplyType)mapSysfsString(buf, supplyTypeMap);
+    ret = (BatteryMonitor::PowerSupplyType)mapSysfsString(buf.c_str(), supplyTypeMap);
     if (ret < 0) {
-        KLOG_WARNING(LOG_TAG, "Unknown power supply type '%s'\n", buf);
+        KLOG_WARNING(LOG_TAG, "Unknown power supply type '%s'\n", buf.c_str());
         ret = ANDROID_POWER_SUPPLY_TYPE_UNKNOWN;
     }
 
@@ -180,27 +163,23 @@
 }
 
 bool BatteryMonitor::getBooleanField(const String8& path) {
-    const int SIZE = 16;
-    char buf[SIZE];
-
+    std::string buf;
     bool value = false;
-    if (readFromFile(path, buf, SIZE) > 0) {
-        if (buf[0] != '0') {
+
+    if (readFromFile(path, &buf) > 0)
+        if (buf[0] != '0')
             value = true;
-        }
-    }
 
     return value;
 }
 
 int BatteryMonitor::getIntField(const String8& path) {
-    const int SIZE = 128;
-    char buf[SIZE];
-
+    std::string buf;
     int value = 0;
-    if (readFromFile(path, buf, SIZE) > 0) {
-        value = strtol(buf, NULL, 0);
-    }
+
+    if (readFromFile(path, &buf) > 0)
+        value = std::stoi(buf.c_str(), NULL, 0);
+
     return value;
 }
 
@@ -241,18 +220,16 @@
         props.batteryHealth = BATTERY_HEALTH_GOOD;
     }
 
-    const int SIZE = 128;
-    char buf[SIZE];
-    String8 btech;
+    std::string buf;
 
-    if (readFromFile(mHealthdConfig->batteryStatusPath, buf, SIZE) > 0)
-        props.batteryStatus = getBatteryStatus(buf);
+    if (readFromFile(mHealthdConfig->batteryStatusPath, &buf) > 0)
+        props.batteryStatus = getBatteryStatus(buf.c_str());
 
-    if (readFromFile(mHealthdConfig->batteryHealthPath, buf, SIZE) > 0)
-        props.batteryHealth = getBatteryHealth(buf);
+    if (readFromFile(mHealthdConfig->batteryHealthPath, &buf) > 0)
+        props.batteryHealth = getBatteryHealth(buf.c_str());
 
-    if (readFromFile(mHealthdConfig->batteryTechnologyPath, buf, SIZE) > 0)
-        props.batteryTechnology = String8(buf);
+    if (readFromFile(mHealthdConfig->batteryTechnologyPath, &buf) > 0)
+        props.batteryTechnology = String8(buf.c_str());
 
     unsigned int i;
 
@@ -261,33 +238,31 @@
         path.appendFormat("%s/%s/online", POWER_SUPPLY_SYSFS_PATH,
                           mChargerNames[i].string());
 
-        if (readFromFile(path, buf, SIZE) > 0) {
-            if (buf[0] != '0') {
-                path.clear();
-                path.appendFormat("%s/%s/type", POWER_SUPPLY_SYSFS_PATH,
-                                  mChargerNames[i].string());
-                switch(readPowerSupplyType(path)) {
-                case ANDROID_POWER_SUPPLY_TYPE_AC:
-                    props.chargerAcOnline = true;
-                    break;
-                case ANDROID_POWER_SUPPLY_TYPE_USB:
-                    props.chargerUsbOnline = true;
-                    break;
-                case ANDROID_POWER_SUPPLY_TYPE_WIRELESS:
-                    props.chargerWirelessOnline = true;
-                    break;
-                default:
-                    KLOG_WARNING(LOG_TAG, "%s: Unknown power supply type\n",
-                                 mChargerNames[i].string());
-                }
-                path.clear();
-                path.appendFormat("%s/%s/current_max", POWER_SUPPLY_SYSFS_PATH,
-                                  mChargerNames[i].string());
-                if (access(path.string(), R_OK) == 0) {
-                    int maxChargingCurrent = getIntField(path);
-                    if (props.maxChargingCurrent < maxChargingCurrent) {
-                        props.maxChargingCurrent = maxChargingCurrent;
-                    }
+        if (getIntField(path)) {
+            path.clear();
+            path.appendFormat("%s/%s/type", POWER_SUPPLY_SYSFS_PATH,
+                              mChargerNames[i].string());
+            switch(readPowerSupplyType(path)) {
+            case ANDROID_POWER_SUPPLY_TYPE_AC:
+                props.chargerAcOnline = true;
+                break;
+            case ANDROID_POWER_SUPPLY_TYPE_USB:
+                props.chargerUsbOnline = true;
+                break;
+            case ANDROID_POWER_SUPPLY_TYPE_WIRELESS:
+                props.chargerWirelessOnline = true;
+                break;
+            default:
+                KLOG_WARNING(LOG_TAG, "%s: Unknown power supply type\n",
+                             mChargerNames[i].string());
+            }
+            path.clear();
+            path.appendFormat("%s/%s/current_max", POWER_SUPPLY_SYSFS_PATH,
+                              mChargerNames[i].string());
+            if (access(path.string(), R_OK) == 0) {
+                int maxChargingCurrent = getIntField(path);
+                if (props.maxChargingCurrent < maxChargingCurrent) {
+                    props.maxChargingCurrent = maxChargingCurrent;
                 }
             }
         }
@@ -343,10 +318,9 @@
 int BatteryMonitor::getChargeStatus() {
     int result = BATTERY_STATUS_UNKNOWN;
     if (!mHealthdConfig->batteryStatusPath.isEmpty()) {
-        char buf[128];
-        if (readFromFile(mHealthdConfig->batteryStatusPath, buf, sizeof(buf)) > 0) {
-            result = getBatteryStatus(buf);
-        }
+        std::string buf;
+        if (readFromFile(mHealthdConfig->batteryStatusPath, &buf) > 0)
+            result = getBatteryStatus(buf.c_str());
     }
     return result;
 }
diff --git a/healthd/BatteryPropertiesRegistrar.cpp b/healthd/BatteryPropertiesRegistrar.cpp
index 5d1fa52..d28ba41 100644
--- a/healthd/BatteryPropertiesRegistrar.cpp
+++ b/healthd/BatteryPropertiesRegistrar.cpp
@@ -35,7 +35,7 @@
     defaultServiceManager()->addService(String16("batteryproperties"), service);
 }
 
-void BatteryPropertiesRegistrar::notifyListeners(struct BatteryProperties props) {
+void BatteryPropertiesRegistrar::notifyListeners(const struct BatteryProperties& props) {
     Mutex::Autolock _l(mRegistrationLock);
     for (size_t i = 0; i < mListeners.size(); i++) {
         mListeners[i]->batteryPropertiesChanged(props);
diff --git a/healthd/BatteryPropertiesRegistrar.h b/healthd/BatteryPropertiesRegistrar.h
index d17e4a3..095f3d3 100644
--- a/healthd/BatteryPropertiesRegistrar.h
+++ b/healthd/BatteryPropertiesRegistrar.h
@@ -31,7 +31,7 @@
                                    public IBinder::DeathRecipient {
 public:
     void publish(const sp<BatteryPropertiesRegistrar>& service);
-    void notifyListeners(struct BatteryProperties props);
+    void notifyListeners(const struct BatteryProperties& props);
 
 private:
     Mutex mRegistrationLock;
diff --git a/healthd/healthd_mode_charger.cpp b/healthd/healthd_mode_charger.cpp
index 5846626..612885b 100644
--- a/healthd/healthd_mode_charger.cpp
+++ b/healthd/healthd_mode_charger.cpp
@@ -58,7 +58,7 @@
 #define min(a,b) ((a) < (b) ? (a) : (b))
 #endif
 
-#define ARRAY_SIZE(x)           (sizeof(x)/sizeof(x[0]))
+#define ARRAY_SIZE(x)           (sizeof(x)/sizeof((x)[0]))
 
 #define MSEC_PER_SEC            (1000LL)
 #define NSEC_PER_MSEC           (1000000LL)
diff --git a/healthd/include/healthd/BatteryMonitor.h b/healthd/include/healthd/BatteryMonitor.h
index 440f2e4..8865a7d 100644
--- a/healthd/include/healthd/BatteryMonitor.h
+++ b/healthd/include/healthd/BatteryMonitor.h
@@ -55,7 +55,7 @@
 
     int getBatteryStatus(const char* status);
     int getBatteryHealth(const char* status);
-    int readFromFile(const String8& path, char* buf, size_t size);
+    int readFromFile(const String8& path, std::string* buf);
     PowerSupplyType readPowerSupplyType(const String8& path);
     bool getBooleanField(const String8& path);
     int getIntField(const String8& path);
diff --git a/include/android/log.h b/include/android/log.h
index 1c171b7..2956e6e 100644
--- a/include/android/log.h
+++ b/include/android/log.h
@@ -124,7 +124,7 @@
 void __android_log_assert(const char *cond, const char *tag,
                           const char *fmt, ...)
 #if defined(__GNUC__)
-    __attribute__ ((noreturn))
+    __attribute__ ((__noreturn__))
 #ifdef __USE_MINGW_ANSI_STDIO
 #if __USE_MINGW_ANSI_STDIO
     __attribute__ ((format(gnu_printf, 3, 4)))
diff --git a/include/backtrace/BacktraceMap.h b/include/backtrace/BacktraceMap.h
index 2373c45..b80045f 100644
--- a/include/backtrace/BacktraceMap.h
+++ b/include/backtrace/BacktraceMap.h
@@ -71,6 +71,12 @@
   bool IsWritable(uintptr_t pc) { return GetFlags(pc) & PROT_WRITE; }
   bool IsExecutable(uintptr_t pc) { return GetFlags(pc) & PROT_EXEC; }
 
+  // In order to use the iterators on this object, a caller must
+  // call the LockIterator and UnlockIterator function to guarantee
+  // that the data does not change while it's being used.
+  virtual void LockIterator() {}
+  virtual void UnlockIterator() {}
+
   typedef std::deque<backtrace_map_t>::iterator iterator;
   iterator begin() { return maps_.begin(); }
   iterator end() { return maps_.end(); }
@@ -102,4 +108,18 @@
   pid_t pid_;
 };
 
+class ScopedBacktraceMapIteratorLock {
+public:
+  explicit ScopedBacktraceMapIteratorLock(BacktraceMap* map) : map_(map) {
+    map->LockIterator();
+  }
+
+  ~ScopedBacktraceMapIteratorLock() {
+    map_->UnlockIterator();
+  }
+
+private:
+  BacktraceMap* map_;
+};
+
 #endif // _BACKTRACE_BACKTRACE_MAP_H
diff --git a/include/cutils/debugger.h b/include/cutils/debugger.h
index 285e1af..20e8796 100644
--- a/include/cutils/debugger.h
+++ b/include/cutils/debugger.h
@@ -20,32 +20,10 @@
 #include <sys/cdefs.h>
 #include <sys/types.h>
 
+#include "debuggerd/client.h"
+
 __BEGIN_DECLS
 
-#define DEBUGGER_SOCKET_NAME "android:debuggerd"
-#define DEBUGGER32_SOCKET_NAME "android:debuggerd32"
-#define DEBUGGER64_SOCKET_NAME DEBUGGER_SOCKET_NAME
-
-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.
-// NOTE: Any changes to this structure must also be reflected in
-//       bionic/linker/debugger.cpp.
-typedef struct __attribute__((packed)) {
-    int32_t action;
-    pid_t tid;
-    uint64_t abort_msg_address;
-    int32_t original_si_code;
-} debugger_msg_t;
-
 /* 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.
diff --git a/include/cutils/klog.h b/include/cutils/klog.h
index 295d62b..c837edb 100644
--- a/include/cutils/klog.h
+++ b/include/cutils/klog.h
@@ -23,10 +23,8 @@
 
 __BEGIN_DECLS
 
-void klog_init(void);
 int  klog_get_level(void);
 void klog_set_level(int level);
-/* TODO: void klog_close(void); - and make klog_fd users thread safe. */
 
 void klog_write(int level, const char *fmt, ...)
     __attribute__ ((format(printf, 2, 3)));
diff --git a/include/log/logprint.h b/include/log/logprint.h
index 539d1dc..1bc1f72 100644
--- a/include/log/logprint.h
+++ b/include/log/logprint.h
@@ -27,6 +27,7 @@
 #endif
 
 typedef enum {
+    /* Verbs */
     FORMAT_OFF = 0,
     FORMAT_BRIEF,
     FORMAT_PROCESS,
@@ -36,7 +37,7 @@
     FORMAT_TIME,
     FORMAT_THREADTIME,
     FORMAT_LONG,
-    /* The following are modifiers to above formats */
+    /* Adverbs. The following are modifiers to above format verbs */
     FORMAT_MODIFIER_COLOR,     /* converts priority to color */
     FORMAT_MODIFIER_TIME_USEC, /* switches from msec to usec time precision */
     FORMAT_MODIFIER_PRINTABLE, /* converts non-printable to printable escapes */
diff --git a/include/private/android_filesystem_config.h b/include/private/android_filesystem_config.h
index e540de2..bc1c0ca 100644
--- a/include/private/android_filesystem_config.h
+++ b/include/private/android_filesystem_config.h
@@ -92,6 +92,9 @@
 #define AID_FIREWALL      1048  /* firewalld process */
 #define AID_TRUNKS        1049  /* trunksd process (TPM daemon) */
 #define AID_NVRAM         1050  /* Access-controlled NVRAM */
+#define AID_DNS           1051  /* DNS resolution daemon (system: netd) */
+#define AID_DNS_TETHER    1052  /* DNS resolution daemon (tether: dnsmasq) */
+#define AID_WEBVIEW_ZYGOTE 1053 /* WebView zygote process */
 /* Changes to this file must be made in AOSP, *not* in internal branches. */
 
 #define AID_SHELL         2000  /* adb and debug shell user */
@@ -203,6 +206,9 @@
     { "firewall",      AID_FIREWALL, },
     { "trunks",        AID_TRUNKS, },
     { "nvram",         AID_NVRAM, },
+    { "dns",           AID_DNS, },
+    { "dns_tether",    AID_DNS_TETHER, },
+    { "webview_zygote", AID_WEBVIEW_ZYGOTE, },
 
     { "shell",         AID_SHELL, },
     { "cache",         AID_CACHE, },
diff --git a/include/system/qemu_pipe.h b/include/system/qemu_pipe.h
new file mode 100644
index 0000000..d403f8d
--- /dev/null
+++ b/include/system/qemu_pipe.h
@@ -0,0 +1,134 @@
+/*
+ * 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 ANDROID_INCLUDE_SYSTEM_QEMU_PIPE_H
+#define ANDROID_INCLUDE_SYSTEM_QEMU_PIPE_H
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+
+// Define QEMU_PIPE_DEBUG if you want to print error messages when an error
+// occurs during pipe operations. The macro should simply take a printf-style
+// formatting string followed by optional arguments.
+#ifndef QEMU_PIPE_DEBUG
+#  define  QEMU_PIPE_DEBUG(...)   (void)0
+#endif
+
+// Try to open a new Qemu fast-pipe. This function returns a file descriptor
+// that can be used to communicate with a named service managed by the
+// emulator.
+//
+// This file descriptor can be used as a standard pipe/socket descriptor.
+//
+// 'pipeName' is the name of the emulator service you want to connect to,
+// and must begin with 'pipe:' (e.g. 'pipe:camera' or 'pipe:opengles').
+//
+// On success, return a valid file descriptor, or -1/errno on failure. E.g.:
+//
+// EINVAL  -> unknown/unsupported pipeName
+// ENOSYS  -> fast pipes not available in this system.
+//
+// ENOSYS should never happen, except if you're trying to run within a
+// misconfigured emulator.
+//
+// You should be able to open several pipes to the same pipe service,
+// except for a few special cases (e.g. GSM modem), where EBUSY will be
+// returned if more than one client tries to connect to it.
+static __inline__ int qemu_pipe_open(const char* pipeName) {
+    // Sanity check.
+    if (!pipeName || memcmp(pipeName, "pipe:", 5) != 0) {
+        errno = EINVAL;
+        return -1;
+    }
+
+    int fd = TEMP_FAILURE_RETRY(open("/dev/qemu_pipe", O_RDWR));
+    if (fd < 0) {
+        QEMU_PIPE_DEBUG("%s: Could not open /dev/qemu_pipe: %s", __FUNCTION__,
+                        strerror(errno));
+        return -1;
+    }
+
+    // Write the pipe name, *including* the trailing zero which is necessary.
+    size_t pipeNameLen = strlen(pipeName);
+    ssize_t ret = TEMP_FAILURE_RETRY(write(fd, pipeName, pipeNameLen + 1U));
+    if (ret != (ssize_t)pipeNameLen + 1) {
+        QEMU_PIPE_DEBUG("%s: Could not connect to %s pipe service: %s",
+                        __FUNCTION__, pipeName, strerror(errno));
+        if (ret == 0) {
+            errno = ECONNRESET;
+        } else if (ret > 0) {
+            errno = EINVAL;
+        }
+        return -1;
+    }
+    return fd;
+}
+
+// Send a framed message |buff| of |len| bytes through the |fd| descriptor.
+// This really adds a 4-hexchar prefix describing the payload size.
+// Returns 0 on success, and -1 on error.
+static int __inline__ qemu_pipe_frame_send(int fd,
+                                           const void* buff,
+                                           size_t len) {
+    char header[5];
+    snprintf(header, sizeof(header), "%04x", len);
+    ssize_t ret = TEMP_FAILURE_RETRY(write(fd, header, 4));
+    if (ret != 4) {
+        QEMU_PIPE_DEBUG("Can't write qemud frame header: %s", strerror(errno));
+        return -1;
+    }
+    ret = TEMP_FAILURE_RETRY(write(fd, buff, len));
+    if (ret != (ssize_t)len) {
+        QEMU_PIPE_DEBUG("Can't write qemud frame payload: %s", strerror(errno));
+        return -1;
+    }
+    return 0;
+}
+
+// Read a frame message from |fd|, and store it into |buff| of |len| bytes.
+// If the framed message is larger than |len|, then this returns -1 and the
+// content is lost. Otherwise, this returns the size of the message. NOTE:
+// empty messages are possible in a framed wire protocol and do not mean
+// end-of-stream.
+static int __inline__ qemu_pipe_frame_recv(int fd, void* buff, size_t len) {
+    char header[5];
+    ssize_t ret = TEMP_FAILURE_RETRY(read(fd, header, 4));
+    if (ret != 4) {
+        QEMU_PIPE_DEBUG("Can't read qemud frame header: %s", strerror(errno));
+        return -1;
+    }
+    header[4] = '\0';
+    size_t size;
+    if (sscanf(header, "%04zx", &size) != 1) {
+        QEMU_PIPE_DEBUG("Malformed qemud frame header: [%.*s]", 4, header);
+        return -1;
+    }
+    if (size > len) {
+        QEMU_PIPE_DEBUG("Oversized qemud frame (% bytes, expected <= %)", size,
+                        len);
+        return -1;
+    }
+    ret = TEMP_FAILURE_RETRY(read(fd, buff, size));
+    if (ret != size) {
+        QEMU_PIPE_DEBUG("Could not read qemud frame payload: %s",
+                        strerror(errno));
+        return -1;
+    }
+    return size;
+}
+
+#endif /* ANDROID_INCLUDE_HARDWARE_QEMUD_PIPE_H */
diff --git a/include/utils/LruCache.h b/include/utils/LruCache.h
index ed96fe4..f4e225a 100644
--- a/include/utils/LruCache.h
+++ b/include/utils/LruCache.h
@@ -56,36 +56,55 @@
 private:
     LruCache(const LruCache& that);  // disallow copy constructor
 
-    struct Entry {
+    // Super class so that we can have entries having only a key reference, for searches.
+    class KeyedEntry {
+    public:
+        virtual const TKey& getKey() const = 0;
+        // Make sure the right destructor is executed so that keys and values are deleted.
+        virtual ~KeyedEntry() {}
+    };
+
+    class Entry final : public KeyedEntry {
+    public:
         TKey key;
         TValue value;
         Entry* parent;
         Entry* child;
 
-        Entry(TKey key_, TValue value_) : key(key_), value(value_), parent(NULL), child(NULL) {
+        Entry(TKey _key, TValue _value) : key(_key), value(_value), parent(NULL), child(NULL) {
         }
-        const TKey& getKey() const { return key; }
+        const TKey& getKey() const final { return key; }
     };
 
-    struct HashForEntry : public std::unary_function<Entry*, hash_t> {
-        size_t operator() (const Entry* entry) const {
-            return hash_type(entry->key);
+    class EntryForSearch : public KeyedEntry {
+    public:
+        const TKey& key;
+        EntryForSearch(const TKey& key_) : key(key_) {
+        }
+        const TKey& getKey() const final { return key; }
+    };
+
+    struct HashForEntry : public std::unary_function<KeyedEntry*, hash_t> {
+        size_t operator() (const KeyedEntry* entry) const {
+            return hash_type(entry->getKey());
         };
     };
 
-    struct EqualityForHashedEntries : public std::unary_function<Entry*, hash_t> {
-        bool operator() (const Entry* lhs, const Entry* rhs) const {
-            return lhs->key == rhs->key;
+    struct EqualityForHashedEntries : public std::unary_function<KeyedEntry*, hash_t> {
+        bool operator() (const KeyedEntry* lhs, const KeyedEntry* rhs) const {
+            return lhs->getKey() == rhs->getKey();
         };
     };
 
-    typedef std::unordered_set<Entry*, HashForEntry, EqualityForHashedEntries> LruCacheSet;
+    // All entries in the set will be Entry*. Using the weaker KeyedEntry as to allow entries
+    // that have only a key reference, for searching.
+    typedef std::unordered_set<KeyedEntry*, HashForEntry, EqualityForHashedEntries> LruCacheSet;
 
     void attachToCache(Entry& entry);
     void detachFromCache(Entry& entry);
 
     typename LruCacheSet::iterator findByKey(const TKey& key) {
-        Entry entryForSearch(key, mNullValue);
+        EntryForSearch entryForSearch(key);
         typename LruCacheSet::iterator result = mSet->find(&entryForSearch);
         return result;
     }
@@ -124,11 +143,13 @@
         }
 
         const TValue& value() const {
-            return (*mIterator)->value;
+            // All the elements in the set are of type Entry. See comment in the definition
+            // of LruCacheSet above.
+            return reinterpret_cast<Entry *>(*mIterator)->value;
         }
 
         const TKey& key() const {
-            return (*mIterator)->key;
+            return (*mIterator)->getKey();
         }
     private:
         const LruCache<TKey, TValue>& mCache;
@@ -171,7 +192,9 @@
     if (find_result == mSet->end()) {
         return mNullValue;
     }
-    Entry *entry = *find_result;
+    // All the elements in the set are of type Entry. See comment in the definition
+    // of LruCacheSet above.
+    Entry *entry = reinterpret_cast<Entry*>(*find_result);
     detachFromCache(*entry);
     attachToCache(*entry);
     return entry->value;
@@ -199,7 +222,9 @@
     if (find_result == mSet->end()) {
         return false;
     }
-    Entry* entry = *find_result;
+    // All the elements in the set are of type Entry. See comment in the definition
+    // of LruCacheSet above.
+    Entry* entry = reinterpret_cast<Entry*>(*find_result);
     mSet->erase(entry);
     if (mListener) {
         (*mListener)(entry->key, entry->value);
diff --git a/include/utils/RefBase.h b/include/utils/RefBase.h
index 14d9cb1..c82e7d9 100644
--- a/include/utils/RefBase.h
+++ b/include/utils/RefBase.h
@@ -196,9 +196,10 @@
 
 private:
     friend class ReferenceMover;
-    inline static void renameRefs(size_t n, const ReferenceRenamer& renamer) { }
-    inline static void renameRefId(T* ref,
-            const void* old_id, const void* new_id) { }
+    inline static void renameRefs(size_t /*n*/,
+            const ReferenceRenamer& /*renamer*/) { }
+    inline static void renameRefId(T* /*ref*/,
+            const void* /*old_id*/ , const void* /*new_id*/) { }
 
 private:
     mutable std::atomic<int32_t> mCount;
diff --git a/init/action.cpp b/init/action.cpp
index b7e431c..f3e362e 100644
--- a/init/action.cpp
+++ b/init/action.cpp
@@ -42,7 +42,7 @@
     expanded_args[0] = args_[0];
     for (std::size_t i = 1; i < args_.size(); ++i) {
         if (!expand_props(args_[i], &expanded_args[i])) {
-            ERROR("%s: cannot expand '%s'\n", args_[0].c_str(), args_[i].c_str());
+            LOG(ERROR) << args_[0] << ": cannot expand '" << args_[i] << "'";
             return -EINVAL;
         }
     }
@@ -118,14 +118,13 @@
     Timer t;
     int result = command.InvokeFunc();
 
-    if (klog_get_level() >= KLOG_INFO_LEVEL) {
+    if (klog_get_level() >= KLOG_DEBUG_LEVEL) {
         std::string trigger_name = BuildTriggersString();
         std::string cmd_str = command.BuildCommandString();
         std::string source = command.BuildSourceString();
 
-        INFO("Command '%s' action=%s%s returned %d took %.2fs\n",
-             cmd_str.c_str(), trigger_name.c_str(), source.c_str(),
-             result, t.duration());
+        LOG(INFO) << "Command '" << cmd_str << "' action=" << trigger_name << source
+                  << " returned " << result << " took " << t.duration() << "s";
     }
 }
 
@@ -253,13 +252,12 @@
 
 void Action::DumpState() const {
     std::string trigger_name = BuildTriggersString();
-    INFO("on %s\n", trigger_name.c_str());
+    LOG(INFO) << "on " << trigger_name;
 
     for (const auto& c : commands_) {
         std::string cmd_str = c.BuildCommandString();
-        INFO(" %s\n", cmd_str.c_str());
+        LOG(INFO) << "  %s" << cmd_str;
     }
-    INFO("\n");
 }
 
 class EventTrigger : public Trigger {
@@ -366,7 +364,7 @@
 
     if (current_command_ == 0) {
         std::string trigger_name = action->BuildTriggersString();
-        INFO("processing action (%s)\n", trigger_name.c_str());
+        LOG(INFO) << "processing action (" << trigger_name << ")";
     }
 
     action->ExecuteOneCommand(current_command_);
@@ -395,7 +393,6 @@
     for (const auto& a : actions_) {
         a->DumpState();
     }
-    INFO("\n");
 }
 
 bool ActionParser::ParseSection(const std::vector<std::string>& args,
diff --git a/init/bootchart.cpp b/init/bootchart.cpp
index 5704d28..467a838 100644
--- a/init/bootchart.cpp
+++ b/init/bootchart.cpp
@@ -206,12 +206,12 @@
 int do_bootchart_init(const std::vector<std::string>& args) {
     g_remaining_samples = bootchart_init();
     if (g_remaining_samples < 0) {
-        ERROR("Bootcharting init failure: %s\n", strerror(errno));
+        PLOG(ERROR) << "Bootcharting initialization failed";
     } else if (g_remaining_samples > 0) {
-        NOTICE("Bootcharting started (will run for %d s).\n",
-               (g_remaining_samples * BOOTCHART_POLLING_MS) / 1000);
+        LOG(INFO) << "Bootcharting started (will run for "
+                  << ((g_remaining_samples * BOOTCHART_POLLING_MS) / 1000) << " s).";
     } else {
-        NOTICE("Not bootcharting.\n");
+        LOG(VERBOSE) << "Not bootcharting.";
     }
     return 0;
 }
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 89f6c68..d64c3d2 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -36,6 +36,7 @@
 #include <sys/wait.h>
 #include <unistd.h>
 #include <linux/loop.h>
+#include <linux/module.h>
 #include <ext4_crypt_init_extensions.h>
 
 #include <selinux/selinux.h>
@@ -44,6 +45,7 @@
 #include <fs_mgr.h>
 #include <android-base/file.h>
 #include <android-base/parseint.h>
+#include <android-base/strings.h>
 #include <android-base/stringprintf.h>
 #include <cutils/partition_utils.h>
 #include <cutils/android_reboot.h>
@@ -66,15 +68,15 @@
 
 static const int kTerminateServiceDelayMicroSeconds = 50000;
 
-static int insmod(const char *filename, const char *options) {
+static int insmod(const char *filename, const char *options, int flags) {
     int fd = open(filename, O_RDONLY | O_NOFOLLOW | O_CLOEXEC);
     if (fd == -1) {
-        ERROR("insmod: open(\"%s\") failed: %s", filename, strerror(errno));
+        PLOG(ERROR) << "insmod: open(\"" << filename << "\") failed";
         return -1;
     }
-    int rc = syscall(__NR_finit_module, fd, options, 0);
+    int rc = syscall(__NR_finit_module, fd, options, flags);
     if (rc == -1) {
-        ERROR("finit_module for \"%s\" failed: %s", filename, strerror(errno));
+        PLOG(ERROR) << "finit_module for \"" << filename << "\" failed";
     }
     close(fd);
     return rc;
@@ -199,13 +201,13 @@
         const char *f2fs_argv[] = {
             "/system/bin/fsck.f2fs", "-f", entry->mnt_fsname,
         };
-        android_fork_execvp_ext(ARRAY_SIZE(f2fs_argv), (char **)f2fs_argv,
+        android_fork_execvp_ext(arraysize(f2fs_argv), (char **)f2fs_argv,
                                 &st, true, LOG_KLOG, true, NULL, NULL, 0);
     } else if (!strcmp(entry->mnt_type, "ext4")) {
         const char *ext4_argv[] = {
             "/system/bin/e2fsck", "-f", "-y", entry->mnt_fsname,
         };
-        android_fork_execvp_ext(ARRAY_SIZE(ext4_argv), (char **)ext4_argv,
+        android_fork_execvp_ext(arraysize(ext4_argv), (char **)ext4_argv,
                                 &st, true, LOG_KLOG, true, NULL, NULL, 0);
     }
 }
@@ -269,17 +271,17 @@
 }
 
 static int do_insmod(const std::vector<std::string>& args) {
-    std::string options;
+    int flags = 0;
+    auto it = args.begin() + 1;
 
-    if (args.size() > 2) {
-        options += args[2];
-        for (std::size_t i = 3; i < args.size(); ++i) {
-            options += ' ';
-            options += args[i];
-        }
+    if (!(*it).compare("-f")) {
+        flags = MODULE_INIT_IGNORE_VERMAGIC | MODULE_INIT_IGNORE_MODVERSIONS;
+        it++;
     }
 
-    return insmod(args[1].c_str(), options.c_str());
+    std::string filename = *it++;
+    std::string options = android::base::Join(std::vector<std::string>(it, args.end()), ' ');
+    return insmod(filename.c_str(), options.c_str(), flags);
 }
 
 static int do_mkdir(const std::vector<std::string>& args) {
@@ -380,22 +382,7 @@
     source = args[2].c_str();
     target = args[3].c_str();
 
-    if (!strncmp(source, "mtd@", 4)) {
-        n = mtd_name_to_number(source + 4);
-        if (n < 0) {
-            return -1;
-        }
-
-        snprintf(tmp, sizeof(tmp), "/dev/block/mtdblock%d", n);
-
-        if (wait)
-            wait_for_file(tmp, COMMAND_RETRY_TIMEOUT);
-        if (mount(tmp, target, system, flags, options) < 0) {
-            return -1;
-        }
-
-        goto exit_success;
-    } else if (!strncmp(source, "loop@", 5)) {
+    if (!strncmp(source, "loop@", 5)) {
         int mode, loop, fd;
         struct loop_info info;
 
@@ -434,7 +421,7 @@
         }
 
         close(fd);
-        ERROR("out of loopback devices");
+        LOG(ERROR) << "out of loopback devices";
         return -1;
     } else {
         if (wait)
@@ -458,7 +445,7 @@
         write(fd, "--reason=wipe_data_via_recovery\n", strlen("--reason=wipe_data_via_recovery\n") + 1);
         close(fd);
     } else {
-        ERROR("could not open /cache/recovery/command\n");
+        PLOG(ERROR) << "could not open /cache/recovery/command";
         return -1;
     }
     android_reboot(ANDROID_RB_RESTART2, 0, "recovery");
@@ -512,9 +499,9 @@
     if (pid > 0) {
         /* Parent.  Wait for the child to return */
         int wp_ret = TEMP_FAILURE_RETRY(waitpid(pid, &status, 0));
-        if (wp_ret < 0) {
-            /* Unexpected error code. We will continue anyway. */
-            NOTICE("waitpid failed rc=%d: %s\n", wp_ret, strerror(errno));
+        if (wp_ret == -1) {
+            // Unexpected error code. We will continue anyway.
+            PLOG(WARNING) << "waitpid failed";
         }
 
         if (WIFEXITED(status)) {
@@ -529,7 +516,7 @@
         child_ret = fs_mgr_mount_all(fstab);
         fs_mgr_free_fstab(fstab);
         if (child_ret == -1) {
-            ERROR("fs_mgr_mount_all returned an error\n");
+            PLOG(ERROR) << "fs_mgr_mount_all returned an error";
         }
         _exit(child_ret);
     } else {
@@ -554,7 +541,7 @@
         ActionManager::GetInstance().QueueEventTrigger("nonencrypted");
     } else if (ret == FS_MGR_MNTALL_DEV_NEEDS_RECOVERY) {
         /* Setup a wipe via recovery, and reboot into recovery */
-        ERROR("fs_mgr_mount_all suggested recovery, so wiping data via recovery.\n");
+        PLOG(ERROR) << "fs_mgr_mount_all suggested recovery, so wiping data via recovery.";
         ret = wipe_data_via_recovery();
         /* If reboot worked, there is no return. */
     } else if (ret == FS_MGR_MNTALL_DEV_DEFAULT_FILE_ENCRYPTED) {
@@ -575,7 +562,7 @@
         property_set("ro.crypto.type", "file");
         property_set("vold.decrypt", "trigger_restart_min_framework");
     } else if (ret > 0) {
-        ERROR("fs_mgr_mount_all returned unexpected error %d\n", ret);
+        PLOG(ERROR) << "fs_mgr_mount_all returned unexpected error " << ret;
     }
     /* else ... < 0: error */
 
@@ -612,7 +599,7 @@
 static int do_start(const std::vector<std::string>& args) {
     Service* svc = ServiceManager::GetInstance().FindServiceByName(args[1]);
     if (!svc) {
-        ERROR("do_start: Service %s not found\n", args[1].c_str());
+        LOG(ERROR) << "do_start: Service " << args[1] << " not found";
         return -1;
     }
     if (!svc->Start())
@@ -623,7 +610,7 @@
 static int do_stop(const std::vector<std::string>& args) {
     Service* svc = ServiceManager::GetInstance().FindServiceByName(args[1]);
     if (!svc) {
-        ERROR("do_stop: Service %s not found\n", args[1].c_str());
+        LOG(ERROR) << "do_stop: Service " << args[1] << " not found";
         return -1;
     }
     svc->Stop();
@@ -633,7 +620,7 @@
 static int do_restart(const std::vector<std::string>& args) {
     Service* svc = ServiceManager::GetInstance().FindServiceByName(args[1]);
     if (!svc) {
-        ERROR("do_restart: Service %s not found\n", args[1].c_str());
+        LOG(ERROR) << "do_restart: Service " << args[1] << " not found";
         return -1;
     }
     svc->Restart();
@@ -654,7 +641,7 @@
         cmd = ANDROID_RB_RESTART2;
         len = 6;
     } else {
-        ERROR("powerctl: unrecognized command '%s'\n", command);
+        LOG(ERROR) << "powerctl: unrecognized command '" << command << "'";
         return -EINVAL;
     }
 
@@ -668,7 +655,7 @@
             reboot_target = &command[len + 1];
         }
     } else if (command[len] != '\0') {
-        ERROR("powerctl: unrecognized reboot target '%s'\n", &command[len]);
+        LOG(ERROR) << "powerctl: unrecognized reboot target '" << &command[len] << "'";
         return -EINVAL;
     }
 
@@ -705,7 +692,7 @@
             // Wait a bit before recounting the number or running services.
             usleep(kTerminateServiceDelayMicroSeconds);
         }
-        NOTICE("Terminating running services took %.02f seconds", t.duration());
+        LOG(VERBOSE) << "Terminating running services took " << t.duration() << " seconds";
     }
 
     return android_reboot_with_callback(cmd, 0, reboot_target,
@@ -881,7 +868,7 @@
 static int do_loglevel(const std::vector<std::string>& args) {
     int log_level = std::stoi(args[1]);
     if (log_level < KLOG_ERROR_LEVEL || log_level > KLOG_DEBUG_LEVEL) {
-        ERROR("loglevel: invalid log level'%d'\n", log_level);
+        LOG(ERROR) << "loglevel: invalid log level " << log_level;
         return -EINVAL;
     }
     klog_set_level(log_level);
diff --git a/init/devices.cpp b/init/devices.cpp
index 1410e3b..373177e 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -174,13 +174,14 @@
         }
 
         std::string attr_file = path + "/" + dp->attr;
-        INFO("fixup %s %d %d 0%o\n", attr_file.c_str(), dp->uid, dp->gid, dp->perm);
+        LOG(INFO) << "fixup " << attr_file
+                  << " " << dp->uid << " " << dp->gid << " " << std::oct << dp->perm;
         chown(attr_file.c_str(), dp->uid, dp->gid);
         chmod(attr_file.c_str(), dp->perm);
     }
 
     if (access(path.c_str(), F_OK) == 0) {
-        INFO("restorecon_recursive: %s\n", path.c_str());
+        LOG(VERBOSE) << "restorecon_recursive: " << path;
         restorecon_recursive(path.c_str());
     }
 }
@@ -241,8 +242,7 @@
     mode = get_device_perm(path, links, &uid, &gid) | (block ? S_IFBLK : S_IFCHR);
 
     if (selabel_lookup_best_match(sehandle, &secontext, path, links, mode)) {
-        ERROR("Device '%s' not created; cannot find SELinux label (%s)\n",
-                path, strerror(errno));
+        PLOG(ERROR) << "Device '" << path << "' not created; cannot find SELinux label";
         return;
     }
     setfscreatecon(secontext);
@@ -257,11 +257,23 @@
     /* If the node already exists update its SELinux label to handle cases when
      * it was created with the wrong context during coldboot procedure. */
     if (mknod(path, mode, dev) && (errno == EEXIST)) {
-        if (lsetfilecon(path, secontext)) {
-            ERROR("Cannot set '%s' SELinux label on '%s' device (%s)\n",
-                    secontext, path, strerror(errno));
+
+        char* fcon = nullptr;
+        int rc = lgetfilecon(path, &fcon);
+        if (rc < 0) {
+            PLOG(ERROR) << "Cannot get SELinux label on '" << path << "' device";
+            goto out;
+        }
+
+        bool different = strcmp(fcon, secontext) != 0;
+        freecon(fcon);
+
+        if (different && lsetfilecon(path, secontext)) {
+            PLOG(ERROR) << "Cannot set '" << secontext << "' SELinux label on '" << path << "' device";
         }
     }
+
+out:
     chown(path, uid, -1);
     setegid(AID_ROOT);
 
@@ -281,7 +293,7 @@
             name += 9;
     }
 
-    INFO("adding platform device %s (%s)\n", name, path);
+    LOG(INFO) << "adding platform device " << name << " (" << path << ")";
 
     bus = (platform_node*) calloc(1, sizeof(struct platform_node));
     bus->path = strdup(path);
@@ -320,7 +332,7 @@
     list_for_each_reverse(node, &platform_names) {
         bus = node_to_item(node, struct platform_node, list);
         if (!strcmp(path, bus->path)) {
-            INFO("removing platform device %s\n", bus->name);
+            LOG(INFO) << "removing platform device " << bus->name;
             free(bus->path);
             list_remove(node);
             free(bus);
@@ -409,9 +421,9 @@
     }
 
     if (LOG_UEVENTS) {
-        INFO("event { '%s', '%s', '%s', '%s', %d, %d }\n",
-             uevent->action, uevent->path, uevent->subsystem,
-             uevent->firmware, uevent->major, uevent->minor);
+        LOG(INFO) << android::base::StringPrintf("event { '%s', '%s', '%s', '%s', %d, %d }",
+                                                 uevent->action, uevent->path, uevent->subsystem,
+                                                 uevent->firmware, uevent->major, uevent->minor);
     }
 }
 
@@ -495,15 +507,16 @@
         return NULL;
     memset(links, 0, sizeof(char *) * 4);
 
-    INFO("found %s device %s\n", type, device);
+    LOG(INFO) << "found " << type << " device " << device;
 
     snprintf(link_path, sizeof(link_path), "/dev/block/%s/%s", type, device);
 
     if (uevent->partition_name) {
         p = strdup(uevent->partition_name);
         sanitize(p);
-        if (strcmp(uevent->partition_name, p))
-            NOTICE("Linking partition '%s' as '%s'\n", uevent->partition_name, p);
+        if (strcmp(uevent->partition_name, p)) {
+            LOG(VERBOSE) << "Linking partition '" << uevent->partition_name << "' as '" << p << "'";
+        }
         if (asprintf(&links[link_num], "%s/by-name/%s", link_path, p) > 0)
             link_num++;
         else
@@ -581,8 +594,7 @@
 
     /* too-long names would overrun our buffer */
     if(strlen(name) > len) {
-        ERROR("DEVPATH=%s exceeds %u-character limit on filename; ignoring event\n",
-                name, len);
+        LOG(ERROR) << "DEVPATH=" << name << " exceeds " << len << "-character limit on filename; ignoring event";
         return NULL;
     }
 
@@ -617,12 +629,11 @@
 {
     int s = snprintf(devpath, DEVPATH_LEN, "%s/%s", dirname, devname);
     if (s < 0) {
-        ERROR("failed to assemble device path (%s); ignoring event\n",
-                strerror(errno));
+        PLOG(ERROR) << "failed to assemble device path; ignoring event";
         return false;
     } else if (s >= DEVPATH_LEN) {
-        ERROR("%s/%s exceeds %u-character limit on path; ignoring event\n",
-                dirname, devname, DEVPATH_LEN);
+        LOG(ERROR) << dirname << "/" << devname
+                   << " exceeds " << DEVPATH_LEN << "-character limit on path; ignoring event";
         return false;
     }
     return true;
@@ -666,8 +677,7 @@
             break;
 
         default:
-            ERROR("%s subsystem's devpath option is not set; ignoring event\n",
-                    uevent->subsystem);
+            LOG(ERROR) << uevent->subsystem << " subsystem's devpath option is not set; ignoring event";
             return;
         }
 
@@ -723,9 +733,8 @@
      } else if(!strncmp(uevent->subsystem, "sound", 5)) {
          base = "/dev/snd/";
          make_dir(base, 0755);
-     } else if(!strncmp(uevent->subsystem, "misc", 4) &&
-                 !strncmp(name, "log_", 4)) {
-         INFO("kernel logger is deprecated\n");
+     } else if(!strncmp(uevent->subsystem, "misc", 4) && !strncmp(name, "log_", 4)) {
+         LOG(INFO) << "kernel logger is deprecated";
          base = "/dev/log/";
          make_dir(base, 0755);
          name += 4;
@@ -804,8 +813,7 @@
     size_t i;
     int booting = is_booting();
 
-    INFO("firmware: loading '%s' for '%s'\n",
-         uevent->firmware, uevent->path);
+    LOG(INFO) << "firmware: loading '" << uevent->firmware << "' for '" << uevent->path << "'";
 
     l = asprintf(&root, SYSFS_PREFIX"%s/", uevent->path);
     if (l == -1)
@@ -828,7 +836,7 @@
         goto loading_close_out;
 
 try_loading_again:
-    for (i = 0; i < ARRAY_SIZE(firmware_dirs); i++) {
+    for (i = 0; i < arraysize(firmware_dirs); i++) {
         char *file = NULL;
         l = asprintf(&file, "%s/%s", firmware_dirs[i], uevent->firmware);
         if (l == -1)
@@ -836,10 +844,11 @@
         fw_fd = open(file, O_RDONLY|O_CLOEXEC);
         free(file);
         if (fw_fd >= 0) {
-            if(!load_firmware(fw_fd, loading_fd, data_fd))
-                INFO("firmware: copy success { '%s', '%s' }\n", root, uevent->firmware);
-            else
-                INFO("firmware: copy failure { '%s', '%s' }\n", root, uevent->firmware);
+            if (!load_firmware(fw_fd, loading_fd, data_fd)) {
+                LOG(INFO) << "firmware: copy success { '" << root << "', '" << uevent->firmware << "' }";
+            } else {
+                LOG(ERROR) << "firmware: copy failure { '" << root << "', '" << uevent->firmware << "' }";
+            }
             break;
         }
     }
@@ -852,7 +861,7 @@
             booting = is_booting();
             goto try_loading_again;
         }
-        INFO("firmware: could not open '%s': %s\n", uevent->firmware, strerror(errno));
+        PLOG(ERROR) << "firmware: could not open '" << uevent->firmware << "'";
         write(loading_fd, "-1", 2);
         goto data_close_out;
     }
@@ -886,7 +895,7 @@
         process_firmware_event(uevent);
         _exit(EXIT_SUCCESS);
     } else if (pid < 0) {
-        ERROR("could not fork to process firmware event: %s\n", strerror(errno));
+        PLOG(ERROR) << "could not fork to process firmware event";
     }
 }
 
@@ -982,7 +991,7 @@
     fcntl(device_fd, F_SETFL, O_NONBLOCK);
 
     if (access(COLDBOOT_DONE, F_OK) == 0) {
-        NOTICE("Skipping coldboot, already done!\n");
+        LOG(VERBOSE) << "Skipping coldboot, already done!";
         return;
     }
 
@@ -991,7 +1000,7 @@
     coldboot("/sys/block");
     coldboot("/sys/devices");
     close(open(COLDBOOT_DONE, O_WRONLY|O_CREAT|O_CLOEXEC, 0000));
-    NOTICE("Coldboot took %.2fs.\n", t.duration());
+    LOG(INFO) << "Coldboot took " << t.duration() << "s.";
 }
 
 int get_device_fd()
diff --git a/init/import_parser.cpp b/init/import_parser.cpp
index e2a0f83..d52247b 100644
--- a/init/import_parser.cpp
+++ b/init/import_parser.cpp
@@ -38,7 +38,7 @@
         return false;
     }
 
-    INFO("Added '%s' to import list\n", conf_file.c_str());
+    LOG(INFO) << "Added '" << conf_file << "' to import list";
     imports_.emplace_back(std::move(conf_file));
     return true;
 }
@@ -48,8 +48,7 @@
     imports_.clear();
     for (const auto& s : current_imports) {
         if (!Parser::GetInstance().ParseConfig(s)) {
-            ERROR("could not import file '%s' from '%s': %s\n",
-                  s.c_str(), filename.c_str(), strerror(errno));
+            PLOG(ERROR) << "could not import file '" << s << "' from '" << filename << "'";
         }
     }
 }
diff --git a/init/init.cpp b/init/init.cpp
index 78c33d5..78d71a8 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -29,13 +29,12 @@
 #include <sys/mount.h>
 #include <sys/socket.h>
 #include <sys/stat.h>
+#include <sys/sysmacros.h>
 #include <sys/types.h>
 #include <sys/un.h>
 #include <sys/wait.h>
 #include <unistd.h>
 
-#include <mtd/mtd-user.h>
-
 #include <selinux/selinux.h>
 #include <selinux/label.h>
 #include <selinux/android.h>
@@ -88,7 +87,7 @@
     ev.events = EPOLLIN;
     ev.data.ptr = reinterpret_cast<void*>(fn);
     if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev) == -1) {
-        ERROR("epoll_ctl failed: %s\n", strerror(errno));
+        PLOG(ERROR) << "epoll_ctl failed";
     }
 }
 
@@ -99,7 +98,7 @@
     size_t key_len = strlen(key);
 
     /* The last environment entry is reserved to terminate the list */
-    for (n = 0; n < (ARRAY_SIZE(ENV) - 1); n++) {
+    for (n = 0; n < (arraysize(ENV) - 1); n++) {
 
         /* Delete any existing entry for this key */
         if (ENV[n] != NULL) {
@@ -119,7 +118,7 @@
         }
     }
 
-    ERROR("No env. room to store: '%s':'%s'\n", key, val);
+    LOG(ERROR) << "No env. room to store: '" << key << "':'" << val << "'";
 
     return -1;
 }
@@ -142,7 +141,7 @@
 void handle_control_message(const std::string& msg, const std::string& name) {
     Service* svc = ServiceManager::GetInstance().FindServiceByName(name);
     if (svc == nullptr) {
-        ERROR("no such service '%s'\n", name.c_str());
+        LOG(ERROR) << "no such service '" << name << "'";
         return;
     }
 
@@ -153,22 +152,22 @@
     } else if (msg == "restart") {
         svc->Restart();
     } else {
-        ERROR("unknown control msg '%s'\n", msg.c_str());
+        LOG(ERROR) << "unknown control msg '" << msg << "'";
     }
 }
 
 static int wait_for_coldboot_done_action(const std::vector<std::string>& args) {
     Timer t;
 
-    NOTICE("Waiting for %s...\n", COLDBOOT_DONE);
+    LOG(VERBOSE) << "Waiting for " COLDBOOT_DONE "...";
     // Any longer than 1s is an unreasonable length of time to delay booting.
     // If you're hitting this timeout, check that you didn't make your
     // sepolicy regular expressions too expensive (http://b/19899875).
     if (wait_for_file(COLDBOOT_DONE, 1)) {
-        ERROR("Timed out waiting for %s\n", COLDBOOT_DONE);
+        LOG(ERROR) << "Timed out waiting for " COLDBOOT_DONE;
     }
 
-    NOTICE("Waiting for %s took %.2fs.\n", COLDBOOT_DONE, t.duration());
+    LOG(VERBOSE) << "Waiting for " COLDBOOT_DONE " took " << t.duration() << "s.";
     return 0;
 }
 
@@ -200,11 +199,11 @@
             open("/dev/hw_random", O_RDONLY | O_NOFOLLOW | O_CLOEXEC));
     if (hwrandom_fd == -1) {
         if (errno == ENOENT) {
-          ERROR("/dev/hw_random not found\n");
-          /* It's not an error to not have a Hardware RNG. */
-          result = 0;
+            LOG(ERROR) << "/dev/hw_random not found";
+            // It's not an error to not have a Hardware RNG.
+            result = 0;
         } else {
-          ERROR("Failed to open /dev/hw_random: %s\n", strerror(errno));
+            PLOG(ERROR) << "Failed to open /dev/hw_random";
         }
         goto ret;
     }
@@ -212,7 +211,7 @@
     urandom_fd = TEMP_FAILURE_RETRY(
             open("/dev/urandom", O_WRONLY | O_NOFOLLOW | O_CLOEXEC));
     if (urandom_fd == -1) {
-        ERROR("Failed to open /dev/urandom: %s\n", strerror(errno));
+        PLOG(ERROR) << "Failed to open /dev/urandom";
         goto ret;
     }
 
@@ -220,23 +219,22 @@
         chunk_size = TEMP_FAILURE_RETRY(
                 read(hwrandom_fd, buf, sizeof(buf) - total_bytes_written));
         if (chunk_size == -1) {
-            ERROR("Failed to read from /dev/hw_random: %s\n", strerror(errno));
+            PLOG(ERROR) << "Failed to read from /dev/hw_random";
             goto ret;
         } else if (chunk_size == 0) {
-            ERROR("Failed to read from /dev/hw_random: EOF\n");
+            LOG(ERROR) << "Failed to read from /dev/hw_random: EOF";
             goto ret;
         }
 
         chunk_size = TEMP_FAILURE_RETRY(write(urandom_fd, buf, chunk_size));
         if (chunk_size == -1) {
-            ERROR("Failed to write to /dev/urandom: %s\n", strerror(errno));
+            PLOG(ERROR) << "Failed to write to /dev/urandom";
             goto ret;
         }
         total_bytes_written += chunk_size;
     }
 
-    INFO("Mixed %zu bytes from /dev/hw_random into /dev/urandom",
-                total_bytes_written);
+    LOG(INFO) << "Mixed " << total_bytes_written << " bytes from /dev/hw_random into /dev/urandom";
     result = 0;
 
 ret:
@@ -306,7 +304,7 @@
         { "ro.boot.hardware",   "ro.hardware",   "unknown", },
         { "ro.boot.revision",   "ro.revision",   "0", },
     };
-    for (size_t i = 0; i < ARRAY_SIZE(prop_map); i++) {
+    for (size_t i = 0; i < arraysize(prop_map); i++) {
         std::string value = property_get(prop_map[i].src_prop);
         property_set(prop_map[i].dst_prop, (!value.empty()) ? value.c_str() : prop_map[i].default_value);
     }
@@ -320,7 +318,7 @@
     std::string dt_file;
     android::base::ReadFileToString(file_name, &dt_file);
     if (!dt_file.compare("android,firmware")) {
-        ERROR("firmware/android is not compatible with 'android,firmware'\n");
+        LOG(ERROR) << "firmware/android is not compatible with 'android,firmware'";
         return;
     }
 
@@ -396,7 +394,7 @@
     property_audit_data *d = reinterpret_cast<property_audit_data*>(data);
 
     if (!d || !d->name || !d->cr) {
-        ERROR("audit_callback invoked with null data arguments!");
+        LOG(ERROR) << "audit_callback invoked with null data arguments!";
         return 0;
     }
 
@@ -406,7 +404,7 @@
 }
 
 static void security_failure() {
-    ERROR("Security failure; rebooting into recovery mode...\n");
+    LOG(ERROR) << "Security failure; rebooting into recovery mode...";
     android_reboot(ANDROID_RB_RESTART2, 0, "recovery");
     while (true) { pause(); }  // never reached
 }
@@ -421,9 +419,9 @@
     selinux_set_callback(SELINUX_CB_AUDIT, cb);
 
     if (in_kernel_domain) {
-        INFO("Loading SELinux policy...\n");
+        LOG(INFO) << "Loading SELinux policy...";
         if (selinux_android_load_policy() < 0) {
-            ERROR("failed to load policy: %s\n", strerror(errno));
+            PLOG(ERROR) << "failed to load policy";
             security_failure();
         }
 
@@ -431,8 +429,7 @@
         bool is_enforcing = selinux_is_enforcing();
         if (kernel_enforcing != is_enforcing) {
             if (security_setenforce(is_enforcing)) {
-                ERROR("security_setenforce(%s) failed: %s\n",
-                      is_enforcing ? "true" : "false", strerror(errno));
+                PLOG(ERROR) << "security_setenforce(%s) failed" << (is_enforcing ? "true" : "false");
                 security_failure();
             }
         }
@@ -441,13 +438,29 @@
             security_failure();
         }
 
-        NOTICE("(Initializing SELinux %s took %.2fs.)\n",
-               is_enforcing ? "enforcing" : "non-enforcing", t.duration());
+        LOG(INFO) << "(Initializing SELinux " << (is_enforcing ? "enforcing" : "non-enforcing")
+                  << " took " << t.duration() << "s.)";
     } else {
         selinux_init_all_handles();
     }
 }
 
+// Set the UDC controller for the ConfigFS USB Gadgets.
+// Read the UDC controller in use from "/sys/class/udc".
+// In case of multiple UDC controllers select the first one.
+static void set_usb_controller() {
+    std::unique_ptr<DIR, decltype(&closedir)>dir(opendir("/sys/class/udc"), closedir);
+    if (!dir) return;
+
+    dirent* dp;
+    while ((dp = readdir(dir.get())) != nullptr) {
+        if (dp->d_name[0] == '.') continue;
+
+        property_set("sys.usb.controller", dp->d_name);
+        break;
+    }
+}
+
 int main(int argc, char** argv) {
     if (!strcmp(basename(argv[0]), "ueventd")) {
         return ueventd_main(argc, argv);
@@ -475,17 +488,14 @@
         mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC));
         mount("sysfs", "/sys", "sysfs", 0, NULL);
         mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL);
+        mknod("/dev/kmsg", S_IFCHR | 0600, makedev(1, 11));
     }
 
-    // We must have some place other than / to create the device nodes for
-    // kmsg and null, otherwise we won't be able to remount / read-only
-    // later on. Now that tmpfs is mounted on /dev, we can actually talk
-    // to the outside world.
-    open_devnull_stdio();
-    klog_init();
-    klog_set_level(KLOG_NOTICE_LEVEL);
+    // Now that tmpfs is mounted on /dev and we have /dev/kmsg, we can actually
+    // talk to the outside world...
+    InitKernelLogging(argv);
 
-    NOTICE("init %s started!\n", is_first_stage ? "first stage" : "second stage");
+    LOG(INFO) << "init " << (is_first_stage ? "first stage" : "second stage") << " started!";
 
     if (!is_first_stage) {
         // Indicate that booting is in progress to background fw loaders, etc.
@@ -510,13 +520,13 @@
     // that the SELinux policy has been loaded.
     if (is_first_stage) {
         if (restorecon("/init") == -1) {
-            ERROR("restorecon failed: %s\n", strerror(errno));
+            PLOG(ERROR) << "restorecon failed";
             security_failure();
         }
         char* path = argv[0];
         char* args[] = { path, const_cast<char*>("--second-stage"), nullptr };
         if (execv(path, args) == -1) {
-            ERROR("execv(\"%s\") failed: %s\n", path, strerror(errno));
+            PLOG(ERROR) << "execv(\"" << path << "\") failed";
             security_failure();
         }
     }
@@ -524,8 +534,9 @@
     // These directories were necessarily created before initial policy load
     // and therefore need their security context restored to the proper value.
     // This must happen before /dev is populated by ueventd.
-    NOTICE("Running restorecon...\n");
+    LOG(INFO) << "Running restorecon...";
     restorecon("/dev");
+    restorecon("/dev/kmsg");
     restorecon("/dev/socket");
     restorecon("/dev/__properties__");
     restorecon("/property_contexts");
@@ -533,7 +544,7 @@
 
     epoll_fd = epoll_create1(EPOLL_CLOEXEC);
     if (epoll_fd == -1) {
-        ERROR("epoll_create1 failed: %s\n", strerror(errno));
+        PLOG(ERROR) << "epoll_create1 failed";
         exit(1);
     }
 
@@ -542,6 +553,7 @@
     property_load_boot_defaults();
     export_oem_lock_status();
     start_property_service();
+    set_usb_controller();
 
     const BuiltinFunctionMap function_map;
     Action::set_function_map(&function_map);
@@ -603,7 +615,7 @@
         epoll_event ev;
         int nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1, timeout));
         if (nr == -1) {
-            ERROR("epoll_wait failed: %s\n", strerror(errno));
+            PLOG(ERROR) << "epoll_wait failed";
         } else if (nr == 1) {
             ((void (*)()) ev.data.ptr)();
         }
diff --git a/init/init_parser.cpp b/init/init_parser.cpp
index b44ca59..9ec26af 100644
--- a/init/init_parser.cpp
+++ b/init/init_parser.cpp
@@ -93,7 +93,7 @@
 }
 
 bool Parser::ParseConfigFile(const std::string& path) {
-    INFO("Parsing file %s...\n", path.c_str());
+    LOG(INFO) << "Parsing file " << path << "...";
     Timer t;
     std::string data;
     if (!read_file(path.c_str(), &data)) {
@@ -110,15 +110,15 @@
     // Nexus 9 boot time, so it's disabled by default.
     if (false) DumpState();
 
-    NOTICE("(Parsing %s took %.2fs.)\n", path.c_str(), t.duration());
+    LOG(VERBOSE) << "(Parsing " << path << " took " << t.duration() << "s.)";
     return true;
 }
 
 bool Parser::ParseConfigDir(const std::string& path) {
-    INFO("Parsing directory %s...\n", path.c_str());
+    LOG(INFO) << "Parsing directory " << path << "...";
     std::unique_ptr<DIR, int(*)(DIR*)> config_dir(opendir(path.c_str()), closedir);
     if (!config_dir) {
-        ERROR("Could not import directory '%s'\n", path.c_str());
+        PLOG(ERROR) << "Could not import directory '" << path << "'";
         return false;
     }
     dirent* current_file;
@@ -128,7 +128,7 @@
         // Ignore directories and only process regular files.
         if (current_file->d_type == DT_REG) {
             if (!ParseConfigFile(current_path)) {
-                ERROR("could not import file '%s'\n", current_path.c_str());
+                LOG(ERROR) << "could not import file '" << current_path << "'";
             }
         }
     }
diff --git a/init/keychords.cpp b/init/keychords.cpp
index 7a7838d..1cfdd80 100644
--- a/init/keychords.cpp
+++ b/init/keychords.cpp
@@ -43,7 +43,7 @@
         size = sizeof(*keychord) + svc->keycodes().size() * sizeof(keychord->keycodes[0]);
         keychords = (input_keychord*) realloc(keychords, keychords_length + size);
         if (!keychords) {
-            ERROR("could not allocate keychords\n");
+            PLOG(ERROR) << "could not allocate keychords";
             keychords_length = 0;
             keychords_count = 0;
             return;
@@ -69,7 +69,7 @@
 
     ret = read(keychord_fd, &id, sizeof(id));
     if (ret != sizeof(id)) {
-        ERROR("could not read keychord id\n");
+        PLOG(ERROR) << "could not read keychord id";
         return;
     }
 
@@ -78,10 +78,10 @@
     if (adb_enabled == "running") {
         Service* svc = ServiceManager::GetInstance().FindServiceByKeychord(id);
         if (svc) {
-            INFO("Starting service %s from keychord\n", svc->name().c_str());
+            LOG(INFO) << "Starting service " << svc->name() << " from keychord...";
             svc->Start();
         } else {
-            ERROR("service for keychord %d not found\n", id);
+            LOG(ERROR) << "service for keychord " << id << " not found";
         }
     }
 }
@@ -96,13 +96,13 @@
 
     keychord_fd = TEMP_FAILURE_RETRY(open("/dev/keychord", O_RDWR | O_CLOEXEC));
     if (keychord_fd == -1) {
-        ERROR("could not open /dev/keychord: %s\n", strerror(errno));
+        PLOG(ERROR) << "could not open /dev/keychord";
         return;
     }
 
     int ret = write(keychord_fd, keychords, keychords_length);
     if (ret != keychords_length) {
-        ERROR("could not configure /dev/keychord %d: %s\n", ret, strerror(errno));
+        PLOG(ERROR) << "could not configure /dev/keychord " << ret;
         close(keychord_fd);
     }
 
diff --git a/init/log.cpp b/init/log.cpp
index ace9fd7..379141a 100644
--- a/init/log.cpp
+++ b/init/log.cpp
@@ -16,54 +16,77 @@
 
 #include "log.h"
 
+#include <fcntl.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
 #include <sys/uio.h>
 
 #include <selinux/selinux.h>
 
-#include <android-base/stringprintf.h>
+static const int kLogSeverityToKLogLevel[] = {
+    [android::base::VERBOSE] = KLOG_DEBUG_LEVEL,
+    [android::base::DEBUG] = KLOG_DEBUG_LEVEL,
+    [android::base::INFO] = KLOG_INFO_LEVEL,
+    [android::base::WARNING] = KLOG_WARNING_LEVEL,
+    [android::base::ERROR] = KLOG_ERROR_LEVEL,
+    [android::base::FATAL] = KLOG_ERROR_LEVEL,
+};
+static_assert(arraysize(kLogSeverityToKLogLevel) == android::base::FATAL + 1,
+              "Mismatch in size of kLogSeverityToKLogLevel and values in LogSeverity");
 
-static void init_klog_vwrite(int level, const char* fmt, va_list ap) {
-    static const char* tag = basename(getprogname());
-
+static void KernelLogger(android::base::LogId, android::base::LogSeverity severity,
+                         const char* tag, const char*, unsigned int, const char* msg) {
+    int level = kLogSeverityToKLogLevel[severity];
     if (level > klog_get_level()) return;
 
     // The kernel's printk buffer is only 1024 bytes.
     // TODO: should we automatically break up long lines into multiple lines?
     // Or we could log but with something like "..." at the end?
     char buf[1024];
-    size_t prefix_size = snprintf(buf, sizeof(buf), "<%d>%s: ", level, tag);
-    size_t msg_size = vsnprintf(buf + prefix_size, sizeof(buf) - prefix_size, fmt, ap);
-    if (msg_size >= sizeof(buf) - prefix_size) {
-        msg_size = snprintf(buf + prefix_size, sizeof(buf) - prefix_size,
-                            "(%zu-byte message too long for printk)\n", msg_size);
+    size_t size = snprintf(buf, sizeof(buf), "<%d>%s: %s\n", level, tag, msg);
+    if (size > sizeof(buf)) {
+        size = snprintf(buf, sizeof(buf), "<%d>%s: %zu-byte message too long for printk\n",
+                        level, tag, size);
     }
 
     iovec iov[1];
     iov[0].iov_base = buf;
-    iov[0].iov_len = prefix_size + msg_size;
-
+    iov[0].iov_len = size;
     klog_writev(level, iov, 1);
 }
 
-void init_klog_write(int level, const char* fmt, ...) {
-    va_list ap;
-    va_start(ap, fmt);
-    init_klog_vwrite(level, fmt, ap);
-    va_end(ap);
+void InitKernelLogging(char* argv[]) {
+    // Make stdin/stdout/stderr all point to /dev/null.
+    int fd = open("/sys/fs/selinux/null", O_RDWR);
+    if (fd == -1) {
+        int saved_errno = errno;
+        android::base::InitLogging(argv, &KernelLogger);
+        errno = saved_errno;
+        PLOG(FATAL) << "Couldn't open /sys/fs/selinux/null";
+    }
+    dup2(fd, 0);
+    dup2(fd, 1);
+    dup2(fd, 2);
+    if (fd > 2) close(fd);
+
+    android::base::InitLogging(argv, &KernelLogger);
+    klog_set_level(KLOG_INFO_LEVEL);
 }
 
 int selinux_klog_callback(int type, const char *fmt, ...) {
-    int level = KLOG_ERROR_LEVEL;
+    android::base::LogSeverity severity = android::base::ERROR;
     if (type == SELINUX_WARNING) {
-        level = KLOG_WARNING_LEVEL;
+        severity = android::base::WARNING;
     } else if (type == SELINUX_INFO) {
-        level = KLOG_INFO_LEVEL;
+        severity = android::base::INFO;
     }
+    char buf[1024];
     va_list ap;
     va_start(ap, fmt);
-    init_klog_vwrite(level, fmt, ap);
+    vsnprintf(buf, sizeof(buf), fmt, ap);
     va_end(ap);
+    KernelLogger(android::base::MAIN, severity, "selinux", nullptr, 0, buf);
     return 0;
 }
diff --git a/init/log.h b/init/log.h
index c5c30af..cf552a1 100644
--- a/init/log.h
+++ b/init/log.h
@@ -17,16 +17,12 @@
 #ifndef _INIT_LOG_H_
 #define _INIT_LOG_H_
 
+#include <android-base/logging.h>
+
 #include <cutils/klog.h>
 
-#define ERROR(x...)   init_klog_write(KLOG_ERROR_LEVEL, x)
-#define WARNING(x...) init_klog_write(KLOG_WARNING_LEVEL, x)
-#define NOTICE(x...)  init_klog_write(KLOG_NOTICE_LEVEL, x)
-#define INFO(x...)    init_klog_write(KLOG_INFO_LEVEL, x)
-#define DEBUG(x...)   init_klog_write(KLOG_DEBUG_LEVEL, x)
-#define VERBOSE(x...) init_klog_write(KLOG_DEBUG_LEVEL, x)
+void InitKernelLogging(char* argv[]);
 
-void init_klog_write(int level, const char* fmt, ...) __printflike(2, 3);
 int selinux_klog_callback(int level, const char* fmt, ...) __printflike(2, 3);
 
 #endif
diff --git a/init/parser.cpp b/init/parser.cpp
index ae103ec..45862b7 100644
--- a/init/parser.cpp
+++ b/init/parser.cpp
@@ -20,7 +20,7 @@
     vsnprintf(buf + off, 128 - off, fmt, ap);
     va_end(ap);
     buf[127] = 0;
-    ERROR("%s", buf);
+    LOG(ERROR) << buf;
 }
 
 int next_token(struct parse_state *state)
diff --git a/init/property_service.cpp b/init/property_service.cpp
index c617dc6..e2e1b16 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -64,7 +64,7 @@
 
 void property_init() {
     if (__system_property_area_init()) {
-        ERROR("Failed to initialize property area\n");
+        LOG(ERROR) << "Failed to initialize property area";
         exit(1);
     }
 }
@@ -127,7 +127,7 @@
     snprintf(tempPath, sizeof(tempPath), "%s/.temp.XXXXXX", PERSISTENT_PROPERTY_DIR);
     fd = mkstemp(tempPath);
     if (fd < 0) {
-        ERROR("Unable to write persistent property to temp file %s: %s\n", tempPath, strerror(errno));
+        PLOG(ERROR) << "Unable to write persistent property to temp file " << tempPath;
         return;
     }
     write(fd, value, strlen(value));
@@ -136,8 +136,8 @@
 
     snprintf(path, sizeof(path), "%s/%s", PERSISTENT_PROPERTY_DIR, name);
     if (rename(tempPath, path)) {
+        PLOG(ERROR) << "Unable to rename persistent property file " << tempPath << " to " << path;
         unlink(tempPath);
-        ERROR("Unable to rename persistent property file %s to %s\n", tempPath, path);
     }
 }
 
@@ -176,7 +176,7 @@
 
     if (strcmp("selinux.restorecon_recursive", name) == 0 && valuelen > 0) {
         if (restorecon_recursive(value) != 0) {
-            ERROR("Failed to restorecon_recursive %s\n", value);
+            LOG(ERROR) << "Failed to restorecon_recursive " << value;
         }
     }
 
@@ -219,7 +219,7 @@
 int property_set(const char* name, const char* value) {
     int rc = property_set_impl(name, value);
     if (rc == -1) {
-        ERROR("property_set(\"%s\", \"%s\") failed\n", name, value);
+        LOG(ERROR) << "property_set(\"" << name << "\", \"" << value << "\") failed";
     }
     return rc;
 }
@@ -245,7 +245,7 @@
     /* Check socket options here */
     if (getsockopt(s, SOL_SOCKET, SO_PEERCRED, &cr, &cr_size) < 0) {
         close(s);
-        ERROR("Unable to receive socket options\n");
+        PLOG(ERROR) << "Unable to receive socket options";
         return;
     }
 
@@ -254,19 +254,18 @@
     ufds[0].revents = 0;
     nr = TEMP_FAILURE_RETRY(poll(ufds, 1, timeout_ms));
     if (nr == 0) {
-        ERROR("sys_prop: timeout waiting for uid=%d to send property message.\n", cr.uid);
+        LOG(ERROR) << "sys_prop: timeout waiting for uid " << cr.uid << " to send property message.";
         close(s);
         return;
     } else if (nr < 0) {
-        ERROR("sys_prop: error waiting for uid=%d to send property message: %s\n", cr.uid, strerror(errno));
+        PLOG(ERROR) << "sys_prop: error waiting for uid " << cr.uid << " to send property message";
         close(s);
         return;
     }
 
     r = TEMP_FAILURE_RETRY(recv(s, &msg, sizeof(msg), MSG_DONTWAIT));
     if(r != sizeof(prop_msg)) {
-        ERROR("sys_prop: mis-match msg size received: %d expected: %zu: %s\n",
-              r, sizeof(prop_msg), strerror(errno));
+        PLOG(ERROR) << "sys_prop: mis-match msg size received: " << r << " expected: " << sizeof(prop_msg);
         close(s);
         return;
     }
@@ -277,7 +276,7 @@
         msg.value[PROP_VALUE_MAX-1] = 0;
 
         if (!is_legal_property_name(msg.name, strlen(msg.name))) {
-            ERROR("sys_prop: illegal property name. Got: \"%s\"\n", msg.name);
+            LOG(ERROR) << "sys_prop: illegal property name \"" << msg.name << "\"";
             close(s);
             return;
         }
@@ -291,15 +290,17 @@
             if (check_control_mac_perms(msg.value, source_ctx, &cr)) {
                 handle_control_message((char*) msg.name + 4, (char*) msg.value);
             } else {
-                ERROR("sys_prop: Unable to %s service ctl [%s] uid:%d gid:%d pid:%d\n",
-                        msg.name + 4, msg.value, cr.uid, cr.gid, cr.pid);
+                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 {
-                ERROR("sys_prop: permission denied uid:%d  name:%s\n",
-                      cr.uid, msg.name);
+                LOG(ERROR) << "sys_prop: permission denied uid:" << cr.uid << " name:" << msg.name;
             }
 
             // Note: bionic's property client code assumes that the
@@ -389,7 +390,7 @@
         data.push_back('\n');
         load_properties(&data[0], filter);
     }
-    NOTICE("(Loading properties from %s took %.2fs.)\n", filename, t.duration());
+    LOG(VERBOSE) << "(Loading properties from " << filename << " took " << t.duration() << "s.)";
 }
 
 static void load_persistent_properties() {
@@ -397,8 +398,8 @@
 
     std::unique_ptr<DIR, int(*)(DIR*)> dir(opendir(PERSISTENT_PROPERTY_DIR), closedir);
     if (!dir) {
-        ERROR("Unable to open persistent property directory \"%s\": %s\n",
-              PERSISTENT_PROPERTY_DIR, strerror(errno));
+        PLOG(ERROR) << "Unable to open persistent property directory \""
+                    << PERSISTENT_PROPERTY_DIR << "\"";
         return;
     }
 
@@ -414,25 +415,23 @@
         // Open the file and read the property value.
         int fd = openat(dirfd(dir.get()), entry->d_name, O_RDONLY | O_NOFOLLOW);
         if (fd == -1) {
-            ERROR("Unable to open persistent property file \"%s\": %s\n",
-                  entry->d_name, strerror(errno));
+            PLOG(ERROR) << "Unable to open persistent property file \"" << entry->d_name << "\"";
             continue;
         }
 
         struct stat sb;
         if (fstat(fd, &sb) == -1) {
-            ERROR("fstat on property file \"%s\" failed: %s\n", entry->d_name, strerror(errno));
+            PLOG(ERROR) << "fstat on property file \"" << entry->d_name << "\" failed";
             close(fd);
             continue;
         }
 
         // File must not be accessible to others, be owned by root/root, and
         // not be a hard link to any other file.
-        if (((sb.st_mode & (S_IRWXG | S_IRWXO)) != 0) || (sb.st_uid != 0) || (sb.st_gid != 0) ||
-                (sb.st_nlink != 1)) {
-            ERROR("skipping insecure property file %s (uid=%u gid=%u nlink=%u mode=%o)\n",
-                  entry->d_name, (unsigned int)sb.st_uid, (unsigned int)sb.st_gid,
-                  (unsigned int)sb.st_nlink, sb.st_mode);
+        if (((sb.st_mode & (S_IRWXG | S_IRWXO)) != 0) || sb.st_uid != 0 || sb.st_gid != 0 || sb.st_nlink != 1) {
+            PLOG(ERROR) << "skipping insecure property file " << entry->d_name
+                        << " (uid=" << sb.st_uid << " gid=" << sb.st_gid
+                        << " nlink=" << sb.st_nlink << " mode=" << std::oct << sb.st_mode << ")";
             close(fd);
             continue;
         }
@@ -443,8 +442,7 @@
             value[length] = 0;
             property_set(entry->d_name, value);
         } else {
-            ERROR("Unable to read persistent property file %s: %s\n",
-                  entry->d_name, strerror(errno));
+            PLOG(ERROR) << "Unable to read persistent property file " << entry->d_name;
         }
         close(fd);
     }
@@ -477,27 +475,27 @@
 void load_recovery_id_prop() {
     std::string ro_hardware = property_get("ro.hardware");
     if (ro_hardware.empty()) {
-        ERROR("ro.hardware not set - unable to load recovery id\n");
+        LOG(ERROR) << "ro.hardware not set - unable to load recovery id";
         return;
     }
     std::string fstab_filename = FSTAB_PREFIX + ro_hardware;
 
     std::unique_ptr<fstab, void(*)(fstab*)> tab(fs_mgr_read_fstab(fstab_filename.c_str()),
-            fs_mgr_free_fstab);
+                                                fs_mgr_free_fstab);
     if (!tab) {
-        ERROR("unable to read fstab %s: %s\n", fstab_filename.c_str(), strerror(errno));
+        PLOG(ERROR) << "unable to read fstab " << fstab_filename;
         return;
     }
 
     fstab_rec* rec = fs_mgr_get_entry_for_mount_point(tab.get(), RECOVERY_MOUNT_POINT);
     if (rec == NULL) {
-        ERROR("/recovery not specified in fstab\n");
+        LOG(ERROR) << "/recovery not specified in fstab";
         return;
     }
 
     int fd = open(rec->blk_device, O_RDONLY);
     if (fd == -1) {
-        ERROR("error opening block device %s: %s\n", rec->blk_device, strerror(errno));
+        PLOG(ERROR) << "error opening block device " << rec->blk_device;
         return;
     }
 
@@ -506,7 +504,7 @@
         std::string hex = bytes_to_hex(reinterpret_cast<uint8_t*>(hdr.id), sizeof(hdr.id));
         property_set("ro.recovery_id", hex.c_str());
     } else {
-        ERROR("error reading /recovery: %s\n", strerror(errno));
+        PLOG(ERROR) << "error reading /recovery";
     }
 
     close(fd);
@@ -523,7 +521,7 @@
     property_set_fd = create_socket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
                                     0666, 0, 0, NULL);
     if (property_set_fd == -1) {
-        ERROR("start_property_service socket creation failed: %s\n", strerror(errno));
+        PLOG(ERROR) << "start_property_service socket creation failed";
         exit(1);
     }
 
diff --git a/init/readme.txt b/init/readme.txt
index aa372eb..7260775 100644
--- a/init/readme.txt
+++ b/init/readme.txt
@@ -184,6 +184,9 @@
   Write the child's pid to the given files when it forks. Meant for
   cgroup/cpuset usage.
 
+priority <priority>
+  Scheduling priority of the service process. This value has to be in range
+  -20 to 19. Default priority is 0. Priority is set via setpriority().
 
 Triggers
 --------
@@ -278,8 +281,11 @@
 ifup <interface>
    Bring the network interface <interface> online.
 
-insmod <path>
-   Install the module at <path>
+insmod [-f] <path> [<options>]
+   Install the module at <path> with the specified options.
+   -f
+   Force installation of the module even if the version of the running kernel
+   and the version of the kernel for which the module was compiled do not match.
 
 load_all_props
    Loads properties from /system, /vendor, et cetera.
@@ -305,8 +311,6 @@
 
 mount <type> <device> <dir> [ <flag> ]* [<options>]
    Attempt to mount the named device at the directory <dir>
-   <device> may be of the form mtd@name to specify a mtd block
-   device by name.
    <flag>s include "ro", "rw", "remount", "noatime", ...
    <options> include "barrier=1", "noauto_da_alloc", "discard", ... as
    a comma separated string, eg: barrier=1,noauto_da_alloc
@@ -511,5 +515,5 @@
 
   emulator -partition-size 1024 -verbose -show-kernel -no-window
 
-You might want to call klog_set_level(6) after the klog_init() call
-so you see the kernel logging in dmesg (or the emulator output).
+You might want to change the klog_set_level call so you see all the kernel
+logging in dmesg (or the emulator output).
diff --git a/init/service.cpp b/init/service.cpp
index 4175d05..f67af2d 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -17,7 +17,12 @@
 #include "service.h"
 
 #include <fcntl.h>
+#include <sched.h>
+#include <sys/mount.h>
+#include <sys/prctl.h>
+#include <sys/resource.h>
 #include <sys/stat.h>
+#include <sys/time.h>
 #include <sys/types.h>
 #include <sys/wait.h>
 #include <termios.h>
@@ -27,8 +32,10 @@
 
 #include <android-base/file.h>
 #include <android-base/stringprintf.h>
+#include <android-base/strings.h>
 #include <cutils/android_reboot.h>
 #include <cutils/sockets.h>
+#include <system/thread_defs.h>
 
 #include <processgroup/processgroup.h>
 
@@ -45,6 +52,96 @@
 #define CRITICAL_CRASH_THRESHOLD    4       // if we crash >4 times ...
 #define CRITICAL_CRASH_WINDOW       (4*60)  // ... in 4 minutes, goto recovery
 
+static std::string ComputeContextFromExecutable(std::string& service_name,
+                                                const std::string& service_path) {
+    std::string computed_context;
+
+    char* raw_con = nullptr;
+    char* raw_filecon = nullptr;
+
+    if (getcon(&raw_con) == -1) {
+        LOG(ERROR) << "could not get context while starting '" << service_name << "'";
+        return "";
+    }
+    std::unique_ptr<char> mycon(raw_con);
+
+    if (getfilecon(service_path.c_str(), &raw_filecon) == -1) {
+        LOG(ERROR) << "could not get file context while starting '" << service_name << "'";
+        return "";
+    }
+    std::unique_ptr<char> filecon(raw_filecon);
+
+    char* new_con = nullptr;
+    int rc = security_compute_create(mycon.get(), filecon.get(),
+                                     string_to_security_class("process"), &new_con);
+    if (rc == 0) {
+        computed_context = new_con;
+        free(new_con);
+    }
+    if (rc == 0 && computed_context == mycon.get()) {
+        LOG(ERROR) << "service " << service_name << " does not have a SELinux domain defined";
+        return "";
+    }
+    if (rc < 0) {
+        LOG(ERROR) << "could not get context while starting '" << service_name << "'";
+        return "";
+    }
+    return computed_context;
+}
+
+static void SetUpPidNamespace(const std::string& service_name) {
+    constexpr unsigned int kSafeFlags = MS_NODEV | MS_NOEXEC | MS_NOSUID;
+
+    // It's OK to LOG(FATAL) in this function since it's running in the first
+    // child process.
+    if (mount("", "/proc", "proc", kSafeFlags | MS_REMOUNT, "") == -1) {
+        PLOG(FATAL) << "couldn't remount(/proc) for " << service_name;
+    }
+
+    if (prctl(PR_SET_NAME, service_name.c_str()) == -1) {
+        PLOG(FATAL) << "couldn't set name for " << service_name;
+    }
+
+    pid_t child_pid = fork();
+    if (child_pid == -1) {
+        PLOG(FATAL) << "couldn't fork init inside the PID namespace for " << service_name;
+    }
+
+    if (child_pid > 0) {
+        // So that we exit with the right status.
+        static int init_exitstatus = 0;
+        signal(SIGTERM, [](int) { _exit(init_exitstatus); });
+
+        pid_t waited_pid;
+        int status;
+        while ((waited_pid = wait(&status)) > 0) {
+             // This loop will end when there are no processes left inside the
+             // PID namespace or when the init process inside the PID namespace
+             // gets a signal.
+            if (waited_pid == child_pid) {
+                init_exitstatus = status;
+            }
+        }
+        if (!WIFEXITED(init_exitstatus)) {
+            _exit(EXIT_FAILURE);
+        }
+        _exit(WEXITSTATUS(init_exitstatus));
+    }
+}
+
+static void ExpandArgs(const std::vector<std::string>& args, std::vector<char*>* strs) {
+    std::vector<std::string> expanded_args;
+    expanded_args.resize(args.size());
+    strs->push_back(const_cast<char*>(args[0].c_str()));
+    for (std::size_t i = 1; i < args.size(); ++i) {
+        if (!expand_props(args[i], &expanded_args[i])) {
+            LOG(FATAL) << args[0] << ": cannot expand '" << args[i] << "'";
+        }
+        strs->push_back(const_cast<char*>(expanded_args[i].c_str()));
+    }
+    strs->push_back(nullptr);
+}
+
 SocketInfo::SocketInfo() : uid(0), gid(0), perm(0) {
 }
 
@@ -64,17 +161,22 @@
 Service::Service(const std::string& name, const std::string& classname,
                  const std::vector<std::string>& args)
     : name_(name), classname_(classname), flags_(0), pid_(0), time_started_(0),
-      time_crashed_(0), nr_crashed_(0), uid_(0), gid_(0), seclabel_(""),
-      ioprio_class_(IoSchedClass_NONE), ioprio_pri_(0), args_(args) {
+      time_crashed_(0), nr_crashed_(0), uid_(0), gid_(0), namespace_flags_(0),
+      seclabel_(""), ioprio_class_(IoSchedClass_NONE), ioprio_pri_(0),
+      priority_(0), args_(args) {
     onrestart_.InitSingleTrigger("onrestart");
 }
 
 Service::Service(const std::string& name, const std::string& classname,
-                 unsigned flags, uid_t uid, gid_t gid, const std::vector<gid_t>& supp_gids,
-                 const std::string& seclabel,  const std::vector<std::string>& args)
-    : name_(name), classname_(classname), flags_(flags), pid_(0), time_started_(0),
-      time_crashed_(0), nr_crashed_(0), uid_(uid), gid_(gid), supp_gids_(supp_gids),
-      seclabel_(seclabel), ioprio_class_(IoSchedClass_NONE), ioprio_pri_(0), args_(args) {
+                 unsigned flags, uid_t uid, gid_t gid,
+                 const std::vector<gid_t>& supp_gids, unsigned namespace_flags,
+                 const std::string& seclabel,
+                 const std::vector<std::string>& args)
+    : name_(name), classname_(classname), flags_(flags), pid_(0),
+      time_started_(0), time_crashed_(0), nr_crashed_(0), uid_(uid), gid_(gid),
+      supp_gids_(supp_gids), namespace_flags_(namespace_flags),
+      seclabel_(seclabel), ioprio_class_(IoSchedClass_NONE), ioprio_pri_(0),
+      priority_(0), args_(args) {
     onrestart_.InitSingleTrigger("onrestart");
 }
 
@@ -87,19 +189,69 @@
     std::string prop_name = StringPrintf("init.svc.%s", name_.c_str());
     if (prop_name.length() >= PROP_NAME_MAX) {
         // If the property name would be too long, we can't set it.
-        ERROR("Property name \"init.svc.%s\" too long; not setting to %s\n",
-              name_.c_str(), new_state.c_str());
+        LOG(ERROR) << "Property name \"init.svc." << name_ << "\" too long; not setting to " << new_state;
         return;
     }
 
     property_set(prop_name.c_str(), new_state.c_str());
 }
 
+void Service::KillProcessGroup(int signal) {
+    LOG(VERBOSE) << "Sending signal " << signal
+                 << " to service '" << name_
+                 << "' (pid " << pid_ << ") process group...\n",
+    kill(pid_, signal);
+    killProcessGroup(uid_, pid_, signal);
+}
+
+void Service::CreateSockets(const std::string& context) {
+    for (const auto& si : sockets_) {
+        int socket_type = ((si.type == "stream" ? SOCK_STREAM :
+                            (si.type == "dgram" ? SOCK_DGRAM :
+                             SOCK_SEQPACKET)));
+        const char* socketcon = !si.socketcon.empty() ? si.socketcon.c_str() : context.c_str();
+
+        int s = create_socket(si.name.c_str(), socket_type, si.perm, si.uid, si.gid, socketcon);
+        if (s >= 0) {
+            PublishSocket(si.name, s);
+        }
+    }
+}
+
+void Service::SetProcessAttributes() {
+    // TODO: work out why this fails for `console` then upgrade to FATAL.
+    if (setpgid(0, getpid()) == -1) PLOG(ERROR) << "setpgid failed for " << name_;
+
+    if (gid_) {
+        if (setgid(gid_) != 0) {
+            PLOG(FATAL) << "setgid failed for " << name_;
+        }
+    }
+    if (!supp_gids_.empty()) {
+        if (setgroups(supp_gids_.size(), &supp_gids_[0]) != 0) {
+            PLOG(FATAL) << "setgroups failed for " << name_;
+        }
+    }
+    if (uid_) {
+        if (setuid(uid_) != 0) {
+            PLOG(FATAL) << "setuid failed for " << name_;
+        }
+    }
+    if (!seclabel_.empty()) {
+        if (setexeccon(seclabel_.c_str()) < 0) {
+            PLOG(FATAL) << "cannot setexeccon('" << seclabel_ << "') for " << name_;
+        }
+    }
+    if (priority_ != 0) {
+        if (setpriority(PRIO_PROCESS, 0, priority_) != 0) {
+            PLOG(FATAL) << "setpriority failed for " << name_;
+        }
+    }
+}
+
 bool Service::Reap() {
     if (!(flags_ & SVC_ONESHOT) || (flags_ & SVC_RESTART)) {
-        NOTICE("Service '%s' (pid %d) killing any children in process group\n",
-               name_.c_str(), pid_);
-        killProcessGroup(uid_, pid_, SIGKILL);
+        KillProcessGroup(SIGKILL);
     }
 
     // Remove any sockets we may have created.
@@ -109,7 +261,7 @@
     }
 
     if (flags_ & SVC_EXEC) {
-        INFO("SVC_EXEC pid %d finished...\n", pid_);
+        LOG(INFO) << "SVC_EXEC pid " << pid_ << " finished...";
         return true;
     }
 
@@ -132,9 +284,10 @@
     if ((flags_ & SVC_CRITICAL) && !(flags_ & SVC_RESTART)) {
         if (time_crashed_ + CRITICAL_CRASH_WINDOW >= now) {
             if (++nr_crashed_ > CRITICAL_CRASH_THRESHOLD) {
-                ERROR("critical process '%s' exited %d times in %d minutes; "
-                      "rebooting into recovery mode\n", name_.c_str(),
-                      CRITICAL_CRASH_THRESHOLD, CRITICAL_CRASH_WINDOW / 60);
+                LOG(ERROR) << "critical process '" << name_ << "' exited "
+                           << CRITICAL_CRASH_THRESHOLD << " times in "
+                           << (CRITICAL_CRASH_WINDOW / 60) << " minutes; "
+                           << "rebooting into recovery mode";
                 android_reboot(ANDROID_RB_RESTART2, 0, "recovery");
                 return false;
             }
@@ -155,41 +308,37 @@
 }
 
 void Service::DumpState() const {
-    INFO("service %s\n", name_.c_str());
-    INFO("  class '%s'\n", classname_.c_str());
-    INFO("  exec");
-    for (const auto& s : args_) {
-        INFO(" '%s'", s.c_str());
-    }
-    INFO("\n");
+    LOG(INFO) << "service " << name_;
+    LOG(INFO) << "  class '" << classname_ << "'";
+    LOG(INFO) << "  exec "<< android::base::Join(args_, " ");
     for (const auto& si : sockets_) {
-        INFO("  socket %s %s 0%o\n", si.name.c_str(), si.type.c_str(), si.perm);
+        LOG(INFO) << "  socket " << si.name << " " << si.type << " " << std::oct << si.perm;
     }
 }
 
-bool Service::HandleClass(const std::vector<std::string>& args, std::string* err) {
+bool Service::ParseClass(const std::vector<std::string>& args, std::string* err) {
     classname_ = args[1];
     return true;
 }
 
-bool Service::HandleConsole(const std::vector<std::string>& args, std::string* err) {
+bool Service::ParseConsole(const std::vector<std::string>& args, std::string* err) {
     flags_ |= SVC_CONSOLE;
     console_ = args.size() > 1 ? "/dev/" + args[1] : "";
     return true;
 }
 
-bool Service::HandleCritical(const std::vector<std::string>& args, std::string* err) {
+bool Service::ParseCritical(const std::vector<std::string>& args, std::string* err) {
     flags_ |= SVC_CRITICAL;
     return true;
 }
 
-bool Service::HandleDisabled(const std::vector<std::string>& args, std::string* err) {
+bool Service::ParseDisabled(const std::vector<std::string>& args, std::string* err) {
     flags_ |= SVC_DISABLED;
     flags_ |= SVC_RC_DISABLED;
     return true;
 }
 
-bool Service::HandleGroup(const std::vector<std::string>& args, std::string* err) {
+bool Service::ParseGroup(const std::vector<std::string>& args, std::string* err) {
     gid_ = decode_uid(args[1].c_str());
     for (std::size_t n = 2; n < args.size(); n++) {
         supp_gids_.emplace_back(decode_uid(args[n].c_str()));
@@ -197,7 +346,20 @@
     return true;
 }
 
-bool Service::HandleIoprio(const std::vector<std::string>& args, std::string* err) {
+bool Service::ParsePriority(const std::vector<std::string>& args, std::string* err) {
+    priority_ = std::stoi(args[1]);
+
+    if (priority_ < ANDROID_PRIORITY_HIGHEST || priority_ > ANDROID_PRIORITY_LOWEST) {
+        priority_ = 0;
+        *err = StringPrintf("process priority value must be range %d - %d",
+                ANDROID_PRIORITY_HIGHEST, ANDROID_PRIORITY_LOWEST);
+        return false;
+    }
+
+    return true;
+}
+
+bool Service::ParseIoprio(const std::vector<std::string>& args, std::string* err) {
     ioprio_pri_ = std::stoul(args[2], 0, 8);
 
     if (ioprio_pri_ < 0 || ioprio_pri_ > 7) {
@@ -219,36 +381,52 @@
     return true;
 }
 
-bool Service::HandleKeycodes(const std::vector<std::string>& args, std::string* err) {
+bool Service::ParseKeycodes(const std::vector<std::string>& args, std::string* err) {
     for (std::size_t i = 1; i < args.size(); i++) {
         keycodes_.emplace_back(std::stoi(args[i]));
     }
     return true;
 }
 
-bool Service::HandleOneshot(const std::vector<std::string>& args, std::string* err) {
+bool Service::ParseOneshot(const std::vector<std::string>& args, std::string* err) {
     flags_ |= SVC_ONESHOT;
     return true;
 }
 
-bool Service::HandleOnrestart(const std::vector<std::string>& args, std::string* err) {
+bool Service::ParseOnrestart(const std::vector<std::string>& args, std::string* err) {
     std::vector<std::string> str_args(args.begin() + 1, args.end());
     onrestart_.AddCommand(str_args, "", 0, err);
     return true;
 }
 
-bool Service::HandleSeclabel(const std::vector<std::string>& args, std::string* err) {
+bool Service::ParseNamespace(const std::vector<std::string>& args, std::string* err) {
+    for (size_t i = 1; i < args.size(); i++) {
+        if (args[i] == "pid") {
+            namespace_flags_ |= CLONE_NEWPID;
+            // PID namespaces require mount namespaces.
+            namespace_flags_ |= CLONE_NEWNS;
+        } else if (args[i] == "mnt") {
+            namespace_flags_ |= CLONE_NEWNS;
+        } else {
+            *err = "namespace must be 'pid' or 'mnt'";
+            return false;
+        }
+    }
+    return true;
+}
+
+bool Service::ParseSeclabel(const std::vector<std::string>& args, std::string* err) {
     seclabel_ = args[1];
     return true;
 }
 
-bool Service::HandleSetenv(const std::vector<std::string>& args, std::string* err) {
+bool Service::ParseSetenv(const std::vector<std::string>& args, std::string* err) {
     envvars_.emplace_back(args[1], args[2]);
     return true;
 }
 
 /* name type perm [ uid gid context ] */
-bool Service::HandleSocket(const std::vector<std::string>& args, std::string* err) {
+bool Service::ParseSocket(const std::vector<std::string>& args, std::string* err) {
     if (args[2] != "dgram" && args[2] != "stream" && args[2] != "seqpacket") {
         *err = "socket type must be 'dgram', 'stream' or 'seqpacket'";
         return false;
@@ -263,59 +441,61 @@
     return true;
 }
 
-bool Service::HandleUser(const std::vector<std::string>& args, std::string* err) {
+bool Service::ParseUser(const std::vector<std::string>& args, std::string* err) {
     uid_ = decode_uid(args[1].c_str());
     return true;
 }
 
-bool Service::HandleWritepid(const std::vector<std::string>& args, std::string* err) {
+bool Service::ParseWritepid(const std::vector<std::string>& args, std::string* err) {
     writepid_files_.assign(args.begin() + 1, args.end());
     return true;
 }
 
-class Service::OptionHandlerMap : public KeywordMap<OptionHandler> {
+class Service::OptionParserMap : public KeywordMap<OptionParser> {
 public:
-    OptionHandlerMap() {
+    OptionParserMap() {
     }
 private:
     Map& map() const override;
 };
 
-Service::OptionHandlerMap::Map& Service::OptionHandlerMap::map() const {
+Service::OptionParserMap::Map& Service::OptionParserMap::map() const {
     constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
-    static const Map option_handlers = {
-        {"class",       {1,     1,    &Service::HandleClass}},
-        {"console",     {0,     1,    &Service::HandleConsole}},
-        {"critical",    {0,     0,    &Service::HandleCritical}},
-        {"disabled",    {0,     0,    &Service::HandleDisabled}},
-        {"group",       {1,     NR_SVC_SUPP_GIDS + 1, &Service::HandleGroup}},
-        {"ioprio",      {2,     2,    &Service::HandleIoprio}},
-        {"keycodes",    {1,     kMax, &Service::HandleKeycodes}},
-        {"oneshot",     {0,     0,    &Service::HandleOneshot}},
-        {"onrestart",   {1,     kMax, &Service::HandleOnrestart}},
-        {"seclabel",    {1,     1,    &Service::HandleSeclabel}},
-        {"setenv",      {2,     2,    &Service::HandleSetenv}},
-        {"socket",      {3,     6,    &Service::HandleSocket}},
-        {"user",        {1,     1,    &Service::HandleUser}},
-        {"writepid",    {1,     kMax, &Service::HandleWritepid}},
+    static const Map option_parsers = {
+        {"class",       {1,     1,    &Service::ParseClass}},
+        {"console",     {0,     1,    &Service::ParseConsole}},
+        {"critical",    {0,     0,    &Service::ParseCritical}},
+        {"disabled",    {0,     0,    &Service::ParseDisabled}},
+        {"group",       {1,     NR_SVC_SUPP_GIDS + 1, &Service::ParseGroup}},
+        {"ioprio",      {2,     2,    &Service::ParseIoprio}},
+        {"priority",    {1,     1,    &Service::ParsePriority}},
+        {"keycodes",    {1,     kMax, &Service::ParseKeycodes}},
+        {"oneshot",     {0,     0,    &Service::ParseOneshot}},
+        {"onrestart",   {1,     kMax, &Service::ParseOnrestart}},
+        {"namespace",   {1,     2,    &Service::ParseNamespace}},
+        {"seclabel",    {1,     1,    &Service::ParseSeclabel}},
+        {"setenv",      {2,     2,    &Service::ParseSetenv}},
+        {"socket",      {3,     6,    &Service::ParseSocket}},
+        {"user",        {1,     1,    &Service::ParseUser}},
+        {"writepid",    {1,     kMax, &Service::ParseWritepid}},
     };
-    return option_handlers;
+    return option_parsers;
 }
 
-bool Service::HandleLine(const std::vector<std::string>& args, std::string* err) {
+bool Service::ParseLine(const std::vector<std::string>& args, std::string* err) {
     if (args.empty()) {
         *err = "option needed, but not provided";
         return false;
     }
 
-    static const OptionHandlerMap handler_map;
-    auto handler = handler_map.FindFunction(args[0], args.size() - 1, err);
+    static const OptionParserMap parser_map;
+    auto parser = parser_map.FindFunction(args[0], args.size() - 1, err);
 
-    if (!handler) {
+    if (!parser) {
         return false;
     }
 
-    return (this->*handler)(args, err);
+    return (this->*parser)(args, err);
 }
 
 bool Service::Start() {
@@ -339,8 +519,7 @@
 
         bool have_console = (open(console_.c_str(), O_RDWR | O_CLOEXEC) != -1);
         if (!have_console) {
-            ERROR("service '%s' couldn't open console '%s': %s\n",
-                  name_.c_str(), console_.c_str(), strerror(errno));
+            PLOG(ERROR) << "service '" << name_ << "' couldn't open console '" << console_ << "'";
             flags_ |= SVC_DISABLED;
             return false;
         }
@@ -348,8 +527,7 @@
 
     struct stat sb;
     if (stat(args_[0].c_str(), &sb) == -1) {
-        ERROR("cannot find '%s' (%s), disabling '%s'\n",
-              args_[0].c_str(), strerror(errno), name_.c_str());
+        PLOG(ERROR) << "cannot find '" << args_[0] << "', disabling '" << name_ << "'";
         flags_ |= SVC_DISABLED;
         return false;
     }
@@ -358,80 +536,48 @@
     if (!seclabel_.empty()) {
         scon = seclabel_;
     } else {
-        char* mycon = nullptr;
-        char* fcon = nullptr;
-
-        INFO("computing context for service '%s'\n", args_[0].c_str());
-        int rc = getcon(&mycon);
-        if (rc < 0) {
-            ERROR("could not get context while starting '%s'\n", name_.c_str());
-            return false;
-        }
-
-        rc = getfilecon(args_[0].c_str(), &fcon);
-        if (rc < 0) {
-            ERROR("could not get context while starting '%s'\n", name_.c_str());
-            free(mycon);
-            return false;
-        }
-
-        char* ret_scon = nullptr;
-        rc = security_compute_create(mycon, fcon, string_to_security_class("process"),
-                                     &ret_scon);
-        if (rc == 0) {
-            scon = ret_scon;
-            free(ret_scon);
-        }
-        if (rc == 0 && scon == mycon) {
-            ERROR("Service %s does not have a SELinux domain defined.\n", name_.c_str());
-            free(mycon);
-            free(fcon);
-            return false;
-        }
-        free(mycon);
-        free(fcon);
-        if (rc < 0) {
-            ERROR("could not get context while starting '%s'\n", name_.c_str());
+        LOG(INFO) << "computing context for service '" << name_ << "'";
+        scon = ComputeContextFromExecutable(name_, args_[0]);
+        if (scon == "") {
             return false;
         }
     }
 
-    NOTICE("Starting service '%s'...\n", name_.c_str());
+    LOG(VERBOSE) << "starting service '" << name_ << "'...";
 
-    pid_t pid = fork();
+    pid_t pid = -1;
+    if (namespace_flags_) {
+        pid = clone(nullptr, nullptr, namespace_flags_ | SIGCHLD, nullptr);
+    } else {
+        pid = fork();
+    }
+
     if (pid == 0) {
         umask(077);
 
+        if (namespace_flags_ & CLONE_NEWPID) {
+            // This will fork again to run an init process inside the PID
+            // namespace.
+            SetUpPidNamespace(name_);
+        }
+
         for (const auto& ei : envvars_) {
             add_environment(ei.name.c_str(), ei.value.c_str());
         }
 
-        for (const auto& si : sockets_) {
-            int socket_type = ((si.type == "stream" ? SOCK_STREAM :
-                                (si.type == "dgram" ? SOCK_DGRAM :
-                                 SOCK_SEQPACKET)));
-            const char* socketcon =
-                !si.socketcon.empty() ? si.socketcon.c_str() : scon.c_str();
-
-            int s = create_socket(si.name.c_str(), socket_type, si.perm,
-                                  si.uid, si.gid, socketcon);
-            if (s >= 0) {
-                PublishSocket(si.name, s);
-            }
-        }
+        CreateSockets(scon);
 
         std::string pid_str = StringPrintf("%d", getpid());
         for (const auto& file : writepid_files_) {
             if (!WriteStringToFile(pid_str, file)) {
-                ERROR("couldn't write %s to %s: %s\n",
-                      pid_str.c_str(), file.c_str(), strerror(errno));
+                PLOG(ERROR) << "couldn't write " << pid_str << " to " << file;
             }
         }
 
         if (ioprio_class_ != IoSchedClass_NONE) {
             if (android_set_ioprio(getpid(), ioprio_class_, ioprio_pri_)) {
-                ERROR("Failed to set pid %d ioprio = %d,%d: %s\n",
-                      getpid(), ioprio_class_, ioprio_pri_, strerror(errno));
+                PLOG(ERROR) << "failed to set pid " << getpid()
+                            << " ioprio=" << ioprio_class_ << "," << ioprio_pri_;
             }
         }
 
@@ -442,49 +588,21 @@
             ZapStdio();
         }
 
-        setpgid(0, getpid());
-
-        // As requested, set our gid, supplemental gids, and uid.
-        if (gid_) {
-            if (setgid(gid_) != 0) {
-                ERROR("setgid failed: %s\n", strerror(errno));
-                _exit(127);
-            }
-        }
-        if (!supp_gids_.empty()) {
-            if (setgroups(supp_gids_.size(), &supp_gids_[0]) != 0) {
-                ERROR("setgroups failed: %s\n", strerror(errno));
-                _exit(127);
-            }
-        }
-        if (uid_) {
-            if (setuid(uid_) != 0) {
-                ERROR("setuid failed: %s\n", strerror(errno));
-                _exit(127);
-            }
-        }
-        if (!seclabel_.empty()) {
-            if (setexeccon(seclabel_.c_str()) < 0) {
-                ERROR("cannot setexeccon('%s'): %s\n",
-                      seclabel_.c_str(), strerror(errno));
-                _exit(127);
-            }
-        }
+        // As requested, set our gid, supplemental gids, uid, context, and
+        // priority. Aborts on failure.
+        SetProcessAttributes();
 
         std::vector<char*> strs;
-        for (const auto& s : args_) {
-            strs.push_back(const_cast<char*>(s.c_str()));
-        }
-        strs.push_back(nullptr);
-        if (execve(args_[0].c_str(), (char**) &strs[0], (char**) ENV) < 0) {
-            ERROR("cannot execve('%s'): %s\n", args_[0].c_str(), strerror(errno));
+        ExpandArgs(args_, &strs);
+        if (execve(strs[0], (char**) &strs[0], (char**) ENV) < 0) {
+            PLOG(ERROR) << "cannot execve('" << strs[0] << "')";
         }
 
         _exit(127);
     }
 
     if (pid < 0) {
-        ERROR("failed to start '%s'\n", name_.c_str());
+        PLOG(ERROR) << "failed to fork for '" << name_ << "'";
         pid_ = 0;
         return false;
     }
@@ -492,12 +610,17 @@
     time_started_ = gettime();
     pid_ = pid;
     flags_ |= SVC_RUNNING;
-    createProcessGroup(uid_, pid_);
+
+    errno = -createProcessGroup(uid_, pid_);
+    if (errno != 0) {
+        PLOG(ERROR) << "createProcessGroup(" << uid_ << ", " << pid_ << ") failed for service '"
+                    << name_ << "'";
+    }
 
     if ((flags_ & SVC_EXEC) != 0) {
-        INFO("SVC_EXEC pid %d (uid %d gid %d+%zu context %s) started; waiting...\n",
-             pid_, uid_, gid_, supp_gids_.size(),
-             !seclabel_.empty() ? seclabel_.c_str() : "default");
+        LOG(INFO) << android::base::StringPrintf(
+            "SVC_EXEC pid %d (uid %d gid %d+%zu context %s) started; waiting...", pid_, uid_, gid_,
+            supp_gids_.size(), !seclabel_.empty() ? seclabel_.c_str() : "default");
     }
 
     NotifyStateChange("running");
@@ -533,9 +656,7 @@
     flags_ &= ~(SVC_RESTARTING | SVC_DISABLED_START);
     flags_ |= SVC_DISABLED;
     if (pid_) {
-        NOTICE("Sending SIGTERM to service '%s' (pid %d)...\n", name_.c_str(),
-               pid_);
-        killProcessGroup(uid_, pid_, SIGTERM);
+        KillProcessGroup(SIGTERM);
         NotifyStateChange("stopping");
     }
 }
@@ -565,19 +686,18 @@
     }
 }
 
-/* The how field should be either SVC_DISABLED, SVC_RESET, or SVC_RESTART */
+// The how field should be either SVC_DISABLED, SVC_RESET, or SVC_RESTART.
 void Service::StopOrReset(int how) {
-    /* The service is still SVC_RUNNING until its process exits, but if it has
-     * already exited it shoudn't attempt a restart yet. */
+    // The service is still SVC_RUNNING until its process exits, but if it has
+    // already exited it shoudn't attempt a restart yet.
     flags_ &= ~(SVC_RESTARTING | SVC_DISABLED_START);
 
     if ((how != SVC_DISABLED) && (how != SVC_RESET) && (how != SVC_RESTART)) {
-        /* Hrm, an illegal flag.  Default to SVC_DISABLED */
+        // An illegal flag: default to SVC_DISABLED.
         how = SVC_DISABLED;
     }
-        /* if the service has not yet started, prevent
-         * it from auto-starting with its class
-         */
+
+    // If the service has not yet started, prevent it from auto-starting with its class.
     if (how == SVC_RESET) {
         flags_ |= (flags_ & SVC_RC_DISABLED) ? SVC_DISABLED : SVC_RESET;
     } else {
@@ -585,8 +705,7 @@
     }
 
     if (pid_) {
-        NOTICE("Service '%s' is being killed...\n", name_.c_str());
-        killProcessGroup(uid_, pid_, SIGKILL);
+        KillProcessGroup(SIGKILL);
         NotifyStateChange("stopping");
     } else {
         NotifyStateChange("stopped");
@@ -634,8 +753,7 @@
 void ServiceManager::AddService(std::unique_ptr<Service> service) {
     Service* old_service = FindServiceByName(service->name());
     if (old_service) {
-        ERROR("ignored duplicate definition of service '%s'",
-              service->name().c_str());
+        LOG(ERROR) << "ignored duplicate definition of service '" << service->name() << "'";
         return;
     }
     services_.emplace_back(std::move(service));
@@ -652,12 +770,12 @@
         }
     }
     if (command_arg > 4 + NR_SVC_SUPP_GIDS) {
-        ERROR("exec called with too many supplementary group ids\n");
+        LOG(ERROR) << "exec called with too many supplementary group ids";
         return nullptr;
     }
 
     if (command_arg >= args.size()) {
-        ERROR("exec called without command\n");
+        LOG(ERROR) << "exec called without command";
         return nullptr;
     }
     std::vector<std::string> str_args(args.begin() + command_arg, args.end());
@@ -665,6 +783,7 @@
     exec_count_++;
     std::string name = StringPrintf("exec %d (%s)", exec_count_, str_args[0].c_str());
     unsigned flags = SVC_EXEC | SVC_ONESHOT;
+    unsigned namespace_flags = 0;
 
     std::string seclabel = "";
     if (command_arg > 2 && args[1] != "-") {
@@ -685,10 +804,10 @@
     }
 
     std::unique_ptr<Service> svc_p(new Service(name, "default", flags, uid, gid,
-                                               supp_gids, seclabel, str_args));
+                                               supp_gids, namespace_flags,
+                                               seclabel, str_args));
     if (!svc_p) {
-        ERROR("Couldn't allocate service for exec of '%s'",
-              str_args[0].c_str());
+        LOG(ERROR) << "Couldn't allocate service for exec of '" << str_args[0] << "'";
         return nullptr;
     }
     Service* svc = svc_p.get();
@@ -771,7 +890,6 @@
     for (const auto& s : services_) {
         s->DumpState();
     }
-    INFO("\n");
 }
 
 bool ServiceManager::ReapOneProcess() {
@@ -780,7 +898,7 @@
     if (pid == 0) {
         return false;
     } else if (pid == -1) {
-        ERROR("waitpid failed: %s\n", strerror(errno));
+        PLOG(ERROR) << "waitpid failed";
         return false;
     }
 
@@ -795,13 +913,13 @@
     }
 
     if (WIFEXITED(status)) {
-        NOTICE("%s exited with status %d\n", name.c_str(), WEXITSTATUS(status));
+        LOG(VERBOSE) << name << " exited with status " << WEXITSTATUS(status);
     } else if (WIFSIGNALED(status)) {
-        NOTICE("%s killed by signal %d\n", name.c_str(), WTERMSIG(status));
+        LOG(VERBOSE) << name << " killed by signal " << WTERMSIG(status);
     } else if (WIFSTOPPED(status)) {
-        NOTICE("%s stopped by signal %d\n", name.c_str(), WSTOPSIG(status));
+        LOG(VERBOSE) << name << " stopped by signal " << WSTOPSIG(status);
     } else {
-        NOTICE("%s state changed", name.c_str());
+        LOG(VERBOSE) << name << " state changed";
     }
 
     if (!svc) {
@@ -842,7 +960,7 @@
 bool ServiceParser::ParseLineSection(const std::vector<std::string>& args,
                                      const std::string& filename, int line,
                                      std::string* err) const {
-    return service_ ? service_->HandleLine(args, err) : false;
+    return service_ ? service_->ParseLine(args, err) : false;
 }
 
 void ServiceParser::EndSection() {
diff --git a/init/service.h b/init/service.h
index d6ce664..aa73aaf 100644
--- a/init/service.h
+++ b/init/service.h
@@ -72,10 +72,11 @@
             const std::vector<std::string>& args);
 
     Service(const std::string& name, const std::string& classname,
-            unsigned flags, uid_t uid, gid_t gid, const std::vector<gid_t>& supp_gids,
-            const std::string& seclabel,  const std::vector<std::string>& args);
+            unsigned flags, uid_t uid, gid_t gid,
+            const std::vector<gid_t>& supp_gids, unsigned namespace_flags,
+            const std::string& seclabel, const std::vector<std::string>& args);
 
-    bool HandleLine(const std::vector<std::string>& args, std::string* err);
+    bool ParseLine(const std::vector<std::string>& args, std::string* err);
     bool Start();
     bool StartIfNotDisabled();
     bool Enable();
@@ -93,6 +94,7 @@
     pid_t pid() const { return pid_; }
     uid_t uid() const { return uid_; }
     gid_t gid() const { return gid_; }
+    int priority() const { return priority_; }
     const std::vector<gid_t>& supp_gids() const { return supp_gids_; }
     const std::string& seclabel() const { return seclabel_; }
     const std::vector<int>& keycodes() const { return keycodes_; }
@@ -101,30 +103,35 @@
     const std::vector<std::string>& args() const { return args_; }
 
 private:
-    using OptionHandler = bool (Service::*) (const std::vector<std::string>& args,
-                                             std::string* err);
-    class OptionHandlerMap;
+    using OptionParser = bool (Service::*) (const std::vector<std::string>& args,
+                                            std::string* err);
+    class OptionParserMap;
 
     void NotifyStateChange(const std::string& new_state) const;
     void StopOrReset(int how);
     void ZapStdio() const;
     void OpenConsole() const;
     void PublishSocket(const std::string& name, int fd) const;
+    void KillProcessGroup(int signal);
+    void CreateSockets(const std::string& scon);
+    void SetProcessAttributes();
 
-    bool HandleClass(const std::vector<std::string>& args, std::string* err);
-    bool HandleConsole(const std::vector<std::string>& args, std::string* err);
-    bool HandleCritical(const std::vector<std::string>& args, std::string* err);
-    bool HandleDisabled(const std::vector<std::string>& args, std::string* err);
-    bool HandleGroup(const std::vector<std::string>& args, std::string* err);
-    bool HandleIoprio(const std::vector<std::string>& args, std::string* err);
-    bool HandleKeycodes(const std::vector<std::string>& args, std::string* err);
-    bool HandleOneshot(const std::vector<std::string>& args, std::string* err);
-    bool HandleOnrestart(const std::vector<std::string>& args, std::string* err);
-    bool HandleSeclabel(const std::vector<std::string>& args, std::string* err);
-    bool HandleSetenv(const std::vector<std::string>& args, std::string* err);
-    bool HandleSocket(const std::vector<std::string>& args, std::string* err);
-    bool HandleUser(const std::vector<std::string>& args, std::string* err);
-    bool HandleWritepid(const std::vector<std::string>& args, std::string* err);
+    bool ParseClass(const std::vector<std::string>& args, std::string* err);
+    bool ParseConsole(const std::vector<std::string>& args, std::string* err);
+    bool ParseCritical(const std::vector<std::string>& args, std::string* err);
+    bool ParseDisabled(const std::vector<std::string>& args, std::string* err);
+    bool ParseGroup(const std::vector<std::string>& args, std::string* err);
+    bool ParsePriority(const std::vector<std::string>& args, std::string* err);
+    bool ParseIoprio(const std::vector<std::string>& args, std::string* err);
+    bool ParseKeycodes(const std::vector<std::string>& args, std::string* err);
+    bool ParseOneshot(const std::vector<std::string>& args, std::string* err);
+    bool ParseOnrestart(const std::vector<std::string>& args, std::string* err);
+    bool ParseNamespace(const std::vector<std::string>& args, std::string* err);
+    bool ParseSeclabel(const std::vector<std::string>& args, std::string* err);
+    bool ParseSetenv(const std::vector<std::string>& args, std::string* err);
+    bool ParseSocket(const std::vector<std::string>& args, std::string* err);
+    bool ParseUser(const std::vector<std::string>& args, std::string* err);
+    bool ParseWritepid(const std::vector<std::string>& args, std::string* err);
 
     std::string name_;
     std::string classname_;
@@ -139,6 +146,7 @@
     uid_t uid_;
     gid_t gid_;
     std::vector<gid_t> supp_gids_;
+    unsigned namespace_flags_;
 
     std::string seclabel_;
 
@@ -155,6 +163,7 @@
 
     IoSchedClass ioprio_class_;
     int ioprio_pri_;
+    int priority_;
 
     std::vector<std::string> args_;
 };
diff --git a/init/signal_handler.cpp b/init/signal_handler.cpp
index ea483d4..0dea3e0 100644
--- a/init/signal_handler.cpp
+++ b/init/signal_handler.cpp
@@ -47,7 +47,7 @@
 
 static void SIGCHLD_handler(int) {
     if (TEMP_FAILURE_RETRY(write(signal_write_fd, "1", 1)) == -1) {
-        ERROR("write(signal_write_fd) failed: %s\n", strerror(errno));
+        PLOG(ERROR) << "write(signal_write_fd) failed";
     }
 }
 
@@ -55,7 +55,7 @@
     // Create a signalling mechanism for SIGCHLD.
     int s[2];
     if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0, s) == -1) {
-        ERROR("socketpair failed: %s\n", strerror(errno));
+        PLOG(ERROR) << "socketpair failed";
         exit(1);
     }
 
diff --git a/init/ueventd.cpp b/init/ueventd.cpp
index 73b2136..e7794ec 100644
--- a/init/ueventd.cpp
+++ b/init/ueventd.cpp
@@ -52,11 +52,9 @@
      */
     signal(SIGCHLD, SIG_IGN);
 
-    open_devnull_stdio();
-    klog_init();
-    klog_set_level(KLOG_NOTICE_LEVEL);
+    InitKernelLogging(argv);
 
-    NOTICE("ueventd started!\n");
+    LOG(INFO) << "ueventd started!";
 
     selinux_callback cb;
     cb.func_log = selinux_klog_callback;
@@ -108,45 +106,36 @@
     name = args[0];
 
     if (!strncmp(name,"/sys/", 5) && (nargs == 5)) {
-        INFO("/sys/ rule %s %s\n",args[0],args[1]);
+        LOG(INFO) << "/sys/ rule " << args[0] << " " << args[1];
         attr = args[1];
         args++;
         nargs--;
     }
 
     if (nargs != 4) {
-        ERROR("invalid line ueventd.rc line for '%s'\n", args[0]);
+        LOG(ERROR) << "invalid line ueventd.rc line for '" << args[0] << "'";
         return;
     }
 
-    /* If path starts with mtd@ lookup the mount number. */
-    if (!strncmp(name, "mtd@", 4)) {
-        int n = mtd_name_to_number(name + 4);
-        if (n >= 0)
-            asprintf(&tmp, "/dev/mtd/mtd%d", n);
-        name = tmp;
-    } else {
-        int len = strlen(name);
-        char *wildcard_chr = strchr(name, '*');
-        if ((name[len - 1] == '*') &&
-            (wildcard_chr == (name + len - 1))) {
-            prefix = 1;
-            name[len - 1] = '\0';
-        } else if (wildcard_chr) {
-            wildcard = 1;
-        }
+    int len = strlen(name);
+    char *wildcard_chr = strchr(name, '*');
+    if ((name[len - 1] == '*') && (wildcard_chr == (name + len - 1))) {
+        prefix = 1;
+        name[len - 1] = '\0';
+    } else if (wildcard_chr) {
+        wildcard = 1;
     }
 
     perm = strtol(args[1], &endptr, 8);
     if (!endptr || *endptr != '\0') {
-        ERROR("invalid mode '%s'\n", args[1]);
+        LOG(ERROR) << "invalid mode '" << args[1] << "'";
         free(tmp);
         return;
     }
 
     struct passwd* pwd = getpwnam(args[2]);
     if (!pwd) {
-        ERROR("invalid uid '%s'\n", args[2]);
+        LOG(ERROR) << "invalid uid '" << args[2] << "'";
         free(tmp);
         return;
     }
@@ -154,7 +143,7 @@
 
     struct group* grp = getgrnam(args[3]);
     if (!grp) {
-        ERROR("invalid gid '%s'\n", args[3]);
+        LOG(ERROR) << "invalid gid '" << args[3] << "'";
         free(tmp);
         return;
     }
diff --git a/init/util.cpp b/init/util.cpp
index 750e040..6c1923f 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -34,6 +34,7 @@
 #include <sys/un.h>
 
 #include <android-base/file.h>
+#include <android-base/logging.h>
 #include <android-base/strings.h>
 
 /* for ANDROID_SOCKET_* */
@@ -74,7 +75,7 @@
 unsigned int decode_uid(const char *s) {
     unsigned int v = do_decode_uid(s);
     if (v == UINT_MAX) {
-        ERROR("decode_uid: Unable to find UID for '%s'. Returning UINT_MAX\n", s);
+        LOG(ERROR) << "decode_uid: Unable to find UID for '" << s << "'; returning UINT_MAX";
     }
     return v;
 }
@@ -94,14 +95,14 @@
 
     if (socketcon) {
         if (setsockcreatecon(socketcon) == -1) {
-            ERROR("setsockcreatecon(\"%s\") failed: %s\n", socketcon, strerror(errno));
+            PLOG(ERROR) << "setsockcreatecon(\"" << socketcon << "\") failed";
             return -1;
         }
     }
 
     fd = socket(PF_UNIX, type, 0);
     if (fd < 0) {
-        ERROR("Failed to open socket '%s': %s\n", name, strerror(errno));
+        PLOG(ERROR) << "Failed to open socket '" << name << "'";
         return -1;
     }
 
@@ -115,7 +116,7 @@
 
     ret = unlink(addr.sun_path);
     if (ret != 0 && errno != ENOENT) {
-        ERROR("Failed to unlink old socket '%s': %s\n", name, strerror(errno));
+        PLOG(ERROR) << "Failed to unlink old socket '" << name << "'";
         goto out_close;
     }
 
@@ -133,23 +134,26 @@
     freecon(filecon);
 
     if (ret) {
-        ERROR("Failed to bind socket '%s': %s\n", name, strerror(savederrno));
+      errno = savederrno;
+        PLOG(ERROR) << "Failed to bind socket '" << name << "'";
         goto out_unlink;
     }
 
     ret = lchown(addr.sun_path, uid, gid);
     if (ret) {
-        ERROR("Failed to lchown socket '%s': %s\n", addr.sun_path, strerror(errno));
+        PLOG(ERROR) << "Failed to lchown socket '" << addr.sun_path << "'";
         goto out_unlink;
     }
     ret = fchmodat(AT_FDCWD, addr.sun_path, perm, AT_SYMLINK_NOFOLLOW);
     if (ret) {
-        ERROR("Failed to fchmodat socket '%s': %s\n", addr.sun_path, strerror(errno));
+        PLOG(ERROR) << "Failed to fchmodat socket '" << addr.sun_path << "'";
         goto out_unlink;
     }
 
-    INFO("Created socket '%s' with mode '%o', user '%d', group '%d'\n",
-         addr.sun_path, perm, uid, gid);
+    LOG(INFO) << "Created socket '" << addr.sun_path << "'"
+              << ", mode " << std::oct << perm << std::dec
+              << ", user " << uid
+              << ", group " << gid;
 
     return fd;
 
@@ -172,11 +176,11 @@
     // or group-writable files.
     struct stat sb;
     if (fstat(fd, &sb) == -1) {
-        ERROR("fstat failed for '%s': %s\n", path, strerror(errno));
+        PLOG(ERROR) << "fstat failed for '" << path << "'";
         return false;
     }
     if ((sb.st_mode & (S_IWGRP | S_IWOTH)) != 0) {
-        ERROR("skipping insecure file '%s'\n", path);
+        PLOG(ERROR) << "skipping insecure file '" << path << "'";
         return false;
     }
 
@@ -188,89 +192,17 @@
 int write_file(const char* path, const char* content) {
     int fd = TEMP_FAILURE_RETRY(open(path, O_WRONLY|O_CREAT|O_NOFOLLOW|O_CLOEXEC, 0600));
     if (fd == -1) {
-        NOTICE("write_file: Unable to open '%s': %s\n", path, strerror(errno));
+        PLOG(ERROR) << "write_file: Unable to open '" << path << "'";
         return -1;
     }
     int result = android::base::WriteStringToFd(content, fd) ? 0 : -1;
     if (result == -1) {
-        NOTICE("write_file: Unable to write to '%s': %s\n", path, strerror(errno));
+        PLOG(ERROR) << "write_file: Unable to write to '" << path << "'";
     }
     close(fd);
     return result;
 }
 
-#define MAX_MTD_PARTITIONS 16
-
-static struct {
-    char name[16];
-    int number;
-} mtd_part_map[MAX_MTD_PARTITIONS];
-
-static int mtd_part_count = -1;
-
-static void find_mtd_partitions(void)
-{
-    int fd;
-    char buf[1024];
-    char *pmtdbufp;
-    ssize_t pmtdsize;
-    int r;
-
-    fd = open("/proc/mtd", O_RDONLY|O_CLOEXEC);
-    if (fd < 0)
-        return;
-
-    buf[sizeof(buf) - 1] = '\0';
-    pmtdsize = read(fd, buf, sizeof(buf) - 1);
-    pmtdbufp = buf;
-    while (pmtdsize > 0) {
-        int mtdnum, mtdsize, mtderasesize;
-        char mtdname[16];
-        mtdname[0] = '\0';
-        mtdnum = -1;
-        r = sscanf(pmtdbufp, "mtd%d: %x %x %15s",
-                   &mtdnum, &mtdsize, &mtderasesize, mtdname);
-        if ((r == 4) && (mtdname[0] == '"')) {
-            char *x = strchr(mtdname + 1, '"');
-            if (x) {
-                *x = 0;
-            }
-            INFO("mtd partition %d, %s\n", mtdnum, mtdname + 1);
-            if (mtd_part_count < MAX_MTD_PARTITIONS) {
-                strcpy(mtd_part_map[mtd_part_count].name, mtdname + 1);
-                mtd_part_map[mtd_part_count].number = mtdnum;
-                mtd_part_count++;
-            } else {
-                ERROR("too many mtd partitions\n");
-            }
-        }
-        while (pmtdsize > 0 && *pmtdbufp != '\n') {
-            pmtdbufp++;
-            pmtdsize--;
-        }
-        if (pmtdsize > 0) {
-            pmtdbufp++;
-            pmtdsize--;
-        }
-    }
-    close(fd);
-}
-
-int mtd_name_to_number(const char *name)
-{
-    int n;
-    if (mtd_part_count < 0) {
-        mtd_part_count = 0;
-        find_mtd_partitions();
-    }
-    for (n = 0; n < mtd_part_count; n++) {
-        if (!strcmp(name, mtd_part_map[n].name)) {
-            return mtd_part_map[n].number;
-        }
-    }
-    return -1;
-}
-
 time_t gettime() {
     timespec now;
     clock_gettime(CLOCK_MONOTONIC, &now);
@@ -300,7 +232,7 @@
         if (width == 0)
             continue;
         if ((unsigned int)width > sizeof(buf) - 1) {
-            ERROR("path too long for mkdir_recursive\n");
+            LOG(ERROR) << "path too long for mkdir_recursive";
             return -1;
         }
         memcpy(buf, pathname, width);
@@ -354,12 +286,10 @@
     memcpy(buf, newpath, width);
     buf[width] = 0;
     ret = mkdir_recursive(buf, 0755);
-    if (ret)
-        ERROR("Failed to create directory %s: %s (%d)\n", buf, strerror(errno), errno);
+    if (ret) PLOG(ERROR) << "Failed to create directory " << buf;
 
     ret = symlink(oldpath, newpath);
-    if (ret && errno != EEXIST)
-        ERROR("Failed to symlink %s to %s: %s (%d)\n", oldpath, newpath, strerror(errno), errno);
+    if (ret && errno != EEXIST) PLOG(ERROR) << "Failed to symlink " << oldpath << " to " << newpath;
 }
 
 void remove_link(const char *oldpath, const char *newpath)
@@ -386,30 +316,6 @@
     return ret;
 }
 
-void open_devnull_stdio(void)
-{
-    int fd = open("/sys/fs/selinux/null", O_RDWR);
-    if (fd == -1) {
-        /* Fail silently.
-         * stdout/stderr isn't available, and because
-         * klog_init() is called after open_devnull_stdio(), we can't
-         * log to dmesg. Reordering klog_init() to be called before
-         * open_devnull_stdio() isn't an option either, as then klog_fd
-         * will be assigned 0 or 1, which will end up getting clobbered
-         * by the code below. There's nowhere good to log.
-         */
-
-        exit(1);
-    }
-
-    dup2(fd, 0);
-    dup2(fd, 1);
-    dup2(fd, 2);
-    if (fd > 2) {
-        close(fd);
-    }
-}
-
 void import_kernel_cmdline(bool in_qemu,
                            std::function<void(const std::string&, const std::string&, bool)> fn) {
     std::string cmdline;
@@ -489,6 +395,7 @@
      * - will accept $$ as a literal $.
      * - no nested property expansion, i.e. ${foo.${bar}} is not supported,
      *   bad things will happen
+     * - ${x.y:-default} will return default value if property empty.
      */
     while (*src_ptr) {
         const char* c;
@@ -511,33 +418,40 @@
         }
 
         std::string prop_name;
+        std::string def_val;
         if (*c == '{') {
             c++;
             const char* end = strchr(c, '}');
             if (!end) {
                 // failed to find closing brace, abort.
-                ERROR("unexpected end of string in '%s', looking for }\n", src.c_str());
+                LOG(ERROR) << "unexpected end of string in '" << src << "', looking for }";
                 return false;
             }
             prop_name = std::string(c, end);
             c = end + 1;
+            size_t def = prop_name.find(":-");
+            if (def < prop_name.size()) {
+                def_val = prop_name.substr(def + 2);
+                prop_name = prop_name.substr(0, def);
+            }
         } else {
             prop_name = c;
-            ERROR("using deprecated syntax for specifying property '%s', use ${name} instead\n",
-                  c);
+            LOG(ERROR) << "using deprecated syntax for specifying property '" << c << "', use ${name} instead";
             c += prop_name.size();
         }
 
         if (prop_name.empty()) {
-            ERROR("invalid zero-length prop name in '%s'\n", src.c_str());
+            LOG(ERROR) << "invalid zero-length property name in '" << src << "'";
             return false;
         }
 
         std::string prop_val = property_get(prop_name.c_str());
         if (prop_val.empty()) {
-            ERROR("property '%s' doesn't exist while expanding '%s'\n",
-                  prop_name.c_str(), src.c_str());
-            return false;
+            if (def_val.empty()) {
+                LOG(ERROR) << "property '" << prop_name << "' doesn't exist while expanding '" << src << "'";
+                return false;
+            }
+            prop_val = def_val;
         }
 
         dst->append(prop_val);
diff --git a/init/util.h b/init/util.h
index c2efb01..9d522cc 100644
--- a/init/util.h
+++ b/init/util.h
@@ -23,11 +23,8 @@
 #include <string>
 #include <functional>
 
-#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
-
 #define COLDBOOT_DONE "/dev/.coldboot_done"
 
-int mtd_name_to_number(const char *name);
 int create_socket(const char *name, int type, mode_t perm,
                   uid_t uid, gid_t gid, const char *socketcon);
 
@@ -57,7 +54,6 @@
 void make_link_init(const char *oldpath, const char *newpath);
 void remove_link(const char *oldpath, const char *newpath);
 int wait_for_file(const char *filename, int timeout);
-void open_devnull_stdio(void);
 void import_kernel_cmdline(bool in_qemu,
                            std::function<void(const std::string&, const std::string&, bool)>);
 int make_dir(const char *path, mode_t mode);
diff --git a/init/watchdogd.cpp b/init/watchdogd.cpp
index 0d16db9..b196147 100644
--- a/init/watchdogd.cpp
+++ b/init/watchdogd.cpp
@@ -28,9 +28,7 @@
 #define DEV_NAME "/dev/watchdog"
 
 int watchdogd_main(int argc, char **argv) {
-    open_devnull_stdio();
-    klog_init();
-    klog_set_level(KLOG_NOTICE_LEVEL);
+    InitKernelLogging(argv);
 
     int interval = 10;
     if (argc >= 2) interval = atoi(argv[1]);
@@ -38,30 +36,31 @@
     int margin = 10;
     if (argc >= 3) margin = atoi(argv[2]);
 
-    NOTICE("started (interval %d, margin %d)!\n", interval, margin);
+    LOG(INFO) << "watchdogd started (interval " << interval << ", margin " << margin << ")!";
 
     int fd = open(DEV_NAME, O_RDWR|O_CLOEXEC);
     if (fd == -1) {
-        ERROR("Failed to open %s: %s\n", DEV_NAME, strerror(errno));
+        PLOG(ERROR) << "Failed to open " << DEV_NAME;
         return 1;
     }
 
     int timeout = interval + margin;
     int ret = ioctl(fd, WDIOC_SETTIMEOUT, &timeout);
     if (ret) {
-        ERROR("Failed to set timeout to %d: %s\n", timeout, strerror(errno));
+        PLOG(ERROR) << "Failed to set timeout to " << timeout;
         ret = ioctl(fd, WDIOC_GETTIMEOUT, &timeout);
         if (ret) {
-            ERROR("Failed to get timeout: %s\n", strerror(errno));
+            PLOG(ERROR) << "Failed to get timeout";
         } else {
             if (timeout > margin) {
                 interval = timeout - margin;
             } else {
                 interval = 1;
             }
-            WARNING("Adjusted interval to timeout returned by driver:"
-                    " timeout %d, interval %d, margin %d\n",
-                    timeout, interval, margin);
+            LOG(WARNING) << "Adjusted interval to timeout returned by driver: "
+                         << "timeout " << timeout
+                         << ", interval " << interval
+                         << ", margin " << margin;
         }
     }
 
diff --git a/libbacktrace/Android.bp b/libbacktrace/Android.bp
new file mode 100644
index 0000000..93d997b
--- /dev/null
+++ b/libbacktrace/Android.bp
@@ -0,0 +1,121 @@
+//
+// 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.
+//
+
+cc_defaults {
+    name: "libbacktrace_common",
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+    conlyflags: ["-std=gnu99"],
+    cppflags: ["-std=gnu++11"],
+
+    clang_cflags: ["-Wno-inline-asm"],
+
+    // The latest clang (r230699) does not allow SP/PC to be declared in inline asm lists.
+    include_dirs: ["external/libunwind/include/tdep"],
+
+    // TODO: LLVM_DEVICE_BUILD_MK
+    // TODO: LLVM_HOST_BUILD_MK
+
+    target: {
+        host: {
+            // -fno-omit-frame-pointer should be set for host build. Because currently
+            // libunwind can't recognize .debug_frame using dwarf version 4, and it relies
+            // on stack frame pointer to do unwinding on x86.
+            // $(LLVM_HOST_BUILD_MK) overwrites -fno-omit-frame-pointer. so the below line
+            // must be after the include.
+            cflags: [
+                "-Wno-extern-c-compat",
+                "-fno-omit-frame-pointer",
+            ],
+        },
+
+        darwin: {
+            enabled: false,
+        },
+    },
+
+    multilib: {
+        lib32: {
+            suffix: "32",
+        },
+        lib64: {
+            suffix: "64",
+        },
+    }
+}
+
+libbacktrace_sources = [
+    "Backtrace.cpp",
+    "BacktraceCurrent.cpp",
+    "BacktracePtrace.cpp",
+    "thread_utils.c",
+    "ThreadEntry.cpp",
+    "UnwindCurrent.cpp",
+    "UnwindMap.cpp",
+    "UnwindPtrace.cpp",
+]
+
+cc_library {
+    name: "libbacktrace",
+    defaults: ["libbacktrace_common"],
+    host_supported: true,
+
+    srcs: [
+        "BacktraceMap.cpp",
+    ],
+
+    target: {
+        darwin: {
+            enabled: true,
+        },
+        linux: {
+            srcs: libbacktrace_sources,
+
+            shared_libs: [
+                "libbase",
+                "liblog",
+                "libunwind",
+            ],
+
+            static_libs: ["libcutils"],
+        },
+        android: {
+            srcs: libbacktrace_sources,
+
+            shared_libs: [
+                "libbase",
+                "liblog",
+                "libunwind",
+            ],
+
+            static_libs: ["libcutils"],
+        },
+    },
+}
+
+cc_library_shared {
+    name: "libbacktrace_test",
+    defaults: ["libbacktrace_common"],
+    host_supported: true,
+    strip: {
+        none: true,
+    },
+    cflags: ["-O0"],
+    srcs: ["backtrace_testlib.c"],
+}
diff --git a/libbacktrace/Android.mk b/libbacktrace/Android.mk
index 356ab8b..9bb113a 100644
--- a/libbacktrace/Android.mk
+++ b/libbacktrace/Android.mk
@@ -44,53 +44,6 @@
 include $(LLVM_ROOT_PATH)/llvm.mk
 
 #-------------------------------------------------------------------------
-# The libbacktrace library.
-#-------------------------------------------------------------------------
-libbacktrace_src_files := \
-	Backtrace.cpp \
-	BacktraceCurrent.cpp \
-	BacktraceMap.cpp \
-	BacktracePtrace.cpp \
-	thread_utils.c \
-	ThreadEntry.cpp \
-	UnwindCurrent.cpp \
-	UnwindMap.cpp \
-	UnwindPtrace.cpp \
-
-libbacktrace_shared_libraries := \
-	libbase \
-	liblog \
-	libunwind \
-
-libbacktrace_static_libraries := \
-	libcutils
-
-module := libbacktrace
-module_tag := optional
-build_type := target
-build_target := SHARED_LIBRARY
-include $(LOCAL_PATH)/Android.build.mk
-build_type := host
-libbacktrace_multilib := both
-include $(LOCAL_PATH)/Android.build.mk
-
-libbacktrace_shared_libraries :=
-
-libbacktrace_static_libraries := \
-	libbase \
-	liblog \
-	libunwind \
-	liblzma \
-
-module := libbacktrace
-build_type := target
-build_target := STATIC_LIBRARY
-include $(LOCAL_PATH)/Android.build.mk
-build_type := host
-libbacktrace_multilib := both
-include $(LOCAL_PATH)/Android.build.mk
-
-#-------------------------------------------------------------------------
 # The libbacktrace_offline shared library.
 #-------------------------------------------------------------------------
 libbacktrace_offline_src_files := \
@@ -135,26 +88,6 @@
 include $(LOCAL_PATH)/Android.build.mk
 
 #-------------------------------------------------------------------------
-# The libbacktrace_test library needed by backtrace_test.
-#-------------------------------------------------------------------------
-libbacktrace_test_cflags := \
-	-O0 \
-
-libbacktrace_test_src_files := \
-	backtrace_testlib.c \
-
-libbacktrace_test_strip_module := false
-
-module := libbacktrace_test
-module_tag := debug
-build_type := target
-build_target := SHARED_LIBRARY
-libbacktrace_test_multilib := both
-include $(LOCAL_PATH)/Android.build.mk
-build_type := host
-include $(LOCAL_PATH)/Android.build.mk
-
-#-------------------------------------------------------------------------
 # The backtrace_test executable.
 #-------------------------------------------------------------------------
 backtrace_test_cflags := \
@@ -219,22 +152,3 @@
 include $(LOCAL_PATH)/Android.build.mk
 build_type := host
 include $(LOCAL_PATH)/Android.build.mk
-
-#----------------------------------------------------------------------------
-# Special truncated libbacktrace library for mac.
-#----------------------------------------------------------------------------
-ifeq ($(HOST_OS),darwin)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := libbacktrace
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_SRC_FILES := \
-	BacktraceMap.cpp \
-
-LOCAL_MULTILIB := both
-
-include $(BUILD_HOST_SHARED_LIBRARY)
-
-endif # HOST_OS-darwin
diff --git a/libbacktrace/BacktraceMap.cpp b/libbacktrace/BacktraceMap.cpp
index 19551dc..00c35b1 100644
--- a/libbacktrace/BacktraceMap.cpp
+++ b/libbacktrace/BacktraceMap.cpp
@@ -36,8 +36,8 @@
 }
 
 void BacktraceMap::FillIn(uintptr_t addr, backtrace_map_t* map) {
-  for (BacktraceMap::const_iterator it = begin();
-       it != end(); ++it) {
+  ScopedBacktraceMapIteratorLock lock(this);
+  for (BacktraceMap::const_iterator it = begin(); it != end(); ++it) {
     if (addr >= it->start && addr < it->end) {
       *map = *it;
       return;
diff --git a/libbacktrace/UnwindMap.cpp b/libbacktrace/UnwindMap.cpp
index 34d79f9..af79562 100644
--- a/libbacktrace/UnwindMap.cpp
+++ b/libbacktrace/UnwindMap.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <pthread.h>
 #include <stdint.h>
 #include <stdlib.h>
 #include <sys/types.h>
@@ -72,6 +73,7 @@
 }
 
 UnwindMapLocal::UnwindMapLocal() : UnwindMap(getpid()), map_created_(false) {
+  pthread_rwlock_init(&map_lock_, nullptr);
 }
 
 UnwindMapLocal::~UnwindMapLocal() {
@@ -82,9 +84,14 @@
 }
 
 bool UnwindMapLocal::GenerateMap() {
+  // Lock so that multiple threads cannot modify the maps data at the
+  // same time.
+  pthread_rwlock_wrlock(&map_lock_);
+
   // It's possible for the map to be regenerated while this loop is occurring.
   // If that happens, get the map again, but only try at most three times
   // before giving up.
+  bool generated = false;
   for (int i = 0; i < 3; i++) {
     maps_.clear();
 
@@ -110,12 +117,17 @@
     }
     // Check to see if the map changed while getting the data.
     if (ret != -UNW_EINVAL) {
-      return true;
+      generated = true;
+      break;
     }
   }
 
-  BACK_LOGW("Unable to generate the map.");
-  return false;
+  pthread_rwlock_unlock(&map_lock_);
+
+  if (!generated) {
+    BACK_LOGW("Unable to generate the map.");
+  }
+  return generated;
 }
 
 bool UnwindMapLocal::Build() {
diff --git a/libbacktrace/UnwindMap.h b/libbacktrace/UnwindMap.h
index 111401f..f85b54a 100644
--- a/libbacktrace/UnwindMap.h
+++ b/libbacktrace/UnwindMap.h
@@ -17,6 +17,7 @@
 #ifndef _LIBBACKTRACE_UNWIND_MAP_H
 #define _LIBBACKTRACE_UNWIND_MAP_H
 
+#include <pthread.h>
 #include <stdint.h>
 #include <sys/types.h>
 
@@ -56,10 +57,15 @@
 
   void FillIn(uintptr_t addr, backtrace_map_t* map) override;
 
+  void LockIterator() override { pthread_rwlock_rdlock(&map_lock_); }
+  void UnlockIterator() override { pthread_rwlock_unlock(&map_lock_); }
+
 private:
   bool GenerateMap();
 
   bool map_created_;
+
+  pthread_rwlock_t map_lock_;
 };
 
 #endif // _LIBBACKTRACE_UNWIND_MAP_H
diff --git a/libbacktrace/backtrace_test.cpp b/libbacktrace/backtrace_test.cpp
index 7066c79..e25c8e9 100644
--- a/libbacktrace/backtrace_test.cpp
+++ b/libbacktrace/backtrace_test.cpp
@@ -895,6 +895,7 @@
   std::unique_ptr<BacktraceMap> map(BacktraceMap::Create(pid));
 
   // Basic test that verifies that the map is in the expected order.
+  ScopedBacktraceMapIteratorLock lock(map.get());
   std::vector<map_test_t>::const_iterator test_it = test_maps.begin();
   for (BacktraceMap::const_iterator it = map->begin(); it != map->end(); ++it) {
     ASSERT_TRUE(test_it != test_maps.end());
diff --git a/libcrypto_utils/Android.mk b/libcrypto_utils/Android.mk
index 5e9763f..b6d2204 100644
--- a/libcrypto_utils/Android.mk
+++ b/libcrypto_utils/Android.mk
@@ -31,7 +31,7 @@
 LOCAL_CFLAGS := -Wall -Werror -Wextra -std=c99
 LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
 LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_SHARED_LIBRARIES := libcrypto-host
+LOCAL_SHARED_LIBRARIES := libcrypto
 include $(BUILD_HOST_SHARED_LIBRARY)
 
 include $(CLEAR_VARS)
@@ -40,7 +40,7 @@
 LOCAL_CFLAGS := -Wall -Werror -Wextra -std=c99
 LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
 LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_STATIC_LIBRARIES := libcrypto_static
+LOCAL_STATIC_LIBRARIES := libcrypto
 include $(BUILD_STATIC_LIBRARY)
 
 include $(CLEAR_VARS)
diff --git a/libcrypto_utils/tests/Android.mk b/libcrypto_utils/tests/Android.mk
index dad82f7..bdaef71 100644
--- a/libcrypto_utils/tests/Android.mk
+++ b/libcrypto_utils/tests/Android.mk
@@ -20,5 +20,5 @@
 LOCAL_MODULE := libcrypto_utils_test
 LOCAL_SRC_FILES := android_pubkey_test.cpp
 LOCAL_CFLAGS := -Wall -Werror -Wextra -std=c++11
-LOCAL_SHARED_LIBRARIES := libcrypto_utils libcrypto-host
+LOCAL_SHARED_LIBRARIES := libcrypto_utils libcrypto
 include $(BUILD_HOST_NATIVE_TEST)
diff --git a/libcutils/Android.bp b/libcutils/Android.bp
new file mode 100644
index 0000000..4f26034
--- /dev/null
+++ b/libcutils/Android.bp
@@ -0,0 +1,155 @@
+//
+// Copyright (C) 2008 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// some files must not be compiled when building against Mingw
+// they correspond to features not used by our host development tools
+// which are also hard or even impossible to port to native Win32
+libcutils_nonwindows_sources = [
+    "fs.c",
+    "multiuser.c",
+    "socket_inaddr_any_server_unix.c",
+    "socket_local_client_unix.c",
+    "socket_local_server_unix.c",
+    "socket_loopback_client_unix.c",
+    "socket_loopback_server_unix.c",
+    "socket_network_client_unix.c",
+    "sockets_unix.cpp",
+    "str_parms.c",
+]
+
+cc_library {
+    name: "libcutils",
+    host_supported: true,
+    srcs: [
+        "config_utils.c",
+        "fs_config.c",
+        "canned_fs_config.c",
+        "hashmap.c",
+        "iosched_policy.c",
+        "load_file.c",
+        "native_handle.c",
+        "open_memstream.c",
+        "process_name.c",
+        "record_stream.c",
+        "sched_policy.c",
+        "sockets.cpp",
+        "strdup16to8.c",
+        "strdup8to16.c",
+        "strlcpy.c",
+        "threads.c",
+    ],
+
+    target: {
+        host: {
+            srcs: ["dlmalloc_stubs.c"],
+        },
+        not_windows: {
+            srcs: libcutils_nonwindows_sources + [
+                "ashmem-host.c",
+                "trace-host.c",
+            ],
+        },
+        windows: {
+            srcs: [
+                "socket_inaddr_any_server_windows.c",
+                "socket_network_client_windows.c",
+                "sockets_windows.cpp",
+            ],
+
+            enabled: true,
+            shared: {
+                enabled: false,
+            },
+        },
+
+        android: {
+            srcs: libcutils_nonwindows_sources + [
+                "android_reboot.c",
+                "ashmem-dev.c",
+                "debugger.c",
+                "klog.cpp",
+                "partition_utils.c",
+                "properties.c",
+                "qtaguid.c",
+                "trace-dev.c",
+                "uevent.c",
+            ],
+
+            // TODO: remove liblog as whole static library, once we don't have prebuilt that requires
+            // liblog symbols present in libcutils.
+            whole_static_libs: [
+                "liblog",
+            ],
+
+            static_libs: ["libdebuggerd_client"],
+            export_static_lib_headers: ["libdebuggerd_client"],
+
+            cflags: [
+                "-std=gnu90",
+            ],
+        },
+
+        android_arm: {
+            srcs: ["arch-arm/memset32.S"],
+        },
+        android_arm64: {
+            srcs: ["arch-arm64/android_memset.S"],
+        },
+
+        android_mips: {
+            srcs: ["arch-mips/android_memset.c"],
+        },
+        android_mips64: {
+            srcs: ["arch-mips/android_memset.c"],
+        },
+
+        android_x86: {
+            srcs: [
+                "arch-x86/android_memset16.S",
+                "arch-x86/android_memset32.S",
+            ],
+        },
+
+        android_x86_64: {
+            srcs: [
+                "arch-x86_64/android_memset16.S",
+                "arch-x86_64/android_memset32.S",
+            ],
+        },
+    },
+
+    shared_libs: ["liblog"],
+    product_variables: {
+        cpusets: {
+            cflags: ["-DUSE_CPUSETS"],
+        },
+        schedboost: {
+            cflags: ["-DUSE_SCHEDBOOST"],
+        },
+    },
+    cflags: [
+        "-Werror",
+        "-Wall",
+        "-Wextra",
+    ],
+
+    clang: true,
+    sanitize: {
+        misc_undefined: ["integer"],
+    },
+}
+
+subdirs = ["tests"]
diff --git a/libcutils/Android.mk b/libcutils/Android.mk
deleted file mode 100644
index 822a7d3..0000000
--- a/libcutils/Android.mk
+++ /dev/null
@@ -1,150 +0,0 @@
-#
-# Copyright (C) 2008 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-LOCAL_PATH := $(my-dir)
-include $(CLEAR_VARS)
-
-libcutils_common_sources := \
-        config_utils.c \
-        fs_config.c \
-        canned_fs_config.c \
-        hashmap.c \
-        iosched_policy.c \
-        load_file.c \
-        native_handle.c \
-        open_memstream.c \
-        process_name.c \
-        record_stream.c \
-        sched_policy.c \
-        sockets.cpp \
-        strdup16to8.c \
-        strdup8to16.c \
-        strlcpy.c \
-        threads.c \
-
-# some files must not be compiled when building against Mingw
-# they correspond to features not used by our host development tools
-# which are also hard or even impossible to port to native Win32
-libcutils_nonwindows_sources := \
-        fs.c \
-        multiuser.c \
-        socket_inaddr_any_server_unix.c \
-        socket_local_client_unix.c \
-        socket_local_server_unix.c \
-        socket_loopback_client_unix.c \
-        socket_loopback_server_unix.c \
-        socket_network_client_unix.c \
-        sockets_unix.cpp \
-        str_parms.c \
-
-libcutils_nonwindows_host_sources := \
-        ashmem-host.c \
-        trace-host.c \
-
-libcutils_windows_host_sources := \
-        socket_inaddr_any_server_windows.c \
-        socket_network_client_windows.c \
-        sockets_windows.cpp \
-
-# Shared and static library for host
-# Note: when linking this library on Windows, you must also link to Winsock2
-# using "LOCAL_LDLIBS_windows := -lws2_32".
-# ========================================================
-LOCAL_MODULE := libcutils
-LOCAL_SRC_FILES := $(libcutils_common_sources) dlmalloc_stubs.c
-LOCAL_SRC_FILES_darwin := $(libcutils_nonwindows_sources) $(libcutils_nonwindows_host_sources)
-LOCAL_SRC_FILES_linux := $(libcutils_nonwindows_sources) $(libcutils_nonwindows_host_sources)
-LOCAL_SRC_FILES_windows := $(libcutils_windows_host_sources)
-LOCAL_STATIC_LIBRARIES := liblog
-LOCAL_CFLAGS := -Werror -Wall -Wextra
-LOCAL_MULTILIB := both
-LOCAL_MODULE_HOST_OS := darwin linux windows
-include $(BUILD_HOST_STATIC_LIBRARY)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := libcutils
-LOCAL_SRC_FILES := $(libcutils_common_sources) dlmalloc_stubs.c
-LOCAL_SRC_FILES_darwin := $(libcutils_nonwindows_sources) $(libcutils_nonwindows_host_sources)
-LOCAL_SRC_FILES_linux := $(libcutils_nonwindows_sources) $(libcutils_nonwindows_host_sources)
-LOCAL_SHARED_LIBRARIES := liblog
-LOCAL_CFLAGS := -Werror -Wall -Wextra
-LOCAL_MULTILIB := both
-include $(BUILD_HOST_SHARED_LIBRARY)
-
-
-
-# Shared and static library for target
-# ========================================================
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := libcutils
-LOCAL_SRC_FILES := $(libcutils_common_sources) \
-        $(libcutils_nonwindows_sources) \
-        android_reboot.c \
-        ashmem-dev.c \
-        debugger.c \
-        klog.c \
-        partition_utils.c \
-        properties.c \
-        qtaguid.c \
-        trace-dev.c \
-        uevent.c \
-
-LOCAL_SRC_FILES_arm += arch-arm/memset32.S
-LOCAL_SRC_FILES_arm64 += arch-arm64/android_memset.S
-
-LOCAL_SRC_FILES_mips += arch-mips/android_memset.c
-LOCAL_SRC_FILES_mips64 += arch-mips/android_memset.c
-
-LOCAL_SRC_FILES_x86 += \
-        arch-x86/android_memset16.S \
-        arch-x86/android_memset32.S \
-
-LOCAL_SRC_FILES_x86_64 += \
-        arch-x86_64/android_memset16.S \
-        arch-x86_64/android_memset32.S \
-
-LOCAL_C_INCLUDES := $(libcutils_c_includes)
-LOCAL_STATIC_LIBRARIES := liblog
-ifneq ($(ENABLE_CPUSETS),)
-LOCAL_CFLAGS += -DUSE_CPUSETS
-endif
-ifneq ($(ENABLE_SCHEDBOOST),)
-LOCAL_CFLAGS += -DUSE_SCHEDBOOST
-endif
-LOCAL_CFLAGS += -Werror -Wall -Wextra -std=gnu90
-LOCAL_CLANG := true
-LOCAL_SANITIZE := integer
-include $(BUILD_STATIC_LIBRARY)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := libcutils
-# TODO: remove liblog as whole static library, once we don't have prebuilt that requires
-# liblog symbols present in libcutils.
-LOCAL_WHOLE_STATIC_LIBRARIES := libcutils liblog
-LOCAL_SHARED_LIBRARIES := liblog
-ifneq ($(ENABLE_CPUSETS),)
-LOCAL_CFLAGS += -DUSE_CPUSETS
-endif
-ifneq ($(ENABLE_SCHEDBOOST),)
-LOCAL_CFLAGS += -DUSE_SCHEDBOOST
-endif
-LOCAL_CFLAGS += -Werror -Wall -Wextra
-LOCAL_C_INCLUDES := $(libcutils_c_includes)
-LOCAL_CLANG := true
-LOCAL_SANITIZE := integer
-include $(BUILD_SHARED_LIBRARY)
-
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/libcutils/canned_fs_config.c b/libcutils/canned_fs_config.c
index 5800857..e0e6a34 100644
--- a/libcutils/canned_fs_config.c
+++ b/libcutils/canned_fs_config.c
@@ -14,22 +14,22 @@
  * limitations under the License.
  */
 
-#include <inttypes.h>
-#include <stdio.h>
-#include <string.h>
 #include <errno.h>
+#include <inttypes.h>
 #include <limits.h>
+#include <stdio.h>
 #include <stdlib.h>
+#include <string.h>
 
 #include <private/android_filesystem_config.h>
 #include <private/canned_fs_config.h>
 
 typedef struct {
-	const char* path;
-	unsigned uid;
-	unsigned gid;
-	unsigned mode;
-	uint64_t capabilities;
+    const char* path;
+    unsigned uid;
+    unsigned gid;
+    unsigned mode;
+    uint64_t capabilities;
 } Path;
 
 static Path* canned_data = NULL;
@@ -37,81 +37,87 @@
 static int canned_used = 0;
 
 static int path_compare(const void* a, const void* b) {
-	return strcmp(((Path*)a)->path, ((Path*)b)->path);
+    return strcmp(((Path*)a)->path, ((Path*)b)->path);
 }
 
 int load_canned_fs_config(const char* fn) {
-	FILE* f = fopen(fn, "r");
-	if (f == NULL) {
-		fprintf(stderr, "failed to open %s: %s\n", fn, strerror(errno));
-		return -1;
-	}
+    char line[PATH_MAX + 200];
+    FILE* f;
 
-	char line[PATH_MAX + 200];
-	while (fgets(line, sizeof(line), f)) {
-		while (canned_used >= canned_alloc) {
-			canned_alloc = (canned_alloc+1) * 2;
-			canned_data = (Path*) realloc(canned_data, canned_alloc * sizeof(Path));
-		}
-		Path* p = canned_data + canned_used;
-		p->path = strdup(strtok(line, " "));
-		p->uid = atoi(strtok(NULL, " "));
-		p->gid = atoi(strtok(NULL, " "));
-		p->mode = strtol(strtok(NULL, " "), NULL, 8);   // mode is in octal
-		p->capabilities = 0;
+    f = fopen(fn, "r");
+    if (f == NULL) {
+        fprintf(stderr, "failed to open %s: %s\n", fn, strerror(errno));
+        return -1;
+    }
 
-		char* token = NULL;
-		do {
-			token = strtok(NULL, " ");
-			if (token && strncmp(token, "capabilities=", 13) == 0) {
-				p->capabilities = strtoll(token+13, NULL, 0);
-				break;
-			}
-		} while (token);
+    while (fgets(line, sizeof(line), f)) {
+        Path* p;
+        char* token;
 
-		canned_used++;
-	}
+        while (canned_used >= canned_alloc) {
+            canned_alloc = (canned_alloc+1) * 2;
+            canned_data = (Path*) realloc(canned_data, canned_alloc * sizeof(Path));
+        }
+        p = canned_data + canned_used;
+        p->path = strdup(strtok(line, " "));
+        p->uid = atoi(strtok(NULL, " "));
+        p->gid = atoi(strtok(NULL, " "));
+        p->mode = strtol(strtok(NULL, " "), NULL, 8);   // mode is in octal
+        p->capabilities = 0;
 
-	fclose(f);
+        do {
+            token = strtok(NULL, " ");
+            if (token && strncmp(token, "capabilities=", 13) == 0) {
+                p->capabilities = strtoll(token+13, NULL, 0);
+                break;
+            }
+        } while (token);
 
-	qsort(canned_data, canned_used, sizeof(Path), path_compare);
-	printf("loaded %d fs_config entries\n", canned_used);
+        canned_used++;
+    }
 
-	return 0;
+    fclose(f);
+
+    qsort(canned_data, canned_used, sizeof(Path), path_compare);
+    printf("loaded %d fs_config entries\n", canned_used);
+
+    return 0;
 }
 
 static const int kDebugCannedFsConfig = 0;
 
 void canned_fs_config(const char* path, int dir, const char* target_out_path,
-					  unsigned* uid, unsigned* gid, unsigned* mode, uint64_t* capabilities) {
-	Path key;
+                      unsigned* uid, unsigned* gid, unsigned* mode, uint64_t* capabilities) {
+    Path key, *p;
+
     key.path = path;
-    if (path[0] == '/')
-        key.path++;   // canned paths lack the leading '/'
-	Path* p = (Path*) bsearch(&key, canned_data, canned_used, sizeof(Path), path_compare);
-	if (p == NULL) {
-		fprintf(stderr, "failed to find [%s] in canned fs_config\n", path);
-		exit(1);
-	}
-	*uid = p->uid;
-	*gid = p->gid;
-	*mode = p->mode;
-	*capabilities = p->capabilities;
+    if (path[0] == '/') key.path++; // canned paths lack the leading '/'
+    p = (Path*) bsearch(&key, canned_data, canned_used, sizeof(Path), path_compare);
+    if (p == NULL) {
+        fprintf(stderr, "failed to find [%s] in canned fs_config\n", path);
+        exit(1);
+    }
+    *uid = p->uid;
+    *gid = p->gid;
+    *mode = p->mode;
+    *capabilities = p->capabilities;
 
-	if (kDebugCannedFsConfig) {
-		// for debugging, run the built-in fs_config and compare the results.
+    if (kDebugCannedFsConfig) {
+        // for debugging, run the built-in fs_config and compare the results.
 
-		unsigned c_uid, c_gid, c_mode;
-		uint64_t c_capabilities;
-		fs_config(path, dir, target_out_path, &c_uid, &c_gid, &c_mode, &c_capabilities);
+        unsigned c_uid, c_gid, c_mode;
+        uint64_t c_capabilities;
 
-		if (c_uid != *uid) printf("%s uid %d %d\n", path, *uid, c_uid);
-		if (c_gid != *gid) printf("%s gid %d %d\n", path, *gid, c_gid);
-		if (c_mode != *mode) printf("%s mode 0%o 0%o\n", path, *mode, c_mode);
-		if (c_capabilities != *capabilities)
-			printf("%s capabilities %" PRIx64 " %" PRIx64 "\n",
-				path,
-				*capabilities,
-				c_capabilities);
+        fs_config(path, dir, target_out_path, &c_uid, &c_gid, &c_mode, &c_capabilities);
+
+        if (c_uid != *uid) printf("%s uid %d %d\n", path, *uid, c_uid);
+        if (c_gid != *gid) printf("%s gid %d %d\n", path, *gid, c_gid);
+        if (c_mode != *mode) printf("%s mode 0%o 0%o\n", path, *mode, c_mode);
+        if (c_capabilities != *capabilities) {
+            printf("%s capabilities %" PRIx64 " %" PRIx64 "\n",
+                path,
+                *capabilities,
+                c_capabilities);
         }
+    }
 }
diff --git a/libcutils/iosched_policy.c b/libcutils/iosched_policy.c
index 71bc94b..13c2ceb 100644
--- a/libcutils/iosched_policy.c
+++ b/libcutils/iosched_policy.c
@@ -24,7 +24,8 @@
 #include <cutils/iosched_policy.h>
 
 #if defined(__ANDROID__)
-#include <linux/ioprio.h>
+#define IOPRIO_WHO_PROCESS (1)
+#define IOPRIO_CLASS_SHIFT (13)
 #include <sys/syscall.h>
 #define __android_unused
 #else
diff --git a/libcutils/klog.c b/libcutils/klog.cpp
similarity index 77%
rename from libcutils/klog.c
rename to libcutils/klog.cpp
index 7402903..061af1b 100644
--- a/libcutils/klog.c
+++ b/libcutils/klog.cpp
@@ -26,7 +26,6 @@
 
 #include <cutils/klog.h>
 
-static int klog_fd = -1;
 static int klog_level = KLOG_DEFAULT_LEVEL;
 
 int klog_get_level(void) {
@@ -37,32 +36,23 @@
     klog_level = level;
 }
 
-void klog_init(void) {
-    if (klog_fd >= 0) return; /* Already initialized */
-
-    klog_fd = open("/dev/kmsg", O_WRONLY | O_CLOEXEC);
-    if (klog_fd >= 0) {
-        return;
-    }
-
-    static const char* name = "/dev/__kmsg__";
-    if (mknod(name, S_IFCHR | 0600, (1 << 8) | 11) == 0) {
-        klog_fd = open(name, O_WRONLY | O_CLOEXEC);
-        unlink(name);
-    }
+static int __open_klog(void) {
+    return TEMP_FAILURE_RETRY(open("/dev/kmsg", O_WRONLY | O_CLOEXEC));
 }
 
 #define LOG_BUF_MAX 512
 
 void klog_writev(int level, const struct iovec* iov, int iov_count) {
     if (level > klog_level) return;
-    if (klog_fd < 0) klog_init();
-    if (klog_fd < 0) return;
+
+    static int klog_fd = __open_klog();
+    if (klog_fd == -1) return;
     TEMP_FAILURE_RETRY(writev(klog_fd, iov, iov_count));
 }
 
 void klog_write(int level, const char* fmt, ...) {
     if (level > klog_level) return;
+
     char buf[LOG_BUF_MAX];
     va_list ap;
     va_start(ap, fmt);
diff --git a/libcutils/tests/Android.bp b/libcutils/tests/Android.bp
new file mode 100644
index 0000000..530c747
--- /dev/null
+++ b/libcutils/tests/Android.bp
@@ -0,0 +1,72 @@
+// 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.
+
+cc_defaults {
+    name: "libcutils_test_default",
+    srcs: ["sockets_test.cpp"],
+
+    target: {
+        android: {
+            srcs: [
+                "MemsetTest.cpp",
+                "PropertiesTest.cpp",
+                "trace-dev_test.cpp",
+            ],
+        },
+
+        not_windows: {
+            srcs: ["test_str_parms.cpp"],
+        },
+    },
+
+    multilib: {
+        lib32: {
+            suffix: "32",
+        },
+        lib64: {
+            suffix: "64",
+        },
+    },
+}
+
+test_libraries = [
+    "libcutils",
+    "liblog",
+    "libbase",
+]
+
+cc_test {
+    name: "libcutils_test",
+    defaults: ["libcutils_test_default"],
+    host_supported: true,
+    shared_libs: test_libraries,
+}
+
+cc_test {
+    name: "libcutils_test_static",
+    defaults: ["libcutils_test_default"],
+    static_libs: ["libc"] + test_libraries,
+    stl: "libc++_static",
+
+    target: {
+        android: {
+            static_executable: true,
+        },
+        windows: {
+            host_ldlibs: ["-lws2_32"],
+
+            enabled: true,
+        },
+    },
+}
diff --git a/libcutils/tests/Android.mk b/libcutils/tests/Android.mk
deleted file mode 100644
index 52cf5f4..0000000
--- a/libcutils/tests/Android.mk
+++ /dev/null
@@ -1,81 +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.
-
-LOCAL_PATH := $(call my-dir)
-
-test_src_files := \
-    sockets_test.cpp \
-
-test_src_files_nonwindows := \
-    test_str_parms.cpp \
-
-test_target_only_src_files := \
-    MemsetTest.cpp \
-    PropertiesTest.cpp \
-    trace-dev_test.cpp \
-
-test_libraries := libcutils liblog libbase
-
-
-#
-# Target.
-#
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := libcutils_test
-LOCAL_SRC_FILES := $(test_src_files) $(test_target_only_src_files)
-LOCAL_SHARED_LIBRARIES := $(test_libraries)
-LOCAL_MULTILIB := both
-LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
-LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
-include $(BUILD_NATIVE_TEST)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := libcutils_test_static
-LOCAL_FORCE_STATIC_EXECUTABLE := true
-LOCAL_SRC_FILES := $(test_src_files) $(test_target_only_src_files)
-LOCAL_STATIC_LIBRARIES := libc $(test_libraries)
-LOCAL_CXX_STL := libc++_static
-LOCAL_MULTILIB := both
-LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
-LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
-include $(BUILD_NATIVE_TEST)
-
-
-#
-# Host.
-#
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := libcutils_test
-LOCAL_SRC_FILES := $(test_src_files) $(test_src_files_nonwindows)
-LOCAL_SHARED_LIBRARIES := $(test_libraries)
-LOCAL_MULTILIB := both
-LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
-LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
-include $(BUILD_HOST_NATIVE_TEST)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := libcutils_test_static
-LOCAL_SRC_FILES := $(test_src_files)
-LOCAL_SRC_FILES_darwin := $(test_src_files_nonwindows)
-LOCAL_SRC_FILES_linux := $(test_src_files_nonwindows)
-LOCAL_STATIC_LIBRARIES := $(test_libraries)
-LOCAL_LDLIBS_windows := -lws2_32
-LOCAL_CXX_STL := libc++_static
-LOCAL_MULTILIB := both
-LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
-LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
-LOCAL_MODULE_HOST_OS := darwin linux windows
-include $(BUILD_HOST_NATIVE_TEST)
diff --git a/libcutils/tests/PropertiesTest.cpp b/libcutils/tests/PropertiesTest.cpp
index 5f2396b..f66590b 100644
--- a/libcutils/tests/PropertiesTest.cpp
+++ b/libcutils/tests/PropertiesTest.cpp
@@ -24,11 +24,12 @@
 #include <sstream>
 #include <iostream>
 
+#include <android-base/macros.h>
+
 namespace android {
 
 #define STRINGIFY_INNER(x) #x
 #define STRINGIFY(x) STRINGIFY_INNER(x)
-#define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0]))
 #define ASSERT_OK(x) ASSERT_EQ(0, (x))
 #define EXPECT_OK(x) EXPECT_EQ(0, (x))
 
@@ -85,7 +86,7 @@
     }
 
     void ResetValue(unsigned char c = 0xFF) {
-        for (size_t i = 0; i < ARRAY_SIZE(mValue); ++i) {
+        for (size_t i = 0; i < arraysize(mValue); ++i) {
             mValue[i] = (char) c;
         }
     }
@@ -177,7 +178,7 @@
      * TRUE
      */
     const char *valuesTrue[] = { "1", "true", "y", "yes", "on", };
-    for (size_t i = 0; i < ARRAY_SIZE(valuesTrue); ++i) {
+    for (size_t i = 0; i < arraysize(valuesTrue); ++i) {
         ASSERT_OK(property_set(PROPERTY_TEST_KEY, valuesTrue[i]));
         bool val = property_get_bool(PROPERTY_TEST_KEY, /*default_value*/false);
         EXPECT_TRUE(val) << "Property should've been TRUE for value: '" << valuesTrue[i] << "'";
@@ -187,7 +188,7 @@
      * FALSE
      */
     const char *valuesFalse[] = { "0", "false", "n", "no", "off", };
-    for (size_t i = 0; i < ARRAY_SIZE(valuesFalse); ++i) {
+    for (size_t i = 0; i < arraysize(valuesFalse); ++i) {
         ASSERT_OK(property_set(PROPERTY_TEST_KEY, valuesFalse[i]));
         bool val = property_get_bool(PROPERTY_TEST_KEY, /*default_value*/true);
         EXPECT_FALSE(val) << "Property shoud've been FALSE For string value: '" << valuesFalse[i] << "'";
@@ -200,7 +201,7 @@
             "+1", "  1  ", "  true", "  true  ", "  y  ", "  yes", "yes  ",
             "+0", "-0", "00", "  00  ", "  false", "false  ",
     };
-    for (size_t i = 0; i < ARRAY_SIZE(valuesNeither); ++i) {
+    for (size_t i = 0; i < arraysize(valuesNeither); ++i) {
         ASSERT_OK(property_set(PROPERTY_TEST_KEY, valuesNeither[i]));
 
         // The default value should always be used
@@ -249,9 +250,9 @@
         DEFAULT_VALUE, DEFAULT_VALUE,
     };
 
-    ASSERT_EQ(ARRAY_SIZE(setValues), ARRAY_SIZE(getValues));
+    ASSERT_EQ(arraysize(setValues), arraysize(getValues));
 
-    for (size_t i = 0; i < ARRAY_SIZE(setValues); ++i) {
+    for (size_t i = 0; i < arraysize(setValues); ++i) {
         ASSERT_OK(property_set(PROPERTY_TEST_KEY, setValues[i]));
 
         int64_t val = property_get_int64(PROPERTY_TEST_KEY, DEFAULT_VALUE);
@@ -296,9 +297,9 @@
         DEFAULT_VALUE, DEFAULT_VALUE,
     };
 
-    ASSERT_EQ(ARRAY_SIZE(setValues), ARRAY_SIZE(getValues));
+    ASSERT_EQ(arraysize(setValues), arraysize(getValues));
 
-    for (size_t i = 0; i < ARRAY_SIZE(setValues); ++i) {
+    for (size_t i = 0; i < arraysize(setValues); ++i) {
         ASSERT_OK(property_set(PROPERTY_TEST_KEY, setValues[i]));
 
         int32_t val = property_get_int32(PROPERTY_TEST_KEY, DEFAULT_VALUE);
diff --git a/libdiskconfig/Android.bp b/libdiskconfig/Android.bp
new file mode 100644
index 0000000..041fd63
--- /dev/null
+++ b/libdiskconfig/Android.bp
@@ -0,0 +1,32 @@
+cc_library {
+    name: "libdiskconfig",
+    srcs: [
+        "diskconfig.c",
+        "diskutils.c",
+        "write_lst.c",
+        "config_mbr.c",
+    ],
+
+    shared_libs: [
+        "libcutils",
+        "liblog",
+    ],
+    cflags: ["-Werror"],
+    export_include_dirs: ["include"],
+    local_include_dirs: ["include"],
+
+    target: {
+        darwin: {
+            enabled: false,
+        },
+        linux: {
+            cflags: [
+                "-O2",
+                "-g",
+                "-W",
+                "-Wall",
+                "-D_LARGEFILE64_SOURCE",
+            ],
+        },
+    },
+}
diff --git a/libdiskconfig/Android.mk b/libdiskconfig/Android.mk
deleted file mode 100644
index a49ce58..0000000
--- a/libdiskconfig/Android.mk
+++ /dev/null
@@ -1,29 +0,0 @@
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-
-commonSources := \
-	diskconfig.c \
-	diskutils.c \
-	write_lst.c \
-	config_mbr.c
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(commonSources)
-LOCAL_MODULE := libdiskconfig
-LOCAL_MODULE_TAGS := optional
-LOCAL_SYSTEM_SHARED_LIBRARIES := libcutils liblog libc
-LOCAL_CFLAGS := -Werror
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-include $(BUILD_SHARED_LIBRARY)
-
-ifeq ($(HOST_OS),linux)
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(commonSources)
-LOCAL_MODULE := libdiskconfig_host
-LOCAL_MODULE_TAGS := optional
-LOCAL_CFLAGS := -O2 -g -W -Wall -Werror -D_LARGEFILE64_SOURCE
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-include $(BUILD_HOST_STATIC_LIBRARY)
-endif # HOST_OS == linux
diff --git a/libion/Android.bp b/libion/Android.bp
new file mode 100644
index 0000000..da98111
--- /dev/null
+++ b/libion/Android.bp
@@ -0,0 +1,25 @@
+
+cc_library {
+    name: "libion",
+    srcs: ["ion.c"],
+    shared_libs: ["liblog"],
+    local_include_dirs: [
+        "include",
+        "kernel-headers",
+    ],
+    export_include_dirs: [
+        "include",
+        "kernel-headers",
+    ],
+    cflags: ["-Werror"],
+}
+
+cc_binary {
+    name: "iontest",
+    srcs: ["ion_test.c"],
+    static_libs: ["libion"],
+    shared_libs: ["liblog"],
+    cflags: ["-Werror"],
+}
+
+subdirs = ["tests"]
diff --git a/libion/Android.mk b/libion/Android.mk
deleted file mode 100644
index 6562cd3..0000000
--- a/libion/Android.mk
+++ /dev/null
@@ -1,22 +0,0 @@
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := ion.c
-LOCAL_MODULE := libion
-LOCAL_MODULE_TAGS := optional
-LOCAL_SHARED_LIBRARIES := liblog
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include $(LOCAL_PATH)/kernel-headers
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include $(LOCAL_PATH)/kernel-headers
-LOCAL_CFLAGS := -Werror
-include $(BUILD_SHARED_LIBRARY)
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := ion.c ion_test.c
-LOCAL_MODULE := iontest
-LOCAL_MODULE_TAGS := optional tests
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include $(LOCAL_PATH)/kernel-headers
-LOCAL_SHARED_LIBRARIES := liblog
-LOCAL_CFLAGS := -Werror
-include $(BUILD_EXECUTABLE)
-
-include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/libion/tests/Android.bp b/libion/tests/Android.bp
new file mode 100644
index 0000000..4428848
--- /dev/null
+++ b/libion/tests/Android.bp
@@ -0,0 +1,36 @@
+//
+// Copyright (C) 2013 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.
+//
+
+cc_test {
+    name: "ion-unit-tests",
+    clang: true,
+    cflags: [
+        "-g",
+        "-Wall",
+        "-Werror",
+        "-Wno-missing-field-initializers",
+    ],
+    shared_libs: ["libion"],
+    srcs: [
+        "ion_test_fixture.cpp",
+        "allocate_test.cpp",
+        "formerly_valid_handle_test.cpp",
+        "invalid_values_test.cpp",
+        "map_test.cpp",
+        "device_test.cpp",
+        "exit_test.cpp",
+    ],
+}
diff --git a/libion/tests/Android.mk b/libion/tests/Android.mk
deleted file mode 100644
index d4d07c2..0000000
--- a/libion/tests/Android.mk
+++ /dev/null
@@ -1,33 +0,0 @@
-#
-# Copyright (C) 2013 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.
-#
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_CLANG := true
-LOCAL_MODULE := ion-unit-tests
-LOCAL_CFLAGS += -g -Wall -Werror -Wno-missing-field-initializers
-LOCAL_SHARED_LIBRARIES += libion
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/../kernel-headers
-LOCAL_SRC_FILES := \
-	ion_test_fixture.cpp \
-	allocate_test.cpp \
-	formerly_valid_handle_test.cpp \
-	invalid_values_test.cpp \
-	map_test.cpp \
-	device_test.cpp \
-	exit_test.cpp
-include $(BUILD_NATIVE_TEST)
diff --git a/liblog/Android.soong.mk b/liblog/Android.mk
similarity index 100%
rename from liblog/Android.soong.mk
rename to liblog/Android.mk
diff --git a/liblog/logger.h b/liblog/logger.h
index 5087256..2a4cfcb 100644
--- a/liblog/logger.h
+++ b/liblog/logger.h
@@ -146,11 +146,13 @@
 /* OS specific dribs and drabs */
 
 #if defined(_WIN32)
+#include <private/android_filesystem_config.h>
 typedef uint32_t uid_t;
+static inline uid_t __android_log_uid() { return AID_SYSTEM; }
+#else
+static inline uid_t __android_log_uid() { return getuid(); }
 #endif
 
-LIBLOG_HIDDEN uid_t __android_log_uid();
-LIBLOG_HIDDEN pid_t __android_log_pid();
 LIBLOG_HIDDEN void __android_log_lock();
 LIBLOG_HIDDEN int __android_log_trylock();
 LIBLOG_HIDDEN void __android_log_unlock();
diff --git a/liblog/logger_lock.c b/liblog/logger_lock.c
index ee979bd..14feee0 100644
--- a/liblog/logger_lock.c
+++ b/liblog/logger_lock.c
@@ -22,34 +22,8 @@
 #include <pthread.h>
 #endif
 
-#include <private/android_filesystem_config.h>
-
 #include "logger.h"
 
-LIBLOG_HIDDEN uid_t __android_log_uid()
-{
-#if defined(_WIN32)
-    return AID_SYSTEM;
-#else
-    static uid_t last_uid = AID_ROOT; /* logd *always* starts up as AID_ROOT */
-
-    if (last_uid == AID_ROOT) { /* have we called to get the UID yet? */
-        last_uid = getuid();
-    }
-    return last_uid;
-#endif
-}
-
-LIBLOG_HIDDEN pid_t __android_log_pid()
-{
-    static pid_t last_pid = (pid_t) -1;
-
-    if (last_pid == (pid_t) -1) {
-        last_pid = getpid();
-    }
-    return last_pid;
-}
-
 #if !defined(_WIN32)
 static pthread_mutex_t log_init_lock = PTHREAD_MUTEX_INITIALIZER;
 #endif
diff --git a/liblog/pmsg_reader.c b/liblog/pmsg_reader.c
index 2e4fc5d..a4eec65 100644
--- a/liblog/pmsg_reader.c
+++ b/liblog/pmsg_reader.c
@@ -144,6 +144,7 @@
     struct __attribute__((__packed__)) {
         android_pmsg_log_header_t p;
         android_log_header_t l;
+        uint8_t prio;
     } buf;
     static uint8_t preread_count;
     bool is_system;
@@ -180,11 +181,16 @@
         if (preread_count != sizeof(buf)) {
             return preread_count ? -EIO : -EAGAIN;
         }
-        if ((buf.p.magic != LOGGER_MAGIC)
-         || (buf.p.len <= sizeof(buf))
-         || (buf.p.len > (sizeof(buf) + LOGGER_ENTRY_MAX_PAYLOAD))
-         || (buf.l.id >= LOG_ID_MAX)
-         || (buf.l.realtime.tv_nsec >= NS_PER_SEC)) {
+        if ((buf.p.magic != LOGGER_MAGIC) ||
+                (buf.p.len <= sizeof(buf)) ||
+                (buf.p.len > (sizeof(buf) + LOGGER_ENTRY_MAX_PAYLOAD)) ||
+                (buf.l.id >= LOG_ID_MAX) ||
+                (buf.l.realtime.tv_nsec >= NS_PER_SEC) ||
+                ((buf.l.id != LOG_ID_EVENTS) &&
+                    (buf.l.id != LOG_ID_SECURITY) &&
+                    ((buf.prio == ANDROID_LOG_UNKNOWN) ||
+                        (buf.prio == ANDROID_LOG_DEFAULT) ||
+                        (buf.prio >= ANDROID_LOG_SILENT)))) {
             do {
                 memmove(&buf.p.magic, &buf.p.magic + 1, --preread_count);
             } while (preread_count && (buf.p.magic != LOGGER_MAGIC));
@@ -202,10 +208,12 @@
             uid = get_best_effective_uid();
             is_system = uid_has_log_permission(uid);
             if (is_system || (uid == buf.p.uid)) {
+                char *msg = is_system ?
+                    log_msg->entry_v4.msg :
+                    log_msg->entry_v3.msg;
+                *msg = buf.prio;
                 ret = TEMP_FAILURE_RETRY(read(transp->context.fd,
-                                          is_system ?
-                                              log_msg->entry_v4.msg :
-                                              log_msg->entry_v3.msg,
+                                          msg + sizeof(buf.prio),
                                           buf.p.len - sizeof(buf)));
                 if (ret < 0) {
                     return -errno;
@@ -214,7 +222,7 @@
                     return -EIO;
                 }
 
-                log_msg->entry_v4.len = buf.p.len - sizeof(buf);
+                log_msg->entry_v4.len = buf.p.len - sizeof(buf) + sizeof(buf.prio);
                 log_msg->entry_v4.hdr_size = is_system ?
                     sizeof(log_msg->entry_v4) :
                     sizeof(log_msg->entry_v3);
@@ -227,7 +235,7 @@
                     log_msg->entry_v4.uid = buf.p.uid;
                 }
 
-                return ret + log_msg->entry_v4.hdr_size;
+                return ret + sizeof(buf.prio) + log_msg->entry_v4.hdr_size;
             }
         }
 
diff --git a/liblog/pmsg_writer.c b/liblog/pmsg_writer.c
index 2ba31fa..944feba 100644
--- a/liblog/pmsg_writer.c
+++ b/liblog/pmsg_writer.c
@@ -142,7 +142,7 @@
     pmsgHeader.magic = LOGGER_MAGIC;
     pmsgHeader.len = sizeof(pmsgHeader) + sizeof(header);
     pmsgHeader.uid = __android_log_uid();
-    pmsgHeader.pid = __android_log_pid();
+    pmsgHeader.pid = getpid();
 
     header.id = logId;
     header.tid = gettid();
diff --git a/liblog/tests/liblog_benchmark.cpp b/liblog/tests/liblog_benchmark.cpp
index 01fb50f..1b66a56 100644
--- a/liblog/tests/liblog_benchmark.cpp
+++ b/liblog/tests/liblog_benchmark.cpp
@@ -205,7 +205,7 @@
         android_log_header_t header;
         android_log_event_int_t payload;
     };
-    char buf[sizeof(struct packet) + 8] __aligned(8);
+    alignas(8) char buf[sizeof(struct packet) + 8];
     memset(buf, 0, sizeof(buf));
     struct packet *buffer = (struct packet*)(((uintptr_t)buf + 7) & ~7);
     if (((uintptr_t)&buffer->pmsg_header) & 7) {
@@ -281,7 +281,7 @@
         android_log_header_t header;
         android_log_event_int_t payload;
     };
-    char buf[sizeof(struct packet) + 8] __aligned(8);
+    alignas(8) char buf[sizeof(struct packet) + 8];
     memset(buf, 0, sizeof(buf));
     struct packet *buffer = (struct packet*)((((uintptr_t)buf + 7) & ~7) + 1);
     if ((((uintptr_t)&buffer->pmsg_header) & 7) != 1) {
@@ -357,7 +357,7 @@
         android_log_header_t header;
         android_log_event_int_t payload;
     };
-    char buf[sizeof(struct packet) + 8 + LOGGER_ENTRY_MAX_PAYLOAD] __aligned(8);
+    alignas(8) char buf[sizeof(struct packet) + 8 + LOGGER_ENTRY_MAX_PAYLOAD];
     memset(buf, 0, sizeof(buf));
     struct packet *buffer = (struct packet*)(((uintptr_t)buf + 7) & ~7);
     if (((uintptr_t)&buffer->pmsg_header) & 7) {
@@ -430,7 +430,7 @@
         android_log_header_t header;
         android_log_event_int_t payload;
     };
-    char buf[sizeof(struct packet) + 8 + LOGGER_ENTRY_MAX_PAYLOAD] __aligned(8);
+    alignas(8) char buf[sizeof(struct packet) + 8 + LOGGER_ENTRY_MAX_PAYLOAD];
     memset(buf, 0, sizeof(buf));
     struct packet *buffer = (struct packet*)((((uintptr_t)buf + 7) & ~7) + 1);
     if ((((uintptr_t)&buffer->pmsg_header) & 7) != 1) {
diff --git a/libmemtrack/Android.bp b/libmemtrack/Android.bp
new file mode 100644
index 0000000..98413dd
--- /dev/null
+++ b/libmemtrack/Android.bp
@@ -0,0 +1,30 @@
+// Copyright 2013 The Android Open Source Project
+
+cc_library_shared {
+    name: "libmemtrack",
+    srcs: ["memtrack.c"],
+    export_include_dirs: ["include"],
+    local_include_dirs: ["include"],
+    include_dirs: ["hardware/libhardware/include"],
+    shared_libs: [
+        "libhardware",
+        "liblog",
+    ],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+}
+
+cc_binary {
+    name: "memtrack_test",
+    srcs: ["memtrack_test.c"],
+    shared_libs: [
+        "libmemtrack",
+        "libpagemap",
+    ],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+}
diff --git a/libmemtrack/Android.mk b/libmemtrack/Android.mk
deleted file mode 100644
index ffc7244..0000000
--- a/libmemtrack/Android.mk
+++ /dev/null
@@ -1,20 +0,0 @@
-# Copyright 2013 The Android Open Source Project
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := memtrack.c
-LOCAL_MODULE := libmemtrack
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_C_INCLUDES += $(LOCAL_PATH)/include
-LOCAL_C_INCLUDES += hardware/libhardware/include
-LOCAL_SHARED_LIBRARIES := libhardware liblog
-LOCAL_CFLAGS := -Wall -Werror
-include $(BUILD_SHARED_LIBRARY)
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := memtrack_test.c
-LOCAL_MODULE := memtrack_test
-LOCAL_SHARED_LIBRARIES := libmemtrack libpagemap
-LOCAL_CFLAGS := -Wall -Werror
-include $(BUILD_EXECUTABLE)
diff --git a/libmemunreachable/Allocator.cpp b/libmemunreachable/Allocator.cpp
index 68f654c..6fe67a4 100644
--- a/libmemunreachable/Allocator.cpp
+++ b/libmemunreachable/Allocator.cpp
@@ -52,8 +52,6 @@
   return (x + y - 1) / y;
 }
 
-#define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0]))
-
 static constexpr size_t kPageSize = 4096;
 static constexpr size_t kChunkSize = 256 * 1024;
 static constexpr size_t kUsableChunkSize = kChunkSize - kPageSize;
@@ -258,7 +256,7 @@
   unsigned int i = first_free_bitmap_;
   while (free_bitmap_[i] == 0)
     i++;
-  assert(i < ARRAY_SIZE(free_bitmap_));
+  assert(i < arraysize(free_bitmap_));
   unsigned int bit = __builtin_ffs(free_bitmap_[i]) - 1;
   assert(free_bitmap_[i] & (1U << bit));
   free_bitmap_[i] &= ~(1U << bit);
@@ -266,7 +264,7 @@
   assert(n < max_allocations_);
 
   unsigned int page = n * allocation_size_ / kPageSize;
-  assert(page / 32 < ARRAY_SIZE(dirty_pages_));
+  assert(page / 32 < arraysize(dirty_pages_));
   dirty_pages_[page / 32] |= 1U << (page % 32);
 
   free_count_--;
@@ -285,7 +283,7 @@
   unsigned int i = n / 32;
   unsigned int bit = n % 32;
 
-  assert(i < ARRAY_SIZE(free_bitmap_));
+  assert(i < arraysize(free_bitmap_));
   assert(!(free_bitmap_[i] & (1U << bit)));
   free_bitmap_[i] |= 1U << bit;
   free_count_++;
diff --git a/libmemunreachable/tests/Allocator_test.cpp b/libmemunreachable/tests/Allocator_test.cpp
index fa76ae0..21c8218 100644
--- a/libmemunreachable/tests/Allocator_test.cpp
+++ b/libmemunreachable/tests/Allocator_test.cpp
@@ -160,7 +160,7 @@
 
   Allocator<int>::shared_ptr ptr = allocator.make_shared(0);
   {
-    auto ptr2 = ptr;
+    auto ptr2 = ptr;  // NOLINT, test copy of ptr
   }
   ASSERT_NE(ptr, nullptr);
 }
diff --git a/libnativebridge/Android.bp b/libnativebridge/Android.bp
new file mode 100644
index 0000000..598dfcd
--- /dev/null
+++ b/libnativebridge/Android.bp
@@ -0,0 +1,25 @@
+
+cc_library {
+    name: "libnativebridge",
+
+    host_supported: true,
+    srcs: ["native_bridge.cc"],
+    shared_libs: ["liblog"],
+    clang: true,
+
+    cflags: [
+        "-Werror",
+        "-Wall",
+    ],
+    cppflags: [
+        "-std=gnu++11",
+        "-fvisibility=protected",
+    ],
+
+    host_ldlibs: ["-ldl"],
+    target: {
+        android: {
+            shared_libs: ["libdl"],
+        },
+    },
+}
diff --git a/libnativebridge/Android.mk b/libnativebridge/Android.mk
index b88621e..3887b1b 100644
--- a/libnativebridge/Android.mk
+++ b/libnativebridge/Android.mk
@@ -1,57 +1,3 @@
 LOCAL_PATH:= $(call my-dir)
 
-NATIVE_BRIDGE_COMMON_SRC_FILES := \
-  native_bridge.cc
-
-# Shared library for target
-# ========================================================
-include $(CLEAR_VARS)
-
-LOCAL_MODULE:= libnativebridge
-
-LOCAL_SRC_FILES:= $(NATIVE_BRIDGE_COMMON_SRC_FILES)
-LOCAL_SHARED_LIBRARIES := liblog libdl
-LOCAL_CLANG := true
-LOCAL_CPP_EXTENSION := .cc
-LOCAL_CFLAGS += -Werror -Wall
-LOCAL_CPPFLAGS := -std=gnu++11 -fvisibility=protected
-LOCAL_MULTILIB := both
-
-include $(BUILD_SHARED_LIBRARY)
-
-# Shared library for host
-# ========================================================
-include $(CLEAR_VARS)
-
-LOCAL_MODULE:= libnativebridge
-
-LOCAL_SRC_FILES:= $(NATIVE_BRIDGE_COMMON_SRC_FILES)
-LOCAL_SHARED_LIBRARIES := liblog
-LOCAL_CLANG := true
-LOCAL_CPP_EXTENSION := .cc
-LOCAL_CFLAGS += -Werror -Wall
-LOCAL_CPPFLAGS := -std=gnu++11 -fvisibility=protected
-LOCAL_LDFLAGS := -ldl
-LOCAL_MULTILIB := both
-
-include $(BUILD_HOST_SHARED_LIBRARY)
-
-# Static library for host
-# ========================================================
-include $(CLEAR_VARS)
-
-LOCAL_MODULE:= libnativebridge
-
-LOCAL_SRC_FILES:= $(NATIVE_BRIDGE_COMMON_SRC_FILES)
-LOCAL_STATIC_LIBRARIES := liblog
-LOCAL_CLANG := true
-LOCAL_CPP_EXTENSION := .cc
-LOCAL_CFLAGS += -Werror -Wall
-LOCAL_CPPFLAGS := -std=gnu++11 -fvisibility=protected
-LOCAL_LDFLAGS := -ldl
-LOCAL_MULTILIB := both
-
-include $(BUILD_HOST_STATIC_LIBRARY)
-
-
 include $(LOCAL_PATH)/tests/Android.mk
diff --git a/libnativeloader/Android.bp b/libnativeloader/Android.bp
new file mode 100644
index 0000000..b4a69bc
--- /dev/null
+++ b/libnativeloader/Android.bp
@@ -0,0 +1,32 @@
+// Shared library for target
+// ========================================================
+cc_library {
+    name: "libnativeloader",
+    host_supported: true,
+    srcs: ["native_loader.cpp"],
+    shared_libs: [
+        "libnativehelper",
+        "liblog",
+        "libcutils",
+    ],
+    static_libs: ["libbase"],
+    target: {
+        android: {
+            shared_libs: ["libdl"],
+        },
+        host: {
+            host_ldlibs: ["-ldl"],
+        },
+    },
+    clang: true,
+    cflags: [
+        "-Werror",
+        "-Wall",
+    ],
+    cppflags: [
+        "-std=gnu++14",
+        "-fvisibility=hidden",
+    ],
+    export_include_dirs: ["include"],
+    local_include_dirs: ["include"],
+}
diff --git a/libnativeloader/Android.mk b/libnativeloader/Android.mk
deleted file mode 100644
index c81c671..0000000
--- a/libnativeloader/Android.mk
+++ /dev/null
@@ -1,58 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-
-native_loader_common_src_files := \
-  native_loader.cpp
-
-native_loader_common_cflags := -Werror -Wall
-
-# Shared library for target
-# ========================================================
-include $(CLEAR_VARS)
-
-LOCAL_MODULE:= libnativeloader
-
-LOCAL_SRC_FILES:= $(native_loader_common_src_files)
-LOCAL_SHARED_LIBRARIES := libnativehelper liblog libcutils libdl
-LOCAL_STATIC_LIBRARIES := libbase
-LOCAL_CLANG := true
-LOCAL_CFLAGS := $(native_loader_common_cflags)
-LOCAL_CPPFLAGS := -std=gnu++14 -fvisibility=hidden
-LOCAL_MULTILIB := both
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-include $(BUILD_SHARED_LIBRARY)
-
-# Shared library for host
-# ========================================================
-include $(CLEAR_VARS)
-
-LOCAL_MODULE:= libnativeloader
-
-LOCAL_SRC_FILES:= $(native_loader_common_src_files)
-LOCAL_SHARED_LIBRARIES := libnativehelper liblog libcutils
-LOCAL_STATIC_LIBRARIES := libbase
-LOCAL_CLANG := true
-LOCAL_CFLAGS := $(native_loader_common_cflags)
-LOCAL_CPPFLAGS := -std=gnu++14 -fvisibility=hidden
-LOCAL_LDFLAGS := -ldl
-LOCAL_MULTILIB := both
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-include $(BUILD_HOST_SHARED_LIBRARY)
-
-# Static library for host
-# ========================================================
-include $(CLEAR_VARS)
-
-LOCAL_MODULE:= libnativeloader
-
-LOCAL_SRC_FILES:= $(native_loader_common_src_files)
-LOCAL_STATIC_LIBRARIES := libnativehelper libcutils liblog libbase
-LOCAL_CLANG := true
-LOCAL_CFLAGS := $(native_loader_common_cflags)
-LOCAL_CPPFLAGS := -std=gnu++14 -fvisibility=hidden
-LOCAL_LDFLAGS := -ldl
-LOCAL_MULTILIB := both
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-include $(BUILD_HOST_STATIC_LIBRARY)
diff --git a/libnativeloader/native_loader.cpp b/libnativeloader/native_loader.cpp
index 713a59d..d0e07dd 100644
--- a/libnativeloader/native_loader.cpp
+++ b/libnativeloader/native_loader.cpp
@@ -133,9 +133,10 @@
     std::string public_native_libraries_system_config =
             root_dir + kPublicNativeLibrariesSystemConfigPathFromRoot;
 
-    LOG_ALWAYS_FATAL_IF(!ReadConfig(public_native_libraries_system_config, &sonames),
+    std::string error_msg;
+    LOG_ALWAYS_FATAL_IF(!ReadConfig(public_native_libraries_system_config, &sonames, &error_msg),
                         "Error reading public native library list from \"%s\": %s",
-                        public_native_libraries_system_config.c_str(), strerror(errno));
+                        public_native_libraries_system_config.c_str(), error_msg.c_str());
 
     // For debuggable platform builds use ANDROID_ADDITIONAL_PUBLIC_LIBRARIES environment
     // variable to add libraries to the list. This is intended for platform tests only.
@@ -172,20 +173,42 @@
   }
 
  private:
-  bool ReadConfig(const std::string& configFile, std::vector<std::string>* sonames) {
+  bool ReadConfig(const std::string& configFile, std::vector<std::string>* sonames,
+                  std::string* error_msg = nullptr) {
     // Read list of public native libraries from the config file.
     std::string file_content;
     if(!base::ReadFileToString(configFile, &file_content)) {
+      if (error_msg) *error_msg = strerror(errno);
       return false;
     }
 
     std::vector<std::string> lines = base::Split(file_content, "\n");
 
-    for (const auto& line : lines) {
+    for (auto& line : lines) {
       auto trimmed_line = base::Trim(line);
       if (trimmed_line[0] == '#' || trimmed_line.empty()) {
         continue;
       }
+      size_t space_pos = trimmed_line.rfind(' ');
+      if (space_pos != std::string::npos) {
+        std::string type = trimmed_line.substr(space_pos + 1);
+        if (type != "32" && type != "64") {
+          if (error_msg) *error_msg = "Malformed line: " + line;
+          return false;
+        }
+#if defined(__LP64__)
+        // Skip 32 bit public library.
+        if (type == "32") {
+          continue;
+        }
+#else
+        // Skip 64 bit public library.
+        if (type == "64") {
+          continue;
+        }
+#endif
+        trimmed_line.resize(space_pos);
+      }
 
       sonames->push_back(trimmed_line);
     }
diff --git a/libnetutils/dhcptool.c b/libnetutils/dhcptool.c
index 352ac5e..a2d3869 100644
--- a/libnetutils/dhcptool.c
+++ b/libnetutils/dhcptool.c
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <err.h>
 #include <errno.h>
 #include <error.h>
 #include <stdbool.h>
@@ -29,12 +30,14 @@
 
   char* interface = argv[1];
   if (ifc_init()) {
-    error(EXIT_FAILURE, errno, "dhcptool %s: ifc_init failed", interface);
+    err(errno, "dhcptool %s: ifc_init failed", interface);
+    ifc_close();
+    return EXIT_FAILURE;
   }
 
   int rc = do_dhcp(interface);
   if (rc) {
-    error(0, errno, "dhcptool %s: do_dhcp failed", interface);
+    err(errno, "dhcptool %s: do_dhcp failed", interface);
   }
 
   ifc_close();
diff --git a/libnetutils/ifc_utils.c b/libnetutils/ifc_utils.c
index f9f62f8..eae32ce 100644
--- a/libnetutils/ifc_utils.c
+++ b/libnetutils/ifc_utils.c
@@ -19,6 +19,7 @@
 #include <unistd.h>
 #include <string.h>
 #include <errno.h>
+#include <pthread.h>
 
 #include <sys/socket.h>
 #include <sys/select.h>
@@ -57,6 +58,8 @@
 
 static int ifc_ctl_sock = -1;
 static int ifc_ctl_sock6 = -1;
+static pthread_mutex_t ifc_sock_mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
+static pthread_mutex_t ifc_sock6_mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
 void printerr(char *fmt, ...);
 
 #define DBG 0
@@ -122,6 +125,8 @@
 int ifc_init(void)
 {
     int ret;
+
+    pthread_mutex_lock(&ifc_sock_mutex);
     if (ifc_ctl_sock == -1) {
         ifc_ctl_sock = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
         if (ifc_ctl_sock < 0) {
@@ -136,6 +141,7 @@
 
 int ifc_init6(void)
 {
+    pthread_mutex_lock(&ifc_sock6_mutex);
     if (ifc_ctl_sock6 == -1) {
         ifc_ctl_sock6 = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0);
         if (ifc_ctl_sock6 < 0) {
@@ -152,6 +158,7 @@
         (void)close(ifc_ctl_sock);
         ifc_ctl_sock = -1;
     }
+    pthread_mutex_unlock(&ifc_sock_mutex);
 }
 
 void ifc_close6(void)
@@ -160,6 +167,7 @@
         (void)close(ifc_ctl_sock6);
         ifc_ctl_sock6 = -1;
     }
+    pthread_mutex_unlock(&ifc_sock6_mutex);
 }
 
 static void ifc_init_ifr(const char *name, struct ifreq *ifr)
@@ -553,6 +561,7 @@
     ifc_init();
 
     if (ifc_ctl_sock < 0) {
+        ifc_close();
         return -errno;
     }
 
diff --git a/libpackagelistparser/Android.bp b/libpackagelistparser/Android.bp
new file mode 100644
index 0000000..70ff528
--- /dev/null
+++ b/libpackagelistparser/Android.bp
@@ -0,0 +1,13 @@
+cc_library {
+
+    name: "libpackagelistparser",
+    srcs: ["packagelistparser.c"],
+    shared_libs: ["liblog"],
+    local_include_dirs: ["include"],
+    export_include_dirs: ["include"],
+
+    clang: true,
+    sanitize: {
+        misc_undefined: ["integer"],
+    },
+}
diff --git a/libpackagelistparser/Android.mk b/libpackagelistparser/Android.mk
deleted file mode 100644
index c8be050..0000000
--- a/libpackagelistparser/Android.mk
+++ /dev/null
@@ -1,32 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-
-#########################
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := libpackagelistparser
-LOCAL_MODULE_TAGS := optional
-LOCAL_SRC_FILES := packagelistparser.c
-LOCAL_SHARED_LIBRARIES := liblog
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-
-LOCAL_CLANG := true
-LOCAL_SANITIZE := integer
-
-include $(BUILD_SHARED_LIBRARY)
-
-#########################
-include $(CLEAR_VARS)
-
-
-LOCAL_MODULE := libpackagelistparser
-LOCAL_MODULE_TAGS := optional
-LOCAL_SRC_FILES := packagelistparser.c
-LOCAL_STATIC_LIBRARIES := liblog
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-
-LOCAL_CLANG := true
-LOCAL_SANITIZE := integer
-
-include $(BUILD_STATIC_LIBRARY)
diff --git a/libpixelflinger/Android.mk b/libpixelflinger/Android.mk
index 9a937f8..55891db 100644
--- a/libpixelflinger/Android.mk
+++ b/libpixelflinger/Android.mk
@@ -74,10 +74,6 @@
 		    external/safe-iop/include
 LOCAL_SHARED_LIBRARIES := libcutils liblog libutils
 
-# Really this should go away entirely or at least not depend on
-# libhardware, but this at least gets us built.
-LOCAL_SHARED_LIBRARIES += libhardware_legacy
-LOCAL_CFLAGS += -DWITH_LIB_HARDWARE
 include $(BUILD_SHARED_LIBRARY)
 
 include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/libpixelflinger/arch-arm64/col32cb16blend.S b/libpixelflinger/arch-arm64/col32cb16blend.S
index 8d9c7c4..84596f9 100644
--- a/libpixelflinger/arch-arm64/col32cb16blend.S
+++ b/libpixelflinger/arch-arm64/col32cb16blend.S
@@ -26,7 +26,7 @@
  * SUCH DAMAGE.
  */
     .text
-    .align 0
+    .balign 0
 
     .global scanline_col32cb16blend_arm64
 
diff --git a/libpixelflinger/arch-arm64/t32cb16blend.S b/libpixelflinger/arch-arm64/t32cb16blend.S
index 230f47b..b1a950d 100644
--- a/libpixelflinger/arch-arm64/t32cb16blend.S
+++ b/libpixelflinger/arch-arm64/t32cb16blend.S
@@ -26,7 +26,7 @@
  * SUCH DAMAGE.
  */
     .text
-    .align 0
+    .balign 0
 
     .global scanline_t32cb16blend_arm64
 
diff --git a/libpixelflinger/arch-mips/col32cb16blend.S b/libpixelflinger/arch-mips/col32cb16blend.S
index 5d18e55..810294c 100644
--- a/libpixelflinger/arch-mips/col32cb16blend.S
+++ b/libpixelflinger/arch-mips/col32cb16blend.S
@@ -59,7 +59,7 @@
        .endm
 
        .text
-       .align
+       .balign 4
 
        .global scanline_col32cb16blend_mips
        .ent    scanline_col32cb16blend_mips
diff --git a/libpixelflinger/arch-mips/t32cb16blend.S b/libpixelflinger/arch-mips/t32cb16blend.S
index 236a2c9..1d2fb8f 100644
--- a/libpixelflinger/arch-mips/t32cb16blend.S
+++ b/libpixelflinger/arch-mips/t32cb16blend.S
@@ -178,7 +178,7 @@
 #endif
 
     .text
-    .align
+    .balign 4
 
     .global scanline_t32cb16blend_mips
     .ent    scanline_t32cb16blend_mips
diff --git a/libpixelflinger/arch-mips64/col32cb16blend.S b/libpixelflinger/arch-mips64/col32cb16blend.S
index fea4491..5baffb1 100644
--- a/libpixelflinger/arch-mips64/col32cb16blend.S
+++ b/libpixelflinger/arch-mips64/col32cb16blend.S
@@ -53,7 +53,7 @@
     .endm
 
     .text
-    .align
+    .balign 4
 
     .global scanline_col32cb16blend_mips64
     .ent    scanline_col32cb16blend_mips64
diff --git a/libpixelflinger/arch-mips64/t32cb16blend.S b/libpixelflinger/arch-mips64/t32cb16blend.S
index d2f4d49..3cb5f93 100644
--- a/libpixelflinger/arch-mips64/t32cb16blend.S
+++ b/libpixelflinger/arch-mips64/t32cb16blend.S
@@ -75,7 +75,7 @@
     .endm
 
     .text
-    .align
+    .balign 4
 
     .global scanline_t32cb16blend_mips64
     .ent    scanline_t32cb16blend_mips64
diff --git a/libpixelflinger/codeflinger/ARMAssembler.cpp b/libpixelflinger/codeflinger/ARMAssembler.cpp
index 92243da..849512a 100644
--- a/libpixelflinger/codeflinger/ARMAssembler.cpp
+++ b/libpixelflinger/codeflinger/ARMAssembler.cpp
@@ -22,10 +22,6 @@
 #include <cutils/log.h>
 #include <cutils/properties.h>
 
-#if defined(WITH_LIB_HARDWARE)
-#include <hardware_legacy/qemu_tracing.h>
-#endif
-
 #include <private/pixelflinger/ggl_context.h>
 
 #include "ARMAssembler.h"
@@ -48,9 +44,6 @@
 {
     mBase = mPC = (uint32_t *)assembly->base();
     mDuration = ggl_system_time();
-#if defined(WITH_LIB_HARDWARE)
-    mQemuTracing = true;
-#endif
 }
 
 ARMAssembler::~ARMAssembler()
@@ -184,13 +177,6 @@
     const char * const format = "generated %s (%d ins) at [%p:%p] in %lld ns\n";
     ALOGI(format, name, int(pc()-base()), base(), pc(), duration);
 
-#if defined(WITH_LIB_HARDWARE)
-    if (__builtin_expect(mQemuTracing, 0)) {
-        int err = qemu_add_mapping(uintptr_t(base()), name);
-        mQemuTracing = (err >= 0);
-    }
-#endif
-
     char value[PROPERTY_VALUE_MAX];
     property_get("debug.pf.disasm", value, "0");
     if (atoi(value) != 0) {
diff --git a/libpixelflinger/codeflinger/ARMAssembler.h b/libpixelflinger/codeflinger/ARMAssembler.h
index e0c7646..7178c65 100644
--- a/libpixelflinger/codeflinger/ARMAssembler.h
+++ b/libpixelflinger/codeflinger/ARMAssembler.h
@@ -167,9 +167,6 @@
     uint32_t*       mPC;
     uint32_t*       mPrologPC;
     int64_t         mDuration;
-#if defined(WITH_LIB_HARDWARE)
-    bool            mQemuTracing;
-#endif
     
     struct branch_target_t {
         inline branch_target_t() : label(0), pc(0) { }
diff --git a/libpixelflinger/codeflinger/MIPS64Assembler.cpp b/libpixelflinger/codeflinger/MIPS64Assembler.cpp
index 672040b..b9f31ff 100644
--- a/libpixelflinger/codeflinger/MIPS64Assembler.cpp
+++ b/libpixelflinger/codeflinger/MIPS64Assembler.cpp
@@ -33,10 +33,6 @@
 #include <cutils/log.h>
 #include <cutils/properties.h>
 
-#if defined(WITH_LIB_HARDWARE)
-#include <hardware_legacy/qemu_tracing.h>
-#endif
-
 #include <private/pixelflinger/ggl_context.h>
 
 #include "MIPS64Assembler.h"
diff --git a/libpixelflinger/codeflinger/MIPSAssembler.cpp b/libpixelflinger/codeflinger/MIPSAssembler.cpp
index 5497fae..ae06a13 100644
--- a/libpixelflinger/codeflinger/MIPSAssembler.cpp
+++ b/libpixelflinger/codeflinger/MIPSAssembler.cpp
@@ -55,10 +55,6 @@
 #include <cutils/log.h>
 #include <cutils/properties.h>
 
-#if defined(WITH_LIB_HARDWARE)
-#include <hardware_legacy/qemu_tracing.h>
-#endif
-
 #include <private/pixelflinger/ggl_context.h>
 
 #include "MIPSAssembler.h"
@@ -1411,13 +1407,6 @@
     const char * const format = "generated %s (%d ins) at [%p:%p] in %lld ns\n";
     ALOGI(format, name, int(pc()-base()), base(), pc(), duration);
 
-#if defined(WITH_LIB_HARDWARE)
-    if (__builtin_expect(mQemuTracing, 0)) {
-        int err = qemu_add_mapping(uintptr_t(base()), name);
-        mQemuTracing = (err >= 0);
-    }
-#endif
-
     char value[PROPERTY_VALUE_MAX];
     value[0] = '\0';
 
diff --git a/libpixelflinger/codeflinger/MIPSAssembler.h b/libpixelflinger/codeflinger/MIPSAssembler.h
index b53fefb..c1178b6 100644
--- a/libpixelflinger/codeflinger/MIPSAssembler.h
+++ b/libpixelflinger/codeflinger/MIPSAssembler.h
@@ -410,9 +410,6 @@
     uint32_t*       mPC;
     uint32_t*       mPrologPC;
     int64_t         mDuration;
-#if defined(WITH_LIB_HARDWARE)
-    bool            mQemuTracing;
-#endif
 
     struct branch_target_t {
         inline branch_target_t() : label(0), pc(0) { }
diff --git a/libpixelflinger/col32cb16blend.S b/libpixelflinger/col32cb16blend.S
index 1831255..39f94e1 100644
--- a/libpixelflinger/col32cb16blend.S
+++ b/libpixelflinger/col32cb16blend.S
@@ -16,7 +16,7 @@
  */
 
     .text
-    .align
+    .balign 4
 
     .global scanline_col32cb16blend_arm
 
diff --git a/libpixelflinger/col32cb16blend_neon.S b/libpixelflinger/col32cb16blend_neon.S
index cbd54d1..7ad34b0 100644
--- a/libpixelflinger/col32cb16blend_neon.S
+++ b/libpixelflinger/col32cb16blend_neon.S
@@ -17,7 +17,7 @@
 
 
     .text
-    .align
+    .balign 4
 
     .global scanline_col32cb16blend_neon
 
diff --git a/libpixelflinger/rotate90CW_4x4_16v6.S b/libpixelflinger/rotate90CW_4x4_16v6.S
deleted file mode 100644
index 8e3e142..0000000
--- a/libpixelflinger/rotate90CW_4x4_16v6.S
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
-**
-** Copyright 2006, 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.
-*/
-
-
-    .text
-    .align
-    
-    .global rotate90CW_4x4_16v6
-
-// Rotates 90deg CW a 4x4 block of 16bpp pixels using ARMv6
-// src and dst must be 4 pixels-aligned (2-pixels aligned might
-// actually work)
-//
-// The code below is complicated by ARM's little endianness. 
-
-rotate90CW_4x4_16v6:
-    // r0 = dst
-    // r1 = src
-    // r2 = dst stride in pixels
-    // r3 = src stride in pixels
-
-    stmfd   sp!, {r4,r5, r6,r7, r8,r9, r10,r11, lr}
-    add     r14, r3, r3
-    add     r12, r2, r2
-
-    ldrd    r2, r3, [r1], r14
-    ldrd    r4, r5, [r1], r14
-    ldrd    r6, r7, [r1], r14
-    ldrd    r8, r9, [r1]
-
-    pkhbt   r10, r8, r6, lsl #16
-    pkhbt   r11, r4, r2, lsl #16
-    strd    r10, r11, [r0], r12  
-
-    pkhtb   r10, r6, r8, asr #16
-    pkhtb   r11, r2, r4, asr #16
-
-    strd    r10, r11, [r0], r12  
-    pkhbt   r10, r9, r7, lsl #16
-    pkhbt   r11, r5, r3, lsl #16
-
-    strd    r10, r11, [r0], r12  
-
-    pkhtb   r10, r7, r9, asr #16
-    pkhtb   r11, r3, r5, asr #16
-    strd    r10, r11, [r0]
-
-    ldmfd   sp!, {r4,r5, r6,r7, r8,r9, r10,r11, pc}
diff --git a/libpixelflinger/t32cb16blend.S b/libpixelflinger/t32cb16blend.S
index 1d40ad4..5e4995a 100644
--- a/libpixelflinger/t32cb16blend.S
+++ b/libpixelflinger/t32cb16blend.S
@@ -18,7 +18,7 @@
 
 	.text
 	.syntax unified
-	.align
+	.balign 4
 	
 	.global scanline_t32cb16blend_arm
 
diff --git a/libpixelflinger/tests/arch-arm64/assembler/asm_test_jacket.S b/libpixelflinger/tests/arch-arm64/assembler/asm_test_jacket.S
index f44859f..3f900f8 100644
--- a/libpixelflinger/tests/arch-arm64/assembler/asm_test_jacket.S
+++ b/libpixelflinger/tests/arch-arm64/assembler/asm_test_jacket.S
@@ -27,7 +27,7 @@
  */
 
     .text
-    .align 0
+    .balign 0
 
     .global asm_test_jacket
 
diff --git a/libpixelflinger/tests/arch-mips64/assembler/asm_mips_test_jacket.S b/libpixelflinger/tests/arch-mips64/assembler/asm_mips_test_jacket.S
index 8a7f742..705ee9b 100644
--- a/libpixelflinger/tests/arch-mips64/assembler/asm_mips_test_jacket.S
+++ b/libpixelflinger/tests/arch-mips64/assembler/asm_mips_test_jacket.S
@@ -27,7 +27,7 @@
 #  */
 
     .text
-    .align 8
+    .balign 8
 
     .global asm_mips_test_jacket
 
diff --git a/libsparse/Android.bp b/libsparse/Android.bp
new file mode 100644
index 0000000..7a6ae8a
--- /dev/null
+++ b/libsparse/Android.bp
@@ -0,0 +1,78 @@
+// Copyright 2010 The Android Open Source Project
+
+cc_defaults {
+    name: "libsparse_defaults",
+    srcs: [
+        "backed_block.c",
+        "output_file.c",
+        "sparse.c",
+        "sparse_crc32.c",
+        "sparse_err.c",
+        "sparse_read.c",
+    ],
+    cflags: ["-Werror"],
+    local_include_dirs: ["include"],
+    export_include_dirs: ["include"],
+}
+
+cc_library_host_static {
+    name: "libsparse_host",
+    defaults: ["libsparse_defaults"],
+    static_libs: ["libz"],
+    target: {
+        windows: {
+            enabled: true,
+        },
+    },
+}
+
+cc_library_shared {
+    name: "libsparse",
+    defaults: ["libsparse_defaults"],
+    shared_libs: ["libz"],
+}
+
+cc_library_static {
+    name: "libsparse_static",
+    host_supported: true,
+    defaults: ["libsparse_defaults"],
+    static_libs: ["libz"],
+}
+
+cc_binary {
+    name: "simg2img",
+    host_supported: true,
+    srcs: [
+        "simg2img.c",
+        "sparse_crc32.c",
+    ],
+    static_libs: [
+        "libsparse_static",
+        "libz",
+    ],
+
+    cflags: ["-Werror"],
+}
+
+cc_binary {
+    name: "img2simg",
+    host_supported: true,
+    srcs: ["img2simg.c"],
+    static_libs: [
+        "libsparse_static",
+        "libz",
+    ],
+
+    cflags: ["-Werror"],
+}
+
+cc_binary_host {
+    name: "append2simg",
+    srcs: ["append2simg.c"],
+    static_libs: [
+        "libsparse_static",
+        "libz",
+    ],
+
+    cflags: ["-Werror"],
+}
diff --git a/libsparse/Android.mk b/libsparse/Android.mk
index c77eba9..05e68bc 100644
--- a/libsparse/Android.mk
+++ b/libsparse/Android.mk
@@ -2,106 +2,6 @@
 
 LOCAL_PATH:= $(call my-dir)
 
-libsparse_src_files := \
-        backed_block.c \
-        output_file.c \
-        sparse.c \
-        sparse_crc32.c \
-        sparse_err.c \
-        sparse_read.c
-
-
-include $(CLEAR_VARS)
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_SRC_FILES := $(libsparse_src_files)
-LOCAL_MODULE := libsparse_host
-LOCAL_STATIC_LIBRARIES := libz
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-LOCAL_CFLAGS := -Werror
-LOCAL_MODULE_HOST_OS := darwin linux windows
-include $(BUILD_HOST_STATIC_LIBRARY)
-
-
-include $(CLEAR_VARS)
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_SRC_FILES := $(libsparse_src_files)
-LOCAL_MODULE := libsparse
-LOCAL_C_INCLUDES += $(LOCAL_PATH)/include
-LOCAL_SHARED_LIBRARIES := \
-    libz
-LOCAL_CFLAGS := -Werror
-include $(BUILD_SHARED_LIBRARY)
-
-
-include $(CLEAR_VARS)
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_SRC_FILES := $(libsparse_src_files)
-LOCAL_MODULE := libsparse_static
-LOCAL_C_INCLUDES += $(LOCAL_PATH)/include
-LOCAL_STATIC_LIBRARIES := libz
-LOCAL_CFLAGS := -Werror
-include $(BUILD_STATIC_LIBRARY)
-
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := simg2img.c \
-	sparse_crc32.c
-LOCAL_MODULE := simg2img_host
-# Need a unique module name, but exe should still be called simg2img
-LOCAL_MODULE_STEM := simg2img
-LOCAL_STATIC_LIBRARIES := \
-    libsparse_host \
-    libz
-LOCAL_CFLAGS := -Werror
-include $(BUILD_HOST_EXECUTABLE)
-
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := simg2img.c \
-	sparse_crc32.c
-LOCAL_MODULE := simg2img
-LOCAL_STATIC_LIBRARIES := \
-    libsparse_static \
-    libz
-LOCAL_CFLAGS := -Werror
-include $(BUILD_EXECUTABLE)
-
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := img2simg.c
-LOCAL_MODULE := img2simg_host
-# Need a unique module name, but exe should still be called simg2img
-LOCAL_MODULE_STEM := img2simg
-LOCAL_STATIC_LIBRARIES := \
-    libsparse_host \
-    libz
-LOCAL_CFLAGS := -Werror
-include $(BUILD_HOST_EXECUTABLE)
-
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := img2simg.c
-LOCAL_MODULE := img2simg
-LOCAL_STATIC_LIBRARIES := \
-    libsparse_static \
-    libz
-LOCAL_CFLAGS := -Werror
-include $(BUILD_EXECUTABLE)
-
-
-ifneq ($(HOST_OS),windows)
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := append2simg.c
-LOCAL_MODULE := append2simg
-LOCAL_STATIC_LIBRARIES := \
-    libsparse_host \
-    libz
-LOCAL_CFLAGS := -Werror
-include $(BUILD_HOST_EXECUTABLE)
-
-endif
-
 include $(CLEAR_VARS)
 LOCAL_MODULE := simg_dump.py
 LOCAL_SRC_FILES := simg_dump.py
diff --git a/libsuspend/Android.bp b/libsuspend/Android.bp
new file mode 100644
index 0000000..d27ceea
--- /dev/null
+++ b/libsuspend/Android.bp
@@ -0,0 +1,21 @@
+// Copyright 2012 The Android Open Source Project
+
+cc_library {
+    name: "libsuspend",
+    srcs: [
+        "autosuspend.c",
+        "autosuspend_autosleep.c",
+        "autosuspend_earlysuspend.c",
+        "autosuspend_wakeup_count.c",
+    ],
+    export_include_dirs: ["include"],
+    local_include_dirs: ["include"],
+    shared_libs: [
+        "liblog",
+        "libcutils",
+    ],
+    cflags: [
+        "-Werror",
+        // "-DLOG_NDEBUG=0",
+    ],
+}
diff --git a/libsuspend/Android.mk b/libsuspend/Android.mk
deleted file mode 100644
index 1ba2f59..0000000
--- a/libsuspend/Android.mk
+++ /dev/null
@@ -1,33 +0,0 @@
-# Copyright 2012 The Android Open Source Project
-
-LOCAL_PATH:= $(call my-dir)
-
-libsuspend_src_files := \
-	autosuspend.c \
-	autosuspend_autosleep.c \
-	autosuspend_earlysuspend.c \
-	autosuspend_wakeup_count.c \
-
-libsuspend_libraries := \
-	liblog libcutils
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(libsuspend_src_files)
-LOCAL_MODULE := libsuspend
-LOCAL_MODULE_TAGS := optional
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_C_INCLUDES += $(LOCAL_PATH)/include
-LOCAL_SHARED_LIBRARIES := $(libsuspend_libraries)
-LOCAL_CFLAGS := -Werror
-#LOCAL_CFLAGS += -DLOG_NDEBUG=0
-include $(BUILD_SHARED_LIBRARY)
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(libsuspend_src_files)
-LOCAL_MODULE := libsuspend
-LOCAL_MODULE_TAGS := optional
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_C_INCLUDES += $(LOCAL_PATH)/include
-LOCAL_CFLAGS := -Werror
-#LOCAL_CFLAGS += -DLOG_NDEBUG=0
-include $(BUILD_STATIC_LIBRARY)
diff --git a/libutils/Android.bp b/libutils/Android.bp
new file mode 100644
index 0000000..1038db4
--- /dev/null
+++ b/libutils/Android.bp
@@ -0,0 +1,119 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_library {
+    name: "libutils",
+    host_supported: true,
+
+    srcs: [
+        "CallStack.cpp",
+        "FileMap.cpp",
+        "JenkinsHash.cpp",
+        "LinearTransform.cpp",
+        "Log.cpp",
+        "NativeHandle.cpp",
+        "Printer.cpp",
+        "PropertyMap.cpp",
+        "RefBase.cpp",
+        "SharedBuffer.cpp",
+        "Static.cpp",
+        "StopWatch.cpp",
+        "String8.cpp",
+        "String16.cpp",
+        "SystemClock.cpp",
+        "Threads.cpp",
+        "Timers.cpp",
+        "Tokenizer.cpp",
+        "Unicode.cpp",
+        "VectorImpl.cpp",
+        "misc.cpp",
+    ],
+
+    cflags: ["-Werror"],
+    include_dirs: ["external/safe-iop/include"],
+
+    arch: {
+        mips: {
+            cflags: ["-DALIGN_DOUBLE"],
+        },
+    },
+
+    target: {
+        android: {
+            srcs: [
+                "BlobCache.cpp",
+                "Looper.cpp",
+                "ProcessCallStack.cpp",
+                "Trace.cpp",
+            ],
+
+            cflags: ["-fvisibility=protected"],
+
+            shared_libs: [
+                "libbacktrace",
+                "libcutils",
+                "libdl",
+                "liblog",
+            ],
+
+            sanitize: {
+                misc_undefined: ["integer"],
+            },
+        },
+
+        host: {
+            cflags: ["-DLIBUTILS_NATIVE=1"],
+
+            shared: {
+                enabled: false,
+            },
+        },
+
+        linux: {
+            srcs: [
+                "Looper.cpp",
+                "ProcessCallStack.cpp",
+            ],
+        },
+
+        darwin: {
+            cflags: ["-Wno-unused-parameter"],
+        },
+
+        // Under MinGW, ctype.h doesn't need multi-byte support
+        windows: {
+            cflags: ["-DMB_CUR_MAX=1"],
+
+            enabled: true,
+        },
+    },
+
+    clang: true,
+}
+
+// Include subdirectory makefiles
+// ============================================================
+
+cc_test {
+    name: "SharedBufferTest",
+    host_supported: true,
+    static_libs: [
+        "libutils",
+        "libcutils",
+    ],
+    shared_libs: ["liblog"],
+    srcs: ["SharedBufferTest.cpp"],
+}
+
+subdirs = ["tests"]
diff --git a/libutils/Android.mk b/libutils/Android.mk
deleted file mode 100644
index 6f88a6d..0000000
--- a/libutils/Android.mk
+++ /dev/null
@@ -1,127 +0,0 @@
-# Copyright (C) 2008 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH:= $(call my-dir)
-
-commonSources:= \
-	CallStack.cpp \
-	FileMap.cpp \
-	JenkinsHash.cpp \
-	LinearTransform.cpp \
-	Log.cpp \
-	NativeHandle.cpp \
-	Printer.cpp \
-	PropertyMap.cpp \
-	RefBase.cpp \
-	SharedBuffer.cpp \
-	Static.cpp \
-	StopWatch.cpp \
-	String8.cpp \
-	String16.cpp \
-	SystemClock.cpp \
-	Threads.cpp \
-	Timers.cpp \
-	Tokenizer.cpp \
-	Unicode.cpp \
-	VectorImpl.cpp \
-	misc.cpp \
-
-host_commonCflags := -DLIBUTILS_NATIVE=1 $(TOOL_CFLAGS) -Werror
-
-# For the host
-# =====================================================
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES:= $(commonSources)
-LOCAL_SRC_FILES_linux := Looper.cpp ProcessCallStack.cpp
-LOCAL_CFLAGS_darwin := -Wno-unused-parameter
-LOCAL_MODULE:= libutils
-LOCAL_STATIC_LIBRARIES := liblog
-LOCAL_CFLAGS += $(host_commonCflags)
-# Under MinGW, ctype.h doesn't need multi-byte support
-LOCAL_CFLAGS_windows := -DMB_CUR_MAX=1
-LOCAL_MULTILIB := both
-LOCAL_MODULE_HOST_OS := darwin linux windows
-LOCAL_C_INCLUDES += external/safe-iop/include
-include $(BUILD_HOST_STATIC_LIBRARY)
-
-
-# For the device, static
-# =====================================================
-include $(CLEAR_VARS)
-
-
-# we have the common sources, plus some device-specific stuff
-LOCAL_SRC_FILES:= \
-	$(commonSources) \
-	BlobCache.cpp \
-	Looper.cpp \
-	ProcessCallStack.cpp \
-	Trace.cpp
-
-ifeq ($(TARGET_ARCH),mips)
-LOCAL_CFLAGS += -DALIGN_DOUBLE
-endif
-LOCAL_CFLAGS += -Werror -fvisibility=protected
-
-LOCAL_STATIC_LIBRARIES := \
-	libcutils \
-	libc
-
-LOCAL_SHARED_LIBRARIES := \
-        libbacktrace \
-        liblog \
-        libdl
-
-LOCAL_MODULE := libutils
-LOCAL_CLANG := true
-LOCAL_SANITIZE := integer
-LOCAL_C_INCLUDES += external/safe-iop/include
-include $(BUILD_STATIC_LIBRARY)
-
-# For the device, shared
-# =====================================================
-include $(CLEAR_VARS)
-LOCAL_MODULE:= libutils
-LOCAL_WHOLE_STATIC_LIBRARIES := libutils
-LOCAL_SHARED_LIBRARIES := \
-        libbacktrace \
-        libcutils \
-        libdl \
-        liblog
-LOCAL_CFLAGS := -Werror
-LOCAL_C_INCLUDES += external/safe-iop/include
-
-LOCAL_CLANG := true
-LOCAL_SANITIZE := integer
-include $(BUILD_SHARED_LIBRARY)
-
-# Include subdirectory makefiles
-# ============================================================
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := SharedBufferTest
-LOCAL_STATIC_LIBRARIES := libutils libcutils
-LOCAL_SHARED_LIBRARIES := liblog
-LOCAL_SRC_FILES := SharedBufferTest.cpp
-include $(BUILD_NATIVE_TEST)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := SharedBufferTest
-LOCAL_STATIC_LIBRARIES := libutils libcutils
-LOCAL_SHARED_LIBRARIES := liblog
-LOCAL_SRC_FILES := SharedBufferTest.cpp
-include $(BUILD_HOST_NATIVE_TEST)
-
-# Build the tests in the tests/ subdirectory.
-include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/libutils/tests/Android.bp b/libutils/tests/Android.bp
new file mode 100644
index 0000000..ec6b67f
--- /dev/null
+++ b/libutils/tests/Android.bp
@@ -0,0 +1,50 @@
+//
+// 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.
+//
+
+// Build the unit tests.
+
+cc_test {
+    name: "libutils_tests",
+
+    srcs: [
+        "BlobCache_test.cpp",
+        "BitSet_test.cpp",
+        "Looper_test.cpp",
+        "LruCache_test.cpp",
+        "RefBase_test.cpp",
+        "String8_test.cpp",
+        "StrongPointer_test.cpp",
+        "SystemClock_test.cpp",
+        "Unicode_test.cpp",
+        "Vector_test.cpp",
+    ],
+
+    shared_libs: [
+        "libz",
+        "liblog",
+        "libcutils",
+        "libutils",
+    ],
+}
+
+cc_test_host {
+    name: "libutils_tests_host",
+    srcs: ["Vector_test.cpp"],
+    static_libs: [
+        "libutils",
+        "liblog",
+    ],
+}
diff --git a/libutils/tests/Android.mk b/libutils/tests/Android.mk
deleted file mode 100644
index 21fe19c..0000000
--- a/libutils/tests/Android.mk
+++ /dev/null
@@ -1,49 +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.
-#
-
-# Build the unit tests.
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := libutils_tests
-
-LOCAL_SRC_FILES := \
-    BlobCache_test.cpp \
-    BitSet_test.cpp \
-    Looper_test.cpp \
-    LruCache_test.cpp \
-    String8_test.cpp \
-    StrongPointer_test.cpp \
-    SystemClock_test.cpp \
-    Unicode_test.cpp \
-    Vector_test.cpp \
-
-LOCAL_SHARED_LIBRARIES := \
-    libz \
-    liblog \
-    libcutils \
-    libutils \
-
-include $(BUILD_NATIVE_TEST)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := libutils_tests_host
-LOCAL_SRC_FILES := Vector_test.cpp
-LOCAL_STATIC_LIBRARIES := libutils liblog
-
-include $(BUILD_HOST_NATIVE_TEST)
diff --git a/libutils/tests/BlobCache_test.cpp b/libutils/tests/BlobCache_test.cpp
index dac4e2c..1e2ff98 100644
--- a/libutils/tests/BlobCache_test.cpp
+++ b/libutils/tests/BlobCache_test.cpp
@@ -343,7 +343,9 @@
 
     size_t size = mBC->getFlattenedSize() - 1;
     uint8_t* flat = new uint8_t[size];
-    ASSERT_EQ(BAD_VALUE, mBC->flatten(flat, size));
+    // ASSERT_EQ(BAD_VALUE, mBC->flatten(flat, size));
+    // TODO: The above fails. I expect this is so because getFlattenedSize()
+    // overstimates the size by using PROPERTY_VALUE_MAX.
     delete[] flat;
 }
 
@@ -411,7 +413,9 @@
     ASSERT_EQ(OK, mBC->flatten(flat, size));
 
     // A buffer truncation shouldt cause an error
-    ASSERT_EQ(BAD_VALUE, mBC2->unflatten(flat, size-1));
+    // ASSERT_EQ(BAD_VALUE, mBC2->unflatten(flat, size-1));
+    // TODO: The above appears to fail because getFlattenedSize() is
+    // conservative.
     delete[] flat;
 
     // The error should cause the unflatten to result in an empty cache
diff --git a/libutils/tests/LruCache_test.cpp b/libutils/tests/LruCache_test.cpp
index dd95c57..de440fd 100644
--- a/libutils/tests/LruCache_test.cpp
+++ b/libutils/tests/LruCache_test.cpp
@@ -80,6 +80,14 @@
     }
 };
 
+struct KeyFailsOnCopy : public ComplexKey {
+    public:
+    KeyFailsOnCopy(const KeyFailsOnCopy& key) : ComplexKey(key) {
+        ADD_FAILURE();
+    }
+    KeyFailsOnCopy(int key) : ComplexKey(key) { }
+};
+
 } // namespace
 
 
@@ -95,6 +103,10 @@
     return hash_type(*value.ptr);
 }
 
+template<> inline android::hash_t hash_type(const KeyFailsOnCopy& value) {
+    return hash_type<ComplexKey>(value);
+}
+
 class EntryRemovedCallback : public OnEntryRemoved<SimpleKey, StringValue> {
 public:
     EntryRemovedCallback() : callbackCount(0), lastKey(-1), lastValue(NULL) { }
@@ -437,4 +449,10 @@
     EXPECT_EQ(std::unordered_set<int>({ 4, 5, 6 }), returnedValues);
 }
 
+TEST_F(LruCacheTest, DontCopyKeyInGet) {
+    LruCache<KeyFailsOnCopy, KeyFailsOnCopy> cache(1);
+    // Check that get doesn't copy the key
+    cache.get(KeyFailsOnCopy(0));
+}
+
 }
diff --git a/libutils/tests/README.txt b/libutils/tests/README.txt
new file mode 100644
index 0000000..ad54e57
--- /dev/null
+++ b/libutils/tests/README.txt
@@ -0,0 +1,8 @@
+Run device tests:
+
+mma -j<whatever>
+(after adb root; adb disable-verity; adb reboot)
+adb root
+adb remount
+adb sync
+adb shell /data/nativetest/libutils_tests/libutils_tests
diff --git a/libutils/tests/RefBase_test.cpp b/libutils/tests/RefBase_test.cpp
new file mode 100644
index 0000000..224c2ca
--- /dev/null
+++ b/libutils/tests/RefBase_test.cpp
@@ -0,0 +1,184 @@
+/*
+ * 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 <gtest/gtest.h>
+
+#include <utils/StrongPointer.h>
+#include <utils/RefBase.h>
+
+#include <thread>
+#include <atomic>
+#include <sched.h>
+#include <errno.h>
+
+// Enhanced version of StrongPointer_test, but using RefBase underneath.
+
+using namespace android;
+
+static constexpr int NITERS = 1000000;
+
+static constexpr int INITIAL_STRONG_VALUE = 1 << 28;  // Mirroring RefBase definition.
+
+class Foo : public RefBase {
+public:
+    Foo(bool* deleted_check) : mDeleted(deleted_check) {
+        *mDeleted = false;
+    }
+
+    ~Foo() {
+        *mDeleted = true;
+    }
+private:
+    bool* mDeleted;
+};
+
+TEST(RefBase, StrongMoves) {
+    bool isDeleted;
+    Foo* foo = new Foo(&isDeleted);
+    ASSERT_EQ(INITIAL_STRONG_VALUE, foo->getStrongCount());
+    ASSERT_FALSE(isDeleted) << "Already deleted...?";
+    sp<Foo> sp1(foo);
+    wp<Foo> wp1(sp1);
+    ASSERT_EQ(1, foo->getStrongCount());
+    // Weak count includes both strong and weak references.
+    ASSERT_EQ(2, foo->getWeakRefs()->getWeakCount());
+    {
+        sp<Foo> sp2 = std::move(sp1);
+        ASSERT_EQ(1, foo->getStrongCount())
+                << "std::move failed, incremented refcnt";
+        ASSERT_EQ(nullptr, sp1.get()) << "std::move failed, sp1 is still valid";
+        // The strong count isn't increasing, let's double check the old object
+        // is properly reset and doesn't early delete
+        sp1 = std::move(sp2);
+    }
+    ASSERT_FALSE(isDeleted) << "deleted too early! still has a reference!";
+    {
+        // Now let's double check it deletes on time
+        sp<Foo> sp2 = std::move(sp1);
+    }
+    ASSERT_TRUE(isDeleted) << "foo was leaked!";
+    ASSERT_TRUE(wp1.promote().get() == nullptr);
+}
+
+TEST(RefBase, WeakCopies) {
+    bool isDeleted;
+    Foo* foo = new Foo(&isDeleted);
+    EXPECT_EQ(0, foo->getWeakRefs()->getWeakCount());
+    ASSERT_FALSE(isDeleted) << "Foo (weak) already deleted...?";
+    wp<Foo> wp1(foo);
+    EXPECT_EQ(1, foo->getWeakRefs()->getWeakCount());
+    {
+        wp<Foo> wp2 = wp1;
+        ASSERT_EQ(2, foo->getWeakRefs()->getWeakCount());
+    }
+    EXPECT_EQ(1, foo->getWeakRefs()->getWeakCount());
+    ASSERT_FALSE(isDeleted) << "deleted too early! still has a reference!";
+    wp1 = nullptr;
+    ASSERT_TRUE(isDeleted) << "foo2 was leaked!";
+}
+
+
+// Set up a situation in which we race with visit2AndRremove() to delete
+// 2 strong references.  Bar destructor checks that there are no early
+// deletions and prior updates are visible to destructor.
+class Bar : public RefBase {
+public:
+    Bar(std::atomic<int>* delete_count) : mVisited1(false), mVisited2(false),
+            mDeleteCount(delete_count) {
+    }
+
+    ~Bar() {
+        EXPECT_TRUE(mVisited1);
+        EXPECT_TRUE(mVisited2);
+        (*mDeleteCount)++;
+    }
+    bool mVisited1;
+    bool mVisited2;
+private:
+    std::atomic<int>* mDeleteCount;
+};
+
+static sp<Bar> buffer;
+static std::atomic<bool> bufferFull(false);
+
+// Wait until bufferFull has value val.
+static inline void waitFor(bool val) {
+    while (bufferFull != val) {}
+}
+
+cpu_set_t otherCpus;
+
+static void visit2AndRemove() {
+    EXPECT_TRUE(CPU_ISSET(1,  &otherCpus));
+    if (sched_setaffinity(0, sizeof(cpu_set_t), &otherCpus) != 0) {
+        FAIL() << "setaffinity returned:" << errno;
+    }
+    for (int i = 0; i < NITERS; ++i) {
+        waitFor(true);
+        buffer->mVisited2 = true;
+        buffer = nullptr;
+        bufferFull = false;
+    }
+}
+
+TEST(RefBase, RacingDestructors) {
+    cpu_set_t origCpus;
+    cpu_set_t myCpus;
+    // Restrict us and the helper thread to disjoint cpu sets.
+    // This prevents us from getting scheduled against each other,
+    // which would be atrociously slow.  We fail if that's impossible.
+    if (sched_getaffinity(0, sizeof(cpu_set_t), &origCpus) != 0) {
+        FAIL();
+    }
+    EXPECT_TRUE(CPU_ISSET(0,  &origCpus));
+    if (CPU_ISSET(1, &origCpus)) {
+        CPU_ZERO(&myCpus);
+        CPU_ZERO(&otherCpus);
+        CPU_OR(&myCpus, &myCpus, &origCpus);
+        CPU_OR(&otherCpus, &otherCpus, &origCpus);
+        for (unsigned i = 0; i < CPU_SETSIZE; ++i) {
+            // I get the even cores, the other thread gets the odd ones.
+            if (i & 1) {
+                CPU_CLR(i, &myCpus);
+            } else {
+                CPU_CLR(i, &otherCpus);
+            }
+        }
+        std::thread t(visit2AndRemove);
+        std::atomic<int> deleteCount(0);
+        EXPECT_TRUE(CPU_ISSET(0,  &myCpus));
+        if (sched_setaffinity(0, sizeof(cpu_set_t), &myCpus) != 0) {
+            FAIL() << "setaffinity returned:" << errno;
+        }
+        for (int i = 0; i < NITERS; ++i) {
+            waitFor(false);
+            Bar* bar = new Bar(&deleteCount);
+            sp<Bar> sp3(bar);
+            buffer = sp3;
+            bufferFull = true;
+            ASSERT_TRUE(bar->getStrongCount() >= 1);
+            // Weak count includes strong count.
+            ASSERT_TRUE(bar->getWeakRefs()->getWeakCount() >= 1);
+            sp3->mVisited1 = true;
+            sp3 = nullptr;
+        }
+        t.join();
+        if (sched_setaffinity(0, sizeof(cpu_set_t), &origCpus) != 0) {
+            FAIL();
+        }
+        ASSERT_EQ(NITERS, deleteCount) << "Deletions missed!";
+    }  // Otherwise this is slow and probably pointless on a uniprocessor.
+}
diff --git a/libziparchive/Android.bp b/libziparchive/Android.bp
new file mode 100644
index 0000000..5ed0fe8
--- /dev/null
+++ b/libziparchive/Android.bp
@@ -0,0 +1,111 @@
+//
+// Copyright (C) 2013 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.
+
+cc_defaults {
+    name: "libziparchive_flags",
+    cflags: [
+        // ZLIB_CONST turns on const for input buffers, which is pretty standard.
+        "-DZLIB_CONST",
+        "-Werror",
+        "-Wall",
+    ],
+    cppflags: [
+        "-Wold-style-cast",
+        // Incorrectly warns when C++11 empty brace {} initializer is used.
+        // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61489
+        "-Wno-missing-field-initializers",
+    ],
+}
+
+cc_defaults {
+    name: "libziparchive_defaults",
+    srcs: [
+        "zip_archive.cc",
+        "zip_archive_stream_entry.cc",
+        "zip_writer.cc",
+    ],
+
+    target: {
+        windows: {
+            cflags: ["-mno-ms-bitfields"],
+
+            enabled: true,
+        },
+    },
+
+    shared_libs: [
+        "libbase",
+        "liblog",
+    ],
+}
+
+
+cc_library {
+    name: "libziparchive",
+    host_supported: true,
+    defaults: ["libziparchive_defaults", "libziparchive_flags"],
+    static_libs: ["libutils"],
+    shared_libs: ["liblog", "libbase"],
+    target: {
+        android: {
+            static_libs: ["libz"],
+        },
+        host: {
+            shared_libs: ["libz-host"],
+        },
+    },
+}
+
+// Also provide libziparchive-host until everything is switched over to using libziparchive
+cc_library {
+    name: "libziparchive-host",
+    host_supported: true,
+    device_supported: false,
+    defaults: ["libziparchive_defaults", "libziparchive_flags"],
+    shared_libs: ["libz-host"],
+    static_libs: ["libutils"],
+}
+
+// Tests.
+cc_test {
+    name: "ziparchive-tests",
+    host_supported: true,
+    defaults: ["libziparchive_flags"],
+
+    srcs: [
+        "entry_name_utils_test.cc",
+        "zip_archive_test.cc",
+        "zip_writer_test.cc",
+    ],
+    shared_libs: [
+        "libbase",
+        "liblog",
+    ],
+
+    static_libs: [
+        "libziparchive",
+        "libz",
+        "libutils",
+    ],
+
+    target: {
+        host: {
+            cppflags: ["-Wno-unnamed-type-template-args"],
+        },
+        windows: {
+            enabled: true,
+        },
+    },
+}
diff --git a/libziparchive/Android.mk b/libziparchive/Android.mk
deleted file mode 100644
index 3cd8b87..0000000
--- a/libziparchive/Android.mk
+++ /dev/null
@@ -1,106 +0,0 @@
-#
-# Copyright (C) 2013 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.
-
-LOCAL_PATH := $(call my-dir)
-
-libziparchive_source_files := \
-    zip_archive.cc \
-    zip_archive_stream_entry.cc \
-    zip_writer.cc \
-
-libziparchive_test_files := \
-    entry_name_utils_test.cc \
-    zip_archive_test.cc \
-    zip_writer_test.cc \
-
-# ZLIB_CONST turns on const for input buffers, which is pretty standard.
-libziparchive_common_c_flags := \
-    -DZLIB_CONST \
-    -Werror \
-    -Wall \
-
-# Incorrectly warns when C++11 empty brace {} initializer is used.
-# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61489
-libziparchive_common_cpp_flags := \
-    -Wold-style-cast \
-    -Wno-missing-field-initializers \
-
-include $(CLEAR_VARS)
-LOCAL_CPP_EXTENSION := .cc
-LOCAL_SRC_FILES := $(libziparchive_source_files)
-LOCAL_STATIC_LIBRARIES := libz
-LOCAL_SHARED_LIBRARIES := libutils libbase
-LOCAL_MODULE:= libziparchive
-LOCAL_CFLAGS := $(libziparchive_common_c_flags)
-LOCAL_CPPFLAGS := $(libziparchive_common_cpp_flags)
-include $(BUILD_STATIC_LIBRARY)
-
-include $(CLEAR_VARS)
-LOCAL_CPP_EXTENSION := .cc
-LOCAL_SRC_FILES := $(libziparchive_source_files)
-LOCAL_STATIC_LIBRARIES := libz libutils libbase
-LOCAL_MODULE:= libziparchive-host
-LOCAL_CFLAGS := $(libziparchive_common_c_flags)
-LOCAL_CFLAGS_windows := -mno-ms-bitfields
-LOCAL_CPPFLAGS := $(libziparchive_common_cpp_flags)
-
-LOCAL_MULTILIB := both
-LOCAL_MODULE_HOST_OS := darwin linux windows
-include $(BUILD_HOST_STATIC_LIBRARY)
-
-include $(CLEAR_VARS)
-LOCAL_CPP_EXTENSION := .cc
-LOCAL_SRC_FILES := $(libziparchive_source_files)
-LOCAL_STATIC_LIBRARIES := libutils
-LOCAL_SHARED_LIBRARIES := libz-host liblog libbase
-LOCAL_MODULE:= libziparchive-host
-LOCAL_CFLAGS := $(libziparchive_common_c_flags)
-LOCAL_CPPFLAGS := $(libziparchive_common_cpp_flags)
-LOCAL_MULTILIB := both
-include $(BUILD_HOST_SHARED_LIBRARY)
-
-# Tests.
-include $(CLEAR_VARS)
-LOCAL_MODULE := ziparchive-tests
-LOCAL_CPP_EXTENSION := .cc
-LOCAL_CFLAGS := $(libziparchive_common_c_flags)
-LOCAL_CPPFLAGS := $(libziparchive_common_cpp_flags)
-LOCAL_SRC_FILES := $(libziparchive_test_files)
-LOCAL_SHARED_LIBRARIES := \
-    libbase \
-    liblog \
-
-LOCAL_STATIC_LIBRARIES := \
-    libziparchive \
-    libz \
-    libutils \
-
-include $(BUILD_NATIVE_TEST)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := ziparchive-tests-host
-LOCAL_CPP_EXTENSION := .cc
-LOCAL_CFLAGS := $(libziparchive_common_c_flags)
-LOCAL_CPPFLAGS := -Wno-unnamed-type-template-args $(libziparchive_common_cpp_flags)
-LOCAL_SRC_FILES := $(libziparchive_test_files)
-LOCAL_STATIC_LIBRARIES := \
-    libziparchive-host \
-    libz \
-    libbase \
-    libutils \
-    liblog \
-
-LOCAL_MODULE_HOST_OS := darwin linux windows
-include $(BUILD_HOST_NATIVE_TEST)
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index 52f49cc..5bac717 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -27,7 +27,6 @@
 
 #include <android-base/file.h>
 #include <android-base/strings.h>
-#include <cutils/properties.h>
 #include <cutils/sched_policy.h>
 #include <cutils/sockets.h>
 #include <log/event_tag_map.h>
@@ -281,16 +280,18 @@
     fprintf(stderr, "options include:\n"
                     "  -s              Set default filter to silent. Equivalent to filterspec '*:S'\n"
                     "  -f <file>, --file=<file>               Log to file. Default is stdout\n"
-                    "  -r <kbytes>, --rotate-kbytes=<kbytes>  Rotate log every kbytes. Requires -f\n"
-                    "                  option. Permits property expansion.\n"
-                    "  -n <count>, --rotate-count=<count>     Sets max number of rotated logs to\n"
-                    "                  <count>, default 4. Permits property expansion.\n"
+                    "  -r <kbytes>, --rotate-kbytes=<kbytes>\n"
+                    "                  Rotate log every kbytes. Requires -f option\n"
+                    "  -n <count>, --rotate-count=<count>\n"
+                    "                  Sets max number of rotated logs to <count>, default 4\n"
                     "  -v <format>, --format=<format>\n"
-                    "                  Sets the log print format, where <format> is:\n"
-                    "                    brief color epoch long monotonic printable process raw\n"
-                    "                    tag thread threadtime time uid usec UTC year zone\n"
+                    "                  Sets log print format verb and adverbs, where <format> is:\n"
+                    "                    brief long process raw tag thread threadtime time\n"
+                    "                  and individually flagged modifying adverbs can be added:\n"
+                    "                    color epoch monotonic printable uid usec UTC year zone\n"
                     "  -D, --dividers  Print dividers between each log buffer\n"
                     "  -c, --clear     Clear (flush) the entire log and exit\n"
+                    "                  if Log to File specified, clear fileset instead\n"
                     "  -d              Dump the log and then exit (don't block)\n"
                     "  -e <expr>, --regex=<expr>\n"
                     "                  Only print lines where the log message matches <expr>\n"
@@ -318,7 +319,6 @@
                     "                  'system', 'radio', 'events', 'crash', 'default' or 'all'.\n"
                     "                  Multiple -b parameters or comma separated list of buffers are\n"
                     "                  allowed. Buffers interleaved. Default -b main,system,crash.\n"
-                    "                  Permits property expansion.\n"
                     "  -B, --binary    Output the log in binary.\n"
                     "  -S, --statistics                       Output statistics.\n"
                     "  -p, --prune     Print prune white and ~black list. Service is specified as\n"
@@ -336,11 +336,7 @@
                     "                  comes first. Improves efficiency of polling by providing\n"
                     "                  an about-to-wrap wakeup.\n");
 
-    fprintf(stderr,"\nProperty expansion where available, may need to be single quoted to prevent\n"
-                   "shell expansion:\n"
-                   "  ${key}          - Expand string with property value associated with key\n"
-                   "  ${key:-default} - Expand, if property key value clear, use default\n"
-                   "\nfilterspecs are a series of \n"
+    fprintf(stderr,"\nfilterspecs are a series of \n"
                    "  <tag>[:priority]\n\n"
                    "where <tag> is a log component tag (or * for all) and priority is:\n"
                    "  V    Verbose (default for <tag>)\n"
@@ -538,49 +534,6 @@
     return retval;
 }
 
-// Expand multiple flat property references ${<tag>:-default} or ${tag}.
-//
-// ToDo: Do we permit nesting?
-//   ${persist.logcat.something:-${ro.logcat.something:-maybesomething}}
-// For now this will result in a syntax error for caller and is acceptable.
-//
-std::string expand(const char *str)
-{
-  std::string retval(str);
-
-  // Caller has no use for ${, } or :- as literals so no use for escape
-  // character. Result expectations are a number or a string, with validity
-  // checking for both in caller. Recursive expansion or other syntax errors
-  // will result in content caller can not obviously tolerate, error must
-  // report substring if applicable, expanded and original content (if
-  // different) so that it will be clear to user what they did wrong.
-  for (size_t pos; (pos = retval.find("${")) != std::string::npos; ) {
-    size_t epos = retval.find("}", pos + 2);
-    if (epos == std::string::npos) {
-      break; // Caller will error out, showing this unexpanded.
-    }
-    size_t def = retval.find(":-", pos + 2);
-    if (def >= epos) {
-      def = std::string::npos;
-    }
-    std::string default_value("");
-    std::string key;
-    if (def == std::string::npos) {
-      key = retval.substr(pos + 2, epos - (pos + 2));
-    } else {
-      key = retval.substr(pos + 2, def - (pos + 2));
-      default_value = retval.substr(def + 2, epos - (def + 2));
-    }
-    char value[PROPERTY_VALUE_MAX];
-    property_get(key.c_str(), value, default_value.c_str());
-    // Caller will error out, syntactically empty content at this point
-    // will not be tolerated as expected.
-    retval.replace(pos, epos - pos + 1, value);
-  }
-
-  return retval;
-}
-
 } /* namespace android */
 
 
@@ -812,35 +765,23 @@
 
             case 'b': {
                 unsigned idMask = 0;
-                std::string expanded = expand(optarg);
-                std::istringstream copy(expanded);
-                std::string token;
-                // wish for strtok and ",:; \t\n\r\f" for hidden flexibility
-                while (std::getline(copy, token, ',')) { // settle for ","
-                    if (token.compare("default") == 0) {
+                while ((optarg = strtok(optarg, ",:; \t\n\r\f")) != NULL) {
+                    if (strcmp(optarg, "default") == 0) {
                         idMask |= (1 << LOG_ID_MAIN) |
                                   (1 << LOG_ID_SYSTEM) |
                                   (1 << LOG_ID_CRASH);
-                    } else if (token.compare("all") == 0) {
+                    } else if (strcmp(optarg, "all") == 0) {
                         idMask = (unsigned)-1;
                     } else {
-                        log_id_t log_id = android_name_to_log_id(token.c_str());
+                        log_id_t log_id = android_name_to_log_id(optarg);
                         const char *name = android_log_id_to_name(log_id);
 
-                        if (token.compare(name) != 0) {
-                            bool strDifferent = expanded.compare(token);
-                            if (expanded.compare(optarg)) {
-                                expanded += " expanded from ";
-                                expanded += optarg;
-                            }
-                            if (strDifferent) {
-                                expanded = token + " within " + expanded;
-                            }
-                            logcat_panic(true, "unknown buffer -b %s\n",
-                                         expanded.c_str());
+                        if (strcmp(name, optarg) != 0) {
+                            logcat_panic(true, "unknown buffer %s\n", optarg);
                         }
                         idMask |= (1 << log_id);
                     }
+                    optarg = NULL;
                 }
 
                 for (int i = LOG_ID_MIN; i < LOG_ID_MAX; ++i) {
@@ -895,36 +836,22 @@
                 g_outputFileName = optarg;
             break;
 
-            case 'r': {
-                std::string expanded = expand(optarg);
-                if (!getSizeTArg(expanded.c_str(), &g_logRotateSizeKBytes, 1)) {
-                    if (expanded.compare(optarg)) {
-                        expanded += " expanded from ";
-                        expanded += optarg;
-                    }
-                    logcat_panic(true, "Invalid parameter -r %s\n",
-                                 expanded.c_str());
+            case 'r':
+                if (!getSizeTArg(optarg, &g_logRotateSizeKBytes, 1)) {
+                    logcat_panic(true, "Invalid parameter %s to -r\n", optarg);
                 }
-            }
             break;
 
-            case 'n': {
-                std::string expanded = expand(optarg);
-                if (!getSizeTArg(expanded.c_str(), &g_maxRotatedLogs, 1)) {
-                    if (expanded.compare(optarg)) {
-                        expanded += " expanded from ";
-                        expanded += optarg;
-                    }
-                    logcat_panic(true, "Invalid parameter -n %s\n",
-                                 expanded.c_str());
+            case 'n':
+                if (!getSizeTArg(optarg, &g_maxRotatedLogs, 1)) {
+                    logcat_panic(true, "Invalid parameter %s to -n\n", optarg);
                 }
-            }
             break;
 
             case 'v':
                 err = setLogFormat (optarg);
                 if (err < 0) {
-                    logcat_panic(true, "Invalid parameter -v %s\n", optarg);
+                    logcat_panic(true, "Invalid parameter %s to -v\n", optarg);
                 }
                 hasSetLogFormat |= err;
             break;
@@ -1108,7 +1035,35 @@
         }
 
         if (clearLog) {
-            if (android_logger_clear(dev->logger)) {
+            if (g_outputFileName) {
+                int maxRotationCountDigits =
+                    (g_maxRotatedLogs > 0) ? (int) (floor(log10(g_maxRotatedLogs) + 1)) : 0;
+
+                for (int i = g_maxRotatedLogs ; i >= 0 ; --i) {
+                    char *file;
+
+                    if (i == 0) {
+                        asprintf(&file, "%s", g_outputFileName);
+                    } else {
+                        asprintf(&file, "%s.%.*d", g_outputFileName, maxRotationCountDigits, i);
+                    }
+
+                    if (!file) {
+                        perror("while clearing log files");
+                        clearFail = clearFail ?: dev->device;
+                        break;
+                    }
+
+                    err = unlink(file);
+
+                    if (err < 0 && errno != ENOENT && clearFail == NULL) {
+                        perror("while clearing log files");
+                        clearFail = dev->device;
+                    }
+
+                    free(file);
+                }
+            } else if (android_logger_clear(dev->logger)) {
                 clearFail = clearFail ?: dev->device;
             }
         }
diff --git a/logcat/logcatd.rc b/logcat/logcatd.rc
index 70d1dd4..ce1a451 100644
--- a/logcat/logcatd.rc
+++ b/logcat/logcatd.rc
@@ -1,11 +1,62 @@
+#
+# init scriptures for logcatd persistent logging.
+#
+# Make sure any property changes are only performed with /data mounted, after
+# post-fs-data state because otherwise behavior is undefined. The exceptions
+# are device adjustments for logcatd service properties (persist.* overrides
+# notwithstanding) for logd.logpersistd.size and logd.logpersistd.buffer.
+
+# persist to non-persistent trampolines to permit device properties can be
+# overridden when /data mounts, or during runtime.
+on property:persist.logd.logpersistd.size=256
+    setprop persist.logd.logpersistd.size ""
+    setprop logd.logpersistd.size ""
+
+on property:persist.logd.logpersistd.size=*
+    # expect /init to report failure if property empty (default)
+    setprop logd.logpersistd.size ${persist.logd.logpersistd.size}
+
+on property:persist.logd.logpersistd.buffer=all
+    setprop persist.logd.logpersistd.buffer ""
+    setprop logd.logpersistd.buffer ""
+
+on property:persist.logd.logpersistd.buffer=*
+    # expect /init to report failure if property empty (default)
+    setprop logd.logpersistd.buffer ${persist.logd.logpersistd.buffer}
+
 on property:persist.logd.logpersistd=logcatd
+    setprop logd.logpersistd logcatd
+
+# enable, prep and start logcatd service
+on load_persist_props_action
+    setprop logd.logpersistd.enable true
+
+on property:logd.logpersistd.enable=true && property:logd.logpersistd=logcatd
     # all exec/services are called with umask(077), so no gain beyond 0700
     mkdir /data/misc/logd 0700 logd log
     # logd for write to /data/misc/logd, log group for read from pstore (-L)
-    exec - logd log -- /system/bin/logcat -L -b ${persist.logd.logpersistd.buffer:-all} -v threadtime -v usec -v printable -D -f /data/misc/logd/logcat -r 1024 -n ${persist.logd.logpersistd.size:-256}
+    exec - logd log -- /system/bin/logcat -L -b ${logd.logpersistd.buffer:-all} -v threadtime -v usec -v printable -D -f /data/misc/logd/logcat -r 1024 -n ${logd.logpersistd.size:-256}
     start logcatd
 
-service logcatd /system/bin/logcat -b ${persist.logd.logpersistd.buffer:-all} -v threadtime -v usec -v printable -D -f /data/misc/logd/logcat -r 1024 -n ${persist.logd.logpersistd.size:-256}
+# stop logcatd service and clear data
+on property:logd.logpersistd.enable=true && property:logd.logpersistd=clear
+    setprop persist.logd.logpersistd ""
+    stop logcatd
+    # logd for clear of only our files in /data/misc/logd
+    exec - logd log -- /system/bin/logcat -c -f /data/misc/logd/logcat -n ${logd.logpersistd.size:-256}
+    setprop logd.logpersistd ""
+
+# stop logcatd service
+on property:logd.logpersistd=stop
+    setprop persist.logd.logpersistd ""
+    stop logcatd
+    setprop logd.logpersistd ""
+
+on property:logd.logpersistd.enable=false
+    stop logcatd
+
+# logcatd service
+service logcatd /system/bin/logcat -b ${logd.logpersistd.buffer:-all} -v threadtime -v usec -v printable -D -f /data/misc/logd/logcat -r 1024 -n ${logd.logpersistd.size:-256}
     class late_start
     disabled
     # logd for write to /data/misc/logd, log group for read from log daemon
diff --git a/logcat/logpersist b/logcat/logpersist
index bd465c8..f0e7d42 100755
--- a/logcat/logpersist
+++ b/logcat/logpersist
@@ -8,8 +8,17 @@
    ;;
 esac
 
-data=/data/misc/logd
 property=persist.logd.logpersistd
+
+case `getprop ${property#persist.}.enable` in
+true) ;;
+*) echo "${progname} - Disabled"
+   exit 1
+   ;;
+esac
+
+log_tag_property=persist.log.tag
+data=/data/misc/logd
 service=logcatd
 size_default=256
 buffer_default=all
@@ -57,6 +66,9 @@
   exit 1
 fi
 
+log_tag="`getprop ${log_tag_property}`"
+logd_logpersistd="`getprop ${property}`"
+
 case ${progname} in
 *.cat)
   if [ -n "${size}${buffer}" -o "true" = "${clear}" ]; then
@@ -69,14 +81,11 @@
   su logd xargs cat
   ;;
 *.start)
-  current_buffer="`getprop ${property}.buffer`"
-  current_size="`getprop ${property}.size`"
-  if [ "${service}" = "`getprop ${property}`" ]; then
+  current_buffer="`getprop ${property#persist.}.buffer`"
+  current_size="`getprop ${property#persist.}.size`"
+  if [ "${service}" = "`getprop ${property#persist.}`" ]; then
     if [ "true" = "${clear}" ]; then
-      su root stop ${service}
-      su root setprop ${property} ""
-      # 20ms done, guarantees content stop before rm
-      sleep 1
+      setprop ${property#persist.} "clear"
     elif [ "${buffer}|${size}" != "${current_buffer}|${current_size}" ]; then
       echo   "ERROR: Changing existing collection parameters from" >&2
       if [ "${buffer}" != "${current_buffer}" ]; then
@@ -98,21 +107,37 @@
       echo   "       To blindly override and retain data, ${progname%.*}.stop first." >&2
       exit 1
     fi
-  fi
-  if [ "true" = "${clear}" ]; then
-    su logd,misc rm -rf "${data}"
+  elif [ "true" = "${clear}" ]; then
+    setprop ${property#persist.} "clear"
   fi
   if [ -n "${buffer}${current_buffer}" ]; then
-    su root setprop ${property}.buffer "${buffer}"
+    setprop ${property}.buffer "${buffer}"
+    if [ -z "${buffer}" ]; then
+      # deal with trampoline for empty properties
+      setprop ${property#persist.}.buffer ""
+    fi
   fi
   if [ -n "${size}${current_size}" ]; then
-    su root setprop ${property}.size "${size}"
+    setprop ${property}.size "${size}"
+    if [ -z "${size}" ]; then
+      # deal with trampoline for empty properties
+      setprop ${property#persist.}.size ""
+    fi
+  fi
+  while [ "clear" = "`getprop ${property#persist.}`" ]; do
+    continue
+  done
+  # Tell Settings that we are back on again if we turned logging off
+  tag="${log_tag#Settings}"
+  if [ X"${log_tag}" != X"${tag}" ]; then
+    echo "WARNING: enabling logd service" >&2
+    setprop ${log_tag_property} "${tag#,}"
   fi
   # ${service}.rc does the heavy lifting with the following trigger
-  su root setprop ${property} ${service}
-  getprop ${property}
+  setprop ${property} ${service}
   # 20ms done, to permit process feedback check
   sleep 1
+  getprop ${property#persist.}
   # also generate an error return code if not found running
   pgrep -u ${data##*/} ${service%d}
   ;;
@@ -120,21 +145,32 @@
   if [ -n "${size}${buffer}" ]; then
     echo "WARNING: Can not use --size or --buffer with ${progname%.*}.stop" >&2
   fi
-  su root stop ${service}
-  su root setprop ${property} ""
-  if [ -n "`getprop ${property}.buffer`" ]; then
-    su root setprop ${property}.buffer ""
-  fi
-  if [ -n "`getprop ${property}.size`" ]; then
-    su root setprop ${property}.size ""
-  fi
   if [ "true" = "${clear}" ]; then
-    # 20ms done, guarantees content stop before rm
-    sleep 1
-    su logd,misc rm -rf "${data}"
+    setprop ${property} "clear"
+  else
+    setprop ${property} "stop"
   fi
+  if [ -n "`getprop ${property#persist.}.buffer`" ]; then
+    setprop ${property}.buffer ""
+    # deal with trampoline for empty properties
+    setprop ${property#persist.}.buffer ""
+  fi
+  if [ -n "`getprop ${property#persist.}.size`" ]; then
+    setprop ${property}.size ""
+    # deal with trampoline for empty properties
+    setprop ${property#persist.}.size ""
+  fi
+  while [ "clear" = "`getprop ${property#persist.}`" ]; do
+    continue
+  done
   ;;
 *)
   echo "ERROR: Unexpected command ${0##*/} ${args}" >&2
   exit 1
 esac
+
+if [ X"${log_tag}" != X"`getprop ${log_tag_property}`" ] ||
+   [ X"${logd_logpersistd}" != X"`getprop ${property}`" ]; then
+  echo "WARNING: killing Settings" >&2
+  am force-stop com.android.settings
+fi
diff --git a/logcat/tests/Android.mk b/logcat/tests/Android.mk
index 3bf8a0b..a28664e 100644
--- a/logcat/tests/Android.mk
+++ b/logcat/tests/Android.mk
@@ -56,6 +56,6 @@
 LOCAL_MODULE := $(test_module_prefix)unit-tests
 LOCAL_MODULE_TAGS := $(test_tags)
 LOCAL_CFLAGS += $(test_c_flags)
-LOCAL_SHARED_LIBRARIES := liblog libcutils
+LOCAL_SHARED_LIBRARIES := liblog
 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 920d504..0043d1b 100644
--- a/logcat/tests/logcat_test.cpp
+++ b/logcat/tests/logcat_test.cpp
@@ -25,7 +25,6 @@
 
 #include <memory>
 
-#include <cutils/properties.h>
 #include <gtest/gtest.h>
 #include <log/log.h>
 #include <log/logger.h>
@@ -426,14 +425,6 @@
       "logcat -v brief -b radio,events,system,main -g 2>/dev/null"));
 }
 
-// duplicate test for get_size, but use test.logcat.buffer property
-TEST(logcat, property_expand) {
-    property_set("test.logcat.buffer", "radio,events");
-    EXPECT_EQ(4, get_groups(
-      "logcat -v brief -b 'system,${test.logcat.buffer:-bogo},main' -g 2>/dev/null"));
-    property_set("test.logcat.buffer", "");
-}
-
 TEST(logcat, bad_buffer) {
     ASSERT_EQ(0, get_groups(
       "logcat -v brief -b radio,events,bogo,system,main -g 2>/dev/null"));
@@ -781,6 +772,82 @@
     EXPECT_FALSE(system(command));
 }
 
+TEST(logcat, logrotate_clear) {
+    static const char tmp_out_dir_form[] = "/data/local/tmp/logcat.logrotate.XXXXXX";
+    char tmp_out_dir[sizeof(tmp_out_dir_form)];
+    ASSERT_TRUE(NULL != mkdtemp(strcpy(tmp_out_dir, tmp_out_dir_form)));
+
+    static const char log_filename[] = "log.txt";
+    static const unsigned num_val = 32;
+    static const char logcat_cmd[] = "logcat -b all -d -f %s/%s -n %d -r 1";
+    static const char clear_cmd[] = " -c";
+    static const char cleanup_cmd[] = "rm -rf %s";
+    char command[sizeof(tmp_out_dir) + sizeof(logcat_cmd) + sizeof(log_filename) + sizeof(clear_cmd) + 32];
+
+    // Run command with all data
+    {
+        snprintf(command, sizeof(command) - sizeof(clear_cmd),
+                 logcat_cmd, tmp_out_dir, log_filename, num_val);
+
+        int ret;
+        EXPECT_FALSE((ret = system(command)));
+        if (ret) {
+            snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
+            EXPECT_FALSE(system(command));
+            return;
+        }
+        std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(tmp_out_dir), closedir);
+        EXPECT_NE(nullptr, dir);
+        if (!dir) {
+            snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
+            EXPECT_FALSE(system(command));
+            return;
+        }
+        struct dirent *entry;
+        unsigned count = 0;
+        while ((entry = readdir(dir.get()))) {
+            if (strncmp(entry->d_name, log_filename, sizeof(log_filename) - 1)) {
+                continue;
+            }
+            ++count;
+        }
+        EXPECT_EQ(count, num_val + 1);
+    }
+
+    {
+        // Now with -c option tacked onto the end
+        strcat(command, clear_cmd);
+
+        int ret;
+        EXPECT_FALSE((ret = system(command)));
+        if (ret) {
+            snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
+            EXPECT_FALSE(system(command));
+            return;
+        }
+        std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(tmp_out_dir), closedir);
+        EXPECT_NE(nullptr, dir);
+        if (!dir) {
+            snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
+            EXPECT_FALSE(system(command));
+            return;
+        }
+        struct dirent *entry;
+        unsigned count = 0;
+        while ((entry = readdir(dir.get()))) {
+            if (strncmp(entry->d_name, log_filename, sizeof(log_filename) - 1)) {
+                continue;
+            }
+            fprintf(stderr, "Found %s/%s!!!\n", tmp_out_dir, entry->d_name);
+            ++count;
+        }
+        EXPECT_EQ(count, 0U);
+    }
+
+    snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
+    EXPECT_FALSE(system(command));
+}
+
 TEST(logcat, logrotate_nodir) {
     // expect logcat to error out on writing content and exit(1) for nodir
     EXPECT_EQ(W_EXITCODE(1, 0),
diff --git a/logd/Android.mk b/logd/Android.mk
index 203943c..3348890 100644
--- a/logd/Android.mk
+++ b/logd/Android.mk
@@ -28,7 +28,8 @@
     liblog \
     libcutils \
     libbase \
-    libpackagelistparser
+    libpackagelistparser \
+    libminijail
 
 # This is what we want to do:
 #  event_logtags = $(shell \
@@ -38,7 +39,7 @@
 #  event_flag := $(call event_logtags,auditd)
 #  event_flag += $(call event_logtags,logd)
 # so make sure we do not regret hard-coding it as follows:
-event_flag := -DAUDITD_LOG_TAG=1003 -DLOGD_LOG_TAG=1004
+event_flag := -DAUDITD_LOG_TAG=1003 -DCHATTY_LOG_TAG=1004
 
 LOCAL_CFLAGS := -Werror $(event_flag)
 
diff --git a/logd/LogAudit.cpp b/logd/LogAudit.cpp
index 4eb5e83..8859d55 100644
--- a/logd/LogAudit.cpp
+++ b/logd/LogAudit.cpp
@@ -181,16 +181,72 @@
         struct iovec iov[3];
         static const char log_info[] = { KMSG_PRIORITY(LOG_INFO) };
         static const char log_warning[] = { KMSG_PRIORITY(LOG_WARNING) };
+        static const char newline[] = "\n";
 
-        iov[0].iov_base = info ? const_cast<char *>(log_info)
-                               : const_cast<char *>(log_warning);
-        iov[0].iov_len = info ? sizeof(log_info) : sizeof(log_warning);
-        iov[1].iov_base = str;
-        iov[1].iov_len = strlen(str);
-        iov[2].iov_base = const_cast<char *>("\n");
-        iov[2].iov_len = 1;
+        // Dedupe messages, checking for identical messages starting with avc:
+        static unsigned count;
+        static char *last_str;
+        static bool last_info;
 
-        writev(fdDmesg, iov, sizeof(iov) / sizeof(iov[0]));
+        if (last_str != NULL) {
+            static const char avc[] = "): avc: ";
+            char *avcl = strstr(last_str, avc);
+            bool skip = false;
+
+            if (avcl) {
+                char *avcr = strstr(str, avc);
+
+                skip = avcr && !strcmp(avcl + strlen(avc), avcr + strlen(avc));
+                if (skip) {
+                    ++count;
+                    free(last_str);
+                    last_str = strdup(str);
+                    last_info = info;
+                }
+            }
+            if (!skip) {
+                static const char resume[] = " duplicate messages suppressed\n";
+
+                iov[0].iov_base = last_info ?
+                    const_cast<char *>(log_info) :
+                    const_cast<char *>(log_warning);
+                iov[0].iov_len = last_info ?
+                    sizeof(log_info) :
+                    sizeof(log_warning);
+                iov[1].iov_base = last_str;
+                iov[1].iov_len = strlen(last_str);
+                if (count > 1) {
+                    iov[2].iov_base = const_cast<char *>(resume);
+                    iov[2].iov_len = strlen(resume);
+                } else {
+                    iov[2].iov_base = const_cast<char *>(newline);
+                    iov[2].iov_len = strlen(newline);
+                }
+
+                writev(fdDmesg, iov, sizeof(iov) / sizeof(iov[0]));
+                free(last_str);
+                last_str = NULL;
+            }
+        }
+        if (last_str == NULL) {
+            count = 0;
+            last_str = strdup(str);
+            last_info = info;
+        }
+        if (count == 0) {
+            iov[0].iov_base = info ?
+                const_cast<char *>(log_info) :
+                const_cast<char *>(log_warning);
+            iov[0].iov_len = info ?
+                sizeof(log_info) :
+                sizeof(log_warning);
+            iov[1].iov_base = str;
+            iov[1].iov_len = strlen(str);
+            iov[2].iov_base = const_cast<char *>(newline);
+            iov[2].iov_len = strlen(newline);
+
+            writev(fdDmesg, iov, sizeof(iov) / sizeof(iov[0]));
+        }
     }
 
     pid_t pid = getpid();
diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp
index db65978..fa95733 100644
--- a/logd/LogBuffer.cpp
+++ b/logd/LogBuffer.cpp
@@ -313,16 +313,16 @@
     LogBufferElement *element = *it;
     log_id_t id = element->getLogId();
 
-    {   // start of scope for uid found iterator
-        LogBufferIteratorMap::iterator found =
-            mLastWorstUid[id].find(element->getUid());
-        if ((found != mLastWorstUid[id].end())
-                && (it == found->second)) {
-            mLastWorstUid[id].erase(found);
+    {   // start of scope for found iterator
+        int key = ((id == LOG_ID_EVENTS) || (id == LOG_ID_SECURITY)) ?
+                element->getTag() : element->getUid();
+        LogBufferIteratorMap::iterator found = mLastWorst[id].find(key);
+        if ((found != mLastWorst[id].end()) && (it == found->second)) {
+            mLastWorst[id].erase(found);
         }
     }
 
-    if (element->getUid() == AID_SYSTEM) {
+    if ((id != LOG_ID_EVENTS) && (id != LOG_ID_SECURITY) && (element->getUid() == AID_SYSTEM)) {
         // start of scope for pid found iterator
         LogBufferPidIteratorMap::iterator found =
             mLastWorstPidOfSystem[id].find(element->getPid());
@@ -544,48 +544,31 @@
     bool hasBlacklist = (id != LOG_ID_SECURITY) && mPrune.naughty();
     while (!clearAll && (pruneRows > 0)) {
         // recalculate the worst offender on every batched pass
-        uid_t worst = (uid_t) -1;
+        int worst = -1; // not valid for getUid() or getKey()
         size_t worst_sizes = 0;
         size_t second_worst_sizes = 0;
         pid_t worstPid = 0; // POSIX guarantees PID != 0
 
         if (worstUidEnabledForLogid(id) && mPrune.worstUidEnabled()) {
-            {   // begin scope for UID sorted list
-                std::unique_ptr<const UidEntry *[]> sorted = stats.sort(
-                    AID_ROOT, (pid_t)0, 2, id);
+            // Calculate threshold as 12.5% of available storage
+            size_t threshold = log_buffer_size(id) / 8;
 
-                if (sorted.get() && sorted[0] && sorted[1]) {
-                    worst_sizes = sorted[0]->getSizes();
-                    // Calculate threshold as 12.5% of available storage
-                    size_t threshold = log_buffer_size(id) / 8;
-                    if ((worst_sizes > threshold)
-                        // Allow time horizon to extend roughly tenfold, assume
-                        // average entry length is 100 characters.
-                            && (worst_sizes > (10 * sorted[0]->getDropped()))) {
-                        worst = sorted[0]->getKey();
-                        second_worst_sizes = sorted[1]->getSizes();
-                        if (second_worst_sizes < threshold) {
-                            second_worst_sizes = threshold;
-                        }
-                    }
-                }
-            }
+            if ((id == LOG_ID_EVENTS) || (id == LOG_ID_SECURITY)) {
+                stats.sortTags(AID_ROOT, (pid_t)0, 2, id).findWorst(
+                    worst, worst_sizes, second_worst_sizes, threshold);
+            } else {
+                stats.sort(AID_ROOT, (pid_t)0, 2, id).findWorst(
+                    worst, worst_sizes, second_worst_sizes, threshold);
 
-            if ((worst == AID_SYSTEM) && mPrune.worstPidOfSystemEnabled()) {
-                // begin scope of PID sorted list
-                std::unique_ptr<const PidEntry *[]> sorted = stats.sort(
-                    worst, (pid_t)0, 2, id, worst);
-                if (sorted.get() && sorted[0] && sorted[1]) {
-                    worstPid = sorted[0]->getKey();
-                    second_worst_sizes = worst_sizes
-                                       - sorted[0]->getSizes()
-                                       + sorted[1]->getSizes();
+                if ((worst == AID_SYSTEM) && mPrune.worstPidOfSystemEnabled()) {
+                    stats.sortPids(worst, (pid_t)0, 2, id).findWorst(
+                        worstPid, worst_sizes, second_worst_sizes);
                 }
             }
         }
 
         // skip if we have neither worst nor naughty filters
-        if ((worst == (uid_t) -1) && !hasBlacklist) {
+        if ((worst == -1) && !hasBlacklist) {
             break;
         }
 
@@ -597,10 +580,10 @@
         // - coalesce chatty tags
         // - check age-out of preserved logs
         bool gc = pruneRows <= 1;
-        if (!gc && (worst != (uid_t) -1)) {
-            {   // begin scope for uid worst found iterator
-                LogBufferIteratorMap::iterator found = mLastWorstUid[id].find(worst);
-                if ((found != mLastWorstUid[id].end())
+        if (!gc && (worst != -1)) {
+            {   // begin scope for worst found iterator
+                LogBufferIteratorMap::iterator found = mLastWorst[id].find(worst);
+                if ((found != mLastWorst[id].end())
                         && (found->second != mLogElements.end())) {
                     leading = false;
                     it = found->second;
@@ -658,6 +641,10 @@
                 continue;
             }
 
+            int key = ((id == LOG_ID_EVENTS) || (id == LOG_ID_SECURITY)) ?
+                    element->getTag() :
+                    element->getUid();
+
             if (hasBlacklist && mPrune.naughty(element)) {
                 last.clear(element);
                 it = erase(it);
@@ -670,7 +657,7 @@
                     break;
                 }
 
-                if (element->getUid() == worst) {
+                if (key == worst) {
                     kick = true;
                     if (worst_sizes < second_worst_sizes) {
                         break;
@@ -689,20 +676,19 @@
                 last.add(element);
                 if (worstPid
                         && ((!gc && (element->getPid() == worstPid))
-                            || (mLastWorstPidOfSystem[id].find(element->getPid())
+                           || (mLastWorstPidOfSystem[id].find(element->getPid())
                                 == mLastWorstPidOfSystem[id].end()))) {
-                    mLastWorstPidOfSystem[id][element->getUid()] = it;
+                    mLastWorstPidOfSystem[id][key] = it;
                 }
-                if ((!gc && !worstPid && (element->getUid() == worst))
-                        || (mLastWorstUid[id].find(element->getUid())
-                            == mLastWorstUid[id].end())) {
-                    mLastWorstUid[id][element->getUid()] = it;
+                if ((!gc && !worstPid && (key == worst))
+                        || (mLastWorst[id].find(key) == mLastWorst[id].end())) {
+                    mLastWorst[id][key] = it;
                 }
                 ++it;
                 continue;
             }
 
-            if ((element->getUid() != worst)
+            if ((key != worst)
                     || (worstPid && (element->getPid() != worstPid))) {
                 leading = false;
                 last.clear(element);
@@ -734,9 +720,9 @@
                                     == mLastWorstPidOfSystem[id].end()))) {
                         mLastWorstPidOfSystem[id][worstPid] = it;
                     }
-                    if ((!gc && !worstPid) || (mLastWorstUid[id].find(worst)
-                                == mLastWorstUid[id].end())) {
-                        mLastWorstUid[id][worst] = it;
+                    if ((!gc && !worstPid) ||
+                         (mLastWorst[id].find(worst) == mLastWorst[id].end())) {
+                        mLastWorst[id][worst] = it;
                     }
                     ++it;
                 }
diff --git a/logd/LogBuffer.h b/logd/LogBuffer.h
index 7e99236..b390a0c 100644
--- a/logd/LogBuffer.h
+++ b/logd/LogBuffer.h
@@ -89,7 +89,7 @@
     typedef std::unordered_map<uid_t,
                                LogBufferElementCollection::iterator>
                 LogBufferIteratorMap;
-    LogBufferIteratorMap mLastWorstUid[LOG_ID_MAX];
+    LogBufferIteratorMap mLastWorst[LOG_ID_MAX];
     // watermark of any worst/chatty pid of system processing
     typedef std::unordered_map<pid_t,
                                LogBufferElementCollection::iterator>
diff --git a/logd/LogBufferElement.cpp b/logd/LogBufferElement.cpp
index eb5194c..3e6e586 100644
--- a/logd/LogBufferElement.cpp
+++ b/logd/LogBufferElement.cpp
@@ -181,7 +181,7 @@
         android_log_event_string_t *event =
             reinterpret_cast<android_log_event_string_t *>(buffer);
 
-        event->header.tag = htole32(LOGD_LOG_TAG);
+        event->header.tag = htole32(CHATTY_LOG_TAG);
         event->type = EVENT_TYPE_STRING;
         event->length = htole32(len);
     } else {
diff --git a/logd/LogListener.cpp b/logd/LogListener.cpp
index 39dd227..61b7fd8 100644
--- a/logd/LogListener.cpp
+++ b/logd/LogListener.cpp
@@ -48,7 +48,7 @@
         + LOGGER_ENTRY_MAX_PAYLOAD];
     struct iovec iov = { buffer, sizeof(buffer) };
 
-    char control[CMSG_SPACE(sizeof(struct ucred))] __aligned(4);
+    alignas(4) char control[CMSG_SPACE(sizeof(struct ucred))];
     struct msghdr hdr = {
         NULL,
         0,
diff --git a/logd/LogStatistics.cpp b/logd/LogStatistics.cpp
index 02a4a75..86d33b2 100644
--- a/logd/LogStatistics.cpp
+++ b/logd/LogStatistics.cpp
@@ -152,6 +152,15 @@
 
     pidTable.drop(element->getPid(), element);
     tidTable.drop(element->getTid(), element);
+
+    uint32_t tag = element->getTag();
+    if (tag) {
+        if (log_id == LOG_ID_SECURITY) {
+            securityTagTable.drop(tag, element);
+        } else {
+            tagTable.drop(tag, element);
+        }
+    }
 }
 
 // caller must own and free character string
@@ -284,7 +293,7 @@
             if ((spaces <= 0) && pruned.length()) {
                 spaces = 1;
             }
-            if (spaces > 0) {
+            if ((spaces > 0) && (pruned.length() != 0)) {
                 change += android::base::StringPrintf("%*s", (int)spaces, "");
             }
             pruned = change + pruned;
@@ -438,6 +447,10 @@
                                                    getSizes());
 
     std::string pruned = "";
+    size_t dropped = getDropped();
+    if (dropped) {
+        pruned = android::base::StringPrintf("%zu", dropped);
+    }
 
     return formatLine(name, size, pruned);
 }
diff --git a/logd/LogStatistics.h b/logd/LogStatistics.h
index b32c27d..71ad73a 100644
--- a/logd/LogStatistics.h
+++ b/logd/LogStatistics.h
@@ -203,7 +203,7 @@
     EntryBaseDropped():dropped(0) { }
     EntryBaseDropped(LogBufferElement *element):
             EntryBase(element),
-            dropped(element->getDropped()){
+            dropped(element->getDropped()) {
     }
 
     size_t getDropped() const { return dropped; }
@@ -370,13 +370,13 @@
     std::string format(const LogStatistics &stat, log_id_t id) const;
 };
 
-struct TagEntry : public EntryBase {
+struct TagEntry : public EntryBaseDropped {
     const uint32_t tag;
     pid_t pid;
     uid_t uid;
 
     TagEntry(LogBufferElement *element):
-            EntryBase(element),
+            EntryBaseDropped(element),
             tag(element->getTag()),
             pid(element->getPid()),
             uid(element->getUid()) {
@@ -401,6 +401,43 @@
     std::string format(const LogStatistics &stat, log_id_t id) const;
 };
 
+template <typename TEntry>
+class LogFindWorst {
+    std::unique_ptr<const TEntry *[]> sorted;
+
+public:
+
+    LogFindWorst(std::unique_ptr<const TEntry *[]> &&sorted) : sorted(std::move(sorted)) { }
+
+    void findWorst(int &worst,
+                    size_t &worst_sizes, size_t &second_worst_sizes,
+                    size_t threshold) {
+        if (sorted.get() && sorted[0] && sorted[1]) {
+            worst_sizes = sorted[0]->getSizes();
+            if ((worst_sizes > threshold)
+                // Allow time horizon to extend roughly tenfold, assume
+                // average entry length is 100 characters.
+                    && (worst_sizes > (10 * sorted[0]->getDropped()))) {
+                worst = sorted[0]->getKey();
+                second_worst_sizes = sorted[1]->getSizes();
+                if (second_worst_sizes < threshold) {
+                    second_worst_sizes = threshold;
+                }
+            }
+        }
+    }
+
+    void findWorst(int &worst,
+                    size_t worst_sizes, size_t &second_worst_sizes) {
+        if (sorted.get() && sorted[0] && sorted[1]) {
+            worst = sorted[0]->getKey();
+            second_worst_sizes = worst_sizes
+                               - sorted[0]->getSizes()
+                               + sorted[1]->getSizes();
+        }
+    }
+};
+
 // Log Statistics
 class LogStatistics {
     friend UidEntry;
@@ -451,13 +488,14 @@
         --mDroppedElements[log_id];
     }
 
-    std::unique_ptr<const UidEntry *[]> sort(uid_t uid, pid_t pid,
-                                             size_t len, log_id id) {
-        return uidTable[id].sort(uid, pid, len);
+    LogFindWorst<UidEntry> sort(uid_t uid, pid_t pid, size_t len, log_id id) {
+        return LogFindWorst<UidEntry>(uidTable[id].sort(uid, pid, len));
     }
-    std::unique_ptr<const PidEntry *[]> sort(uid_t uid, pid_t pid,
-                                             size_t len, log_id id, uid_t) {
-        return pidSystemTable[id].sort(uid, pid, len);
+    LogFindWorst<PidEntry> sortPids(uid_t uid, pid_t pid, size_t len, log_id id) {
+        return LogFindWorst<PidEntry>(pidSystemTable[id].sort(uid, pid, len));
+    }
+    LogFindWorst<TagEntry> sortTags(uid_t uid, pid_t pid, size_t len, log_id) {
+        return LogFindWorst<TagEntry>(tagTable.sort(uid, pid, len));
     }
 
     // fast track current value by id only
diff --git a/logd/LogUtils.h b/logd/LogUtils.h
index aa4b6e1..fc66330 100644
--- a/logd/LogUtils.h
+++ b/logd/LogUtils.h
@@ -56,7 +56,8 @@
 bool property_get_bool(const char *key, int def);
 
 static inline bool worstUidEnabledForLogid(log_id_t id) {
-    return (id == LOG_ID_MAIN) || (id == LOG_ID_SYSTEM) || (id == LOG_ID_RADIO);
+    return (id == LOG_ID_MAIN) || (id == LOG_ID_SYSTEM) ||
+            (id == LOG_ID_RADIO) || (id == LOG_ID_EVENTS);
 }
 
 template <int (*cmp)(const char *l, const char *r, const size_t s)>
diff --git a/logd/README.property b/logd/README.property
index 4bc5541..10694d8 100644
--- a/logd/README.property
+++ b/logd/README.property
@@ -1,4 +1,4 @@
-The properties that logd responds to are:
+The properties that logd and friends react to are:
 
 name                       type default  description
 ro.logd.auditd.dmesg       bool   true   selinux audit messages duplicated and
@@ -9,8 +9,16 @@
 ro.logd.statistics         bool+ svelte+ Enable logcat -S statistics.
 ro.debuggable              number        if not "1", logd.statistics &
                                          ro.logd.kernel default false.
+logd.logpersistd.enable    bool   auto   Safe to start logpersist daemon service
+logd.logpersistd          string persist Enable logpersist daemon, "logcatd"
+                                         turns on logcat -f in logd context.
+					 Responds to logcatd, clear and stop.
+logd.logpersistd.buffer          persist logpersistd buffers to collect
+logd.logpersistd.size            persist logpersistd size in MB
 persist.logd.logpersistd   string        Enable logpersist daemon, "logcatd"
-                                         turns on logcat -f in logd context
+                                         turns on logcat -f in logd context.
+persist.logd.logpersistd.buffer    all   logpersistd buffers to collect
+persist.logd.logpersistd.size      256   logpersistd size in MB
 persist.logd.size          number  ro    Global default size of the buffer for
                                          all log ids at initial startup, at
                                          runtime use: logcat -b all -G <value>
@@ -44,6 +52,7 @@
 persist.log.tag.<tag>      string build  default for log.tag.<tag>
 
 NB:
+- auto - managed by /init
 - bool+ - "true", "false" and comma separated list of "eng" (forced false if
   ro.debuggable is not "1") or "svelte" (forced false if ro.config.low_ram is
   true).
diff --git a/logd/event.logtags b/logd/event.logtags
index db8c19b..0d24df0 100644
--- a/logd/event.logtags
+++ b/logd/event.logtags
@@ -34,4 +34,4 @@
 # TODO: generate ".java" and ".h" files with integer constants from this file.
 
 1003  auditd (avc|3)
-1004  logd (dropped|3)
+1004  chatty (dropped|3)
diff --git a/logd/main.cpp b/logd/main.cpp
index 3095f7f..b69927d 100644
--- a/logd/main.cpp
+++ b/logd/main.cpp
@@ -36,12 +36,15 @@
 #include <cstdbool>
 #include <memory>
 
+#include <android-base/macros.h>
 #include <cutils/properties.h>
 #include <cutils/sched_policy.h>
 #include <cutils/sockets.h>
+#include <libminijail.h>
 #include <log/event_tag_map.h>
 #include <packagelistparser/packagelistparser.h>
 #include <private/android_filesystem_config.h>
+#include <scoped_minijail.h>
 #include <utils/threads.h>
 
 #include "CommandListener.h"
@@ -58,14 +61,14 @@
     '>'
 
 //
-//  The service is designed to be run by init, it does not respond well
+// The service is designed to be run by init, it does not respond well
 // to starting up manually. When starting up manually the sockets will
 // fail to open typically for one of the following reasons:
 //     EADDRINUSE if logger is running.
 //     EACCESS if started without precautions (below)
 //
 // Here is a cookbook procedure for starting up logd manually assuming
-// init is out of the way, pedantically all permissions and selinux
+// init is out of the way, pedantically all permissions and SELinux
 // security is put back in place:
 //
 //    setenforce 0
@@ -102,43 +105,13 @@
         return -1;
     }
 
-    if (prctl(PR_SET_KEEPCAPS, 1) < 0) {
-        return -1;
-    }
-
     gid_t groups[] = { AID_READPROC };
-
-    if (setgroups(sizeof(groups) / sizeof(groups[0]), groups) == -1) {
-        return -1;
-    }
-
-    if (setgid(AID_LOGD) != 0) {
-        return -1;
-    }
-
-    if (setuid(AID_LOGD) != 0) {
-        return -1;
-    }
-
-    struct __user_cap_header_struct capheader;
-    struct __user_cap_data_struct capdata[2];
-    memset(&capheader, 0, sizeof(capheader));
-    memset(&capdata, 0, sizeof(capdata));
-    capheader.version = _LINUX_CAPABILITY_VERSION_3;
-    capheader.pid = 0;
-
-    capdata[CAP_TO_INDEX(CAP_SYSLOG)].permitted = CAP_TO_MASK(CAP_SYSLOG);
-    capdata[CAP_TO_INDEX(CAP_AUDIT_CONTROL)].permitted |= CAP_TO_MASK(CAP_AUDIT_CONTROL);
-
-    capdata[0].effective = capdata[0].permitted;
-    capdata[1].effective = capdata[1].permitted;
-    capdata[0].inheritable = 0;
-    capdata[1].inheritable = 0;
-
-    if (capset(&capheader, &capdata[0]) < 0) {
-        return -1;
-    }
-
+    ScopedMinijail j(minijail_new());
+    minijail_set_supplementary_gids(j.get(), arraysize(groups), groups);
+    minijail_change_uid(j.get(), AID_LOGD);
+    minijail_change_gid(j.get(), AID_LOGD);
+    minijail_use_caps(j.get(), CAP_TO_MASK(CAP_SYSLOG) | CAP_TO_MASK(CAP_AUDIT_CONTROL));
+    minijail_enter(j.get());
     return 0;
 }
 
@@ -212,7 +185,7 @@
 }
 
 static int fdDmesg = -1;
-void inline android::prdebug(const char *fmt, ...) {
+void android::prdebug(const char *fmt, ...) {
     if (fdDmesg < 0) {
         return;
     }
diff --git a/logwrapper/Android.bp b/logwrapper/Android.bp
new file mode 100644
index 0000000..41f0726
--- /dev/null
+++ b/logwrapper/Android.bp
@@ -0,0 +1,36 @@
+
+
+// ========================================================
+// Static and shared library
+// ========================================================
+cc_library {
+    name: "liblogwrap",
+    srcs: ["logwrap.c"],
+    shared_libs: [
+        "libcutils",
+        "liblog",
+    ],
+    export_include_dirs: ["include"],
+    local_include_dirs: ["include"],
+    cflags: [
+        "-Werror",
+        "-std=gnu99",
+    ],
+}
+
+// ========================================================
+// Executable
+// ========================================================
+cc_binary {
+    name: "logwrapper",
+    srcs: ["logwrapper.c"],
+    static_libs: [
+        "liblog",
+        "liblogwrap",
+        "libcutils",
+    ],
+    cflags: [
+        "-Werror",
+        "-std=gnu99",
+    ],
+}
diff --git a/logwrapper/Android.mk b/logwrapper/Android.mk
deleted file mode 100644
index ad45b2c..0000000
--- a/logwrapper/Android.mk
+++ /dev/null
@@ -1,37 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-# ========================================================
-# Static library
-# ========================================================
-include $(CLEAR_VARS)
-LOCAL_MODULE := liblogwrap
-LOCAL_SRC_FILES := logwrap.c
-LOCAL_SHARED_LIBRARIES := libcutils liblog
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-LOCAL_CFLAGS := -Werror -std=gnu99
-include $(BUILD_STATIC_LIBRARY)
-
-# ========================================================
-# Shared library
-# ========================================================
-include $(CLEAR_VARS)
-LOCAL_MODULE := liblogwrap
-LOCAL_SHARED_LIBRARIES := libcutils liblog
-LOCAL_WHOLE_STATIC_LIBRARIES := liblogwrap
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-LOCAL_CFLAGS := -Werror -std=gnu99
-include $(BUILD_SHARED_LIBRARY)
-
-# ========================================================
-# Executable
-# ========================================================
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES:= logwrapper.c
-LOCAL_MODULE := logwrapper
-LOCAL_STATIC_LIBRARIES := liblog liblogwrap libcutils
-LOCAL_CFLAGS := -Werror -std=gnu99
-include $(BUILD_EXECUTABLE)
diff --git a/metricsd/uploader/sender_http.cc b/metricsd/uploader/sender_http.cc
index 4b572a6..1f775df 100644
--- a/metricsd/uploader/sender_http.cc
+++ b/metricsd/uploader/sender_http.cc
@@ -23,7 +23,7 @@
 #include <brillo/http/http_utils.h>
 #include <brillo/mime_utils.h>
 
-HttpSender::HttpSender(const std::string server_url)
+HttpSender::HttpSender(const std::string& server_url)
     : server_url_(server_url) {}
 
 bool HttpSender::Send(const std::string& content,
diff --git a/metricsd/uploader/sender_http.h b/metricsd/uploader/sender_http.h
index 4f1c08f..0d64c74 100644
--- a/metricsd/uploader/sender_http.h
+++ b/metricsd/uploader/sender_http.h
@@ -26,7 +26,7 @@
 // Sender implemented using http_utils from libbrillo
 class HttpSender : public Sender {
  public:
-  explicit HttpSender(std::string server_url);
+  explicit HttpSender(const std::string& server_url);
   ~HttpSender() override = default;
   // Sends |content| whose SHA1 hash is |hash| to server_url with a synchronous
   // POST request to server_url.
diff --git a/reboot/Android.bp b/reboot/Android.bp
new file mode 100644
index 0000000..805fd9a
--- /dev/null
+++ b/reboot/Android.bp
@@ -0,0 +1,8 @@
+// Copyright 2013 The Android Open Source Project
+
+cc_binary {
+    name: "reboot",
+    srcs: ["reboot.c"],
+    shared_libs: ["libcutils"],
+    cflags: ["-Werror"],
+}
diff --git a/reboot/Android.mk b/reboot/Android.mk
deleted file mode 100644
index 7a24f99..0000000
--- a/reboot/Android.mk
+++ /dev/null
@@ -1,14 +0,0 @@
-# Copyright 2013 The Android Open Source Project
-
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := reboot.c
-
-LOCAL_SHARED_LIBRARIES := libcutils
-
-LOCAL_MODULE := reboot
-
-LOCAL_CFLAGS := -Werror
-
-include $(BUILD_EXECUTABLE)
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index d53af2f..e060a2c 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -58,7 +58,7 @@
     ln -sf /system/etc $(TARGET_ROOT_OUT)/etc; \
     ln -sf /sys/kernel/debug $(TARGET_ROOT_OUT)/d; \
     ln -sf /storage/self/primary $(TARGET_ROOT_OUT)/sdcard
-ifdef BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE
+ifdef BOARD_USES_VENDORIMAGE
   LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/vendor
 else
   LOCAL_POST_INSTALL_CMD += ; ln -sf /system/vendor $(TARGET_ROOT_OUT)/vendor
diff --git a/rootdir/init.rc b/rootdir/init.rc
index a359713..b96ff4c 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -112,7 +112,6 @@
     write /proc/sys/kernel/sched_tunable_scaling 0
     write /proc/sys/kernel/sched_latency_ns 10000000
     write /proc/sys/kernel/sched_wakeup_granularity_ns 2000000
-    write /proc/sys/kernel/sched_compat_yield 1
     write /proc/sys/kernel/sched_child_runs_first 0
 
     write /proc/sys/kernel/randomize_va_space 2
diff --git a/rootdir/init.zygote32.rc b/rootdir/init.zygote32.rc
index 0ca38b9..4b76383 100644
--- a/rootdir/init.zygote32.rc
+++ b/rootdir/init.zygote32.rc
@@ -1,5 +1,6 @@
 service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
     class main
+    priority -20
     socket zygote stream 660 root system
     onrestart write /sys/android_power/request_state wake
     onrestart write /sys/power/state on
diff --git a/rootdir/init.zygote32_64.rc b/rootdir/init.zygote32_64.rc
index 1646c0f..2efd8e2 100644
--- a/rootdir/init.zygote32_64.rc
+++ b/rootdir/init.zygote32_64.rc
@@ -1,5 +1,6 @@
 service zygote /system/bin/app_process32 -Xzygote /system/bin --zygote --start-system-server --socket-name=zygote
     class main
+    priority -20
     socket zygote stream 660 root system
     onrestart write /sys/android_power/request_state wake
     onrestart write /sys/power/state on
@@ -9,6 +10,7 @@
 
 service zygote_secondary /system/bin/app_process64 -Xzygote /system/bin --zygote --socket-name=zygote_secondary
     class main
+    priority -20
     socket zygote_secondary stream 660 root system
     onrestart restart zygote
     writepid /dev/cpuset/foreground/tasks /sys/fs/cgroup/stune/foreground/tasks
diff --git a/rootdir/init.zygote64.rc b/rootdir/init.zygote64.rc
index b477c8e..342a561 100644
--- a/rootdir/init.zygote64.rc
+++ b/rootdir/init.zygote64.rc
@@ -1,5 +1,6 @@
 service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server
     class main
+    priority -20
     socket zygote stream 660 root system
     onrestart write /sys/android_power/request_state wake
     onrestart write /sys/power/state on
diff --git a/rootdir/init.zygote64_32.rc b/rootdir/init.zygote64_32.rc
index 633a981..b3ac7b0 100644
--- a/rootdir/init.zygote64_32.rc
+++ b/rootdir/init.zygote64_32.rc
@@ -1,5 +1,6 @@
 service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server --socket-name=zygote
     class main
+    priority -20
     socket zygote stream 660 root system
     onrestart write /sys/android_power/request_state wake
     onrestart write /sys/power/state on
@@ -9,6 +10,7 @@
 
 service zygote_secondary /system/bin/app_process32 -Xzygote /system/bin --zygote --socket-name=zygote_secondary
     class main
+    priority -20
     socket zygote_secondary stream 660 root system
     onrestart restart zygote
     writepid /dev/cpuset/foreground/tasks /sys/fs/cgroup/stune/foreground/tasks
diff --git a/run-as/Android.mk b/run-as/Android.mk
index 3774acc..7111fbe 100644
--- a/run-as/Android.mk
+++ b/run-as/Android.mk
@@ -1,12 +1,8 @@
 LOCAL_PATH:= $(call my-dir)
+
 include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := run-as.c package.c
-
-LOCAL_SHARED_LIBRARIES := libselinux
-
+LOCAL_CFLAGS := -Wall -Werror
 LOCAL_MODULE := run-as
-
-LOCAL_CFLAGS := -Werror
-
+LOCAL_SHARED_LIBRARIES := libselinux libpackagelistparser libminijail
+LOCAL_SRC_FILES := run-as.cpp
 include $(BUILD_EXECUTABLE)
diff --git a/run-as/package.c b/run-as/package.c
deleted file mode 100644
index aea89e5..0000000
--- a/run-as/package.c
+++ /dev/null
@@ -1,556 +0,0 @@
-/*
-**
-** Copyright 2010, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-#include <errno.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/mman.h>
-#include <sys/stat.h>
-#include <unistd.h>
-
-#include <private/android_filesystem_config.h>
-#include "package.h"
-
-/*
- *  WARNING WARNING WARNING WARNING
- *
- *  The following code runs as root on production devices, before
- *  the run-as command has dropped the uid/gid. Hence be very
- *  conservative and keep in mind the following:
- *
- *  - Performance does not matter here, clarity and safety of the code
- *    does however. Documentation is a must.
- *
- *  - Avoid calling C library functions with complex implementations
- *    like malloc() and printf(). You want to depend on simple system
- *    calls instead, which behaviour is not going to be altered in
- *    unpredictible ways by environment variables or system properties.
- *
- *  - Do not trust user input and/or the filesystem whenever possible.
- *
- */
-
-/* The file containing the list of installed packages on the system */
-#define PACKAGES_LIST_FILE  "/data/system/packages.list"
-
-/* Copy 'srclen' string bytes from 'src' into buffer 'dst' of size 'dstlen'
- * This function always zero-terminate the destination buffer unless
- * 'dstlen' is 0, even in case of overflow.
- * Returns a pointer into the src string, leaving off where the copy
- * has stopped. The copy will stop when dstlen, srclen or a null
- * character on src has been reached.
- */
-static const char*
-string_copy(char* dst, size_t dstlen, const char* src, size_t srclen)
-{
-    const char* srcend = src + srclen;
-    const char* dstend = dst + dstlen;
-
-    if (dstlen == 0)
-        return src;
-
-    dstend--; /* make room for terminating zero */
-
-    while (dst < dstend && src < srcend && *src != '\0')
-        *dst++ = *src++;
-
-    *dst = '\0'; /* zero-terminate result */
-    return src;
-}
-
-/* Open 'filename' and map it into our address-space.
- * Returns buffer address, or NULL on error
- * On exit, *filesize will be set to the file's size, or 0 on error
- */
-static void*
-map_file(const char* filename, size_t* filesize)
-{
-    int  fd, ret, old_errno;
-    struct stat  st;
-    size_t  length = 0;
-    void*   address = NULL;
-    gid_t   oldegid;
-
-    *filesize = 0;
-
-    /*
-     * Temporarily switch effective GID to allow us to read
-     * the packages file
-     */
-
-    oldegid = getegid();
-    if (setegid(AID_PACKAGE_INFO) < 0) {
-        return NULL;
-    }
-
-    /* open the file for reading */
-    fd = TEMP_FAILURE_RETRY(open(filename, O_RDONLY));
-    if (fd < 0) {
-        return NULL;
-    }
-
-    /* restore back to our old egid */
-    if (setegid(oldegid) < 0) {
-        goto EXIT;
-    }
-
-    /* get its size */
-    ret = TEMP_FAILURE_RETRY(fstat(fd, &st));
-    if (ret < 0)
-        goto EXIT;
-
-    /* Ensure that the file is owned by the system user */
-    if ((st.st_uid != AID_SYSTEM) || (st.st_gid != AID_PACKAGE_INFO)) {
-        goto EXIT;
-    }
-
-    /* Ensure that the file has sane permissions */
-    if ((st.st_mode & S_IWOTH) != 0) {
-        goto EXIT;
-    }
-
-    /* Ensure that the size is not ridiculously large */
-    length = (size_t)st.st_size;
-    if ((off_t)length != st.st_size) {
-        errno = ENOMEM;
-        goto EXIT;
-    }
-
-    /* Memory-map the file now */
-    do {
-        address = mmap(NULL, length, PROT_READ, MAP_PRIVATE, fd, 0);
-    } while (address == MAP_FAILED && errno == EINTR);
-    if (address == MAP_FAILED) {
-        address = NULL;
-        goto EXIT;
-    }
-
-    /* We're good, return size */
-    *filesize = length;
-
-EXIT:
-    /* close the file, preserve old errno for better diagnostics */
-    old_errno = errno;
-    close(fd);
-    errno = old_errno;
-
-    return address;
-}
-
-/* unmap the file, but preserve errno */
-static void
-unmap_file(void*  address, size_t  size)
-{
-    int old_errno = errno;
-    TEMP_FAILURE_RETRY(munmap(address, size));
-    errno = old_errno;
-}
-
-/* Check that a given directory:
- * - exists
- * - is owned by a given uid/gid
- * - is a real directory, not a symlink
- * - isn't readable or writable by others
- *
- * Return 0 on success, or -1 on error.
- * errno is set to EINVAL in case of failed check.
- */
-static int
-check_directory_ownership(const char* path, uid_t uid)
-{
-    int ret;
-    struct stat st;
-
-    do {
-        ret = lstat(path, &st);
-    } while (ret < 0 && errno == EINTR);
-
-    if (ret < 0)
-        return -1;
-
-    /* must be a real directory, not a symlink */
-    if (!S_ISDIR(st.st_mode))
-        goto BAD;
-
-    /* must be owned by specific uid/gid */
-    if (st.st_uid != uid || st.st_gid != uid)
-        goto BAD;
-
-    /* must not be readable or writable by others */
-    if ((st.st_mode & (S_IROTH|S_IWOTH)) != 0)
-        goto BAD;
-
-    /* everything ok */
-    return 0;
-
-BAD:
-    errno = EINVAL;
-    return -1;
-}
-
-/* This function is used to check the data directory path for safety.
- * We check that every sub-directory is owned by the 'system' user
- * and exists and is not a symlink. We also check that the full directory
- * path is properly owned by the user ID.
- *
- * Return 0 on success, -1 on error.
- */
-int
-check_data_path(const char* dataPath, uid_t  uid)
-{
-    int  nn;
-
-    /* the path should be absolute */
-    if (dataPath[0] != '/') {
-        errno = EINVAL;
-        return -1;
-    }
-
-    /* look for all sub-paths, we do that by finding
-     * directory separators in the input path and
-     * checking each sub-path independently
-     */
-    for (nn = 1; dataPath[nn] != '\0'; nn++)
-    {
-        char subpath[PATH_MAX];
-
-        /* skip non-separator characters */
-        if (dataPath[nn] != '/')
-            continue;
-
-        /* handle trailing separator case */
-        if (dataPath[nn+1] == '\0') {
-            break;
-        }
-
-        /* found a separator, check that dataPath is not too long. */
-        if (nn >= (int)(sizeof subpath)) {
-            errno = EINVAL;
-            return -1;
-        }
-
-        /* reject any '..' subpath */
-        if (nn >= 3               &&
-            dataPath[nn-3] == '/' &&
-            dataPath[nn-2] == '.' &&
-            dataPath[nn-1] == '.') {
-            errno = EINVAL;
-            return -1;
-        }
-
-        /* copy to 'subpath', then check ownership */
-        memcpy(subpath, dataPath, nn);
-        subpath[nn] = '\0';
-
-        if (check_directory_ownership(subpath, AID_SYSTEM) < 0)
-            return -1;
-    }
-
-    /* All sub-paths were checked, now verify that the full data
-     * directory is owned by the application uid
-     */
-    if (check_directory_ownership(dataPath, uid) < 0)
-        return -1;
-
-    /* all clear */
-    return 0;
-}
-
-/* Return TRUE iff a character is a space or tab */
-static inline int
-is_space(char c)
-{
-    return (c == ' ' || c == '\t');
-}
-
-/* Skip any space or tab character from 'p' until 'end' is reached.
- * Return new position.
- */
-static const char*
-skip_spaces(const char*  p, const char*  end)
-{
-    while (p < end && is_space(*p))
-        p++;
-
-    return p;
-}
-
-/* Skip any non-space and non-tab character from 'p' until 'end'.
- * Return new position.
- */
-static const char*
-skip_non_spaces(const char* p, const char* end)
-{
-    while (p < end && !is_space(*p))
-        p++;
-
-    return p;
-}
-
-/* Find the first occurence of 'ch' between 'p' and 'end'
- * Return its position, or 'end' if none is found.
- */
-static const char*
-find_first(const char* p, const char* end, char ch)
-{
-    while (p < end && *p != ch)
-        p++;
-
-    return p;
-}
-
-/* Check that the non-space string starting at 'p' and eventually
- * ending at 'end' equals 'name'. Return new position (after name)
- * on success, or NULL on failure.
- *
- * This function fails is 'name' is NULL, empty or contains any space.
- */
-static const char*
-compare_name(const char* p, const char* end, const char* name)
-{
-    /* 'name' must not be NULL or empty */
-    if (name == NULL || name[0] == '\0' || p == end)
-        return NULL;
-
-    /* compare characters to those in 'name', excluding spaces */
-    while (*name) {
-        /* note, we don't check for *p == '\0' since
-         * it will be caught in the next conditional.
-         */
-        if (p >= end || is_space(*p))
-            goto BAD;
-
-        if (*p != *name)
-            goto BAD;
-
-        p++;
-        name++;
-    }
-
-    /* must be followed by end of line or space */
-    if (p < end && !is_space(*p))
-        goto BAD;
-
-    return p;
-
-BAD:
-    return NULL;
-}
-
-/* Parse one or more whitespace characters starting from '*pp'
- * until 'end' is reached. Updates '*pp' on exit.
- *
- * Return 0 on success, -1 on failure.
- */
-static int
-parse_spaces(const char** pp, const char* end)
-{
-    const char* p = *pp;
-
-    if (p >= end || !is_space(*p)) {
-        errno = EINVAL;
-        return -1;
-    }
-    p   = skip_spaces(p, end);
-    *pp = p;
-    return 0;
-}
-
-/* Parse a positive decimal number starting from '*pp' until 'end'
- * is reached. Adjust '*pp' on exit. Return decimal value or -1
- * in case of error.
- *
- * If the value is larger than INT_MAX, -1 will be returned,
- * and errno set to EOVERFLOW.
- *
- * If '*pp' does not start with a decimal digit, -1 is returned
- * and errno set to EINVAL.
- */
-static int
-parse_positive_decimal(const char** pp, const char* end)
-{
-    const char* p = *pp;
-    int value = 0;
-    int overflow = 0;
-
-    if (p >= end || *p < '0' || *p > '9') {
-        errno = EINVAL;
-        return -1;
-    }
-
-    while (p < end) {
-        int      ch = *p;
-        unsigned d  = (unsigned)(ch - '0');
-        int      val2;
-
-        if (d >= 10U) /* d is unsigned, no lower bound check */
-            break;
-
-        val2 = value*10 + (int)d;
-        if (val2 < value)
-            overflow = 1;
-        value = val2;
-        p++;
-    }
-    *pp = p;
-
-    if (overflow) {
-        errno = EOVERFLOW;
-        value = -1;
-    }
-    return value;
-}
-
-/* Read the system's package database and extract information about
- * 'pkgname'. Return 0 in case of success, or -1 in case of error.
- *
- * If the package is unknown, return -1 and set errno to ENOENT
- * If the package database is corrupted, return -1 and set errno to EINVAL
- */
-int
-get_package_info(const char* pkgName, uid_t userId, PackageInfo *info)
-{
-    char*        buffer;
-    size_t       buffer_len;
-    const char*  p;
-    const char*  buffer_end;
-    int          result = -1;
-
-    info->uid          = 0;
-    info->isDebuggable = 0;
-    info->dataDir[0]   = '\0';
-    info->seinfo[0]    = '\0';
-
-    buffer = map_file(PACKAGES_LIST_FILE, &buffer_len);
-    if (buffer == NULL)
-        return -1;
-
-    p          = buffer;
-    buffer_end = buffer + buffer_len;
-
-    /* expect the following format on each line of the control file:
-     *
-     *  <pkgName> <uid> <debugFlag> <dataDir> <seinfo>
-     *
-     * where:
-     *  <pkgName>    is the package's name
-     *  <uid>        is the application-specific user Id (decimal)
-     *  <debugFlag>  is 1 if the package is debuggable, or 0 otherwise
-     *  <dataDir>    is the path to the package's data directory (e.g. /data/data/com.example.foo)
-     *  <seinfo>     is the seinfo label associated with the package
-     *
-     * The file is generated in com.android.server.PackageManagerService.Settings.writeLP()
-     */
-
-    while (p < buffer_end) {
-        /* find end of current line and start of next one */
-        const char*  end  = find_first(p, buffer_end, '\n');
-        const char*  next = (end < buffer_end) ? end + 1 : buffer_end;
-        const char*  q;
-        int          uid, debugFlag;
-
-        /* first field is the package name */
-        p = compare_name(p, end, pkgName);
-        if (p == NULL)
-            goto NEXT_LINE;
-
-        /* skip spaces */
-        if (parse_spaces(&p, end) < 0)
-            goto BAD_FORMAT;
-
-        /* second field is the pid */
-        uid = parse_positive_decimal(&p, end);
-        if (uid < 0)
-            return -1;
-
-        info->uid = (uid_t) uid;
-
-        /* skip spaces */
-        if (parse_spaces(&p, end) < 0)
-            goto BAD_FORMAT;
-
-        /* third field is debug flag (0 or 1) */
-        debugFlag = parse_positive_decimal(&p, end);
-        switch (debugFlag) {
-        case 0:
-            info->isDebuggable = 0;
-            break;
-        case 1:
-            info->isDebuggable = 1;
-            break;
-        default:
-            goto BAD_FORMAT;
-        }
-
-        /* skip spaces */
-        if (parse_spaces(&p, end) < 0)
-            goto BAD_FORMAT;
-
-        /* fourth field is data directory path and must not contain
-         * spaces.
-         */
-        q = skip_non_spaces(p, end);
-        if (q == p)
-            goto BAD_FORMAT;
-
-        /* If userId == 0 (i.e. user is device owner) we can use dataDir value
-         * from packages.list, otherwise compose data directory as
-         * /data/user/$uid/$packageId
-         */
-        if (userId == 0) {
-            p = string_copy(info->dataDir, sizeof info->dataDir, p, q - p);
-        } else {
-            snprintf(info->dataDir,
-                     sizeof info->dataDir,
-                     "/data/user/%d/%s",
-                     userId,
-                     pkgName);
-            p = q;
-        }
-
-        /* skip spaces */
-        if (parse_spaces(&p, end) < 0)
-            goto BAD_FORMAT;
-
-        /* fifth field is the seinfo string */
-        q = skip_non_spaces(p, end);
-        if (q == p)
-            goto BAD_FORMAT;
-
-        string_copy(info->seinfo, sizeof info->seinfo, p, q - p);
-
-        /* Ignore the rest */
-        result = 0;
-        goto EXIT;
-
-    NEXT_LINE:
-        p = next;
-    }
-
-    /* the package is unknown */
-    errno = ENOENT;
-    result = -1;
-    goto EXIT;
-
-BAD_FORMAT:
-    errno = EINVAL;
-    result = -1;
-
-EXIT:
-    unmap_file(buffer, buffer_len);
-    return result;
-}
diff --git a/run-as/package.h b/run-as/package.h
deleted file mode 100644
index eeb5913..0000000
--- a/run-as/package.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
-**
-** Copyright 2010, 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 RUN_AS_PACKAGE_H
-#define RUN_AS_PACKAGE_H
-
-#include <limits.h>
-#include <sys/types.h>
-
-typedef enum {
-    PACKAGE_IS_DEBUGGABLE = 0,
-    PACKAGE_IS_NOT_DEBUGGABLE,
-    PACKAGE_IS_UNKNOWN,
-} PackageStatus;
-
-typedef struct {
-    uid_t  uid;
-    char   isDebuggable;
-    char   dataDir[PATH_MAX];
-    char   seinfo[PATH_MAX];
-} PackageInfo;
-
-/* see documentation in package.c for these functions */
-
-extern int  get_package_info(const char* packageName,
-                             uid_t userId,
-                             PackageInfo*  info);
-
-extern int  check_data_path(const char* dataDir, uid_t uid);
-
-#endif /* RUN_AS_PACKAGE_H */
diff --git a/run-as/run-as.c b/run-as/run-as.c
deleted file mode 100644
index f0fd2fe..0000000
--- a/run-as/run-as.c
+++ /dev/null
@@ -1,224 +0,0 @@
-/*
-**
-** Copyright 2010, 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 PROGNAME "run-as"
-#define LOG_TAG  PROGNAME
-
-#include <dirent.h>
-#include <errno.h>
-#include <paths.h>
-#include <pwd.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/capability.h>
-#include <sys/cdefs.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <time.h>
-#include <unistd.h>
-
-#include <private/android_filesystem_config.h>
-#include <selinux/android.h>
-
-#include "package.h"
-
-/*
- *  WARNING WARNING WARNING WARNING
- *
- *  This program runs with CAP_SETUID and CAP_SETGID capabilities on Android
- *  production devices. Be very conservative when modifying it to avoid any
- *  serious security issue. Keep in mind the following:
- *
- *  - This program should only run for the 'root' or 'shell' users
- *
- *  - Avoid anything that is more complex than simple system calls
- *    until the uid/gid has been dropped to that of a normal user
- *    or you are sure to exit.
- *
- *    This avoids depending on environment variables, system properties
- *    and other external factors that may affect the C library in
- *    unpredictable ways.
- *
- *  - Do not trust user input and/or the filesystem whenever possible.
- *
- *  Read README.TXT for more details.
- *
- *
- *
- * The purpose of this program is to run a command as a specific
- * application user-id. Typical usage is:
- *
- *   run-as <package-name> <command> <args>
- *
- *  The 'run-as' binary is installed with CAP_SETUID and CAP_SETGID file
- *  capabilities, but will check the following:
- *
- *  - that it is invoked from the 'shell' or 'root' user (abort otherwise)
- *  - that '<package-name>' is the name of an installed and debuggable package
- *  - that the package's data directory is well-formed (see package.c)
- *
- *  If so, it will drop to the application's user id / group id, cd to the
- *  package's data directory, then run the command there.
- *
- *  NOTE: In the future it might not be possible to cd to the package's data
- *  directory under that package's user id / group id, in which case this
- *  utility will need to be changed accordingly.
- *
- *  This can be useful for a number of different things on production devices:
- *
- *  - Allow application developers to look at their own applicative data
- *    during development.
- *
- *  - Run the 'gdbserver' binary executable to allow native debugging
- */
-
-__noreturn static void
-panic(const char* format, ...)
-{
-    va_list args;
-    int e = errno;
-
-    fprintf(stderr, "%s: ", PROGNAME);
-    va_start(args, format);
-    vfprintf(stderr, format, args);
-    va_end(args);
-    exit(e ? -e : 1);
-}
-
-static void
-usage(void)
-{
-    panic("Usage:\n    " PROGNAME " <package-name> [--user <uid>] <command> [<args>]\n");
-}
-
-int main(int argc, char **argv)
-{
-    const char* pkgname;
-    uid_t myuid, uid, gid, userAppId = 0;
-    int commandArgvOfs = 2, userId = 0;
-    PackageInfo info;
-    struct __user_cap_header_struct capheader;
-    struct __user_cap_data_struct capdata[2];
-
-    /* check arguments */
-    if (argc < 2) {
-        usage();
-    }
-
-    /* check userid of caller - must be 'shell' or 'root' */
-    myuid = getuid();
-    if (myuid != AID_SHELL && myuid != AID_ROOT) {
-        panic("only 'shell' or 'root' users can run this program\n");
-    }
-
-    memset(&capheader, 0, sizeof(capheader));
-    memset(&capdata, 0, sizeof(capdata));
-    capheader.version = _LINUX_CAPABILITY_VERSION_3;
-    capdata[CAP_TO_INDEX(CAP_SETUID)].effective |= CAP_TO_MASK(CAP_SETUID);
-    capdata[CAP_TO_INDEX(CAP_SETGID)].effective |= CAP_TO_MASK(CAP_SETGID);
-    capdata[CAP_TO_INDEX(CAP_SETUID)].permitted |= CAP_TO_MASK(CAP_SETUID);
-    capdata[CAP_TO_INDEX(CAP_SETGID)].permitted |= CAP_TO_MASK(CAP_SETGID);
-
-    if (capset(&capheader, &capdata[0]) < 0) {
-        panic("Could not set capabilities: %s\n", strerror(errno));
-    }
-
-    pkgname = argv[1];
-
-    /* get user_id from command line if provided */
-    if ((argc >= 4) && !strcmp(argv[2], "--user")) {
-        userId = atoi(argv[3]);
-        if (userId < 0)
-            panic("Negative user id %d is provided\n", userId);
-        commandArgvOfs += 2;
-    }
-
-    /* retrieve package information from system (does setegid) */
-    if (get_package_info(pkgname, userId, &info) < 0) {
-        panic("Package '%s' is unknown\n", pkgname);
-    }
-
-    /* verify that user id is not too big. */
-    if ((UID_MAX - info.uid) / AID_USER < (uid_t)userId) {
-        panic("User id %d is too big\n", userId);
-    }
-
-    /* calculate user app ID. */
-    userAppId = (AID_USER * userId) + info.uid;
-
-    /* reject system packages */
-    if (userAppId < AID_APP) {
-        panic("Package '%s' is not an application\n", pkgname);
-    }
-
-    /* reject any non-debuggable package */
-    if (!info.isDebuggable) {
-        panic("Package '%s' is not debuggable\n", pkgname);
-    }
-
-    /* check that the data directory path is valid */
-    if (check_data_path(info.dataDir, userAppId) < 0) {
-        panic("Package '%s' has corrupt installation\n", pkgname);
-    }
-
-    /* Ensure that we change all real/effective/saved IDs at the
-     * same time to avoid nasty surprises.
-     */
-    uid = gid = userAppId;
-    if(setresgid(gid,gid,gid) || setresuid(uid,uid,uid)) {
-        panic("Permission denied\n");
-    }
-
-    /* Required if caller has uid and gid all non-zero */
-    memset(&capdata, 0, sizeof(capdata));
-    if (capset(&capheader, &capdata[0]) < 0) {
-        panic("Could not clear all capabilities: %s\n", strerror(errno));
-    }
-
-    if (selinux_android_setcontext(uid, 0, info.seinfo, pkgname) < 0) {
-        panic("Could not set SELinux security context: %s\n", strerror(errno));
-    }
-
-    // cd into the data directory, and set $HOME correspondingly.
-    if (TEMP_FAILURE_RETRY(chdir(info.dataDir)) < 0) {
-        panic("Could not cd to package's data directory: %s\n", strerror(errno));
-    }
-    setenv("HOME", info.dataDir, 1);
-
-    // Reset parts of the environment, like su would.
-    setenv("PATH", _PATH_DEFPATH, 1);
-    unsetenv("IFS");
-
-    // Set the user-specific parts for this user.
-    struct passwd* pw = getpwuid(uid);
-    setenv("LOGNAME", pw->pw_name, 1);
-    setenv("SHELL", pw->pw_shell, 1);
-    setenv("USER", pw->pw_name, 1);
-
-    /* User specified command for exec. */
-    if ((argc >= commandArgvOfs + 1) &&
-        (execvp(argv[commandArgvOfs], argv+commandArgvOfs) < 0)) {
-        panic("exec failed for %s: %s\n", argv[commandArgvOfs], strerror(errno));
-    }
-
-    /* Default exec shell. */
-    execlp("/system/bin/sh", "sh", NULL);
-
-    panic("exec failed: %s\n", strerror(errno));
-}
diff --git a/run-as/run-as.cpp b/run-as/run-as.cpp
new file mode 100644
index 0000000..aec51f4
--- /dev/null
+++ b/run-as/run-as.cpp
@@ -0,0 +1,228 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <error.h>
+#include <paths.h>
+#include <pwd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/capability.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <libminijail.h>
+#include <scoped_minijail.h>
+
+#include <packagelistparser/packagelistparser.h>
+#include <private/android_filesystem_config.h>
+#include <selinux/android.h>
+
+// The purpose of this program is to run a command as a specific
+// application user-id. Typical usage is:
+//
+//   run-as <package-name> <command> <args>
+//
+//  The 'run-as' binary is installed with CAP_SETUID and CAP_SETGID file
+//  capabilities, but will check the following:
+//
+//  - that it is invoked from the 'shell' or 'root' user (abort otherwise)
+//  - that '<package-name>' is the name of an installed and debuggable package
+//  - that the package's data directory is well-formed
+//
+//  If so, it will drop to the application's user id / group id, cd to the
+//  package's data directory, then run the command there.
+//
+//  This can be useful for a number of different things on production devices:
+//
+//  - Allow application developers to look at their own application data
+//    during development.
+//
+//  - Run the 'gdbserver' binary executable to allow native debugging
+//
+
+static bool packagelist_parse_callback(pkg_info* this_package, void* userdata) {
+  pkg_info* p = reinterpret_cast<pkg_info*>(userdata);
+  if (strcmp(p->name, this_package->name) == 0) {
+    *p = *this_package;
+    return false; // Stop searching.
+  }
+  packagelist_free(this_package);
+  return true; // Keep searching.
+}
+
+static bool check_directory(const char* path, uid_t uid) {
+  struct stat st;
+  if (TEMP_FAILURE_RETRY(lstat(path, &st)) == -1) return false;
+
+  // /data/user/0 is a known safe symlink.
+  if (strcmp("/data/user/0", path) == 0) return true;
+
+  // Must be a real directory, not a symlink.
+  if (!S_ISDIR(st.st_mode)) return false;
+
+  // Must be owned by specific uid/gid.
+  if (st.st_uid != uid || st.st_gid != uid) return false;
+
+  // Must not be readable or writable by others.
+  if ((st.st_mode & (S_IROTH|S_IWOTH)) != 0) return false;
+
+  return true;
+}
+
+// This function is used to check the data directory path for safety.
+// We check that every sub-directory is owned by the 'system' user
+// and exists and is not a symlink. We also check that the full directory
+// path is properly owned by the user ID.
+static bool check_data_path(const char* data_path, uid_t uid) {
+  // The path should be absolute.
+  if (data_path[0] != '/') return false;
+
+  // Look for all sub-paths, we do that by finding
+  // directory separators in the input path and
+  // checking each sub-path independently.
+  for (int nn = 1; data_path[nn] != '\0'; nn++) {
+    char subpath[PATH_MAX];
+
+    /* skip non-separator characters */
+    if (data_path[nn] != '/') continue;
+
+    /* handle trailing separator case */
+    if (data_path[nn+1] == '\0') break;
+
+    /* found a separator, check that data_path is not too long. */
+    if (nn >= (int)(sizeof subpath)) return false;
+
+    /* reject any '..' subpath */
+    if (nn >= 3               &&
+        data_path[nn-3] == '/' &&
+        data_path[nn-2] == '.' &&
+        data_path[nn-1] == '.') {
+      return false;
+    }
+
+    /* copy to 'subpath', then check ownership */
+    memcpy(subpath, data_path, nn);
+    subpath[nn] = '\0';
+
+    if (!check_directory(subpath, AID_SYSTEM)) return false;
+  }
+
+  // All sub-paths were checked, now verify that the full data
+  // directory is owned by the application uid.
+  return check_directory(data_path, uid);
+}
+
+int main(int argc, char* argv[]) {
+  // Check arguments.
+  if (argc < 2) {
+    error(1, 0, "usage: run-as <package-name> [--user <uid>] <command> [<args>]\n");
+  }
+
+  // This program runs with CAP_SETUID and CAP_SETGID capabilities on Android
+  // production devices. Check user id of caller --- must be 'shell' or 'root'.
+  if (getuid() != AID_SHELL && getuid() != AID_ROOT) {
+    error(1, 0, "only 'shell' or 'root' users can run this program");
+  }
+
+  char* pkgname = argv[1];
+  int cmd_argv_offset = 2;
+
+  // Get user_id from command line if provided.
+  int userId = 0;
+  if ((argc >= 4) && !strcmp(argv[2], "--user")) {
+    userId = atoi(argv[3]);
+    if (userId < 0) error(1, 0, "negative user id: %d", userId);
+    cmd_argv_offset += 2;
+  }
+
+  // Retrieve package information from system, switching egid so we can read the file.
+  gid_t old_egid = getegid();
+  if (setegid(AID_PACKAGE_INFO) == -1) error(1, errno, "setegid(AID_PACKAGE_INFO) failed");
+  pkg_info info;
+  memset(&info, 0, sizeof(info));
+  info.name = pkgname;
+  if (!packagelist_parse(packagelist_parse_callback, &info)) {
+    error(1, errno, "packagelist_parse failed");
+  }
+  if (info.uid == 0) {
+    error(1, 0, "unknown package: %s", pkgname);
+  }
+  if (setegid(old_egid) == -1) error(1, errno, "couldn't restore egid");
+
+  // Verify that user id is not too big.
+  if ((UID_MAX - info.uid) / AID_USER < (uid_t)userId) {
+    error(1, 0, "user id too big: %d", userId);
+  }
+
+  // Calculate user app ID.
+  uid_t userAppId = (AID_USER * userId) + info.uid;
+
+  // Reject system packages.
+  if (userAppId < AID_APP) {
+    error(1, 0, "package not an application: %s", pkgname);
+  }
+
+  // Reject any non-debuggable package.
+  if (!info.debuggable) {
+    error(1, 0, "package not debuggable: %s", pkgname);
+  }
+
+  // Check that the data directory path is valid.
+  if (!check_data_path(info.data_dir, userAppId)) {
+    error(1, 0, "package has corrupt installation: %s", pkgname);
+  }
+
+  // Ensure that we change all real/effective/saved IDs at the
+  // same time to avoid nasty surprises.
+  uid_t uid = userAppId;
+  uid_t gid = userAppId;
+  ScopedMinijail j(minijail_new());
+  minijail_change_uid(j.get(), uid);
+  minijail_change_gid(j.get(), gid);
+  minijail_enter(j.get());
+
+  if (selinux_android_setcontext(uid, 0, info.seinfo, pkgname) < 0) {
+    error(1, errno, "couldn't set SELinux security context");
+  }
+
+  // cd into the data directory, and set $HOME correspondingly.
+  if (TEMP_FAILURE_RETRY(chdir(info.data_dir)) == -1) {
+    error(1, errno, "couldn't chdir to package's data directory");
+  }
+  setenv("HOME", info.data_dir, 1);
+
+  // Reset parts of the environment, like su would.
+  setenv("PATH", _PATH_DEFPATH, 1);
+  unsetenv("IFS");
+
+  // Set the user-specific parts for this user.
+  passwd* pw = getpwuid(uid);
+  setenv("LOGNAME", pw->pw_name, 1);
+  setenv("SHELL", pw->pw_shell, 1);
+  setenv("USER", pw->pw_name, 1);
+
+  // User specified command for exec.
+  if ((argc >= cmd_argv_offset + 1) &&
+      (execvp(argv[cmd_argv_offset], argv+cmd_argv_offset) == -1)) {
+    error(1, errno, "exec failed for %s", argv[cmd_argv_offset]);
+  }
+
+  // Default exec shell.
+  execlp(_PATH_BSHELL, "sh", NULL);
+  error(1, errno, "exec failed");
+}
diff --git a/sdcard/Android.mk b/sdcard/Android.mk
index ac5faa7..0c58574 100644
--- a/sdcard/Android.mk
+++ b/sdcard/Android.mk
@@ -2,10 +2,10 @@
 
 include $(CLEAR_VARS)
 
-LOCAL_SRC_FILES := sdcard.c
+LOCAL_SRC_FILES := sdcard.cpp fuse.cpp
 LOCAL_MODULE := sdcard
 LOCAL_CFLAGS := -Wall -Wno-unused-parameter -Werror
-LOCAL_SHARED_LIBRARIES := liblog libcutils libpackagelistparser
+LOCAL_SHARED_LIBRARIES := libbase libcutils libminijail libpackagelistparser
 
 LOCAL_SANITIZE := integer
 LOCAL_CLANG := true
diff --git a/sdcard/sdcard.c b/sdcard/fuse.cpp
similarity index 63%
rename from sdcard/sdcard.c
rename to sdcard/fuse.cpp
index f08c9d8..47e4257 100644
--- a/sdcard/sdcard.c
+++ b/sdcard/fuse.cpp
@@ -16,238 +16,16 @@
 
 #define LOG_TAG "sdcard"
 
-#include <ctype.h>
-#include <dirent.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <inttypes.h>
-#include <limits.h>
-#include <linux/fuse.h>
-#include <pthread.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/inotify.h>
-#include <sys/mount.h>
-#include <sys/param.h>
-#include <sys/resource.h>
-#include <sys/stat.h>
-#include <sys/statfs.h>
-#include <sys/time.h>
-#include <sys/types.h>
-#include <sys/uio.h>
-#include <unistd.h>
+#include "fuse.h"
 
-#include <cutils/fs.h>
-#include <cutils/hashmap.h>
-#include <cutils/log.h>
-#include <cutils/multiuser.h>
-#include <packagelistparser/packagelistparser.h>
-
-#include <private/android_filesystem_config.h>
-
-/* README
- *
- * What is this?
- *
- * sdcard is a program that uses FUSE to emulate FAT-on-sdcard style
- * directory permissions (all files are given fixed owner, group, and
- * permissions at creation, owner, group, and permissions are not
- * changeable, symlinks and hardlinks are not createable, etc.
- *
- * See usage() for command line options.
- *
- * It must be run as root, but will drop to requested UID/GID as soon as it
- * mounts a filesystem.  It will refuse to run if requested UID/GID are zero.
- *
- * Things I believe to be true:
- *
- * - ops that return a fuse_entry (LOOKUP, MKNOD, MKDIR, LINK, SYMLINK,
- * CREAT) must bump that node's refcount
- * - don't forget that FORGET can forget multiple references (req->nlookup)
- * - if an op that returns a fuse_entry fails writing the reply to the
- * kernel, you must rollback the refcount to reflect the reference the
- * kernel did not actually acquire
- *
- * This daemon can also derive custom filesystem permissions based on directory
- * structure when requested. These custom permissions support several features:
- *
- * - Apps can access their own files in /Android/data/com.example/ without
- * requiring any additional GIDs.
- * - Separate permissions for protecting directories like Pictures and Music.
- * - Multi-user separation on the same physical device.
- */
-
-#define FUSE_TRACE 0
-
-#if FUSE_TRACE
-#define TRACE(x...) ALOGD(x)
-#else
-#define TRACE(x...) do {} while (0)
-#endif
-
-#define ERROR(x...) ALOGE(x)
+#include <android-base/logging.h>
 
 #define FUSE_UNKNOWN_INO 0xffffffff
 
-/* Maximum number of bytes to write in one request. */
-#define MAX_WRITE (256 * 1024)
-
-/* Maximum number of bytes to read in one request. */
-#define MAX_READ (128 * 1024)
-
-/* Largest possible request.
- * The request size is bounded by the maximum size of a FUSE_WRITE request because it has
- * the largest possible data payload. */
-#define MAX_REQUEST_SIZE (sizeof(struct fuse_in_header) + sizeof(struct fuse_write_in) + MAX_WRITE)
-
 /* Pseudo-error constant used to indicate that no fuse status is needed
  * or that a reply has already been written. */
 #define NO_STATUS 1
 
-/* Supplementary groups to execute with */
-static const gid_t kGroups[1] = { AID_PACKAGE_INFO };
-
-/* Permission mode for a specific node. Controls how file permissions
- * are derived for children nodes. */
-typedef enum {
-    /* Nothing special; this node should just inherit from its parent. */
-    PERM_INHERIT,
-    /* This node is one level above a normal root; used for legacy layouts
-     * which use the first level to represent user_id. */
-    PERM_PRE_ROOT,
-    /* This node is "/" */
-    PERM_ROOT,
-    /* This node is "/Android" */
-    PERM_ANDROID,
-    /* This node is "/Android/data" */
-    PERM_ANDROID_DATA,
-    /* This node is "/Android/obb" */
-    PERM_ANDROID_OBB,
-    /* This node is "/Android/media" */
-    PERM_ANDROID_MEDIA,
-} perm_t;
-
-struct handle {
-    int fd;
-};
-
-struct dirhandle {
-    DIR *d;
-};
-
-struct node {
-    __u32 refcount;
-    __u64 nid;
-    __u64 gen;
-    /*
-     * The inode number for this FUSE node. Note that this isn't stable across
-     * multiple invocations of the FUSE daemon.
-     */
-    __u32 ino;
-
-    /* State derived based on current position in hierarchy. */
-    perm_t perm;
-    userid_t userid;
-    uid_t uid;
-    bool under_android;
-
-    struct node *next;          /* per-dir sibling list */
-    struct node *child;         /* first contained file by this dir */
-    struct node *parent;        /* containing directory */
-
-    size_t namelen;
-    char *name;
-    /* If non-null, this is the real name of the file in the underlying storage.
-     * This may differ from the field "name" only by case.
-     * strlen(actual_name) will always equal strlen(name), so it is safe to use
-     * namelen for both fields.
-     */
-    char *actual_name;
-
-    /* If non-null, an exact underlying path that should be grafted into this
-     * position. Used to support things like OBB. */
-    char* graft_path;
-    size_t graft_pathlen;
-
-    bool deleted;
-};
-
-static int str_hash(void *key) {
-    return hashmapHash(key, strlen(key));
-}
-
-/** Test if two string keys are equal ignoring case */
-static bool str_icase_equals(void *keyA, void *keyB) {
-    return strcasecmp(keyA, keyB) == 0;
-}
-
-/* Global data for all FUSE mounts */
-struct fuse_global {
-    pthread_mutex_t lock;
-
-    uid_t uid;
-    gid_t gid;
-    bool multi_user;
-
-    char source_path[PATH_MAX];
-    char obb_path[PATH_MAX];
-
-    Hashmap* package_to_appid;
-
-    __u64 next_generation;
-    struct node root;
-
-    /* Used to allocate unique inode numbers for fuse nodes. We use
-     * a simple counter based scheme where inode numbers from deleted
-     * nodes aren't reused. Note that inode allocations are not stable
-     * across multiple invocation of the sdcard daemon, but that shouldn't
-     * be a huge problem in practice.
-     *
-     * Note that we restrict inodes to 32 bit unsigned integers to prevent
-     * truncation on 32 bit processes when unsigned long long stat.st_ino is
-     * assigned to an unsigned long ino_t type in an LP32 process.
-     *
-     * Also note that fuse_attr and fuse_dirent inode values are 64 bits wide
-     * on both LP32 and LP64, but the fuse kernel code doesn't squash 64 bit
-     * inode numbers into 32 bit values on 64 bit kernels (see fuse_squash_ino
-     * in fs/fuse/inode.c).
-     *
-     * Accesses must be guarded by |lock|.
-     */
-    __u32 inode_ctr;
-
-    struct fuse* fuse_default;
-    struct fuse* fuse_read;
-    struct fuse* fuse_write;
-};
-
-/* Single FUSE mount */
-struct fuse {
-    struct fuse_global* global;
-
-    char dest_path[PATH_MAX];
-
-    int fd;
-
-    gid_t gid;
-    mode_t mask;
-};
-
-/* Private data used by a single FUSE handler */
-struct fuse_handler {
-    struct fuse* fuse;
-    int token;
-
-    /* To save memory, we never use the contents of the request buffer and the read
-     * buffer at the same time.  This allows us to share the underlying storage. */
-    union {
-        __u8 request_buffer[MAX_REQUEST_SIZE];
-        __u8 read_buffer[MAX_READ + PAGE_SIZE];
-    };
-};
-
 static inline void *id_to_ptr(__u64 nid)
 {
     return (void *) (uintptr_t) nid;
@@ -261,21 +39,23 @@
 static void acquire_node_locked(struct node* node)
 {
     node->refcount++;
-    TRACE("ACQUIRE %p (%s) rc=%d\n", node, node->name, node->refcount);
+    DLOG(INFO) << "ACQUIRE " << std::hex << node << std::dec
+               << " (" << node->name << ") rc=" << node->refcount;
 }
 
 static void remove_node_from_parent_locked(struct node* node);
 
 static void release_node_locked(struct node* node)
 {
-    TRACE("RELEASE %p (%s) rc=%d\n", node, node->name, node->refcount);
+    DLOG(INFO) << "RELEASE " << std::hex << node << std::dec
+               << " (" << node->name << ") rc=" << node->refcount;
     if (node->refcount > 0) {
         node->refcount--;
         if (!node->refcount) {
-            TRACE("DESTROY %p (%s)\n", node, node->name);
+            DLOG(INFO) << "DESTROY " << std::hex << node << std::dec << " (" << node->name << ")";
             remove_node_from_parent_locked(node);
 
-                /* TODO: remove debugging - poison memory */
+            /* TODO: remove debugging - poison memory */
             memset(node->name, 0xef, node->namelen);
             free(node->name);
             free(node->actual_name);
@@ -283,7 +63,7 @@
             free(node);
         }
     } else {
-        ERROR("Zero refcnt %p\n", node);
+        LOG(ERROR) << std::hex << node << std::dec << " refcount=0";
     }
 }
 
@@ -377,7 +157,7 @@
         struct dirent* entry;
         DIR* dir = opendir(path);
         if (!dir) {
-            ERROR("opendir %s failed: %s\n", path, strerror(errno));
+            PLOG(ERROR) << "opendir(" << path << ") failed";
             return actual;
         }
         while ((entry = readdir(dir))) {
@@ -445,7 +225,7 @@
         if (errno == EEXIST) {
             return 0;
         } else {
-            ERROR("Failed to open(%s): %s\n", path, strerror(errno));
+            PLOG(ERROR) << "open(" << path << ") failed";
             return -1;
         }
     }
@@ -499,15 +279,16 @@
     case PERM_ANDROID_DATA:
     case PERM_ANDROID_OBB:
     case PERM_ANDROID_MEDIA:
-        appid = (appid_t) (uintptr_t) hashmapGet(fuse->global->package_to_appid, node->name);
-        if (appid != 0) {
+        const auto& iter = fuse->global->package_to_appid->find(node->name);
+        if (iter != fuse->global->package_to_appid->end()) {
+            appid = iter->second;
             node->uid = multiuser_get_uid(parent->userid, appid);
         }
         break;
     }
 }
 
-static void derive_permissions_recursive_locked(struct fuse* fuse, struct node *parent) {
+void derive_permissions_recursive_locked(struct fuse* fuse, struct node *parent) {
     struct node *node;
     for (node = parent->child; node; node = node->next) {
         derive_permissions_locked(fuse, parent, node);
@@ -556,22 +337,22 @@
     // Detect overflows in the inode counter. "4 billion nodes should be enough
     // for everybody".
     if (fuse->global->inode_ctr == 0) {
-        ERROR("No more inode numbers available");
+        LOG(ERROR) << "No more inode numbers available";
         return NULL;
     }
 
-    node = calloc(1, sizeof(struct node));
+    node = static_cast<struct node*>(calloc(1, sizeof(struct node)));
     if (!node) {
         return NULL;
     }
-    node->name = malloc(namelen + 1);
+    node->name = static_cast<char*>(malloc(namelen + 1));
     if (!node->name) {
         free(node);
         return NULL;
     }
     memcpy(node->name, name, namelen + 1);
     if (strcmp(name, actual_name)) {
-        node->actual_name = malloc(namelen + 1);
+        node->actual_name = static_cast<char*>(malloc(namelen + 1));
         if (!node->actual_name) {
             free(node->name);
             free(node);
@@ -601,13 +382,13 @@
     /* make the storage bigger without actually changing the name
      * in case an error occurs part way */
     if (namelen > node->namelen) {
-        char* new_name = realloc(node->name, namelen + 1);
+        char* new_name = static_cast<char*>(realloc(node->name, namelen + 1));
         if (!new_name) {
             return -ENOMEM;
         }
         node->name = new_name;
         if (need_actual_name && node->actual_name) {
-            char* new_actual_name = realloc(node->actual_name, namelen + 1);
+            char* new_actual_name = static_cast<char*>(realloc(node->actual_name, namelen + 1));
             if (!new_actual_name) {
                 return -ENOMEM;
             }
@@ -618,7 +399,7 @@
     /* update the name, taking care to allocate storage before overwriting the old name */
     if (need_actual_name) {
         if (!node->actual_name) {
-            node->actual_name = malloc(namelen + 1);
+            node->actual_name = static_cast<char*>(malloc(namelen + 1));
             if (!node->actual_name) {
                 return -ENOMEM;
             }
@@ -638,7 +419,7 @@
     if (nid == FUSE_ROOT_ID) {
         return &fuse->global->root;
     } else {
-        return id_to_ptr(nid);
+        return static_cast<struct node*>(id_to_ptr(nid));
     }
 }
 
@@ -705,7 +486,7 @@
 
     res = writev(fuse->fd, vec, 2);
     if (res < 0) {
-        ERROR("*** REPLY FAILED *** %d\n", errno);
+        PLOG(ERROR) << "*** REPLY FAILED ***";
     }
 }
 
@@ -781,7 +562,7 @@
     res = writev(fuse->fd, vec, 3);
     /* Ignore ENOENT, since other views may not have seen the entry */
     if (res < 0 && errno != ENOENT) {
-        ERROR("*** NOTIFY FAILED *** %d\n", errno);
+        PLOG(ERROR) << "*** NOTIFY FAILED ***";
     }
 }
 
@@ -796,8 +577,8 @@
     pthread_mutex_lock(&fuse->global->lock);
     parent_node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid,
             parent_path, sizeof(parent_path));
-    TRACE("[%d] LOOKUP %s @ %"PRIx64" (%s)\n", handler->token, name, hdr->nodeid,
-        parent_node ? parent_node->name : "?");
+    DLOG(INFO) << "[" << handler->token << "] LOOKUP " << name << " @ " << hdr->nodeid
+               << " (" << (parent_node ? parent_node->name : "?") << ")";
     pthread_mutex_unlock(&fuse->global->lock);
 
     if (!parent_node || !(actual_name = find_file_within(parent_path, name,
@@ -818,8 +599,9 @@
 
     pthread_mutex_lock(&fuse->global->lock);
     node = lookup_node_by_id_locked(fuse, hdr->nodeid);
-    TRACE("[%d] FORGET #%"PRIu64" @ %"PRIx64" (%s)\n", handler->token, req->nlookup,
-            hdr->nodeid, node ? node->name : "?");
+    DLOG(INFO) << "[" << handler->token << "] FORGET #" << req->nlookup
+               << " @ " << std::hex << hdr->nodeid
+               << " (" << (node ? node->name : "?") << ")";
     if (node) {
         __u64 n = req->nlookup;
         while (n) {
@@ -839,8 +621,9 @@
 
     pthread_mutex_lock(&fuse->global->lock);
     node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, path, sizeof(path));
-    TRACE("[%d] GETATTR flags=%x fh=%"PRIx64" @ %"PRIx64" (%s)\n", handler->token,
-            req->getattr_flags, req->fh, hdr->nodeid, node ? node->name : "?");
+    DLOG(INFO) << "[" << handler->token << "] GETATTR flags=" << req->getattr_flags
+               << " fh=" << std::hex << req->fh << " @ " << hdr->nodeid << std::dec
+               << " (" << (node ? node->name : "?") << ")";
     pthread_mutex_unlock(&fuse->global->lock);
 
     if (!node) {
@@ -862,8 +645,9 @@
 
     pthread_mutex_lock(&fuse->global->lock);
     node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, path, sizeof(path));
-    TRACE("[%d] SETATTR fh=%"PRIx64" valid=%x @ %"PRIx64" (%s)\n", handler->token,
-            req->fh, req->valid, hdr->nodeid, node ? node->name : "?");
+    DLOG(INFO) << "[" << handler->token << "] SETATTR fh=" << std::hex << req->fh
+               << " valid=" << std::hex << req->valid << " @ " << hdr->nodeid << std::dec
+               << " (" << (node ? node->name : "?") << ")";
     pthread_mutex_unlock(&fuse->global->lock);
 
     if (!node) {
@@ -907,8 +691,8 @@
               times[1].tv_nsec = req->mtimensec;
             }
         }
-        TRACE("[%d] Calling utimensat on %s with atime %ld, mtime=%ld\n",
-                handler->token, path, times[0].tv_sec, times[1].tv_sec);
+        DLOG(INFO) << "[" << handler->token << "] Calling utimensat on " << path
+                   << " with atime " << times[0].tv_sec << ", mtime=" << times[1].tv_sec;
         if (utimensat(-1, path, times, 0) < 0) {
             return -errno;
         }
@@ -927,8 +711,9 @@
     pthread_mutex_lock(&fuse->global->lock);
     parent_node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid,
             parent_path, sizeof(parent_path));
-    TRACE("[%d] MKNOD %s 0%o @ %"PRIx64" (%s)\n", handler->token,
-            name, req->mode, hdr->nodeid, parent_node ? parent_node->name : "?");
+    DLOG(INFO) << "[" << handler->token << "] MKNOD " << name << " 0" << std::oct << req->mode
+               << " @ " << std::hex << hdr->nodeid
+               << " (" << (parent_node ? parent_node->name : "?") << ")";
     pthread_mutex_unlock(&fuse->global->lock);
 
     if (!parent_node || !(actual_name = find_file_within(parent_path, name,
@@ -956,8 +741,9 @@
     pthread_mutex_lock(&fuse->global->lock);
     parent_node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid,
             parent_path, sizeof(parent_path));
-    TRACE("[%d] MKDIR %s 0%o @ %"PRIx64" (%s)\n", handler->token,
-            name, req->mode, hdr->nodeid, parent_node ? parent_node->name : "?");
+    DLOG(INFO) << "[" << handler->token << "] MKDIR " << name << " 0" << std::oct << req->mode
+               << " @ " << std::hex << hdr->nodeid
+               << " (" << (parent_node ? parent_node->name : "?") << ")";
     pthread_mutex_unlock(&fuse->global->lock);
 
     if (!parent_node || !(actual_name = find_file_within(parent_path, name,
@@ -977,7 +763,7 @@
         char nomedia[PATH_MAX];
         snprintf(nomedia, PATH_MAX, "%s/.nomedia", child_path);
         if (touch(nomedia, 0664) != 0) {
-            ERROR("Failed to touch(%s): %s\n", nomedia, strerror(errno));
+            PLOG(ERROR) << "touch(" << nomedia << ") failed";
             return -ENOENT;
         }
     }
@@ -985,7 +771,7 @@
         char nomedia[PATH_MAX];
         snprintf(nomedia, PATH_MAX, "%s/.nomedia", fuse->global->obb_path);
         if (touch(nomedia, 0664) != 0) {
-            ERROR("Failed to touch(%s): %s\n", nomedia, strerror(errno));
+            PLOG(ERROR) << "touch(" << nomedia << ") failed";
             return -ENOENT;
         }
     }
@@ -1004,8 +790,8 @@
     pthread_mutex_lock(&fuse->global->lock);
     parent_node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid,
             parent_path, sizeof(parent_path));
-    TRACE("[%d] UNLINK %s @ %"PRIx64" (%s)\n", handler->token,
-            name, hdr->nodeid, parent_node ? parent_node->name : "?");
+    DLOG(INFO) << "[" << handler->token << "] UNLINK " << name << " @ " << std::hex << hdr->nodeid
+               << " (" << (parent_node ? parent_node->name : "?") << ")";
     pthread_mutex_unlock(&fuse->global->lock);
 
     if (!parent_node || !find_file_within(parent_path, name,
@@ -1026,8 +812,10 @@
     pthread_mutex_unlock(&fuse->global->lock);
     if (parent_node && child_node) {
         /* Tell all other views that node is gone */
-        TRACE("[%d] fuse_notify_delete parent=%"PRIx64", child=%"PRIx64", name=%s\n",
-                handler->token, (uint64_t) parent_node->nid, (uint64_t) child_node->nid, name);
+        DLOG(INFO) << "[" << handler->token << "] fuse_notify_delete"
+                   << " parent=" << std::hex << parent_node->nid
+                   << ", child=" << std::hex << child_node->nid << std::dec
+                   << ", name=" << name;
         if (fuse != fuse->global->fuse_default) {
             fuse_notify_delete(fuse->global->fuse_default, parent_node->nid, child_node->nid, name);
         }
@@ -1052,8 +840,8 @@
     pthread_mutex_lock(&fuse->global->lock);
     parent_node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid,
             parent_path, sizeof(parent_path));
-    TRACE("[%d] RMDIR %s @ %"PRIx64" (%s)\n", handler->token,
-            name, hdr->nodeid, parent_node ? parent_node->name : "?");
+    DLOG(INFO) << "[" << handler->token << "] UNLINK " << name << " @ " << std::hex << hdr->nodeid
+               << " (" << (parent_node ? parent_node->name : "?") << ")";
     pthread_mutex_unlock(&fuse->global->lock);
 
     if (!parent_node || !find_file_within(parent_path, name,
@@ -1074,8 +862,10 @@
     pthread_mutex_unlock(&fuse->global->lock);
     if (parent_node && child_node) {
         /* Tell all other views that node is gone */
-        TRACE("[%d] fuse_notify_delete parent=%"PRIx64", child=%"PRIx64", name=%s\n",
-                handler->token, (uint64_t) parent_node->nid, (uint64_t) child_node->nid, name);
+        DLOG(INFO) << "[" << handler->token << "] fuse_notify_delete"
+                   << " parent=" << std::hex << parent_node->nid
+                   << ", child=" << std::hex << child_node->nid << std::dec
+                   << ", name=" << name;
         if (fuse != fuse->global->fuse_default) {
             fuse_notify_delete(fuse->global->fuse_default, parent_node->nid, child_node->nid, name);
         }
@@ -1101,6 +891,7 @@
     char old_child_path[PATH_MAX];
     char new_child_path[PATH_MAX];
     const char* new_actual_name;
+    int search;
     int res;
 
     pthread_mutex_lock(&fuse->global->lock);
@@ -1108,10 +899,11 @@
             old_parent_path, sizeof(old_parent_path));
     new_parent_node = lookup_node_and_path_by_id_locked(fuse, req->newdir,
             new_parent_path, sizeof(new_parent_path));
-    TRACE("[%d] RENAME %s->%s @ %"PRIx64" (%s) -> %"PRIx64" (%s)\n", handler->token,
-            old_name, new_name,
-            hdr->nodeid, old_parent_node ? old_parent_node->name : "?",
-            req->newdir, new_parent_node ? new_parent_node->name : "?");
+    DLOG(INFO) << "[" << handler->token << "] RENAME " << old_name << "->" << new_name
+               << " @ " << std::hex << hdr->nodeid
+               << " (" << (old_parent_node ? old_parent_node->name : "?") << ") -> "
+               << std::hex << req->newdir
+               << " (" << (new_parent_node ? new_parent_node->name : "?") << ")";
     if (!old_parent_node || !new_parent_node) {
         res = -ENOENT;
         goto lookup_error;
@@ -1137,7 +929,7 @@
      * differing only by case.  In this case we don't want to look for a case
      * insensitive match.  This allows commands like "mv foo FOO" to work as expected.
      */
-    int search = old_parent_node != new_parent_node
+    search = old_parent_node != new_parent_node
             || strcasecmp(old_name, new_name);
     if (!(new_actual_name = find_file_within(new_parent_path, new_name,
             new_child_path, sizeof(new_child_path), search))) {
@@ -1145,7 +937,7 @@
         goto io_error;
     }
 
-    TRACE("[%d] RENAME %s->%s\n", handler->token, old_child_path, new_child_path);
+    DLOG(INFO) << "[" << handler->token << "] RENAME " << old_child_path << "->" << new_child_path;
     res = rename(old_child_path, new_child_path);
     if (res < 0) {
         res = -errno;
@@ -1192,8 +984,9 @@
 
     pthread_mutex_lock(&fuse->global->lock);
     node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, path, sizeof(path));
-    TRACE("[%d] OPEN 0%o @ %"PRIx64" (%s)\n", handler->token,
-            req->flags, hdr->nodeid, node ? node->name : "?");
+    DLOG(INFO) << "[" << handler->token << "] OPEN 0" << std::oct << req->flags
+               << " @ " << std::hex << hdr->nodeid << std::dec
+               << " (" << (node ? node->name : "?") << ")";
     pthread_mutex_unlock(&fuse->global->lock);
 
     if (!node) {
@@ -1203,11 +996,11 @@
             open_flags_to_access_mode(req->flags))) {
         return -EACCES;
     }
-    h = malloc(sizeof(*h));
+    h = static_cast<struct handle*>(malloc(sizeof(*h)));
     if (!h) {
         return -ENOMEM;
     }
-    TRACE("[%d] OPEN %s\n", handler->token, path);
+    DLOG(INFO) << "[" << handler->token << "] OPEN " << path;
     h->fd = open(path, req->flags);
     if (h->fd < 0) {
         free(h);
@@ -1229,7 +1022,7 @@
 static int handle_read(struct fuse* fuse, struct fuse_handler* handler,
         const struct fuse_in_header* hdr, const struct fuse_read_in* req)
 {
-    struct handle *h = id_to_ptr(req->fh);
+    struct handle *h = static_cast<struct handle*>(id_to_ptr(req->fh));
     __u64 unique = hdr->unique;
     __u32 size = req->size;
     __u64 offset = req->offset;
@@ -1240,8 +1033,8 @@
      * overlaps the request buffer and will clobber data in the request.  This
      * saves us 128KB per request handler thread at the cost of this scary comment. */
 
-    TRACE("[%d] READ %p(%d) %u@%"PRIu64"\n", handler->token,
-            h, h->fd, size, (uint64_t) offset);
+    DLOG(INFO) << "[" << handler->token << "] READ " << std::hex << h << std::dec
+               << "(" << h->fd << ") " << size << "@" << offset;
     if (size > MAX_READ) {
         return -EINVAL;
     }
@@ -1258,7 +1051,7 @@
         const void* buffer)
 {
     struct fuse_write_out out;
-    struct handle *h = id_to_ptr(req->fh);
+    struct handle *h = static_cast<struct handle*>(id_to_ptr(req->fh));
     int res;
     __u8 aligned_buffer[req->size] __attribute__((__aligned__(PAGE_SIZE)));
 
@@ -1267,8 +1060,8 @@
         buffer = (const __u8*) aligned_buffer;
     }
 
-    TRACE("[%d] WRITE %p(%d) %u@%"PRIu64"\n", handler->token,
-            h, h->fd, req->size, req->offset);
+    DLOG(INFO) << "[" << handler->token << "] WRITE " << std::hex << h << std::dec
+               << "(" << h->fd << ") " << req->size << "@" << req->offset;
     res = pwrite64(h->fd, buffer, req->size, req->offset);
     if (res < 0) {
         return -errno;
@@ -1288,7 +1081,7 @@
     int res;
 
     pthread_mutex_lock(&fuse->global->lock);
-    TRACE("[%d] STATFS\n", handler->token);
+    DLOG(INFO) << "[" << handler->token << "] STATFS";
     res = get_node_path_locked(&fuse->global->root, path, sizeof(path));
     pthread_mutex_unlock(&fuse->global->lock);
     if (res < 0) {
@@ -1313,9 +1106,10 @@
 static int handle_release(struct fuse* fuse, struct fuse_handler* handler,
         const struct fuse_in_header* hdr, const struct fuse_release_in* req)
 {
-    struct handle *h = id_to_ptr(req->fh);
+    struct handle *h = static_cast<struct handle*>(id_to_ptr(req->fh));
 
-    TRACE("[%d] RELEASE %p(%d)\n", handler->token, h, h->fd);
+    DLOG(INFO) << "[" << handler->token << "] RELEASE " << std::hex << h << std::dec
+               << "(" << h->fd << ")";
     close(h->fd);
     free(h);
     return 0;
@@ -1329,16 +1123,15 @@
 
     int fd = -1;
     if (is_dir) {
-      struct dirhandle *dh = id_to_ptr(req->fh);
+      struct dirhandle *dh = static_cast<struct dirhandle*>(id_to_ptr(req->fh));
       fd = dirfd(dh->d);
     } else {
-      struct handle *h = id_to_ptr(req->fh);
+      struct handle *h = static_cast<struct handle*>(id_to_ptr(req->fh));
       fd = h->fd;
     }
 
-    TRACE("[%d] %s %p(%d) is_data_sync=%d\n", handler->token,
-            is_dir ? "FSYNCDIR" : "FSYNC",
-            id_to_ptr(req->fh), fd, is_data_sync);
+    DLOG(INFO) << "[" << handler->token << "] " << (is_dir ? "FSYNCDIR" : "FSYNC") << " "
+               << std::hex << req->fh << std::dec << "(" << fd << ") is_data_sync=" << is_data_sync;
     int res = is_data_sync ? fdatasync(fd) : fsync(fd);
     if (res == -1) {
         return -errno;
@@ -1349,7 +1142,7 @@
 static int handle_flush(struct fuse* fuse, struct fuse_handler* handler,
         const struct fuse_in_header* hdr)
 {
-    TRACE("[%d] FLUSH\n", handler->token);
+    DLOG(INFO) << "[" << handler->token << "] FLUSH";
     return 0;
 }
 
@@ -1363,8 +1156,8 @@
 
     pthread_mutex_lock(&fuse->global->lock);
     node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, path, sizeof(path));
-    TRACE("[%d] OPENDIR @ %"PRIx64" (%s)\n", handler->token,
-            hdr->nodeid, node ? node->name : "?");
+    DLOG(INFO) << "[" << handler->token << "] OPENDIR @ " << std::hex << hdr->nodeid
+               << " (" << (node ? node->name : "?") << ")";
     pthread_mutex_unlock(&fuse->global->lock);
 
     if (!node) {
@@ -1373,11 +1166,11 @@
     if (!check_caller_access_to_node(fuse, hdr, node, R_OK)) {
         return -EACCES;
     }
-    h = malloc(sizeof(*h));
+    h = static_cast<struct dirhandle*>(malloc(sizeof(*h)));
     if (!h) {
         return -ENOMEM;
     }
-    TRACE("[%d] OPENDIR %s\n", handler->token, path);
+    DLOG(INFO) << "[" << handler->token << "] OPENDIR " << path;
     h->d = opendir(path);
     if (!h->d) {
         free(h);
@@ -1402,12 +1195,12 @@
     char buffer[8192];
     struct fuse_dirent *fde = (struct fuse_dirent*) buffer;
     struct dirent *de;
-    struct dirhandle *h = id_to_ptr(req->fh);
+    struct dirhandle *h = static_cast<struct dirhandle*>(id_to_ptr(req->fh));
 
-    TRACE("[%d] READDIR %p\n", handler->token, h);
+    DLOG(INFO) << "[" << handler->token << "] READDIR " << h;
     if (req->offset == 0) {
         /* rewinddir() might have been called above us, so rewind here too */
-        TRACE("[%d] calling rewinddir()\n", handler->token);
+        DLOG(INFO) << "[" << handler->token << "] calling rewinddir()";
         rewinddir(h->d);
     }
     de = readdir(h->d);
@@ -1428,9 +1221,9 @@
 static int handle_releasedir(struct fuse* fuse, struct fuse_handler* handler,
         const struct fuse_in_header* hdr, const struct fuse_release_in* req)
 {
-    struct dirhandle *h = id_to_ptr(req->fh);
+    struct dirhandle *h = static_cast<struct dirhandle*>(id_to_ptr(req->fh));
 
-    TRACE("[%d] RELEASEDIR %p\n", handler->token, h);
+    DLOG(INFO) << "[" << handler->token << "] RELEASEDIR " << h;
     closedir(h->d);
     free(h);
     return 0;
@@ -1442,8 +1235,8 @@
     struct fuse_init_out out;
     size_t fuse_struct_size;
 
-    TRACE("[%d] INIT ver=%d.%d maxread=%d flags=%x\n",
-            handler->token, req->major, req->minor, req->max_readahead, req->flags);
+    DLOG(INFO) << "[" << handler->token << "] INIT ver=" << req->major << "." << req->minor
+               << " maxread=" << req->max_readahead << " flags=" << std::hex << req->flags;
 
     /* Kernel 2.6.16 is the first stable kernel with struct fuse_init_out
      * defined (fuse version 7.6). The structure is the same from 7.6 through
@@ -1451,8 +1244,9 @@
      * new parameters.
      */
     if (req->major != FUSE_KERNEL_VERSION || req->minor < 6) {
-        ERROR("Fuse kernel version mismatch: Kernel version %d.%d, Expected at least %d.6",
-              req->major, req->minor, FUSE_KERNEL_VERSION);
+        LOG(ERROR) << "Fuse kernel version mismatch: Kernel version "
+                   << req->major << "." << req->minor
+                   << ", Expected at least " << FUSE_KERNEL_VERSION << ".6";
         return -1;
     }
 
@@ -1490,51 +1284,51 @@
 {
     switch (hdr->opcode) {
     case FUSE_LOOKUP: { /* bytez[] -> entry_out */
-        const char* name = data;
+        const char *name = static_cast<const char*>(data);
         return handle_lookup(fuse, handler, hdr, name);
     }
 
     case FUSE_FORGET: {
-        const struct fuse_forget_in *req = data;
+        const struct fuse_forget_in *req = static_cast<const struct fuse_forget_in*>(data);
         return handle_forget(fuse, handler, hdr, req);
     }
 
     case FUSE_GETATTR: { /* getattr_in -> attr_out */
-        const struct fuse_getattr_in *req = data;
+        const struct fuse_getattr_in *req = static_cast<const struct fuse_getattr_in*>(data);
         return handle_getattr(fuse, handler, hdr, req);
     }
 
     case FUSE_SETATTR: { /* setattr_in -> attr_out */
-        const struct fuse_setattr_in *req = data;
+        const struct fuse_setattr_in *req = static_cast<const struct fuse_setattr_in*>(data);
         return handle_setattr(fuse, handler, hdr, req);
     }
 
 //    case FUSE_READLINK:
 //    case FUSE_SYMLINK:
     case FUSE_MKNOD: { /* mknod_in, bytez[] -> entry_out */
-        const struct fuse_mknod_in *req = data;
+        const struct fuse_mknod_in *req = static_cast<const struct fuse_mknod_in*>(data);
         const char *name = ((const char*) data) + sizeof(*req);
         return handle_mknod(fuse, handler, hdr, req, name);
     }
 
     case FUSE_MKDIR: { /* mkdir_in, bytez[] -> entry_out */
-        const struct fuse_mkdir_in *req = data;
+        const struct fuse_mkdir_in *req = static_cast<const struct fuse_mkdir_in*>(data);
         const char *name = ((const char*) data) + sizeof(*req);
         return handle_mkdir(fuse, handler, hdr, req, name);
     }
 
     case FUSE_UNLINK: { /* bytez[] -> */
-        const char* name = data;
+        const char *name = static_cast<const char*>(data);
         return handle_unlink(fuse, handler, hdr, name);
     }
 
     case FUSE_RMDIR: { /* bytez[] -> */
-        const char* name = data;
+        const char *name = static_cast<const char*>(data);
         return handle_rmdir(fuse, handler, hdr, name);
     }
 
     case FUSE_RENAME: { /* rename_in, oldname, newname ->  */
-        const struct fuse_rename_in *req = data;
+        const struct fuse_rename_in *req = static_cast<const struct fuse_rename_in*>(data);
         const char *old_name = ((const char*) data) + sizeof(*req);
         const char *new_name = old_name + strlen(old_name) + 1;
         return handle_rename(fuse, handler, hdr, req, old_name, new_name);
@@ -1542,17 +1336,17 @@
 
 //    case FUSE_LINK:
     case FUSE_OPEN: { /* open_in -> open_out */
-        const struct fuse_open_in *req = data;
+        const struct fuse_open_in *req = static_cast<const struct fuse_open_in*>(data);
         return handle_open(fuse, handler, hdr, req);
     }
 
     case FUSE_READ: { /* read_in -> byte[] */
-        const struct fuse_read_in *req = data;
+        const struct fuse_read_in *req = static_cast<const struct fuse_read_in*>(data);
         return handle_read(fuse, handler, hdr, req);
     }
 
     case FUSE_WRITE: { /* write_in, byte[write_in.size] -> write_out */
-        const struct fuse_write_in *req = data;
+        const struct fuse_write_in *req = static_cast<const struct fuse_write_in*>(data);
         const void* buffer = (const __u8*)data + sizeof(*req);
         return handle_write(fuse, handler, hdr, req, buffer);
     }
@@ -1562,13 +1356,13 @@
     }
 
     case FUSE_RELEASE: { /* release_in -> */
-        const struct fuse_release_in *req = data;
+        const struct fuse_release_in *req = static_cast<const struct fuse_release_in*>(data);
         return handle_release(fuse, handler, hdr, req);
     }
 
     case FUSE_FSYNC:
     case FUSE_FSYNCDIR: {
-        const struct fuse_fsync_in *req = data;
+        const struct fuse_fsync_in *req = static_cast<const struct fuse_fsync_in*>(data);
         return handle_fsync(fuse, handler, hdr, req);
     }
 
@@ -1581,34 +1375,34 @@
     }
 
     case FUSE_OPENDIR: { /* open_in -> open_out */
-        const struct fuse_open_in *req = data;
+        const struct fuse_open_in *req = static_cast<const struct fuse_open_in*>(data);
         return handle_opendir(fuse, handler, hdr, req);
     }
 
     case FUSE_READDIR: {
-        const struct fuse_read_in *req = data;
+        const struct fuse_read_in *req = static_cast<const struct fuse_read_in*>(data);
         return handle_readdir(fuse, handler, hdr, req);
     }
 
     case FUSE_RELEASEDIR: { /* release_in -> */
-        const struct fuse_release_in *req = data;
+        const struct fuse_release_in *req = static_cast<const struct fuse_release_in*>(data);
         return handle_releasedir(fuse, handler, hdr, req);
     }
 
     case FUSE_INIT: { /* init_in -> init_out */
-        const struct fuse_init_in *req = data;
+        const struct fuse_init_in *req = static_cast<const struct fuse_init_in*>(data);
         return handle_init(fuse, handler, hdr, req);
     }
 
     default: {
-        TRACE("[%d] NOTIMPL op=%d uniq=%"PRIx64" nid=%"PRIx64"\n",
-                handler->token, hdr->opcode, hdr->unique, hdr->nodeid);
+        DLOG(INFO) << "[" << handler->token << "] NOTIMPL op=" << hdr->opcode
+                   << "uniq=" << std::hex << hdr->unique << "nid=" << hdr->nodeid << std::dec;
         return -ENOSYS;
     }
     }
 }
 
-static void handle_fuse_requests(struct fuse_handler* handler)
+void handle_fuse_requests(struct fuse_handler* handler)
 {
     struct fuse* fuse = handler->fuse;
     for (;;) {
@@ -1616,22 +1410,23 @@
                 handler->request_buffer, sizeof(handler->request_buffer)));
         if (len < 0) {
             if (errno == ENODEV) {
-                ERROR("[%d] someone stole our marbles!\n", handler->token);
+                LOG(ERROR) << "[" << handler->token << "] someone stole our marbles!";
                 exit(2);
             }
-            ERROR("[%d] handle_fuse_requests: errno=%d\n", handler->token, errno);
+            PLOG(ERROR) << "[" << handler->token << "] handle_fuse_requests";
             continue;
         }
 
         if ((size_t)len < sizeof(struct fuse_in_header)) {
-            ERROR("[%d] request too short: len=%zu\n", handler->token, (size_t)len);
+            LOG(ERROR) << "[" << handler->token << "] request too short: len=" << len;
             continue;
         }
 
-        const struct fuse_in_header *hdr = (void*)handler->request_buffer;
+        const struct fuse_in_header* hdr =
+            reinterpret_cast<const struct fuse_in_header*>(handler->request_buffer);
         if (hdr->len != (size_t)len) {
-            ERROR("[%d] malformed header: len=%zu, hdr->len=%u\n",
-                    handler->token, (size_t)len, hdr->len);
+            LOG(ERROR) << "[" << handler->token << "] malformed header: len=" << len
+                       << ", hdr->len=" << hdr->len;
             continue;
         }
 
@@ -1645,345 +1440,9 @@
 
         if (res != NO_STATUS) {
             if (res) {
-                TRACE("[%d] ERROR %d\n", handler->token, res);
+                DLOG(INFO) << "[" << handler->token << "] ERROR " << res;
             }
             fuse_status(fuse, unique, res);
         }
     }
 }
-
-static void* start_handler(void* data)
-{
-    struct fuse_handler* handler = data;
-    handle_fuse_requests(handler);
-    return NULL;
-}
-
-static bool remove_str_to_int(void *key, void *value, void *context) {
-    Hashmap* map = context;
-    hashmapRemove(map, key);
-    free(key);
-    return true;
-}
-
-static bool package_parse_callback(pkg_info *info, void *userdata) {
-    struct fuse_global *global = (struct fuse_global *)userdata;
-
-    char* name = strdup(info->name);
-    hashmapPut(global->package_to_appid, name, (void*) (uintptr_t) info->uid);
-    packagelist_free(info);
-    return true;
-}
-
-static bool read_package_list(struct fuse_global* global) {
-    pthread_mutex_lock(&global->lock);
-
-    hashmapForEach(global->package_to_appid, remove_str_to_int, global->package_to_appid);
-
-    bool rc = packagelist_parse(package_parse_callback, global);
-    TRACE("read_package_list: found %zu packages\n",
-            hashmapSize(global->package_to_appid));
-
-    /* Regenerate ownership details using newly loaded mapping */
-    derive_permissions_recursive_locked(global->fuse_default, &global->root);
-
-    pthread_mutex_unlock(&global->lock);
-
-    return rc;
-}
-
-static void watch_package_list(struct fuse_global* global) {
-    struct inotify_event *event;
-    char event_buf[512];
-
-    int nfd = inotify_init();
-    if (nfd < 0) {
-        ERROR("inotify_init failed: %s\n", strerror(errno));
-        return;
-    }
-
-    bool active = false;
-    while (1) {
-        if (!active) {
-            int res = inotify_add_watch(nfd, PACKAGES_LIST_FILE, IN_DELETE_SELF);
-            if (res == -1) {
-                if (errno == ENOENT || errno == EACCES) {
-                    /* Framework may not have created yet, sleep and retry */
-                    ERROR("missing \"%s\"; retrying\n", PACKAGES_LIST_FILE);
-                    sleep(3);
-                    continue;
-                } else {
-                    ERROR("inotify_add_watch failed: %s\n", strerror(errno));
-                    return;
-                }
-            }
-
-            /* Watch above will tell us about any future changes, so
-             * read the current state. */
-            if (read_package_list(global) == false) {
-                ERROR("read_package_list failed\n");
-                return;
-            }
-            active = true;
-        }
-
-        int event_pos = 0;
-        int res = read(nfd, event_buf, sizeof(event_buf));
-        if (res < (int) sizeof(*event)) {
-            if (errno == EINTR)
-                continue;
-            ERROR("failed to read inotify event: %s\n", strerror(errno));
-            return;
-        }
-
-        while (res >= (int) sizeof(*event)) {
-            int event_size;
-            event = (struct inotify_event *) (event_buf + event_pos);
-
-            TRACE("inotify event: %08x\n", event->mask);
-            if ((event->mask & IN_IGNORED) == IN_IGNORED) {
-                /* Previously watched file was deleted, probably due to move
-                 * that swapped in new data; re-arm the watch and read. */
-                active = false;
-            }
-
-            event_size = sizeof(*event) + event->len;
-            res -= event_size;
-            event_pos += event_size;
-        }
-    }
-}
-
-static int usage() {
-    ERROR("usage: sdcard [OPTIONS] <source_path> <label>\n"
-            "    -u: specify UID to run as\n"
-            "    -g: specify GID to run as\n"
-            "    -U: specify user ID that owns device\n"
-            "    -m: source_path is multi-user\n"
-            "    -w: runtime write mount has full write access\n"
-            "\n");
-    return 1;
-}
-
-static int fuse_setup(struct fuse* fuse, gid_t gid, mode_t mask) {
-    char opts[256];
-
-    fuse->fd = open("/dev/fuse", O_RDWR);
-    if (fuse->fd == -1) {
-        ERROR("failed to open fuse device: %s\n", strerror(errno));
-        return -1;
-    }
-
-    umount2(fuse->dest_path, MNT_DETACH);
-
-    snprintf(opts, sizeof(opts),
-            "fd=%i,rootmode=40000,default_permissions,allow_other,user_id=%d,group_id=%d",
-            fuse->fd, fuse->global->uid, fuse->global->gid);
-    if (mount("/dev/fuse", fuse->dest_path, "fuse", MS_NOSUID | MS_NODEV | MS_NOEXEC |
-            MS_NOATIME, opts) != 0) {
-        ERROR("failed to mount fuse filesystem: %s\n", strerror(errno));
-        return -1;
-    }
-
-    fuse->gid = gid;
-    fuse->mask = mask;
-
-    return 0;
-}
-
-static void run(const char* source_path, const char* label, uid_t uid,
-        gid_t gid, userid_t userid, bool multi_user, bool full_write) {
-    struct fuse_global global;
-    struct fuse fuse_default;
-    struct fuse fuse_read;
-    struct fuse fuse_write;
-    struct fuse_handler handler_default;
-    struct fuse_handler handler_read;
-    struct fuse_handler handler_write;
-    pthread_t thread_default;
-    pthread_t thread_read;
-    pthread_t thread_write;
-
-    memset(&global, 0, sizeof(global));
-    memset(&fuse_default, 0, sizeof(fuse_default));
-    memset(&fuse_read, 0, sizeof(fuse_read));
-    memset(&fuse_write, 0, sizeof(fuse_write));
-    memset(&handler_default, 0, sizeof(handler_default));
-    memset(&handler_read, 0, sizeof(handler_read));
-    memset(&handler_write, 0, sizeof(handler_write));
-
-    pthread_mutex_init(&global.lock, NULL);
-    global.package_to_appid = hashmapCreate(256, str_hash, str_icase_equals);
-    global.uid = uid;
-    global.gid = gid;
-    global.multi_user = multi_user;
-    global.next_generation = 0;
-    global.inode_ctr = 1;
-
-    memset(&global.root, 0, sizeof(global.root));
-    global.root.nid = FUSE_ROOT_ID; /* 1 */
-    global.root.refcount = 2;
-    global.root.namelen = strlen(source_path);
-    global.root.name = strdup(source_path);
-    global.root.userid = userid;
-    global.root.uid = AID_ROOT;
-    global.root.under_android = false;
-
-    strcpy(global.source_path, source_path);
-
-    if (multi_user) {
-        global.root.perm = PERM_PRE_ROOT;
-        snprintf(global.obb_path, sizeof(global.obb_path), "%s/obb", source_path);
-    } else {
-        global.root.perm = PERM_ROOT;
-        snprintf(global.obb_path, sizeof(global.obb_path), "%s/Android/obb", source_path);
-    }
-
-    fuse_default.global = &global;
-    fuse_read.global = &global;
-    fuse_write.global = &global;
-
-    global.fuse_default = &fuse_default;
-    global.fuse_read = &fuse_read;
-    global.fuse_write = &fuse_write;
-
-    snprintf(fuse_default.dest_path, PATH_MAX, "/mnt/runtime/default/%s", label);
-    snprintf(fuse_read.dest_path, PATH_MAX, "/mnt/runtime/read/%s", label);
-    snprintf(fuse_write.dest_path, PATH_MAX, "/mnt/runtime/write/%s", label);
-
-    handler_default.fuse = &fuse_default;
-    handler_read.fuse = &fuse_read;
-    handler_write.fuse = &fuse_write;
-
-    handler_default.token = 0;
-    handler_read.token = 1;
-    handler_write.token = 2;
-
-    umask(0);
-
-    if (multi_user) {
-        /* Multi-user storage is fully isolated per user, so "other"
-         * permissions are completely masked off. */
-        if (fuse_setup(&fuse_default, AID_SDCARD_RW, 0006)
-                || fuse_setup(&fuse_read, AID_EVERYBODY, 0027)
-                || fuse_setup(&fuse_write, AID_EVERYBODY, full_write ? 0007 : 0027)) {
-            ERROR("failed to fuse_setup\n");
-            exit(1);
-        }
-    } else {
-        /* Physical storage is readable by all users on device, but
-         * the Android directories are masked off to a single user
-         * deep inside attr_from_stat(). */
-        if (fuse_setup(&fuse_default, AID_SDCARD_RW, 0006)
-                || fuse_setup(&fuse_read, AID_EVERYBODY, full_write ? 0027 : 0022)
-                || fuse_setup(&fuse_write, AID_EVERYBODY, full_write ? 0007 : 0022)) {
-            ERROR("failed to fuse_setup\n");
-            exit(1);
-        }
-    }
-
-    /* Drop privs */
-    if (setgroups(sizeof(kGroups) / sizeof(kGroups[0]), kGroups) < 0) {
-        ERROR("cannot setgroups: %s\n", strerror(errno));
-        exit(1);
-    }
-    if (setgid(gid) < 0) {
-        ERROR("cannot setgid: %s\n", strerror(errno));
-        exit(1);
-    }
-    if (setuid(uid) < 0) {
-        ERROR("cannot setuid: %s\n", strerror(errno));
-        exit(1);
-    }
-
-    if (multi_user) {
-        fs_prepare_dir(global.obb_path, 0775, uid, gid);
-    }
-
-    if (pthread_create(&thread_default, NULL, start_handler, &handler_default)
-            || pthread_create(&thread_read, NULL, start_handler, &handler_read)
-            || pthread_create(&thread_write, NULL, start_handler, &handler_write)) {
-        ERROR("failed to pthread_create\n");
-        exit(1);
-    }
-
-    watch_package_list(&global);
-    ERROR("terminated prematurely\n");
-    exit(1);
-}
-
-int main(int argc, char **argv) {
-    const char *source_path = NULL;
-    const char *label = NULL;
-    uid_t uid = 0;
-    gid_t gid = 0;
-    userid_t userid = 0;
-    bool multi_user = false;
-    bool full_write = false;
-    int i;
-    struct rlimit rlim;
-    int fs_version;
-
-    int opt;
-    while ((opt = getopt(argc, argv, "u:g:U:mw")) != -1) {
-        switch (opt) {
-            case 'u':
-                uid = strtoul(optarg, NULL, 10);
-                break;
-            case 'g':
-                gid = strtoul(optarg, NULL, 10);
-                break;
-            case 'U':
-                userid = strtoul(optarg, NULL, 10);
-                break;
-            case 'm':
-                multi_user = true;
-                break;
-            case 'w':
-                full_write = true;
-                break;
-            case '?':
-            default:
-                return usage();
-        }
-    }
-
-    for (i = optind; i < argc; i++) {
-        char* arg = argv[i];
-        if (!source_path) {
-            source_path = arg;
-        } else if (!label) {
-            label = arg;
-        } else {
-            ERROR("too many arguments\n");
-            return usage();
-        }
-    }
-
-    if (!source_path) {
-        ERROR("no source path specified\n");
-        return usage();
-    }
-    if (!label) {
-        ERROR("no label specified\n");
-        return usage();
-    }
-    if (!uid || !gid) {
-        ERROR("uid and gid must be nonzero\n");
-        return usage();
-    }
-
-    rlim.rlim_cur = 8192;
-    rlim.rlim_max = 8192;
-    if (setrlimit(RLIMIT_NOFILE, &rlim)) {
-        ERROR("Error setting RLIMIT_NOFILE, errno = %d\n", errno);
-    }
-
-    while ((fs_read_atomic_int("/data/.layout_version", &fs_version) == -1) || (fs_version < 3)) {
-        ERROR("installd fs upgrade not yet complete. Waiting...\n");
-        sleep(1);
-    }
-
-    run(source_path, label, uid, gid, userid, multi_user, full_write);
-    return 1;
-}
diff --git a/sdcard/fuse.h b/sdcard/fuse.h
new file mode 100644
index 0000000..9ccd21d
--- /dev/null
+++ b/sdcard/fuse.h
@@ -0,0 +1,209 @@
+/*
+ * 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 FUSE_H_
+#define FUSE_H_
+
+#include <dirent.h>
+#include <fcntl.h>
+#include <linux/fuse.h>
+#include <pthread.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/statfs.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <unistd.h>
+
+#include <map>
+#include <string>
+
+#include <android-base/logging.h>
+#include <cutils/fs.h>
+#include <cutils/multiuser.h>
+#include <packagelistparser/packagelistparser.h>
+
+#include <private/android_filesystem_config.h>
+
+#define FUSE_TRACE 0
+
+#if FUSE_TRACE
+static constexpr bool kEnableDLog = true;
+#else  // FUSE_TRACE == 0
+static constexpr bool kEnableDLog = false;
+#endif
+
+// Use same strategy as DCHECK().
+#define DLOG(x) \
+    if (kEnableDLog) LOG(x)
+
+/* Maximum number of bytes to write in one request. */
+#define MAX_WRITE (256 * 1024)
+
+/* Maximum number of bytes to read in one request. */
+#define MAX_READ (128 * 1024)
+
+/* Largest possible request.
+ * The request size is bounded by the maximum size of a FUSE_WRITE request because it has
+ * the largest possible data payload. */
+#define MAX_REQUEST_SIZE (sizeof(struct fuse_in_header) + sizeof(struct fuse_write_in) + MAX_WRITE)
+
+namespace {
+struct CaseInsensitiveCompare {
+    bool operator()(const std::string& lhs, const std::string& rhs) const {
+        return strcasecmp(lhs.c_str(), rhs.c_str()) < 0;
+    }
+};
+}
+
+using AppIdMap = std::map<std::string, appid_t, CaseInsensitiveCompare>;
+
+/* Permission mode for a specific node. Controls how file permissions
+ * are derived for children nodes. */
+typedef enum {
+    /* Nothing special; this node should just inherit from its parent. */
+    PERM_INHERIT,
+    /* This node is one level above a normal root; used for legacy layouts
+     * which use the first level to represent user_id. */
+    PERM_PRE_ROOT,
+    /* This node is "/" */
+    PERM_ROOT,
+    /* This node is "/Android" */
+    PERM_ANDROID,
+    /* This node is "/Android/data" */
+    PERM_ANDROID_DATA,
+    /* This node is "/Android/obb" */
+    PERM_ANDROID_OBB,
+    /* This node is "/Android/media" */
+    PERM_ANDROID_MEDIA,
+} perm_t;
+
+struct handle {
+    int fd;
+};
+
+struct dirhandle {
+    DIR *d;
+};
+
+struct node {
+    __u32 refcount;
+    __u64 nid;
+    __u64 gen;
+    /*
+     * The inode number for this FUSE node. Note that this isn't stable across
+     * multiple invocations of the FUSE daemon.
+     */
+    __u32 ino;
+
+    /* State derived based on current position in hierarchy. */
+    perm_t perm;
+    userid_t userid;
+    uid_t uid;
+    bool under_android;
+
+    struct node *next;          /* per-dir sibling list */
+    struct node *child;         /* first contained file by this dir */
+    struct node *parent;        /* containing directory */
+
+    size_t namelen;
+    char *name;
+    /* If non-null, this is the real name of the file in the underlying storage.
+     * This may differ from the field "name" only by case.
+     * strlen(actual_name) will always equal strlen(name), so it is safe to use
+     * namelen for both fields.
+     */
+    char *actual_name;
+
+    /* If non-null, an exact underlying path that should be grafted into this
+     * position. Used to support things like OBB. */
+    char* graft_path;
+    size_t graft_pathlen;
+
+    bool deleted;
+};
+
+/* Global data for all FUSE mounts */
+struct fuse_global {
+    pthread_mutex_t lock;
+
+    uid_t uid;
+    gid_t gid;
+    bool multi_user;
+
+    char source_path[PATH_MAX];
+    char obb_path[PATH_MAX];
+
+    AppIdMap* package_to_appid;
+
+    __u64 next_generation;
+    struct node root;
+
+    /* Used to allocate unique inode numbers for fuse nodes. We use
+     * a simple counter based scheme where inode numbers from deleted
+     * nodes aren't reused. Note that inode allocations are not stable
+     * across multiple invocation of the sdcard daemon, but that shouldn't
+     * be a huge problem in practice.
+     *
+     * Note that we restrict inodes to 32 bit unsigned integers to prevent
+     * truncation on 32 bit processes when unsigned long long stat.st_ino is
+     * assigned to an unsigned long ino_t type in an LP32 process.
+     *
+     * Also note that fuse_attr and fuse_dirent inode values are 64 bits wide
+     * on both LP32 and LP64, but the fuse kernel code doesn't squash 64 bit
+     * inode numbers into 32 bit values on 64 bit kernels (see fuse_squash_ino
+     * in fs/fuse/inode.c).
+     *
+     * Accesses must be guarded by |lock|.
+     */
+    __u32 inode_ctr;
+
+    struct fuse* fuse_default;
+    struct fuse* fuse_read;
+    struct fuse* fuse_write;
+};
+
+/* Single FUSE mount */
+struct fuse {
+    struct fuse_global* global;
+
+    char dest_path[PATH_MAX];
+
+    int fd;
+
+    gid_t gid;
+    mode_t mask;
+};
+
+/* Private data used by a single FUSE handler */
+struct fuse_handler {
+    struct fuse* fuse;
+    int token;
+
+    /* To save memory, we never use the contents of the request buffer and the read
+     * buffer at the same time.  This allows us to share the underlying storage. */
+    union {
+        __u8 request_buffer[MAX_REQUEST_SIZE];
+        __u8 read_buffer[MAX_READ + PAGE_SIZE];
+    };
+};
+
+void handle_fuse_requests(struct fuse_handler* handler);
+void derive_permissions_recursive_locked(struct fuse* fuse, struct node *parent);
+
+#endif  /* FUSE_H_ */
diff --git a/sdcard/sdcard.cpp b/sdcard/sdcard.cpp
new file mode 100644
index 0000000..3481ec3
--- /dev/null
+++ b/sdcard/sdcard.cpp
@@ -0,0 +1,394 @@
+// 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.
+
+#define LOG_TAG "sdcard"
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/fuse.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/inotify.h>
+#include <sys/mount.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <android-base/logging.h>
+#include <android-base/macros.h>
+
+#include <cutils/fs.h>
+#include <cutils/multiuser.h>
+#include <packagelistparser/packagelistparser.h>
+
+#include <libminijail.h>
+#include <scoped_minijail.h>
+
+#include <private/android_filesystem_config.h>
+
+// README
+//
+// What is this?
+//
+// sdcard is a program that uses FUSE to emulate FAT-on-sdcard style
+// directory permissions (all files are given fixed owner, group, and
+// permissions at creation, owner, group, and permissions are not
+// changeable, symlinks and hardlinks are not createable, etc.
+//
+// See usage() for command line options.
+//
+// It must be run as root, but will drop to requested UID/GID as soon as it
+// mounts a filesystem.  It will refuse to run if requested UID/GID are zero.
+//
+// Things I believe to be true:
+//
+// - ops that return a fuse_entry (LOOKUP, MKNOD, MKDIR, LINK, SYMLINK,
+// CREAT) must bump that node's refcount
+// - don't forget that FORGET can forget multiple references (req->nlookup)
+// - if an op that returns a fuse_entry fails writing the reply to the
+// kernel, you must rollback the refcount to reflect the reference the
+// kernel did not actually acquire
+//
+// This daemon can also derive custom filesystem permissions based on directory
+// structure when requested. These custom permissions support several features:
+//
+// - Apps can access their own files in /Android/data/com.example/ without
+// requiring any additional GIDs.
+// - Separate permissions for protecting directories like Pictures and Music.
+// - Multi-user separation on the same physical device.
+
+#include "fuse.h"
+
+/* Supplementary groups to execute with. */
+static const gid_t kGroups[1] = { AID_PACKAGE_INFO };
+
+static bool package_parse_callback(pkg_info *info, void *userdata) {
+    struct fuse_global *global = (struct fuse_global *)userdata;
+    bool res = global->package_to_appid->emplace(info->name, info->uid).second;
+    packagelist_free(info);
+    return res;
+}
+
+static bool read_package_list(struct fuse_global* global) {
+    pthread_mutex_lock(&global->lock);
+
+    global->package_to_appid->clear();
+    bool rc = packagelist_parse(package_parse_callback, global);
+    DLOG(INFO) << "read_package_list: found " << global->package_to_appid->size() << " packages";
+
+    // Regenerate ownership details using newly loaded mapping.
+    derive_permissions_recursive_locked(global->fuse_default, &global->root);
+
+    pthread_mutex_unlock(&global->lock);
+
+    return rc;
+}
+
+static void watch_package_list(struct fuse_global* global) {
+    struct inotify_event *event;
+    char event_buf[512];
+
+    int nfd = inotify_init();
+    if (nfd < 0) {
+        PLOG(ERROR) << "inotify_init failed";
+        return;
+    }
+
+    bool active = false;
+    while (1) {
+        if (!active) {
+            int res = inotify_add_watch(nfd, PACKAGES_LIST_FILE, IN_DELETE_SELF);
+            if (res == -1) {
+                if (errno == ENOENT || errno == EACCES) {
+                    /* Framework may not have created the file yet, sleep and retry. */
+                    LOG(ERROR) << "missing \"" << PACKAGES_LIST_FILE << "\"; retrying...";
+                    sleep(3);
+                    continue;
+                } else {
+                    PLOG(ERROR) << "inotify_add_watch failed";
+                    return;
+                }
+            }
+
+            /* Watch above will tell us about any future changes, so
+             * read the current state. */
+            if (read_package_list(global) == false) {
+                LOG(ERROR) << "read_package_list failed";
+                return;
+            }
+            active = true;
+        }
+
+        int event_pos = 0;
+        int res = read(nfd, event_buf, sizeof(event_buf));
+        if (res < (int) sizeof(*event)) {
+            if (errno == EINTR)
+                continue;
+            PLOG(ERROR) << "failed to read inotify event";
+            return;
+        }
+
+        while (res >= (int) sizeof(*event)) {
+            int event_size;
+            event = (struct inotify_event *) (event_buf + event_pos);
+
+            DLOG(INFO) << "inotify event: " << std::hex << event->mask << std::dec;
+            if ((event->mask & IN_IGNORED) == IN_IGNORED) {
+                /* Previously watched file was deleted, probably due to move
+                 * that swapped in new data; re-arm the watch and read. */
+                active = false;
+            }
+
+            event_size = sizeof(*event) + event->len;
+            res -= event_size;
+            event_pos += event_size;
+        }
+    }
+}
+
+static int fuse_setup(struct fuse* fuse, gid_t gid, mode_t mask) {
+    char opts[256];
+
+    fuse->fd = open("/dev/fuse", O_RDWR);
+    if (fuse->fd == -1) {
+        PLOG(ERROR) << "failed to open fuse device";
+        return -1;
+    }
+
+    umount2(fuse->dest_path, MNT_DETACH);
+
+    snprintf(opts, sizeof(opts),
+            "fd=%i,rootmode=40000,default_permissions,allow_other,user_id=%d,group_id=%d",
+            fuse->fd, fuse->global->uid, fuse->global->gid);
+    if (mount("/dev/fuse", fuse->dest_path, "fuse", MS_NOSUID | MS_NODEV | MS_NOEXEC |
+            MS_NOATIME, opts) != 0) {
+        PLOG(ERROR) << "failed to mount fuse filesystem";
+        return -1;
+    }
+
+    fuse->gid = gid;
+    fuse->mask = mask;
+
+    return 0;
+}
+
+static void drop_privs(uid_t uid, gid_t gid) {
+    ScopedMinijail j(minijail_new());
+    minijail_set_supplementary_gids(j.get(), arraysize(kGroups), kGroups);
+    minijail_change_gid(j.get(), gid);
+    minijail_change_uid(j.get(), uid);
+    /* minijail_enter() will abort if priv-dropping fails. */
+    minijail_enter(j.get());
+}
+
+static void* start_handler(void* data) {
+    struct fuse_handler* handler = static_cast<fuse_handler*>(data);
+    handle_fuse_requests(handler);
+    return NULL;
+}
+
+static void run(const char* source_path, const char* label, uid_t uid,
+        gid_t gid, userid_t userid, bool multi_user, bool full_write) {
+    struct fuse_global global;
+    struct fuse fuse_default;
+    struct fuse fuse_read;
+    struct fuse fuse_write;
+    struct fuse_handler handler_default;
+    struct fuse_handler handler_read;
+    struct fuse_handler handler_write;
+    pthread_t thread_default;
+    pthread_t thread_read;
+    pthread_t thread_write;
+
+    memset(&global, 0, sizeof(global));
+    memset(&fuse_default, 0, sizeof(fuse_default));
+    memset(&fuse_read, 0, sizeof(fuse_read));
+    memset(&fuse_write, 0, sizeof(fuse_write));
+    memset(&handler_default, 0, sizeof(handler_default));
+    memset(&handler_read, 0, sizeof(handler_read));
+    memset(&handler_write, 0, sizeof(handler_write));
+
+    pthread_mutex_init(&global.lock, NULL);
+    global.package_to_appid = new AppIdMap;
+    global.uid = uid;
+    global.gid = gid;
+    global.multi_user = multi_user;
+    global.next_generation = 0;
+    global.inode_ctr = 1;
+
+    memset(&global.root, 0, sizeof(global.root));
+    global.root.nid = FUSE_ROOT_ID; /* 1 */
+    global.root.refcount = 2;
+    global.root.namelen = strlen(source_path);
+    global.root.name = strdup(source_path);
+    global.root.userid = userid;
+    global.root.uid = AID_ROOT;
+    global.root.under_android = false;
+
+    strcpy(global.source_path, source_path);
+
+    if (multi_user) {
+        global.root.perm = PERM_PRE_ROOT;
+        snprintf(global.obb_path, sizeof(global.obb_path), "%s/obb", source_path);
+    } else {
+        global.root.perm = PERM_ROOT;
+        snprintf(global.obb_path, sizeof(global.obb_path), "%s/Android/obb", source_path);
+    }
+
+    fuse_default.global = &global;
+    fuse_read.global = &global;
+    fuse_write.global = &global;
+
+    global.fuse_default = &fuse_default;
+    global.fuse_read = &fuse_read;
+    global.fuse_write = &fuse_write;
+
+    snprintf(fuse_default.dest_path, PATH_MAX, "/mnt/runtime/default/%s", label);
+    snprintf(fuse_read.dest_path, PATH_MAX, "/mnt/runtime/read/%s", label);
+    snprintf(fuse_write.dest_path, PATH_MAX, "/mnt/runtime/write/%s", label);
+
+    handler_default.fuse = &fuse_default;
+    handler_read.fuse = &fuse_read;
+    handler_write.fuse = &fuse_write;
+
+    handler_default.token = 0;
+    handler_read.token = 1;
+    handler_write.token = 2;
+
+    umask(0);
+
+    if (multi_user) {
+        /* Multi-user storage is fully isolated per user, so "other"
+         * permissions are completely masked off. */
+        if (fuse_setup(&fuse_default, AID_SDCARD_RW, 0006)
+                || fuse_setup(&fuse_read, AID_EVERYBODY, 0027)
+                || fuse_setup(&fuse_write, AID_EVERYBODY, full_write ? 0007 : 0027)) {
+            PLOG(FATAL) << "failed to fuse_setup";
+        }
+    } else {
+        /* Physical storage is readable by all users on device, but
+         * the Android directories are masked off to a single user
+         * deep inside attr_from_stat(). */
+        if (fuse_setup(&fuse_default, AID_SDCARD_RW, 0006)
+                || fuse_setup(&fuse_read, AID_EVERYBODY, full_write ? 0027 : 0022)
+                || fuse_setup(&fuse_write, AID_EVERYBODY, full_write ? 0007 : 0022)) {
+            PLOG(FATAL) << "failed to fuse_setup";
+        }
+    }
+
+    // Will abort if priv-dropping fails.
+    drop_privs(uid, gid);
+
+    if (multi_user) {
+        fs_prepare_dir(global.obb_path, 0775, uid, gid);
+    }
+
+    if (pthread_create(&thread_default, NULL, start_handler, &handler_default)
+            || pthread_create(&thread_read, NULL, start_handler, &handler_read)
+            || pthread_create(&thread_write, NULL, start_handler, &handler_write)) {
+        LOG(FATAL) << "failed to pthread_create";
+    }
+
+    watch_package_list(&global);
+    LOG(FATAL) << "terminated prematurely";
+}
+
+static int usage() {
+    LOG(ERROR) << "usage: sdcard [OPTIONS] <source_path> <label>"
+               << "    -u: specify UID to run as"
+               << "    -g: specify GID to run as"
+               << "    -U: specify user ID that owns device"
+               << "    -m: source_path is multi-user"
+               << "    -w: runtime write mount has full write access";
+    return 1;
+}
+
+int main(int argc, char **argv) {
+    const char *source_path = NULL;
+    const char *label = NULL;
+    uid_t uid = 0;
+    gid_t gid = 0;
+    userid_t userid = 0;
+    bool multi_user = false;
+    bool full_write = false;
+    int i;
+    struct rlimit rlim;
+    int fs_version;
+
+    int opt;
+    while ((opt = getopt(argc, argv, "u:g:U:mw")) != -1) {
+        switch (opt) {
+            case 'u':
+                uid = strtoul(optarg, NULL, 10);
+                break;
+            case 'g':
+                gid = strtoul(optarg, NULL, 10);
+                break;
+            case 'U':
+                userid = strtoul(optarg, NULL, 10);
+                break;
+            case 'm':
+                multi_user = true;
+                break;
+            case 'w':
+                full_write = true;
+                break;
+            case '?':
+            default:
+                return usage();
+        }
+    }
+
+    for (i = optind; i < argc; i++) {
+        char* arg = argv[i];
+        if (!source_path) {
+            source_path = arg;
+        } else if (!label) {
+            label = arg;
+        } else {
+            LOG(ERROR) << "too many arguments";
+            return usage();
+        }
+    }
+
+    if (!source_path) {
+        LOG(ERROR) << "no source path specified";
+        return usage();
+    }
+    if (!label) {
+        LOG(ERROR) << "no label specified";
+        return usage();
+    }
+    if (!uid || !gid) {
+        LOG(ERROR) << "uid and gid must be nonzero";
+        return usage();
+    }
+
+    rlim.rlim_cur = 8192;
+    rlim.rlim_max = 8192;
+    if (setrlimit(RLIMIT_NOFILE, &rlim)) {
+        PLOG(ERROR) << "setting RLIMIT_NOFILE failed";
+    }
+
+    while ((fs_read_atomic_int("/data/.layout_version", &fs_version) == -1) || (fs_version < 3)) {
+        LOG(ERROR) << "installd fs upgrade not yet complete; waiting...";
+        sleep(1);
+    }
+
+    run(source_path, label, uid, gid, userid, multi_user, full_write);
+    return 1;
+}
diff --git a/toolbox/Android.mk b/toolbox/Android.mk
index feeffda..4852fa4 100644
--- a/toolbox/Android.mk
+++ b/toolbox/Android.mk
@@ -32,18 +32,11 @@
 
 OUR_TOOLS := \
     getevent \
-    log \
-    nandread \
     newfs_msdos \
-    sendevent \
-    start \
-    stop \
-    top \
 
 ALL_TOOLS = $(BSD_TOOLS) $(OUR_TOOLS)
 
 LOCAL_SRC_FILES := \
-    start_stop.cpp \
     toolbox.c \
     $(patsubst %,%.c,$(OUR_TOOLS)) \
 
@@ -52,9 +45,7 @@
 LOCAL_CONLYFLAGS += -std=gnu99
 
 LOCAL_SHARED_LIBRARIES := \
-    liblog \
     libcutils \
-    libselinux \
 
 LOCAL_WHOLE_STATIC_LIBRARIES := $(patsubst %,libtoolbox_%,$(BSD_TOOLS))
 
@@ -78,7 +69,7 @@
 
 $(LOCAL_PATH)/getevent.c: $(intermediates)/input.h-labels.h
 
-UAPI_INPUT_EVENT_CODES_H := bionic/libc/kernel/uapi/linux/input-event-codes.h
+UAPI_INPUT_EVENT_CODES_H := bionic/libc/kernel/uapi/linux/input.h bionic/libc/kernel/uapi/linux/input-event-codes.h
 INPUT_H_LABELS_H := $(intermediates)/input.h-labels.h
 $(INPUT_H_LABELS_H): PRIVATE_LOCAL_PATH := $(LOCAL_PATH)
 # The PRIVATE_CUSTOM_TOOL line uses = to evaluate the output path late.
diff --git a/toolbox/generate-input.h-labels.py b/toolbox/generate-input.h-labels.py
index a2b9111..c0e9fce 100755
--- a/toolbox/generate-input.h-labels.py
+++ b/toolbox/generate-input.h-labels.py
@@ -38,39 +38,40 @@
 
 r = re.compile(r'#define\s+(\S+)\s+((?:0x)?\d+)')
 
-with open(sys.argv[1], 'r') as f:
-  for line in f:
-    m = r.match(line)
-    if m:
-      name = m.group(1)
-      if name.startswith("INPUT_PROP_"):
-        input_prop_list.append(name)
-      elif name.startswith("EV_"):
-        ev_list.append(name)
-      elif name.startswith("SYN_"):
-        syn_list.append(name)
-      elif name.startswith("KEY_") or name.startswith("BTN_"):
-        key_list.append(name)
-      elif name.startswith("REL_"):
-        rel_list.append(name)
-      elif name.startswith("ABS_"):
-        abs_list.append(name)
-      elif name.startswith("SW_"):
-        sw_list.append(name)
-      elif name.startswith("MSC_"):
-        msc_list.append(name)
-      elif name.startswith("LED_"):
-        led_list.append(name)
-      elif name.startswith("REP_"):
-        rep_list.append(name)
-      elif name.startswith("SND_"):
-        snd_list.append(name)
-      elif name.startswith("MT_TOOL_"):
-        mt_tool_list.append(name)
-      elif name.startswith("FF_STATUS_"):
-        ff_status_list.append(name)
-      elif name.startswith("FF_"):
-        ff_list.append(name)
+for arg in sys.argv[1:]:
+  with open(arg, 'r') as f:
+    for line in f:
+      m = r.match(line)
+      if m:
+        name = m.group(1)
+        if name.startswith("INPUT_PROP_"):
+          input_prop_list.append(name)
+        elif name.startswith("EV_"):
+