Merge "fix static-analyzer logging/CHECK macros"
diff --git a/adb/Android.mk b/adb/Android.mk
index 112bc80..ca337ee 100644
--- a/adb/Android.mk
+++ b/adb/Android.mk
@@ -5,23 +5,27 @@
 
 LOCAL_PATH:= $(call my-dir)
 
+include $(LOCAL_PATH)/../platform_tools_tool_version.mk
+
 adb_host_sanitize :=
 adb_target_sanitize :=
 
-adb_version := $(shell git -C $(LOCAL_PATH) rev-parse --short=12 HEAD 2>/dev/null)-android
-
 ADB_COMMON_CFLAGS := \
     -Wall -Wextra -Werror \
     -Wno-unused-parameter \
     -Wno-missing-field-initializers \
     -Wvla \
-    -DADB_REVISION='"$(adb_version)"' \
+    -DADB_VERSION=\"$(tool_version)\" \
+
+ADB_COMMON_posix_CFLAGS := \
+    -Wexit-time-destructors \
+    -Wthread-safety
 
 ADB_COMMON_linux_CFLAGS := \
-    -Wexit-time-destructors \
+    $(ADB_COMMON_posix_CFLAGS) \
 
 ADB_COMMON_darwin_CFLAGS := \
-    -Wexit-time-destructors \
+    $(ADB_COMMON_posix_CFLAGS) \
 
 # Define windows.h and tchar.h Unicode preprocessor symbols so that
 # CreateFile(), _tfopen(), etc. map to versions that take wchar_t*, breaking the
diff --git a/adb/adb.cpp b/adb/adb.cpp
index 39e71e5..c1e0d98 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -31,6 +31,8 @@
 #include <time.h>
 
 #include <chrono>
+#include <condition_variable>
+#include <mutex>
 #include <string>
 #include <thread>
 #include <vector>
@@ -48,6 +50,7 @@
 #include "adb_io.h"
 #include "adb_listeners.h"
 #include "adb_utils.h"
+#include "sysdeps/chrono.h"
 #include "transport.h"
 
 #if !ADB_HOST
@@ -61,9 +64,9 @@
     // Don't change the format of this --- it's parsed by ddmlib.
     return android::base::StringPrintf(
         "Android Debug Bridge version %d.%d.%d\n"
-        "Revision %s\n"
+        "Version %s\n"
         "Installed as %s\n",
-        ADB_VERSION_MAJOR, ADB_VERSION_MINOR, ADB_SERVER_VERSION, ADB_REVISION,
+        ADB_VERSION_MAJOR, ADB_VERSION_MINOR, ADB_SERVER_VERSION, ADB_VERSION,
         android::base::GetExecutablePath().c_str());
 }
 
@@ -313,19 +316,15 @@
     if (type == "bootloader") {
         D("setting connection_state to kCsBootloader");
         t->SetConnectionState(kCsBootloader);
-        update_transports();
     } else if (type == "device") {
         D("setting connection_state to kCsDevice");
         t->SetConnectionState(kCsDevice);
-        update_transports();
     } else if (type == "recovery") {
         D("setting connection_state to kCsRecovery");
         t->SetConnectionState(kCsRecovery);
-        update_transports();
     } else if (type == "sideload") {
         D("setting connection_state to kCsSideload");
         t->SetConnectionState(kCsSideload);
-        update_transports();
     } else {
         D("setting connection_state to kCsHost");
         t->SetConnectionState(kCsHost);
@@ -353,6 +352,8 @@
         send_auth_request(t);
     }
 #endif
+
+    update_transports();
 }
 
 void handle_packet(apacket *p, atransport *t)
@@ -1229,4 +1230,50 @@
       return ret - 1;
     return -1;
 }
+
+static auto& init_mutex = *new std::mutex();
+static auto& init_cv = *new std::condition_variable();
+static bool device_scan_complete = false;
+static bool transports_ready = false;
+
+void update_transport_status() {
+    bool result = iterate_transports([](const atransport* t) {
+        if (t->type == kTransportUsb && t->online != 1) {
+            return false;
+        }
+        return true;
+    });
+
+    D("update_transport_status: transports_ready = %s", result ? "true" : "false");
+
+    bool ready;
+
+    {
+        std::lock_guard<std::mutex> lock(init_mutex);
+        transports_ready = result;
+        ready = transports_ready && device_scan_complete;
+    }
+
+    if (ready) {
+        D("update_transport_status: notifying");
+        init_cv.notify_all();
+    }
+}
+
+void adb_notify_device_scan_complete() {
+    D("device scan complete");
+
+    {
+        std::lock_guard<std::mutex> lock(init_mutex);
+        device_scan_complete = true;
+    }
+
+    update_transport_status();
+}
+
+void adb_wait_for_device_initialization() {
+    std::unique_lock<std::mutex> lock(init_mutex);
+    init_cv.wait_for(lock, 3s, []() { return device_scan_complete && transports_ready; });
+}
+
 #endif  // ADB_HOST
diff --git a/adb/adb.h b/adb/adb.h
index e3675d8..d6b2b81 100644
--- a/adb/adb.h
+++ b/adb/adb.h
@@ -228,4 +228,18 @@
 
 void parse_banner(const std::string&, atransport* t);
 
+// On startup, the adb server needs to wait until all of the connected devices are ready.
+// To do this, we need to know when the scan has identified all of the potential new transports, and
+// when each transport becomes ready.
+// TODO: Do this for mDNS as well, instead of just USB?
+
+// We've found all of the transports we potentially care about.
+void adb_notify_device_scan_complete();
+
+// One or more transports have changed status, check to see if we're ready.
+void update_transport_status();
+
+// Wait until device scan has completed and every transport is ready, or a timeout elapses.
+void adb_wait_for_device_initialization();
+
 #endif
diff --git a/adb/adb_client.cpp b/adb/adb_client.cpp
index b656887..4f3ff25 100644
--- a/adb/adb_client.cpp
+++ b/adb/adb_client.cpp
@@ -28,12 +28,15 @@
 #include <sys/stat.h>
 #include <sys/types.h>
 
+#include <condition_variable>
+#include <mutex>
 #include <string>
 #include <thread>
 #include <vector>
 
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
+#include <android-base/thread_annotations.h>
 #include <cutils/sockets.h>
 
 #include "adb_io.h"
@@ -177,9 +180,8 @@
         } else {
             fprintf(stderr, "* daemon started successfully\n");
         }
-        // Give the server some time to start properly and detect devices.
-        std::this_thread::sleep_for(3s);
-        // fall through to _adb_connect
+        // The server will wait until it detects all of its connected devices before acking.
+        // Fall through to _adb_connect.
     } else {
         // If a server is already running, check its version matches.
         int version = ADB_SERVER_VERSION - 1;
diff --git a/adb/bugreport.cpp b/adb/bugreport.cpp
index da2cfa2..d0cc072 100644
--- a/adb/bugreport.cpp
+++ b/adb/bugreport.cpp
@@ -48,7 +48,7 @@
           show_progress_(show_progress),
           status_(0),
           line_(),
-          last_progress_(0) {
+          last_progress_percentage_(0) {
         SetLineMessage("generating");
     }
 
@@ -147,13 +147,14 @@
             size_t idx1 = line.rfind(BUGZ_PROGRESS_PREFIX) + strlen(BUGZ_PROGRESS_PREFIX);
             size_t idx2 = line.rfind(BUGZ_PROGRESS_SEPARATOR);
             int progress = std::stoi(line.substr(idx1, (idx2 - idx1)));
-            if (progress <= last_progress_) {
+            int total = std::stoi(line.substr(idx2 + 1));
+            int progress_percentage = (progress * 100 / total);
+            if (progress_percentage <= last_progress_percentage_) {
                 // Ignore.
                 return;
             }
-            last_progress_ = progress;
-            int total = std::stoi(line.substr(idx2 + 1));
-            br_->UpdateProgress(line_message_, progress, total);
+            last_progress_percentage_ = progress_percentage;
+            br_->UpdateProgress(line_message_, progress_percentage);
         } else {
             invalid_lines_.push_back(line);
         }
@@ -189,7 +190,7 @@
 
     // Last displayed progress.
     // Since dumpstate progress can recede, only forward progress should be displayed
-    int last_progress_;
+    int last_progress_percentage_;
 
     DISALLOW_COPY_AND_ASSIGN(BugreportStandardStreamsCallback);
 };
@@ -267,8 +268,7 @@
     return SendShellCommand(transport_type, serial, bugz_command, false, &bugz_callback);
 }
 
-void Bugreport::UpdateProgress(const std::string& message, int progress, int total) {
-    int progress_percentage = (progress * 100 / total);
+void Bugreport::UpdateProgress(const std::string& message, int progress_percentage) {
     line_printer_.Print(
         android::base::StringPrintf("[%3d%%] %s", progress_percentage, message.c_str()),
         LinePrinter::INFO);
diff --git a/adb/bugreport.h b/adb/bugreport.h
index ee99cbc..d9a4468 100644
--- a/adb/bugreport.h
+++ b/adb/bugreport.h
@@ -43,7 +43,7 @@
                             const char* name);
 
   private:
-    virtual void UpdateProgress(const std::string& file_name, int progress, int total);
+    virtual void UpdateProgress(const std::string& file_name, int progress_percentage);
     LinePrinter line_printer_;
     DISALLOW_COPY_AND_ASSIGN(Bugreport);
 };
diff --git a/adb/bugreport_test.cpp b/adb/bugreport_test.cpp
index b500c49..2b368d7 100644
--- a/adb/bugreport_test.cpp
+++ b/adb/bugreport_test.cpp
@@ -123,7 +123,7 @@
                      bool disable_shell_protocol, StandardStreamsCallbackInterface* callback));
     MOCK_METHOD4(DoSyncPull, bool(const std::vector<const char*>& srcs, const char* dst,
                                   bool copy_attrs, const char* name));
-    MOCK_METHOD3(UpdateProgress, void(const std::string&, int, int));
+    MOCK_METHOD2(UpdateProgress, void(const std::string&, int));
 };
 
 class BugreportTest : public ::testing::Test {
@@ -142,8 +142,8 @@
                             WithArg<4>(ReturnCallbackDone(0))));
     }
 
-    void ExpectProgress(int progress, int total, const std::string& file = "file.zip") {
-        EXPECT_CALL(br_, UpdateProgress(StrEq("generating " + file), progress, total));
+    void ExpectProgress(int progress_percentage, const std::string& file = "file.zip") {
+        EXPECT_CALL(br_, UpdateProgress(StrEq("generating " + file), progress_percentage));
     }
 
     BugreportMock br_;
@@ -200,7 +200,7 @@
     ExpectBugreportzVersion("1.1");
     std::string dest_file =
         android::base::StringPrintf("%s%cda_bugreport.zip", cwd_.c_str(), OS_PATH_SEPARATOR);
-    ExpectProgress(50, 100, "da_bugreport.zip");
+    ExpectProgress(50, "da_bugreport.zip");
     EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
         .WillOnce(DoAll(WithArg<4>(WriteOnStdout("BEGIN:/device/da_bugreport.zip\n")),
                         WithArg<4>(WriteOnStdout("PROGRESS:50/100\n")),
@@ -247,10 +247,10 @@
 // Tests 'adb bugreport file.zip' when it succeeds and displays progress.
 TEST_F(BugreportTest, OkProgress) {
     ExpectBugreportzVersion("1.1");
-    ExpectProgress(1, 100);
-    ExpectProgress(10, 100);
-    ExpectProgress(50, 100);
-    ExpectProgress(99, 100);
+    ExpectProgress(1);
+    ExpectProgress(10);
+    ExpectProgress(50);
+    ExpectProgress(99);
     // clang-format off
     EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
         // NOTE: DoAll accepts at most 10 arguments, and we're almost reached that limit...
@@ -283,21 +283,23 @@
 // Tests 'adb bugreport file.zip' when it succeeds and displays progress, even if progress recedes.
 TEST_F(BugreportTest, OkProgressAlwaysForward) {
     ExpectBugreportzVersion("1.1");
-    ExpectProgress(1, 100);
-    ExpectProgress(50, 100);
-    ExpectProgress(75, 100);
+    ExpectProgress(1);
+    ExpectProgress(50);
+    ExpectProgress(75);
     // clang-format off
     EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
         // NOTE: DoAll accepts at most 10 arguments, and we're almost reached that limit...
         .WillOnce(DoAll(
             WithArg<4>(WriteOnStdout("BEGIN:/device/bugreport.zip\n")),
-            WithArg<4>(WriteOnStdout("PROGRESS:1/100\n")),
-            WithArg<4>(WriteOnStdout("PROGRESS:50/100\n")),
-            // 25 should be ignored becaused it receded.
-            WithArg<4>(WriteOnStdout("PROGRESS:25/100\n")),
-            WithArg<4>(WriteOnStdout("PROGRESS:75/100\n")),
-            // 75 should be ignored becaused it didn't change.
-            WithArg<4>(WriteOnStdout("PROGRESS:75/100\n")),
+            WithArg<4>(WriteOnStdout("PROGRESS:1/100\n")), // 1%
+            WithArg<4>(WriteOnStdout("PROGRESS:50/100\n")), // 50%
+            // 25% should be ignored becaused it receded.
+            WithArg<4>(WriteOnStdout("PROGRESS:25/100\n")), // 25%
+            WithArg<4>(WriteOnStdout("PROGRESS:75/100\n")), // 75%
+            // 75% should be ignored becaused it didn't change.
+            WithArg<4>(WriteOnStdout("PROGRESS:75/100\n")), // 75%
+            // Try a receeding percentage with a different max progress
+            WithArg<4>(WriteOnStdout("PROGRESS:700/1000\n")), // 70%
             WithArg<4>(WriteOnStdout("OK:/device/bugreport.zip")),
             WithArg<4>(ReturnCallbackDone())));
     // clang-format on
diff --git a/adb/client/main.cpp b/adb/client/main.cpp
index 606203c..fe5099c 100644
--- a/adb/client/main.cpp
+++ b/adb/client/main.cpp
@@ -156,33 +156,38 @@
         }
 #endif
 
-        // Inform our parent that we are up and running.
+        // Wait for the USB scan to complete before notifying the parent that we're up.
+        // We need to perform this in a thread, because we would otherwise block the event loop.
+        std::thread notify_thread([ack_reply_fd]() {
+            adb_wait_for_device_initialization();
 
-        // Any error output written to stderr now goes to adb.log. We could
-        // keep around a copy of the stderr fd and use that to write any errors
-        // encountered by the following code, but that is probably overkill.
+            // Any error output written to stderr now goes to adb.log. We could
+            // keep around a copy of the stderr fd and use that to write any errors
+            // encountered by the following code, but that is probably overkill.
 #if defined(_WIN32)
-        const HANDLE ack_reply_handle = cast_int_to_handle(ack_reply_fd);
-        const CHAR ack[] = "OK\n";
-        const DWORD bytes_to_write = arraysize(ack) - 1;
-        DWORD written = 0;
-        if (!WriteFile(ack_reply_handle, ack, bytes_to_write, &written, NULL)) {
-            fatal("adb: cannot write ACK to handle 0x%p: %s", ack_reply_handle,
-                  android::base::SystemErrorCodeToString(GetLastError()).c_str());
-        }
-        if (written != bytes_to_write) {
-            fatal("adb: cannot write %lu bytes of ACK: only wrote %lu bytes",
-                  bytes_to_write, written);
-        }
-        CloseHandle(ack_reply_handle);
+            const HANDLE ack_reply_handle = cast_int_to_handle(ack_reply_fd);
+            const CHAR ack[] = "OK\n";
+            const DWORD bytes_to_write = arraysize(ack) - 1;
+            DWORD written = 0;
+            if (!WriteFile(ack_reply_handle, ack, bytes_to_write, &written, NULL)) {
+                fatal("adb: cannot write ACK to handle 0x%p: %s", ack_reply_handle,
+                      android::base::SystemErrorCodeToString(GetLastError()).c_str());
+            }
+            if (written != bytes_to_write) {
+                fatal("adb: cannot write %lu bytes of ACK: only wrote %lu bytes", bytes_to_write,
+                      written);
+            }
+            CloseHandle(ack_reply_handle);
 #else
-        // TODO(danalbert): Can't use SendOkay because we're sending "OK\n", not
-        // "OKAY".
-        if (!android::base::WriteStringToFd("OK\n", ack_reply_fd)) {
-            fatal_errno("error writing ACK to fd %d", ack_reply_fd);
-        }
-        unix_close(ack_reply_fd);
+            // TODO(danalbert): Can't use SendOkay because we're sending "OK\n", not
+            // "OKAY".
+            if (!android::base::WriteStringToFd("OK\n", ack_reply_fd)) {
+                fatal_errno("error writing ACK to fd %d", ack_reply_fd);
+            }
+            unix_close(ack_reply_fd);
 #endif
+        });
+        notify_thread.detach();
     }
 
     D("Event loop starting");
diff --git a/adb/client/usb_dispatch.cpp b/adb/client/usb_dispatch.cpp
index bfc8e16..710a3ce 100644
--- a/adb/client/usb_dispatch.cpp
+++ b/adb/client/usb_dispatch.cpp
@@ -48,3 +48,9 @@
     should_use_libusb() ? libusb::usb_kick(reinterpret_cast<libusb::usb_handle*>(h))
                         : native::usb_kick(reinterpret_cast<native::usb_handle*>(h));
 }
+
+size_t usb_get_max_packet_size(usb_handle* h) {
+    return should_use_libusb()
+               ? libusb::usb_get_max_packet_size(reinterpret_cast<libusb::usb_handle*>(h))
+               : native::usb_get_max_packet_size(reinterpret_cast<native::usb_handle*>(h));
+}
diff --git a/adb/client/usb_libusb.cpp b/adb/client/usb_libusb.cpp
index fec4742..15d4b7a 100644
--- a/adb/client/usb_libusb.cpp
+++ b/adb/client/usb_libusb.cpp
@@ -91,7 +91,7 @@
 struct usb_handle : public ::usb_handle {
     usb_handle(const std::string& device_address, const std::string& serial,
                unique_device_handle&& device_handle, uint8_t interface, uint8_t bulk_in,
-               uint8_t bulk_out, size_t zero_mask)
+               uint8_t bulk_out, size_t zero_mask, size_t max_packet_size)
         : device_address(device_address),
           serial(serial),
           closing(false),
@@ -100,7 +100,8 @@
           write("write", zero_mask, true),
           interface(interface),
           bulk_in(bulk_in),
-          bulk_out(bulk_out) {}
+          bulk_out(bulk_out),
+          max_packet_size(max_packet_size) {}
 
     ~usb_handle() {
         Close();
@@ -143,6 +144,8 @@
     uint8_t interface;
     uint8_t bulk_in;
     uint8_t bulk_out;
+
+    size_t max_packet_size;
 };
 
 static auto& usb_handles = *new std::unordered_map<std::string, std::unique_ptr<usb_handle>>();
@@ -156,6 +159,22 @@
                         libusb_get_device_address(device));
 }
 
+#if defined(__linux__)
+static std::string get_device_serial_path(libusb_device* device) {
+    uint8_t ports[7];
+    int port_count = libusb_get_port_numbers(device, ports, 7);
+    if (port_count < 0) return "";
+
+    std::string path =
+        StringPrintf("/sys/bus/usb/devices/%d-%d", libusb_get_bus_number(device), ports[0]);
+    for (int port = 1; port < port_count; ++port) {
+        path += StringPrintf(".%d", ports[port]);
+    }
+    path += "/serial";
+    return path;
+}
+#endif
+
 static bool endpoint_is_output(uint8_t endpoint) {
     return (endpoint & LIBUSB_ENDPOINT_DIR_MASK) == LIBUSB_ENDPOINT_OUT;
 }
@@ -165,6 +184,195 @@
            (write_length & zero_mask) == 0;
 }
 
+static void process_device(libusb_device* device) {
+    std::string device_address = get_device_address(device);
+    std::string device_serial;
+
+    // Figure out if we want to open the device.
+    libusb_device_descriptor device_desc;
+    int rc = libusb_get_device_descriptor(device, &device_desc);
+    if (rc != 0) {
+        LOG(WARNING) << "failed to get device descriptor for device at " << device_address << ": "
+                     << libusb_error_name(rc);
+        return;
+    }
+
+    if (device_desc.bDeviceClass != LIBUSB_CLASS_PER_INTERFACE) {
+        // Assume that all Android devices have the device class set to per interface.
+        // TODO: Is this assumption valid?
+        LOG(VERBOSE) << "skipping device with incorrect class at " << device_address;
+        return;
+    }
+
+    libusb_config_descriptor* config_raw;
+    rc = libusb_get_active_config_descriptor(device, &config_raw);
+    if (rc != 0) {
+        LOG(WARNING) << "failed to get active config descriptor for device at " << device_address
+                     << ": " << libusb_error_name(rc);
+        return;
+    }
+    const unique_config_descriptor config(config_raw);
+
+    // Use size_t for interface_num so <iostream>s don't mangle it.
+    size_t interface_num;
+    uint16_t zero_mask;
+    uint8_t bulk_in = 0, bulk_out = 0;
+    size_t packet_size = 0;
+    bool found_adb = false;
+
+    for (interface_num = 0; interface_num < config->bNumInterfaces; ++interface_num) {
+        const libusb_interface& interface = config->interface[interface_num];
+        if (interface.num_altsetting != 1) {
+            // Assume that interfaces with alternate settings aren't adb interfaces.
+            // TODO: Is this assumption valid?
+            LOG(VERBOSE) << "skipping interface with incorrect num_altsetting at " << device_address
+                         << " (interface " << interface_num << ")";
+            return;
+        }
+
+        const libusb_interface_descriptor& interface_desc = interface.altsetting[0];
+        if (!is_adb_interface(interface_desc.bInterfaceClass, interface_desc.bInterfaceSubClass,
+                              interface_desc.bInterfaceProtocol)) {
+            LOG(VERBOSE) << "skipping non-adb interface at " << device_address << " (interface "
+                         << interface_num << ")";
+            return;
+        }
+
+        LOG(VERBOSE) << "found potential adb interface at " << device_address << " (interface "
+                     << interface_num << ")";
+
+        bool found_in = false;
+        bool found_out = false;
+        for (size_t endpoint_num = 0; endpoint_num < interface_desc.bNumEndpoints; ++endpoint_num) {
+            const auto& endpoint_desc = interface_desc.endpoint[endpoint_num];
+            const uint8_t endpoint_addr = endpoint_desc.bEndpointAddress;
+            const uint8_t endpoint_attr = endpoint_desc.bmAttributes;
+
+            const uint8_t transfer_type = endpoint_attr & LIBUSB_TRANSFER_TYPE_MASK;
+
+            if (transfer_type != LIBUSB_TRANSFER_TYPE_BULK) {
+                return;
+            }
+
+            if (endpoint_is_output(endpoint_addr) && !found_out) {
+                found_out = true;
+                bulk_out = endpoint_addr;
+                zero_mask = endpoint_desc.wMaxPacketSize - 1;
+            } else if (!endpoint_is_output(endpoint_addr) && !found_in) {
+                found_in = true;
+                bulk_in = endpoint_addr;
+            }
+
+            size_t endpoint_packet_size = endpoint_desc.wMaxPacketSize;
+            CHECK(endpoint_packet_size != 0);
+            if (packet_size == 0) {
+                packet_size = endpoint_packet_size;
+            } else {
+                CHECK(packet_size == endpoint_packet_size);
+            }
+        }
+
+        if (found_in && found_out) {
+            found_adb = true;
+            break;
+        } else {
+            LOG(VERBOSE) << "rejecting potential adb interface at " << device_address
+                         << "(interface " << interface_num << "): missing bulk endpoints "
+                         << "(found_in = " << found_in << ", found_out = " << found_out << ")";
+        }
+    }
+
+    if (!found_adb) {
+        LOG(VERBOSE) << "skipping device with no adb interfaces at " << device_address;
+        return;
+    }
+
+    {
+        std::unique_lock<std::mutex> lock(usb_handles_mutex);
+        if (usb_handles.find(device_address) != usb_handles.end()) {
+            LOG(VERBOSE) << "device at " << device_address
+                         << " has already been registered, skipping";
+            return;
+        }
+    }
+
+    bool writable = true;
+    libusb_device_handle* handle_raw = nullptr;
+    rc = libusb_open(device, &handle_raw);
+    unique_device_handle handle(handle_raw);
+    if (rc == 0) {
+        LOG(DEBUG) << "successfully opened adb device at " << device_address << ", "
+                   << StringPrintf("bulk_in = %#x, bulk_out = %#x", bulk_in, bulk_out);
+
+        device_serial.resize(255);
+        rc = libusb_get_string_descriptor_ascii(handle_raw, device_desc.iSerialNumber,
+                                                reinterpret_cast<unsigned char*>(&device_serial[0]),
+                                                device_serial.length());
+        if (rc == 0) {
+            LOG(WARNING) << "received empty serial from device at " << device_address;
+            return;
+        } else if (rc < 0) {
+            LOG(WARNING) << "failed to get serial from device at " << device_address
+                         << libusb_error_name(rc);
+            return;
+        }
+        device_serial.resize(rc);
+
+        // WARNING: this isn't released via RAII.
+        rc = libusb_claim_interface(handle.get(), interface_num);
+        if (rc != 0) {
+            LOG(WARNING) << "failed to claim adb interface for device '" << device_serial << "'"
+                         << libusb_error_name(rc);
+            return;
+        }
+
+        for (uint8_t endpoint : {bulk_in, bulk_out}) {
+            rc = libusb_clear_halt(handle.get(), endpoint);
+            if (rc != 0) {
+                LOG(WARNING) << "failed to clear halt on device '" << device_serial
+                             << "' endpoint 0x" << std::hex << endpoint << ": "
+                             << libusb_error_name(rc);
+                libusb_release_interface(handle.get(), interface_num);
+                return;
+            }
+        }
+    } else {
+        LOG(WARNING) << "failed to open usb device at " << device_address << ": "
+                     << libusb_error_name(rc);
+        writable = false;
+
+#if defined(__linux__)
+        // libusb doesn't think we should be messing around with devices we don't have
+        // write access to, but Linux at least lets us get the serial number anyway.
+        if (!android::base::ReadFileToString(get_device_serial_path(device), &device_serial)) {
+            // We don't actually want to treat an unknown serial as an error because
+            // devices aren't able to communicate a serial number in early bringup.
+            // http://b/20883914
+            device_serial = "unknown";
+        }
+        device_serial = android::base::Trim(device_serial);
+#else
+        // On Mac OS and Windows, we're screwed. But I don't think this situation actually
+        // happens on those OSes.
+        return;
+#endif
+    }
+
+    auto result =
+        std::make_unique<usb_handle>(device_address, device_serial, std::move(handle),
+                                     interface_num, bulk_in, bulk_out, zero_mask, packet_size);
+    usb_handle* usb_handle_raw = result.get();
+
+    {
+        std::unique_lock<std::mutex> lock(usb_handles_mutex);
+        usb_handles[device_address] = std::move(result);
+    }
+
+    register_usb_transport(usb_handle_raw, device_serial.c_str(), device_address.c_str(), writable);
+
+    LOG(INFO) << "registered new usb device '" << device_serial << "'";
+}
+
 static void poll_for_devices() {
     libusb_device** list;
     adb_thread_setname("device poll");
@@ -174,172 +382,13 @@
         LOG(VERBOSE) << "found " << device_count << " attached devices";
 
         for (ssize_t i = 0; i < device_count; ++i) {
-            libusb_device* device = list[i];
-            std::string device_address = get_device_address(device);
-            std::string device_serial;
-
-            // Figure out if we want to open the device.
-            libusb_device_descriptor device_desc;
-            int rc = libusb_get_device_descriptor(device, &device_desc);
-            if (rc != 0) {
-                LOG(WARNING) << "failed to get device descriptor for device at " << device_address
-                             << ": " << libusb_error_name(rc);
-            }
-
-            if (device_desc.bDeviceClass != LIBUSB_CLASS_PER_INTERFACE) {
-                // Assume that all Android devices have the device class set to per interface.
-                // TODO: Is this assumption valid?
-                LOG(VERBOSE) << "skipping device with incorrect class at " << device_address;
-                continue;
-            }
-
-            libusb_config_descriptor* config_raw;
-            rc = libusb_get_active_config_descriptor(device, &config_raw);
-            if (rc != 0) {
-                LOG(WARNING) << "failed to get active config descriptor for device at "
-                             << device_address << ": " << libusb_error_name(rc);
-                continue;
-            }
-            const unique_config_descriptor config(config_raw);
-
-            // Use size_t for interface_num so <iostream>s don't mangle it.
-            size_t interface_num;
-            uint16_t zero_mask;
-            uint8_t bulk_in = 0, bulk_out = 0;
-            bool found_adb = false;
-
-            for (interface_num = 0; interface_num < config->bNumInterfaces; ++interface_num) {
-                const libusb_interface& interface = config->interface[interface_num];
-                if (interface.num_altsetting != 1) {
-                    // Assume that interfaces with alternate settings aren't adb interfaces.
-                    // TODO: Is this assumption valid?
-                    LOG(VERBOSE) << "skipping interface with incorrect num_altsetting at "
-                                 << device_address << " (interface " << interface_num << ")";
-                    continue;
-                }
-
-                const libusb_interface_descriptor& interface_desc = interface.altsetting[0];
-                if (!is_adb_interface(interface_desc.bInterfaceClass,
-                                      interface_desc.bInterfaceSubClass,
-                                      interface_desc.bInterfaceProtocol)) {
-                    LOG(VERBOSE) << "skipping non-adb interface at " << device_address
-                                 << " (interface " << interface_num << ")";
-                    continue;
-                }
-
-                LOG(VERBOSE) << "found potential adb interface at " << device_address
-                             << " (interface " << interface_num << ")";
-
-                bool found_in = false;
-                bool found_out = false;
-                for (size_t endpoint_num = 0; endpoint_num < interface_desc.bNumEndpoints;
-                     ++endpoint_num) {
-                    const auto& endpoint_desc = interface_desc.endpoint[endpoint_num];
-                    const uint8_t endpoint_addr = endpoint_desc.bEndpointAddress;
-                    const uint8_t endpoint_attr = endpoint_desc.bmAttributes;
-
-                    const uint8_t transfer_type = endpoint_attr & LIBUSB_TRANSFER_TYPE_MASK;
-
-                    if (transfer_type != LIBUSB_TRANSFER_TYPE_BULK) {
-                        continue;
-                    }
-
-                    if (endpoint_is_output(endpoint_addr) && !found_out) {
-                        found_out = true;
-                        bulk_out = endpoint_addr;
-                        zero_mask = endpoint_desc.wMaxPacketSize - 1;
-                    } else if (!endpoint_is_output(endpoint_addr) && !found_in) {
-                        found_in = true;
-                        bulk_in = endpoint_addr;
-                    }
-                }
-
-                if (found_in && found_out) {
-                    found_adb = true;
-                    break;
-                } else {
-                    LOG(VERBOSE) << "rejecting potential adb interface at " << device_address
-                                 << "(interface " << interface_num << "): missing bulk endpoints "
-                                 << "(found_in = " << found_in << ", found_out = " << found_out
-                                 << ")";
-                }
-            }
-
-            if (!found_adb) {
-                LOG(VERBOSE) << "skipping device with no adb interfaces at " << device_address;
-                continue;
-            }
-
-            {
-                std::unique_lock<std::mutex> lock(usb_handles_mutex);
-                if (usb_handles.find(device_address) != usb_handles.end()) {
-                    LOG(VERBOSE) << "device at " << device_address
-                                 << " has already been registered, skipping";
-                    continue;
-                }
-            }
-
-            libusb_device_handle* handle_raw;
-            rc = libusb_open(list[i], &handle_raw);
-            if (rc != 0) {
-                LOG(WARNING) << "failed to open usb device at " << device_address << ": "
-                             << libusb_error_name(rc);
-                continue;
-            }
-
-            unique_device_handle handle(handle_raw);
-            LOG(DEBUG) << "successfully opened adb device at " << device_address << ", "
-                       << StringPrintf("bulk_in = %#x, bulk_out = %#x", bulk_in, bulk_out);
-
-            device_serial.resize(255);
-            rc = libusb_get_string_descriptor_ascii(
-                handle_raw, device_desc.iSerialNumber,
-                reinterpret_cast<unsigned char*>(&device_serial[0]), device_serial.length());
-            if (rc == 0) {
-                LOG(WARNING) << "received empty serial from device at " << device_address;
-                continue;
-            } else if (rc < 0) {
-                LOG(WARNING) << "failed to get serial from device at " << device_address
-                             << libusb_error_name(rc);
-                continue;
-            }
-            device_serial.resize(rc);
-
-            // WARNING: this isn't released via RAII.
-            rc = libusb_claim_interface(handle.get(), interface_num);
-            if (rc != 0) {
-                LOG(WARNING) << "failed to claim adb interface for device '" << device_serial << "'"
-                             << libusb_error_name(rc);
-                continue;
-            }
-
-            for (uint8_t endpoint : {bulk_in, bulk_out}) {
-                rc = libusb_clear_halt(handle.get(), endpoint);
-                if (rc != 0) {
-                    LOG(WARNING) << "failed to clear halt on device '" << device_serial
-                                 << "' endpoint 0x" << std::hex << endpoint << ": "
-                                 << libusb_error_name(rc);
-                    libusb_release_interface(handle.get(), interface_num);
-                    continue;
-                }
-            }
-
-            auto result =
-                std::make_unique<usb_handle>(device_address, device_serial, std::move(handle),
-                                             interface_num, bulk_in, bulk_out, zero_mask);
-            usb_handle* usb_handle_raw = result.get();
-
-            {
-                std::unique_lock<std::mutex> lock(usb_handles_mutex);
-                usb_handles[device_address] = std::move(result);
-            }
-
-            register_usb_transport(usb_handle_raw, device_serial.c_str(), device_address.c_str(), 1);
-
-            LOG(INFO) << "registered new usb device '" << device_serial << "'";
+            process_device(list[i]);
         }
+
         libusb_free_device_list(list, 1);
 
+        adb_notify_device_scan_complete();
+
         std::this_thread::sleep_for(500ms);
     }
 }
@@ -507,4 +556,10 @@
 void usb_kick(usb_handle* h) {
     h->Close();
 }
+
+size_t usb_get_max_packet_size(usb_handle* h) {
+    CHECK(h->max_packet_size != 0);
+    return h->max_packet_size;
+}
+
 } // namespace libusb
diff --git a/adb/client/usb_linux.cpp b/adb/client/usb_linux.cpp
index 6efed27..f9ba7cb 100644
--- a/adb/client/usb_linux.cpp
+++ b/adb/client/usb_linux.cpp
@@ -65,6 +65,7 @@
     unsigned char ep_in;
     unsigned char ep_out;
 
+    size_t max_packet_size;
     unsigned zero_mask;
     unsigned writeable = 1;
 
@@ -120,9 +121,9 @@
 }
 
 static void find_usb_device(const std::string& base,
-        void (*register_device_callback)
-                (const char*, const char*, unsigned char, unsigned char, int, int, unsigned))
-{
+                            void (*register_device_callback)(const char*, const char*,
+                                                             unsigned char, unsigned char, int, int,
+                                                             unsigned, size_t)) {
     std::unique_ptr<DIR, int(*)(DIR*)> bus_dir(opendir(base.c_str()), closedir);
     if (!bus_dir) return;
 
@@ -144,6 +145,7 @@
             struct usb_interface_descriptor* interface;
             struct usb_endpoint_descriptor *ep1, *ep2;
             unsigned zero_mask = 0;
+            size_t max_packet_size = 0;
             unsigned vid, pid;
 
             if (contains_non_digit(de->d_name)) continue;
@@ -251,7 +253,8 @@
                             continue;
                         }
                             /* aproto 01 needs 0 termination */
-                        if(interface->bInterfaceProtocol == 0x01) {
+                        if (interface->bInterfaceProtocol == 0x01) {
+                            max_packet_size = ep1->wMaxPacketSize;
                             zero_mask = ep1->wMaxPacketSize - 1;
                         }
 
@@ -281,9 +284,9 @@
                             }
                         }
 
-                        register_device_callback(dev_name.c_str(), devpath,
-                                local_ep_in, local_ep_out,
-                                interface->bInterfaceNumber, device->iSerialNumber, zero_mask);
+                        register_device_callback(dev_name.c_str(), devpath, local_ep_in,
+                                                 local_ep_out, interface->bInterfaceNumber,
+                                                 device->iSerialNumber, zero_mask, max_packet_size);
                         break;
                     }
                 } else {
@@ -497,10 +500,13 @@
     return 0;
 }
 
-static void register_device(const char* dev_name, const char* dev_path,
-                            unsigned char ep_in, unsigned char ep_out,
-                            int interface, int serial_index,
-                            unsigned zero_mask) {
+size_t usb_get_max_packet_size(usb_handle* h) {
+    return h->max_packet_size;
+}
+
+static void register_device(const char* dev_name, const char* dev_path, unsigned char ep_in,
+                            unsigned char ep_out, int interface, int serial_index,
+                            unsigned zero_mask, size_t max_packet_size) {
     // Since Linux will not reassign the device ID (and dev_name) as long as the
     // device is open, we can add to the list here once we open it and remove
     // from the list when we're finally closed and everything will work out
@@ -523,6 +529,7 @@
     usb->ep_in = ep_in;
     usb->ep_out = ep_out;
     usb->zero_mask = zero_mask;
+    usb->max_packet_size = max_packet_size;
 
     // Initialize mark so we don't get garbage collected after the device scan.
     usb->mark = true;
diff --git a/adb/client/usb_osx.cpp b/adb/client/usb_osx.cpp
index fcd0bc0..e366754 100644
--- a/adb/client/usb_osx.cpp
+++ b/adb/client/usb_osx.cpp
@@ -38,6 +38,7 @@
 
 #include <android-base/logging.h>
 #include <android-base/stringprintf.h>
+#include <android-base/thread_annotations.h>
 
 #include "adb.h"
 #include "transport.h"
@@ -51,15 +52,21 @@
     UInt8 bulkOut;
     IOUSBInterfaceInterface190** interface;
     unsigned int zero_mask;
+    size_t max_packet_size;
 
     // For garbage collecting disconnected devices.
     bool mark;
     std::string devpath;
     std::atomic<bool> dead;
 
-    usb_handle() : bulkIn(0), bulkOut(0), interface(nullptr),
-        zero_mask(0), mark(false), dead(false) {
-    }
+    usb_handle()
+        : bulkIn(0),
+          bulkOut(0),
+          interface(nullptr),
+          zero_mask(0),
+          max_packet_size(0),
+          mark(false),
+          dead(false) {}
 };
 
 static std::atomic<bool> usb_inited_flag;
@@ -390,6 +397,7 @@
         }
 
         handle->zero_mask = maxPacketSize - 1;
+        handle->max_packet_size = maxPacketSize;
     }
 
     handle->interface = interface;
@@ -422,7 +430,7 @@
     VLOG(USB) << "RunLoopThread done";
 }
 
-static void usb_cleanup() {
+static void usb_cleanup() NO_THREAD_SAFETY_ANALYSIS {
     VLOG(USB) << "usb_cleanup";
     // Wait until usb operations in RunLoopThread finish, and prevent further operations.
     operate_device_lock.lock();
@@ -558,4 +566,9 @@
     std::lock_guard<std::mutex> lock_guard(g_usb_handles_mutex);
     usb_kick_locked(handle);
 }
+
+size_t usb_get_max_packet_size(usb_handle* handle) {
+    return handle->max_packet_size;
+}
+
 } // namespace native
diff --git a/adb/client/usb_windows.cpp b/adb/client/usb_windows.cpp
index ee7f802..ec55b0e 100644
--- a/adb/client/usb_windows.cpp
+++ b/adb/client/usb_windows.cpp
@@ -65,6 +65,9 @@
   /// Interface name
   wchar_t*      interface_name;
 
+  /// Maximum packet size.
+  unsigned max_packet_size;
+
   /// Mask for determining when to use zero length packets
   unsigned zero_mask;
 };
@@ -522,6 +525,10 @@
   return 0;
 }
 
+size_t usb_get_max_packet_size(usb_handle* handle) {
+    return handle->max_packet_size;
+}
+
 int recognized_device(usb_handle* handle) {
   if (NULL == handle)
     return 0;
@@ -557,6 +564,7 @@
       AdbEndpointInformation endpoint_info;
       // assuming zero is a valid bulk endpoint ID
       if (AdbGetEndpointInformation(handle->adb_interface, 0, &endpoint_info)) {
+        handle->max_packet_size = endpoint_info.max_packet_size;
         handle->zero_mask = endpoint_info.max_packet_size - 1;
         D("device zero_mask: 0x%x", handle->zero_mask);
       } else {
diff --git a/adb/commandline.cpp b/adb/commandline.cpp
index b8f790d..a9b1540 100644
--- a/adb/commandline.cpp
+++ b/adb/commandline.cpp
@@ -761,55 +761,46 @@
     return RemoteShell(use_shell_protocol, shell_type_arg, escape_char, command);
 }
 
-static int adb_download_buffer(const char* service, const char* filename) {
-    std::string content;
-    if (!android::base::ReadFileToString(filename, &content)) {
-        fprintf(stderr, "error: couldn't read %s: %s\n", filename, strerror(errno));
-        return -1;
-    }
-
-    const uint8_t* data = reinterpret_cast<const uint8_t*>(content.data());
-    unsigned sz = content.size();
-
+static int adb_sideload_legacy(const char* filename, int in_fd, int size) {
     std::string error;
-    int fd = adb_connect(android::base::StringPrintf("%s:%d", service, sz), &error);
-    if (fd < 0) {
-        fprintf(stderr,"error: %s\n", error.c_str());
+    int out_fd = adb_connect(android::base::StringPrintf("sideload:%d", size), &error);
+    if (out_fd < 0) {
+        fprintf(stderr, "adb: pre-KitKat sideload connection failed: %s\n", error.c_str());
         return -1;
     }
 
     int opt = CHUNK_SIZE;
-    opt = adb_setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (const void *) &opt, sizeof(opt));
+    opt = adb_setsockopt(out_fd, SOL_SOCKET, SO_SNDBUF, &opt, sizeof(opt));
 
-    unsigned total = sz;
-    const uint8_t* ptr = reinterpret_cast<const uint8_t*>(data);
-
-    const char* x = strrchr(service, ':');
-    if (x) service = x + 1;
-
-    while (sz > 0) {
-        unsigned xfer = (sz > CHUNK_SIZE) ? CHUNK_SIZE : sz;
-        if (!WriteFdExactly(fd, ptr, xfer)) {
-            std::string error;
-            adb_status(fd, &error);
-            fprintf(stderr,"* failed to write data '%s' *\n", error.c_str());
-            adb_close(fd);
+    char buf[CHUNK_SIZE];
+    int total = size;
+    while (size > 0) {
+        unsigned xfer = (size > CHUNK_SIZE) ? CHUNK_SIZE : size;
+        if (!ReadFdExactly(in_fd, buf, xfer)) {
+            fprintf(stderr, "adb: failed to read data from %s: %s\n", filename, strerror(errno));
+            adb_close(out_fd);
             return -1;
         }
-        sz -= xfer;
-        ptr += xfer;
-        printf("sending: '%s' %4d%%    \r", filename, (int)(100LL - ((100LL * sz) / (total))));
+        if (!WriteFdExactly(out_fd, buf, xfer)) {
+            std::string error;
+            adb_status(out_fd, &error);
+            fprintf(stderr, "adb: failed to write data: %s\n", error.c_str());
+            adb_close(out_fd);
+            return -1;
+        }
+        size -= xfer;
+        printf("sending: '%s' %4d%%    \r", filename, (int)(100LL - ((100LL * size) / (total))));
         fflush(stdout);
     }
     printf("\n");
 
-    if (!adb_status(fd, &error)) {
-        fprintf(stderr,"* error response '%s' *\n", error.c_str());
-        adb_close(fd);
+    if (!adb_status(out_fd, &error)) {
+        fprintf(stderr, "adb: error response: %s\n", error.c_str());
+        adb_close(out_fd);
         return -1;
     }
 
-    adb_close(fd);
+    adb_close(out_fd);
     return 0;
 }
 
@@ -836,22 +827,17 @@
  */
 static int adb_sideload_host(const char* filename) {
     // TODO: use a LinePrinter instead...
-    fprintf(stdout, "opening '%s'...\n", filename);
-    fflush(stdout);
-
     struct stat sb;
     if (stat(filename, &sb) == -1) {
-        fprintf(stderr, "failed to stat file %s: %s\n", filename, strerror(errno));
+        fprintf(stderr, "adb: failed to stat file %s: %s\n", filename, strerror(errno));
         return -1;
     }
     unique_fd package_fd(adb_open(filename, O_RDONLY));
     if (package_fd == -1) {
-        fprintf(stderr, "failed to open file %s: %s\n", filename, strerror(errno));
+        fprintf(stderr, "adb: failed to open file %s: %s\n", filename, strerror(errno));
         return -1;
     }
 
-    fprintf(stdout, "connecting...\n");
-    fflush(stdout);
     std::string service = android::base::StringPrintf(
         "sideload-host:%d:%d", static_cast<int>(sb.st_size), SIDELOAD_HOST_BLOCK_SIZE);
     std::string error;
@@ -859,8 +845,9 @@
     if (device_fd < 0) {
         // Try falling back to the older (<= K) sideload method. Maybe this
         // is an older device that doesn't support sideload-host.
-        fprintf(stderr, "falling back to older sideload method...\n");
-        return adb_download_buffer("sideload", filename);
+        fprintf(stderr, "adb: sideload connection failed: %s\n", error.c_str());
+        fprintf(stderr, "adb: trying pre-KitKat sideload method...\n");
+        return adb_sideload_legacy(filename, package_fd, static_cast<int>(sb.st_size));
     }
 
     int opt = SIDELOAD_HOST_BLOCK_SIZE;
@@ -872,7 +859,7 @@
     int last_percent = -1;
     while (true) {
         if (!ReadFdExactly(device_fd, buf, 8)) {
-            fprintf(stderr, "* failed to read command: %s\n", strerror(errno));
+            fprintf(stderr, "adb: failed to read command: %s\n", strerror(errno));
             return -1;
         }
         buf[8] = '\0';
@@ -888,7 +875,7 @@
 
         size_t offset = block * SIDELOAD_HOST_BLOCK_SIZE;
         if (offset >= static_cast<size_t>(sb.st_size)) {
-            fprintf(stderr, "* attempt to read block %d past end\n", block);
+            fprintf(stderr, "adb: failed to read block %d past end\n", block);
             return -1;
         }
 
@@ -898,17 +885,17 @@
         }
 
         if (adb_lseek(package_fd, offset, SEEK_SET) != static_cast<int>(offset)) {
-            fprintf(stderr, "* failed to seek to package block: %s\n", strerror(errno));
+            fprintf(stderr, "adb: failed to seek to package block: %s\n", strerror(errno));
             return -1;
         }
         if (!ReadFdExactly(package_fd, buf, to_write)) {
-            fprintf(stderr, "* failed to read package block: %s\n", strerror(errno));
+            fprintf(stderr, "adb: failed to read package block: %s\n", strerror(errno));
             return -1;
         }
 
         if (!WriteFdExactly(device_fd, buf, to_write)) {
             adb_status(device_fd, &error);
-            fprintf(stderr,"* failed to write data '%s' *\n", error.c_str());
+            fprintf(stderr, "adb: failed to write data '%s' *\n", error.c_str());
             return -1;
         }
         xfer += to_write;
diff --git a/adb/daemon/main.cpp b/adb/daemon/main.cpp
index 7a87df4..a6d43ea 100644
--- a/adb/daemon/main.cpp
+++ b/adb/daemon/main.cpp
@@ -233,9 +233,8 @@
             adb_device_banner = optarg;
             break;
         case 'v':
-            printf("Android Debug Bridge Daemon version %d.%d.%d %s\n",
-                   ADB_VERSION_MAJOR, ADB_VERSION_MINOR, ADB_SERVER_VERSION,
-                   ADB_REVISION);
+            printf("Android Debug Bridge Daemon version %d.%d.%d (%s)\n", ADB_VERSION_MAJOR,
+                   ADB_VERSION_MINOR, ADB_SERVER_VERSION, ADB_VERSION);
             return 0;
         default:
             // getopt already prints "adbd: invalid option -- %c" for us.
diff --git a/adb/diagnose_usb.cpp b/adb/diagnose_usb.cpp
index 0f067b0..9f721bf 100644
--- a/adb/diagnose_usb.cpp
+++ b/adb/diagnose_usb.cpp
@@ -25,13 +25,14 @@
 
 #if defined(__linux__)
 #include <grp.h>
+#include <pwd.h>
 #endif
 
 static const char kPermissionsHelpUrl[] = "http://developer.android.com/tools/device.html";
 
-// Returns a message describing any potential problems we find with udev, or nullptr if we can't
-// find plugdev information (i.e. udev is not installed).
-static const char* GetUdevProblem() {
+// Returns a message describing any potential problems we find with udev, or an empty string if we
+// can't find plugdev information (i.e. udev is not installed).
+static std::string GetUdevProblem() {
 #if defined(__linux__)
     errno = 0;
     group* plugdev_group = getgrnam("plugdev");
@@ -41,43 +42,45 @@
             perror("failed to read plugdev group info");
         }
         // We can't give any generally useful advice here, just let the caller print the help URL.
-        return nullptr;
+        return "";
     }
 
-    // getgroups(2) indicates that the group_member() may not check the egid so we check it
+    // getgroups(2) indicates that the GNU group_member(3) may not check the egid so we check it
     // additionally just to be sure.
     if (group_member(plugdev_group->gr_gid) || getegid() == plugdev_group->gr_gid) {
         // The user is in plugdev so the problem is likely with the udev rules.
-        return "verify udev rules";
+        return "user in plugdev group; are your udev rules wrong?";
     }
-    return "udev requires plugdev group membership";
+    passwd* pwd = getpwuid(getuid());
+    return android::base::StringPrintf("user %s is not in the plugdev group",
+                                       pwd ? pwd->pw_name : "?");
 #else
-    return nullptr;
+    return "";
 #endif
 }
 
 // Short help text must be a single line, and will look something like:
-//   no permissions (reason); see <URL>
+//
+//   no permissions (reason); see [URL]
 std::string UsbNoPermissionsShortHelpText() {
     std::string help_text = "no permissions";
 
-    const char* problem = GetUdevProblem();
-    if (problem != nullptr) {
-        help_text += android::base::StringPrintf(" (%s)", problem);
-    }
+    std::string problem(GetUdevProblem());
+    if (!problem.empty()) help_text += " (" + problem + ")";
 
     return android::base::StringPrintf("%s; see [%s]", help_text.c_str(), kPermissionsHelpUrl);
 }
 
-// Long help text can span multiple lines and should provide more detailed information.
+// Long help text can span multiple lines but doesn't currently provide more detailed information:
+//
+//   insufficient permissions for device: reason
+//   See [URL] for more information
 std::string UsbNoPermissionsLongHelpText() {
     std::string header = "insufficient permissions for device";
 
-    const char* problem = GetUdevProblem();
-    if (problem != nullptr) {
-        header += android::base::StringPrintf(": %s", problem);
-    }
+    std::string problem(GetUdevProblem());
+    if (!problem.empty()) header += ": " + problem;
 
-    return android::base::StringPrintf("%s.\nSee [%s] for more information.",
-                                       header.c_str(), kPermissionsHelpUrl);
+    return android::base::StringPrintf("%s\nSee [%s] for more information", header.c_str(),
+                                       kPermissionsHelpUrl);
 }
diff --git a/adb/fdevent.cpp b/adb/fdevent.cpp
index 72c9eef..b28de4b 100644
--- a/adb/fdevent.cpp
+++ b/adb/fdevent.cpp
@@ -26,15 +26,19 @@
 #include <unistd.h>
 
 #include <atomic>
+#include <functional>
 #include <list>
+#include <mutex>
 #include <unordered_map>
 #include <vector>
 
 #include <android-base/logging.h>
 #include <android-base/stringprintf.h>
+#include <android-base/thread_annotations.h>
 
 #include "adb_io.h"
 #include "adb_trace.h"
+#include "adb_unique_fd.h"
 #include "adb_utils.h"
 
 #if !ADB_HOST
@@ -75,6 +79,10 @@
 static bool main_thread_valid;
 static unsigned long main_thread_id;
 
+static auto& run_queue_notify_fd = *new unique_fd();
+static auto& run_queue_mutex = *new std::mutex();
+static auto& run_queue GUARDED_BY(run_queue_mutex) = *new std::vector<std::function<void()>>();
+
 void check_main_thread() {
     if (main_thread_valid) {
         CHECK_EQ(main_thread_id, adb_thread_id());
@@ -112,8 +120,7 @@
     return android::base::StringPrintf("(fdevent %d %s)", fde->fd, state.c_str());
 }
 
-fdevent *fdevent_create(int fd, fd_func func, void *arg)
-{
+fdevent* fdevent_create(int fd, fd_func func, void* arg) {
     check_main_thread();
     fdevent *fde = (fdevent*) malloc(sizeof(fdevent));
     if(fde == 0) return 0;
@@ -122,8 +129,7 @@
     return fde;
 }
 
-void fdevent_destroy(fdevent *fde)
-{
+void fdevent_destroy(fdevent* fde) {
     check_main_thread();
     if(fde == 0) return;
     if(!(fde->state & FDE_CREATED)) {
@@ -278,8 +284,7 @@
     }
 }
 
-static void fdevent_call_fdfunc(fdevent* fde)
-{
+static void fdevent_call_fdfunc(fdevent* fde) {
     unsigned events = fde->events;
     fde->events = 0;
     CHECK(fde->state & FDE_PENDING);
@@ -292,10 +297,7 @@
 
 #include <sys/ioctl.h>
 
-static void fdevent_subproc_event_func(int fd, unsigned ev,
-                                       void* /* userdata */)
-{
-
+static void fdevent_subproc_event_func(int fd, unsigned ev, void* /* userdata */) {
     D("subproc handling on fd = %d, ev = %x", fd, ev);
 
     CHECK_GE(fd, 0);
@@ -342,8 +344,7 @@
     }
 }
 
-void fdevent_subproc_setup()
-{
+static void fdevent_subproc_setup() {
     int s[2];
 
     if(adb_socketpair(s)) {
@@ -358,12 +359,63 @@
 }
 #endif // !ADB_HOST
 
-void fdevent_loop()
-{
+static void fdevent_run_flush() REQUIRES(run_queue_mutex) {
+    for (auto& f : run_queue) {
+        f();
+    }
+    run_queue.clear();
+}
+
+static void fdevent_run_func(int fd, unsigned ev, void* /* userdata */) {
+    CHECK_GE(fd, 0);
+    CHECK(ev & FDE_READ);
+
+    char buf[1024];
+
+    // Empty the fd.
+    if (adb_read(fd, buf, sizeof(buf)) == -1) {
+        PLOG(FATAL) << "failed to empty run queue notify fd";
+    }
+
+    std::lock_guard<std::mutex> lock(run_queue_mutex);
+    fdevent_run_flush();
+}
+
+static void fdevent_run_setup() {
+    std::lock_guard<std::mutex> lock(run_queue_mutex);
+    CHECK(run_queue_notify_fd.get() == -1);
+    int s[2];
+    if (adb_socketpair(s) != 0) {
+        PLOG(FATAL) << "failed to create run queue notify socketpair";
+    }
+
+    run_queue_notify_fd.reset(s[0]);
+    fdevent* fde = fdevent_create(s[1], fdevent_run_func, nullptr);
+    CHECK(fde != nullptr);
+    fdevent_add(fde, FDE_READ);
+
+    fdevent_run_flush();
+}
+
+void fdevent_run_on_main_thread(std::function<void()> fn) {
+    std::lock_guard<std::mutex> lock(run_queue_mutex);
+    run_queue.push_back(std::move(fn));
+
+    // run_queue_notify_fd could still be -1 if we're called before fdevent has finished setting up.
+    // In that case, rely on the setup code to flush the queue without a notification being needed.
+    if (run_queue_notify_fd != -1) {
+        if (adb_write(run_queue_notify_fd.get(), "", 1) != 1) {
+            PLOG(FATAL) << "failed to write to run queue notify fd";
+        }
+    }
+}
+
+void fdevent_loop() {
     set_main_thread();
 #if !ADB_HOST
     fdevent_subproc_setup();
 #endif // !ADB_HOST
+    fdevent_run_setup();
 
     while (true) {
         if (terminate_loop) {
@@ -393,6 +445,11 @@
 void fdevent_reset() {
     g_poll_node_map.clear();
     g_pending_list.clear();
+
+    std::lock_guard<std::mutex> lock(run_queue_mutex);
+    run_queue_notify_fd.reset();
+    run_queue.clear();
+
     main_thread_valid = false;
     terminate_loop = false;
 }
diff --git a/adb/fdevent.h b/adb/fdevent.h
index e32845a..896400a 100644
--- a/adb/fdevent.h
+++ b/adb/fdevent.h
@@ -20,6 +20,8 @@
 #include <stddef.h>
 #include <stdint.h>  /* for int64_t */
 
+#include <functional>
+
 /* events that may be observed */
 #define FDE_READ              0x0001
 #define FDE_WRITE             0x0002
@@ -78,6 +80,9 @@
 
 void check_main_thread();
 
+// Queue an operation to run on the main thread.
+void fdevent_run_on_main_thread(std::function<void()> fn);
+
 // The following functions are used only for tests.
 void fdevent_terminate_loop();
 size_t fdevent_installed_count();
diff --git a/adb/fdevent_test.cpp b/adb/fdevent_test.cpp
index bdb973a..86e0209 100644
--- a/adb/fdevent_test.cpp
+++ b/adb/fdevent_test.cpp
@@ -173,3 +173,24 @@
     std::thread thread(InvalidFdThreadFunc);
     thread.join();
 }
+
+TEST_F(FdeventTest, run_on_main_thread) {
+    std::vector<int> vec;
+
+    PrepareThread();
+    std::thread thread(fdevent_loop);
+
+    for (int i = 0; i < 100; ++i) {
+        fdevent_run_on_main_thread([i, &vec]() {
+            check_main_thread();
+            vec.push_back(i);
+        });
+    }
+
+    TerminateThread(thread);
+
+    ASSERT_EQ(100u, vec.size());
+    for (int i = 0; i < 100; ++i) {
+        ASSERT_EQ(i, vec[i]);
+    }
+}
diff --git a/adb/fdevent_test.h b/adb/fdevent_test.h
index f4215ae..5ca49ac 100644
--- a/adb/fdevent_test.h
+++ b/adb/fdevent_test.h
@@ -53,11 +53,11 @@
 
     size_t GetAdditionalLocalSocketCount() {
 #if ADB_HOST
-        // dummy socket installed in PrepareThread()
-        return 1;
-#else
-        // dummy socket and one more socket installed in fdevent_subproc_setup()
+        // dummy socket installed in PrepareThread() + fdevent_run_on_main_thread socket
         return 2;
+#else
+        // dummy socket + fdevent_run_on_main_thread + fdevent_subproc_setup() sockets
+        return 3;
 #endif
     }
 
diff --git a/adb/sysdeps_test.cpp b/adb/sysdeps_test.cpp
index edb0fb3..fd19882 100644
--- a/adb/sysdeps_test.cpp
+++ b/adb/sysdeps_test.cpp
@@ -201,55 +201,6 @@
     }
 }
 
-TEST(sysdeps_mutex, mutex_smoke) {
-    static std::atomic<bool> finished(false);
-    static std::mutex &m = *new std::mutex();
-    m.lock();
-    ASSERT_FALSE(m.try_lock());
-    std::thread thread([]() {
-        ASSERT_FALSE(m.try_lock());
-        m.lock();
-        finished.store(true);
-        std::this_thread::sleep_for(200ms);
-        m.unlock();
-    });
-
-    ASSERT_FALSE(finished.load());
-    std::this_thread::sleep_for(100ms);
-    ASSERT_FALSE(finished.load());
-    m.unlock();
-    std::this_thread::sleep_for(100ms);
-    m.lock();
-    ASSERT_TRUE(finished.load());
-    m.unlock();
-
-    thread.join();
-}
-
-TEST(sysdeps_mutex, recursive_mutex_smoke) {
-    static std::recursive_mutex &m = *new std::recursive_mutex();
-
-    m.lock();
-    ASSERT_TRUE(m.try_lock());
-    m.unlock();
-
-    std::thread thread([]() {
-        ASSERT_FALSE(m.try_lock());
-        m.lock();
-        std::this_thread::sleep_for(500ms);
-        m.unlock();
-    });
-
-    std::this_thread::sleep_for(100ms);
-    m.unlock();
-    std::this_thread::sleep_for(100ms);
-    ASSERT_FALSE(m.try_lock());
-    m.lock();
-    m.unlock();
-
-    thread.join();
-}
-
 TEST(sysdeps_condition_variable, smoke) {
     static std::mutex &m = *new std::mutex;
     static std::condition_variable &cond = *new std::condition_variable;
diff --git a/adb/test_device.py b/adb/test_device.py
index a30972e..e44cc83 100644
--- a/adb/test_device.py
+++ b/adb/test_device.py
@@ -1259,6 +1259,26 @@
         self.assertEqual(self._get_device_state(serialno), 'device')
 
 
+    def test_packet_size_regression(self):
+        """Test for http://b/37783561
+
+        Receiving packets of a length divisible by 512 but not 1024 resulted in
+        the adb client waiting indefinitely for more input.
+        """
+        # The values that trigger things are 507 (512 - 5 bytes from shell protocol) + 1024*n
+        # Probe some surrounding values as well, for the hell of it.
+        for length in [506, 507, 508, 1018, 1019, 1020, 1530, 1531, 1532]:
+            cmd = ['dd', 'if=/dev/zero', 'bs={}'.format(length), 'count=1', '2>/dev/null;'
+                   'echo', 'foo']
+            rc, stdout, _ = self.device.shell_nocheck(cmd)
+
+            self.assertEqual(0, rc)
+
+            # Output should be '\0' * length, followed by "foo\n"
+            self.assertEqual(length, len(stdout) - 4)
+            self.assertEqual(stdout, "\0" * length + "foo\n")
+
+
 def main():
     random.seed(0)
     if len(adb.get_devices()) > 0:
diff --git a/adb/transport.cpp b/adb/transport.cpp
index cc8c162..20de642 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -400,8 +400,27 @@
     return &tracker->socket;
 }
 
+// Check if all of the USB transports are connected.
+bool iterate_transports(std::function<bool(const atransport*)> fn) {
+    std::lock_guard<std::mutex> lock(transport_lock);
+    for (const auto& t : transport_list) {
+        if (!fn(t)) {
+            return false;
+        }
+    }
+    for (const auto& t : pending_list) {
+        if (!fn(t)) {
+            return false;
+        }
+    }
+    return true;
+}
+
 // Call this function each time the transport list has changed.
 void update_transports() {
+    update_transport_status();
+
+    // Notify `adb track-devices` clients.
     std::string transports = list_transports(false);
 
     device_tracker* tracker = device_tracker_list;
diff --git a/adb/transport.h b/adb/transport.h
index 8c15d66..e129355 100644
--- a/adb/transport.h
+++ b/adb/transport.h
@@ -198,6 +198,10 @@
 void kick_transport(atransport* t);
 void update_transports(void);
 
+// Iterates across all of the current and pending transports.
+// Stops iteration and returns false if fn returns false, otherwise returns true.
+bool iterate_transports(std::function<bool(const atransport*)> fn);
+
 void init_transport_registration(void);
 void init_mdns_transport_discovery(void);
 std::string list_transports(bool long_listing);
diff --git a/adb/transport_mdns.cpp b/adb/transport_mdns.cpp
index e49b1c6..3603f09 100644
--- a/adb/transport_mdns.cpp
+++ b/adb/transport_mdns.cpp
@@ -24,6 +24,8 @@
 #include <arpa/inet.h>
 #endif
 
+#include <thread>
+
 #include <android-base/stringprintf.h>
 #include <dns_sd.h>
 
@@ -262,19 +264,22 @@
     }
 }
 
-void init_mdns_transport_discovery(void) {
-    DNSServiceErrorType errorCode =
-        DNSServiceBrowse(&service_ref, 0, 0, kADBServiceType, nullptr,
-                         register_mdns_transport, nullptr);
+void init_mdns_transport_discovery_thread(void) {
+    DNSServiceErrorType errorCode = DNSServiceBrowse(&service_ref, 0, 0, kADBServiceType, nullptr,
+                                                     register_mdns_transport, nullptr);
 
     if (errorCode != kDNSServiceErr_NoError) {
         D("Got %d initiating mDNS browse.", errorCode);
         return;
     }
 
-    fdevent_install(&service_ref_fde,
-                    adb_DNSServiceRefSockFD(service_ref),
-                    pump_service_ref,
-                    &service_ref);
-    fdevent_set(&service_ref_fde, FDE_READ);
+    fdevent_run_on_main_thread([]() {
+        fdevent_install(&service_ref_fde, adb_DNSServiceRefSockFD(service_ref), pump_service_ref,
+                        &service_ref);
+        fdevent_set(&service_ref_fde, FDE_READ);
+    });
+}
+
+void init_mdns_transport_discovery(void) {
+    std::thread(init_mdns_transport_discovery_thread).detach();
 }
diff --git a/adb/transport_usb.cpp b/adb/transport_usb.cpp
index ce419b8..885d723 100644
--- a/adb/transport_usb.cpp
+++ b/adb/transport_usb.cpp
@@ -27,57 +27,43 @@
 
 #if ADB_HOST
 
-static constexpr size_t MAX_USB_BULK_PACKET_SIZE = 1024u;
-
-// Call usb_read using a buffer having a multiple of MAX_USB_BULK_PACKET_SIZE bytes
+// Call usb_read using a buffer having a multiple of usb_get_max_packet_size() bytes
 // to avoid overflow. See http://libusb.sourceforge.net/api-1.0/packetoverflow.html.
 static int UsbReadMessage(usb_handle* h, amessage* msg) {
     D("UsbReadMessage");
-    char buffer[MAX_USB_BULK_PACKET_SIZE];
-    int n = usb_read(h, buffer, sizeof(buffer));
-    if (n == sizeof(*msg)) {
-        memcpy(msg, buffer, sizeof(*msg));
+
+    size_t usb_packet_size = usb_get_max_packet_size(h);
+    CHECK(usb_packet_size >= sizeof(*msg));
+    CHECK(usb_packet_size < 4096);
+
+    char buffer[4096];
+    int n = usb_read(h, buffer, usb_packet_size);
+    if (n != sizeof(*msg)) {
+        D("usb_read returned unexpected length %d (expected %zu)", n, sizeof(*msg));
+        return -1;
     }
+    memcpy(msg, buffer, sizeof(*msg));
     return n;
 }
 
-// Call usb_read using a buffer having a multiple of MAX_USB_BULK_PACKET_SIZE bytes
+// Call usb_read using a buffer having a multiple of usb_get_max_packet_size() bytes
 // to avoid overflow. See http://libusb.sourceforge.net/api-1.0/packetoverflow.html.
 static int UsbReadPayload(usb_handle* h, apacket* p) {
-    D("UsbReadPayload");
-    size_t need_size = p->msg.data_length;
-    size_t data_pos = 0u;
-    while (need_size > 0u) {
-        int n = 0;
-        if (data_pos + MAX_USB_BULK_PACKET_SIZE <= sizeof(p->data)) {
-            // Read directly to p->data.
-            size_t rem_size = need_size % MAX_USB_BULK_PACKET_SIZE;
-            size_t direct_read_size = need_size - rem_size;
-            if (rem_size &&
-                data_pos + direct_read_size + MAX_USB_BULK_PACKET_SIZE <= sizeof(p->data)) {
-                direct_read_size += MAX_USB_BULK_PACKET_SIZE;
-            }
-            n = usb_read(h, &p->data[data_pos], direct_read_size);
-            if (n < 0) {
-                D("usb_read(size %zu) failed", direct_read_size);
-                return n;
-            }
-        } else {
-            // Read indirectly using a buffer.
-            char buffer[MAX_USB_BULK_PACKET_SIZE];
-            n = usb_read(h, buffer, sizeof(buffer));
-            if (n < 0) {
-                D("usb_read(size %zu) failed", sizeof(buffer));
-                return -1;
-            }
-            size_t copy_size = std::min(static_cast<size_t>(n), need_size);
-            D("usb read %d bytes, need %zu bytes, copy %zu bytes", n, need_size, copy_size);
-            memcpy(&p->data[data_pos], buffer, copy_size);
-        }
-        data_pos += n;
-        need_size -= std::min(static_cast<size_t>(n), need_size);
+    D("UsbReadPayload(%d)", p->msg.data_length);
+
+    size_t usb_packet_size = usb_get_max_packet_size(h);
+    CHECK(sizeof(p->data) % usb_packet_size == 0);
+
+    // Round the data length up to the nearest packet size boundary.
+    // The device won't send a zero packet for packet size aligned payloads,
+    // so don't read any more packets than needed.
+    size_t len = p->msg.data_length;
+    size_t rem_size = len % usb_packet_size;
+    if (rem_size) {
+        len += usb_packet_size - rem_size;
     }
-    return static_cast<int>(data_pos);
+    CHECK(len <= sizeof(p->data));
+    return usb_read(h, &p->data, len);
 }
 
 static int remote_read(apacket* p, atransport* t) {
diff --git a/adb/usb.h b/adb/usb.h
index ba70de4..e867ec8 100644
--- a/adb/usb.h
+++ b/adb/usb.h
@@ -16,6 +16,8 @@
 
 #pragma once
 
+#include <sys/types.h>
+
 // USB host/client interface.
 
 #define ADB_USB_INTERFACE(handle_ref_type)                       \
@@ -23,7 +25,8 @@
     int usb_write(handle_ref_type h, const void* data, int len); \
     int usb_read(handle_ref_type h, void* data, int len);        \
     int usb_close(handle_ref_type h);                            \
-    void usb_kick(handle_ref_type h)
+    void usb_kick(handle_ref_type h);                            \
+    size_t usb_get_max_packet_size(handle_ref_type)
 
 #if defined(_WIN32) || !ADB_HOST
 // Windows and the daemon have a single implementation.
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index 4783d6e..37d54d7 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -1,6 +1,5 @@
 cc_defaults {
     name: "debuggerd_defaults",
-    defaults: ["linux_bionic_supported"],
     cflags: [
         "-Wall",
         "-Wextra",
@@ -9,10 +8,6 @@
         "-Os",
     ],
 
-    // util.cpp gets async signal safe logging via libc_logging,
-    // which defines its interface in bionic private headers.
-    include_dirs: ["bionic/libc"],
-
     local_include_dirs: ["include"],
 }
 
@@ -26,7 +21,7 @@
     ],
 
     whole_static_libs: [
-        "libc_logging",
+        "libasync_safe",
         "libcutils",
         "libbase",
     ],
@@ -39,7 +34,7 @@
     srcs: ["handler/debuggerd_handler.cpp"],
 
     whole_static_libs: [
-        "libc_logging",
+        "libasync_safe",
         "libdebuggerd",
     ],
 
@@ -70,6 +65,7 @@
     whole_static_libs: [
         "libdebuggerd_handler_core",
         "libtombstoned_client",
+        "libasync_safe",
         "libbase",
         "libdebuggerd",
         "libbacktrace",
@@ -166,6 +162,7 @@
                 "tombstoned_client.cpp",
                 "util.cpp"
             ],
+            static_libs: ["libasync_safe"],
         },
     },
 
@@ -178,7 +175,6 @@
 
     static_libs: [
         "libdebuggerd",
-        "libc_logging",
     ],
 
     local_include_dirs: [
@@ -225,7 +221,6 @@
         "libbase",
         "liblog",
         "libprocinfo",
-        "libselinux",
     ],
 }
 
diff --git a/debuggerd/crash_dump.cpp b/debuggerd/crash_dump.cpp
index 88f390b..d2a4239 100644
--- a/debuggerd/crash_dump.cpp
+++ b/debuggerd/crash_dump.cpp
@@ -42,7 +42,6 @@
 #include <cutils/sockets.h>
 #include <log/log.h>
 #include <procinfo/process.h>
-#include <selinux/selinux.h>
 
 #include "backtrace.h"
 #include "tombstone.h"
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index 568879e..0b4bbfb 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -29,6 +29,7 @@
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
+#include <android-base/macros.h>
 #include <android-base/parseint.h>
 #include <android-base/properties.h>
 #include <android-base/strings.h>
@@ -149,7 +150,7 @@
   // Returns -1 if we fail to read a response from tombstoned, otherwise the received return code.
   void FinishIntercept(int* result);
 
-  void StartProcess(std::function<void()> function);
+  void StartProcess(std::function<void()> function, std::function<pid_t()> forker = fork);
   void StartCrasher(const std::string& crash_type);
   void FinishCrasher();
   void AssertDeath(int signo);
@@ -195,14 +196,14 @@
   }
 }
 
-void CrasherTest::StartProcess(std::function<void()> function) {
+void CrasherTest::StartProcess(std::function<void()> function, std::function<pid_t()> forker) {
   unique_fd read_pipe;
   unique_fd crasher_read_pipe;
   if (!Pipe(&crasher_read_pipe, &crasher_pipe)) {
     FAIL() << "failed to create pipe: " << strerror(errno);
   }
 
-  crasher_pid = fork();
+  crasher_pid = forker();
   if (crasher_pid == -1) {
     FAIL() << "fork failed: " << strerror(errno);
   } else if (crasher_pid == 0) {
@@ -527,6 +528,37 @@
   ASSERT_MATCH(result, R"(#00 pc [0-9a-f]+\s+ /system/lib)" ARCH_SUFFIX R"(/libc.so \(tgkill)");
 }
 
+TEST_F(CrasherTest, fake_pid) {
+  int intercept_result;
+  unique_fd output_fd;
+
+  // Prime the getpid/gettid caches.
+  UNUSED(getpid());
+  UNUSED(gettid());
+
+  std::function<pid_t()> clone_fn = []() {
+    return syscall(__NR_clone, SIGCHLD, nullptr, nullptr, nullptr, nullptr);
+  };
+  StartProcess(
+      []() {
+        ASSERT_NE(getpid(), syscall(__NR_getpid));
+        ASSERT_NE(gettid(), syscall(__NR_gettid));
+        raise(SIGSEGV);
+      },
+      clone_fn);
+
+  StartIntercept(&output_fd);
+  FinishCrasher();
+  AssertDeath(SIGSEGV);
+  FinishIntercept(&intercept_result);
+
+  ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+  std::string result;
+  ConsumeFd(std::move(output_fd), &result);
+  ASSERT_MATCH(result, R"(#00 pc [0-9a-f]+\s+ /system/lib)" ARCH_SUFFIX R"(/libc.so \(tgkill)");
+}
+
 TEST(crash_dump, zombie) {
   pid_t forkpid = fork();
 
diff --git a/debuggerd/handler/debuggerd_fallback.cpp b/debuggerd/handler/debuggerd_fallback.cpp
index 5c6c59c..47c98d1 100644
--- a/debuggerd/handler/debuggerd_fallback.cpp
+++ b/debuggerd/handler/debuggerd_fallback.cpp
@@ -39,6 +39,7 @@
 
 #include <android-base/file.h>
 #include <android-base/unique_fd.h>
+#include <async_safe/log.h>
 
 #include "debuggerd/handler.h"
 #include "debuggerd/tombstoned.h"
@@ -47,8 +48,6 @@
 #include "backtrace.h"
 #include "tombstone.h"
 
-#include "private/libc_logging.h"
-
 using android::base::unique_fd;
 
 extern "C" void __linker_enable_fallback_allocator();
@@ -81,7 +80,7 @@
   DIR* dir = opendir(buf);
 
   if (!dir) {
-    __libc_format_log(ANDROID_LOG_ERROR, "libc", "failed to open %s: %s", buf, strerror(errno));
+    async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to open %s: %s", buf, strerror(errno));
     return;
   }
 
@@ -145,7 +144,8 @@
   static pthread_mutex_t trace_mutex = PTHREAD_MUTEX_INITIALIZER;
   int ret = pthread_mutex_trylock(&trace_mutex);
   if (ret != 0) {
-    __libc_format_log(ANDROID_LOG_INFO, "libc", "pthread_mutex_try_lock failed: %s", strerror(ret));
+    async_safe_format_log(ANDROID_LOG_INFO, "libc", "pthread_mutex_try_lock failed: %s",
+                          strerror(ret));
     return;
   }
 
@@ -167,7 +167,8 @@
       // receiving our signal.
       unique_fd pipe_read, pipe_write;
       if (!Pipe(&pipe_read, &pipe_write)) {
-        __libc_format_log(ANDROID_LOG_ERROR, "libc", "failed to create pipe: %s", strerror(errno));
+        async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to create pipe: %s",
+                              strerror(errno));
         return false;
       }
 
@@ -180,8 +181,8 @@
       siginfo.si_uid = getuid();
 
       if (syscall(__NR_rt_tgsigqueueinfo, getpid(), tid, DEBUGGER_SIGNAL, &siginfo) != 0) {
-        __libc_format_log(ANDROID_LOG_ERROR, "libc", "failed to send trace signal to %d: %s", tid,
-                          strerror(errno));
+        async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to send trace signal to %d: %s",
+                              tid, strerror(errno));
         return false;
       }
 
@@ -209,7 +210,7 @@
   static pthread_mutex_t crash_mutex = PTHREAD_MUTEX_INITIALIZER;
   int ret = pthread_mutex_lock(&crash_mutex);
   if (ret != 0) {
-    __libc_format_log(ANDROID_LOG_INFO, "libc", "pthread_mutex_lock failed: %s", strerror(ret));
+    async_safe_format_log(ANDROID_LOG_INFO, "libc", "pthread_mutex_lock failed: %s", strerror(ret));
     return;
   }
 
diff --git a/debuggerd/handler/debuggerd_handler.cpp b/debuggerd/handler/debuggerd_handler.cpp
index b70554f..8fd6e11 100644
--- a/debuggerd/handler/debuggerd_handler.cpp
+++ b/debuggerd/handler/debuggerd_handler.cpp
@@ -48,8 +48,7 @@
 #include <sys/wait.h>
 #include <unistd.h>
 
-#include "private/bionic_futex.h"
-#include "private/libc_logging.h"
+#include <async_safe/log.h>
 
 // see man(2) prctl, specifically the section about PR_GET_NAME
 #define MAX_TASK_NAME_LEN (16)
@@ -62,6 +61,20 @@
 
 #define CRASH_DUMP_PATH "/system/bin/" CRASH_DUMP_NAME
 
+// Wrappers that directly invoke the respective syscalls, in case the cached values are invalid.
+#pragma GCC poison getpid gettid
+static pid_t __getpid() {
+  return syscall(__NR_getpid);
+}
+
+static pid_t __gettid() {
+  return syscall(__NR_gettid);
+}
+
+static inline void futex_wait(volatile void* ftx, int value) {
+  syscall(__NR_futex, ftx, FUTEX_WAIT, value, nullptr, nullptr, 0);
+}
+
 class ErrnoRestorer {
  public:
   ErrnoRestorer() : saved_errno_(errno) {
@@ -82,11 +95,12 @@
 // Mutex to ensure only one crashing thread dumps itself.
 static pthread_mutex_t crash_mutex = PTHREAD_MUTEX_INITIALIZER;
 
-// Don't use __libc_fatal because it exits via abort, which might put us back into a signal handler.
+// Don't use async_safe_fatal because it exits via abort, which might put us back into
+// a signal handler.
 static void __noreturn __printflike(1, 2) fatal(const char* fmt, ...) {
   va_list args;
   va_start(args, fmt);
-  __libc_format_log_va_list(ANDROID_LOG_FATAL, "libc", fmt, args);
+  async_safe_format_log_va_list(ANDROID_LOG_FATAL, "libc", fmt, args);
   _exit(1);
 }
 
@@ -96,7 +110,7 @@
   va_start(args, fmt);
 
   char buf[4096];
-  __libc_format_buffer_va_list(buf, sizeof(buf), fmt, args);
+  async_safe_format_buffer_va_list(buf, sizeof(buf), fmt, args);
   fatal("%s: %s", buf, strerror(err));
 }
 
@@ -120,8 +134,8 @@
   }
 
   if (signum == DEBUGGER_SIGNAL) {
-    __libc_format_log(ANDROID_LOG_INFO, "libc", "Requested dump for tid %d (%s)", gettid(),
-                      thread_name);
+    async_safe_format_log(ANDROID_LOG_INFO, "libc", "Requested dump for tid %d (%s)", __gettid(),
+                          thread_name);
     return;
   }
 
@@ -166,14 +180,14 @@
   char addr_desc[32];  // ", fault addr 0x1234"
   addr_desc[0] = code_desc[0] = 0;
   if (info != nullptr) {
-    __libc_format_buffer(code_desc, sizeof(code_desc), ", code %d", info->si_code);
+    async_safe_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);
+      async_safe_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);
+  async_safe_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);
 }
 
 /*
@@ -182,8 +196,8 @@
 static bool have_siginfo(int signum) {
   struct sigaction old_action;
   if (sigaction(signum, nullptr, &old_action) < 0) {
-    __libc_format_log(ANDROID_LOG_WARN, "libc", "Failed testing for SA_SIGINFO: %s",
-                      strerror(errno));
+    async_safe_format_log(ANDROID_LOG_WARN, "libc", "Failed testing for SA_SIGINFO: %s",
+                          strerror(errno));
     return false;
   }
   return (old_action.sa_flags & SA_SIGINFO) != 0;
@@ -207,7 +221,7 @@
     capdata[1].inheritable = capdata[1].permitted;
 
     if (capset(&capheader, &capdata[0]) == -1) {
-      __libc_format_log(ANDROID_LOG_ERROR, "libc", "capset failed: %s", strerror(errno));
+      async_safe_format_log(ANDROID_LOG_ERROR, "libc", "capset failed: %s", strerror(errno));
     }
   }
 
@@ -217,8 +231,8 @@
   for (unsigned long i = 0; i < 64; ++i) {
     if (capmask & (1ULL << i)) {
       if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, i, 0, 0) != 0) {
-        __libc_format_log(ANDROID_LOG_ERROR, "libc", "failed to raise ambient capability %lu: %s",
-                          i, strerror(errno));
+        async_safe_format_log(ANDROID_LOG_ERROR, "libc",
+                              "failed to raise ambient capability %lu: %s", i, strerror(errno));
       }
     }
   }
@@ -260,8 +274,8 @@
   // Don't use fork(2) to avoid calling pthread_atfork handlers.
   int forkpid = clone(nullptr, nullptr, 0, nullptr);
   if (forkpid == -1) {
-    __libc_format_log(ANDROID_LOG_FATAL, "libc", "failed to fork in debuggerd signal handler: %s",
-                      strerror(errno));
+    async_safe_format_log(ANDROID_LOG_FATAL, "libc",
+                          "failed to fork in debuggerd signal handler: %s", strerror(errno));
   } else if (forkpid == 0) {
     TEMP_FAILURE_RETRY(dup2(pipefds[1], STDOUT_FILENO));
     close(pipefds[0]);
@@ -271,8 +285,9 @@
 
     char main_tid[10];
     char pseudothread_tid[10];
-    __libc_format_buffer(main_tid, sizeof(main_tid), "%d", thread_info->crashing_tid);
-    __libc_format_buffer(pseudothread_tid, sizeof(pseudothread_tid), "%d", thread_info->pseudothread_tid);
+    async_safe_format_buffer(main_tid, sizeof(main_tid), "%d", thread_info->crashing_tid);
+    async_safe_format_buffer(pseudothread_tid, sizeof(pseudothread_tid), "%d",
+                             thread_info->pseudothread_tid);
 
     execl(CRASH_DUMP_PATH, CRASH_DUMP_NAME, main_tid, pseudothread_tid, nullptr);
 
@@ -282,15 +297,16 @@
     char buf[4];
     ssize_t rc = TEMP_FAILURE_RETRY(read(pipefds[0], &buf, sizeof(buf)));
     if (rc == -1) {
-      __libc_format_log(ANDROID_LOG_FATAL, "libc", "read of IPC pipe failed: %s", strerror(errno));
+      async_safe_format_log(ANDROID_LOG_FATAL, "libc", "read of IPC pipe failed: %s",
+                            strerror(errno));
     } else if (rc == 0) {
-      __libc_format_log(ANDROID_LOG_FATAL, "libc", "crash_dump helper failed to exec");
+      async_safe_format_log(ANDROID_LOG_FATAL, "libc", "crash_dump helper failed to exec");
     } else if (rc != 1) {
-      __libc_format_log(ANDROID_LOG_FATAL, "libc",
-                        "read of IPC pipe returned unexpected value: %zd", rc);
+      async_safe_format_log(ANDROID_LOG_FATAL, "libc",
+                            "read of IPC pipe returned unexpected value: %zd", rc);
     } else {
       if (buf[0] != '\1') {
-        __libc_format_log(ANDROID_LOG_FATAL, "libc", "crash_dump helper reported failure");
+        async_safe_format_log(ANDROID_LOG_FATAL, "libc", "crash_dump helper reported failure");
       } else {
         thread_info->crash_dump_started = true;
       }
@@ -300,10 +316,10 @@
     // Don't leave a zombie child.
     int status;
     if (TEMP_FAILURE_RETRY(waitpid(forkpid, &status, 0)) == -1) {
-      __libc_format_log(ANDROID_LOG_FATAL, "libc", "failed to wait for crash_dump helper: %s",
-                        strerror(errno));
+      async_safe_format_log(ANDROID_LOG_FATAL, "libc", "failed to wait for crash_dump helper: %s",
+                            strerror(errno));
     } else if (WIFSTOPPED(status) || WIFSIGNALED(status)) {
-      __libc_format_log(ANDROID_LOG_FATAL, "libc", "crash_dump helper crashed or stopped");
+      async_safe_format_log(ANDROID_LOG_FATAL, "libc", "crash_dump helper crashed or stopped");
       thread_info->crash_dump_started = false;
     }
   }
@@ -331,7 +347,7 @@
   // rt_tgsigqueueinfo(2) to preserve SA_SIGINFO) will cause it to be delivered
   // when our signal handler returns.
   if (crash_dump_started || info->si_signo != DEBUGGER_SIGNAL) {
-    int rc = syscall(SYS_rt_tgsigqueueinfo, getpid(), gettid(), info->si_signo, info);
+    int rc = syscall(SYS_rt_tgsigqueueinfo, __getpid(), __gettid(), info->si_signo, info);
     if (rc != 0) {
       fatal_errno("failed to resend signal during crash");
     }
@@ -356,7 +372,7 @@
     memset(&si, 0, sizeof(si));
     si.si_signo = signal_number;
     si.si_code = SI_USER;
-    si.si_pid = getpid();
+    si.si_pid = __getpid();
     si.si_uid = getuid();
     info = &si;
   } else if (info->si_code >= 0 || info->si_code == SI_TKILL) {
@@ -383,7 +399,7 @@
   // Only allow one thread to handle a signal at a time.
   int ret = pthread_mutex_lock(&crash_mutex);
   if (ret != 0) {
-    __libc_format_log(ANDROID_LOG_INFO, "libc", "pthread_mutex_lock failed: %s", strerror(ret));
+    async_safe_format_log(ANDROID_LOG_INFO, "libc", "pthread_mutex_lock failed: %s", strerror(ret));
     return;
   }
 
@@ -398,7 +414,7 @@
   debugger_thread_info thread_info = {
     .crash_dump_started = false,
     .pseudothread_tid = -1,
-    .crashing_tid = gettid(),
+    .crashing_tid = __gettid(),
     .signal_number = signal_number,
     .info = info
   };
@@ -419,10 +435,10 @@
   }
 
   // Wait for the child to start...
-  __futex_wait(&thread_info.pseudothread_tid, -1, nullptr);
+  futex_wait(&thread_info.pseudothread_tid, -1);
 
   // and then wait for it to finish.
-  __futex_wait(&thread_info.pseudothread_tid, child_pid, nullptr);
+  futex_wait(&thread_info.pseudothread_tid, child_pid);
 
   // Restore PR_SET_DUMPABLE to its original value.
   if (prctl(PR_SET_DUMPABLE, orig_dumpable) != 0) {
diff --git a/debuggerd/tombstoned_client.cpp b/debuggerd/tombstoned_client.cpp
index 03b4a20..4741fa6 100644
--- a/debuggerd/tombstoned_client.cpp
+++ b/debuggerd/tombstoned_client.cpp
@@ -22,11 +22,11 @@
 #include <utility>
 
 #include <android-base/unique_fd.h>
+#include <async_safe/log.h>
 #include <cutils/sockets.h>
 
 #include "debuggerd/protocol.h"
 #include "debuggerd/util.h"
-#include "private/libc_logging.h"
 
 using android::base::unique_fd;
 
@@ -34,8 +34,8 @@
   unique_fd sockfd(socket_local_client(kTombstonedCrashSocketName,
                                        ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_SEQPACKET));
   if (sockfd == -1) {
-    __libc_format_log(ANDROID_LOG_ERROR, "libc", "failed to connect to tombstoned: %s",
-                      strerror(errno));
+    async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to connect to tombstoned: %s",
+                          strerror(errno));
     return false;
   }
 
@@ -43,22 +43,22 @@
   packet.packet_type = CrashPacketType::kDumpRequest;
   packet.packet.dump_request.pid = pid;
   if (TEMP_FAILURE_RETRY(write(sockfd, &packet, sizeof(packet))) != sizeof(packet)) {
-    __libc_format_log(ANDROID_LOG_ERROR, "libc", "failed to write DumpRequest packet: %s",
-                      strerror(errno));
+    async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to write DumpRequest packet: %s",
+                          strerror(errno));
     return false;
   }
 
   unique_fd tmp_output_fd;
   ssize_t rc = recv_fd(sockfd, &packet, sizeof(packet), &tmp_output_fd);
   if (rc == -1) {
-    __libc_format_log(ANDROID_LOG_ERROR, "libc",
-                      "failed to read response to DumpRequest packet: %s", strerror(errno));
+    async_safe_format_log(ANDROID_LOG_ERROR, "libc",
+                          "failed to read response to DumpRequest packet: %s", strerror(errno));
     return false;
   } else if (rc != sizeof(packet)) {
-    __libc_format_log(
-      ANDROID_LOG_ERROR, "libc",
-      "received DumpRequest response packet of incorrect length (expected %zu, got %zd)",
-      sizeof(packet), rc);
+    async_safe_format_log(
+        ANDROID_LOG_ERROR, "libc",
+        "received DumpRequest response packet of incorrect length (expected %zu, got %zd)",
+        sizeof(packet), rc);
     return false;
   }
 
@@ -67,8 +67,8 @@
   // a regular fd, and writing to an fd with O_APPEND).
   int flags = fcntl(tmp_output_fd.get(), F_GETFL);
   if (fcntl(tmp_output_fd.get(), F_SETFL, flags | O_APPEND) != 0) {
-    __libc_format_log(ANDROID_LOG_WARN, "libc", "failed to set output fd flags: %s",
-                      strerror(errno));
+    async_safe_format_log(ANDROID_LOG_WARN, "libc", "failed to set output fd flags: %s",
+                          strerror(errno));
   }
 
   *tombstoned_socket = std::move(sockfd);
diff --git a/debuggerd/util.cpp b/debuggerd/util.cpp
index 4c015d7..32d2f18 100644
--- a/debuggerd/util.cpp
+++ b/debuggerd/util.cpp
@@ -24,8 +24,6 @@
 #include <cutils/sockets.h>
 #include <debuggerd/protocol.h>
 
-#include "private/libc_logging.h"
-
 using android::base::unique_fd;
 
 ssize_t send_fd(int sockfd, const void* data, size_t len, unique_fd fd) {
diff --git a/fastboot/Android.mk b/fastboot/Android.mk
index 2c578a9..4d54175 100644
--- a/fastboot/Android.mk
+++ b/fastboot/Android.mk
@@ -14,10 +14,12 @@
 
 LOCAL_PATH:= $(call my-dir)
 
-fastboot_version := $(shell git -C $(LOCAL_PATH) rev-parse --short=12 HEAD 2>/dev/null)-android
+include $(LOCAL_PATH)/../platform_tools_tool_version.mk
 
 include $(CLEAR_VARS)
 
+LOCAL_CFLAGS += -DFASTBOOT_VERSION=\"$(tool_version)\"
+
 LOCAL_C_INCLUDES := \
   $(LOCAL_PATH)/../adb \
   $(LOCAL_PATH)/../mkbootimg \
@@ -39,8 +41,6 @@
 LOCAL_MODULE_HOST_OS := darwin linux windows
 LOCAL_CFLAGS += -Wall -Wextra -Werror -Wunreachable-code
 
-LOCAL_CFLAGS += -DFASTBOOT_REVISION='"$(fastboot_version)"'
-
 LOCAL_SRC_FILES_linux := usb_linux.cpp
 LOCAL_STATIC_LIBRARIES_linux := libselinux
 
diff --git a/fastboot/engine.cpp b/fastboot/engine.cpp
index 56ee816..7e10cc9 100644
--- a/fastboot/engine.cpp
+++ b/fastboot/engine.cpp
@@ -258,6 +258,12 @@
     return cb_check(a, status, resp, 1);
 }
 
+static char* xstrdup(const char* s) {
+    char* result = strdup(s);
+    if (!result) die("out of memory");
+    return result;
+}
+
 void fb_queue_require(const char *prod, const char *var,
                       bool invert, size_t nvalues, const char **value)
 {
@@ -276,16 +282,14 @@
         fprintf(stderr, "%s FAILED (%s)\n", a->cmd, resp);
         return status;
     }
-    fprintf(stderr, "%s: %s\n", (char*) a->data, resp);
+    fprintf(stderr, "%s: %s\n", static_cast<const char*>(a->data), resp);
+    free(static_cast<char*>(a->data));
     return 0;
 }
 
-void fb_queue_display(const char *var, const char *prettyname)
-{
-    Action *a;
-    a = queue_action(OP_QUERY, "getvar:%s", var);
-    a->data = strdup(prettyname);
-    if (a->data == nullptr) die("out of memory");
+void fb_queue_display(const char* var, const char* prettyname) {
+    Action* a = queue_action(OP_QUERY, "getvar:%s", var);
+    a->data = xstrdup(prettyname);
     a->func = cb_display;
 }
 
@@ -298,11 +302,9 @@
     return 0;
 }
 
-void fb_queue_query_save(const char *var, char *dest, uint32_t dest_size)
-{
-    Action *a;
-    a = queue_action(OP_QUERY, "getvar:%s", var);
-    a->data = (void *)dest;
+void fb_queue_query_save(const char* var, char* dest, uint32_t dest_size) {
+    Action* a = queue_action(OP_QUERY, "getvar:%s", var);
+    a->data = dest;
     a->size = dest_size;
     a->func = cb_save;
 }
@@ -342,15 +344,13 @@
     a->msg = mkmsg("sending '%s' (%d KB)", name, sz / 1024);
 }
 
-void fb_queue_upload(char *outfile)
-{
-    Action *a = queue_action(OP_UPLOAD, "");
-    a->data = outfile;
+void fb_queue_upload(const char* outfile) {
+    Action* a = queue_action(OP_UPLOAD, "");
+    a->data = xstrdup(outfile);
     a->msg = mkmsg("uploading '%s'", outfile);
 }
 
-void fb_queue_notice(const char *notice)
-{
+void fb_queue_notice(const char* notice) {
     Action *a = queue_action(OP_NOTICE, "");
     a->data = (void*) notice;
 }
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 982545c..f10b9ce 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -26,8 +26,6 @@
  * SUCH DAMAGE.
  */
 
-#define _LARGEFILE64_SOURCE
-
 #include <ctype.h>
 #include <errno.h>
 #include <fcntl.h>
@@ -77,7 +75,6 @@
 char cur_product[FB_RESPONSE_SZ + 1];
 
 static const char* serial = nullptr;
-static const char* product = nullptr;
 static const char* cmdline = nullptr;
 static unsigned short vendor_id = 0;
 static int long_listing = 0;
@@ -110,63 +107,46 @@
 };
 
 static struct {
-    char img_name[17];
-    char sig_name[17];
-    char part_name[9];
+    const char* nickname;
+    const char* img_name;
+    const char* sig_name;
+    const char* part_name;
     bool is_optional;
     bool is_secondary;
 } images[] = {
-    {"boot.img", "boot.sig", "boot", false, false},
-    {"boot_other.img", "boot.sig", "boot", true, true},
-    {"recovery.img", "recovery.sig", "recovery", true, false},
-    {"system.img", "system.sig", "system", false, false},
-    {"system_other.img", "system.sig", "system", true, true},
-    {"vendor.img", "vendor.sig", "vendor", true, false},
-    {"vendor_other.img", "vendor.sig", "vendor", true, true},
-    {"vbmeta.img", "vbmeta.sig", "vbmeta", true, false},
+    // clang-format off
+    { "boot",     "boot.img",         "boot.sig",     "boot",     false, false },
+    { nullptr,    "boot_other.img",   "boot.sig",     "boot",     true,  true  },
+    { "dtbo",     "dtbo.img",         "dtbo.sig",     "dtbo",     true,  false },
+    { "recovery", "recovery.img",     "recovery.sig", "recovery", true,  false },
+    { "system",   "system.img",       "system.sig",   "system",   false, false },
+    { nullptr,    "system_other.img", "system.sig",   "system",   true,  true  },
+    { "vbmeta",   "vbmeta.img",       "vbmeta.sig",   "vbmeta",   true,  false },
+    { "vendor",   "vendor.img",       "vendor.sig",   "vendor",   true,  false },
+    { nullptr,    "vendor_other.img", "vendor.sig",   "vendor",   true,  true  },
+    // clang-format on
 };
 
-static std::string find_item_given_name(const char* img_name, const char* product) {
-    if(product) {
-        std::string path = android::base::GetExecutablePath();
-        path.erase(path.find_last_of('/'));
-        return android::base::StringPrintf("%s/../../../target/product/%s/%s",
-                                           path.c_str(), product, img_name);
-    }
-
-    char *dir = getenv("ANDROID_PRODUCT_OUT");
+static std::string find_item_given_name(const char* img_name) {
+    char* dir = getenv("ANDROID_PRODUCT_OUT");
     if (dir == nullptr || dir[0] == '\0') {
-        die("neither -p product specified nor ANDROID_PRODUCT_OUT set");
+        die("ANDROID_PRODUCT_OUT not set");
     }
-
     return android::base::StringPrintf("%s/%s", dir, img_name);
 }
 
-std::string find_item(const char* item, const char* product) {
-    const char *fn;
-
-    if (!strcmp(item,"boot")) {
-        fn = "boot.img";
-    } else if(!strcmp(item,"recovery")) {
-        fn = "recovery.img";
-    } else if(!strcmp(item,"system")) {
-        fn = "system.img";
-    } else if(!strcmp(item,"vendor")) {
-        fn = "vendor.img";
-    } else if(!strcmp(item,"vbmeta")) {
-        fn = "vbmeta.img";
-    } else if(!strcmp(item,"userdata")) {
-        fn = "userdata.img";
-    } else if(!strcmp(item,"cache")) {
-        fn = "cache.img";
-    } else if(!strcmp(item,"info")) {
-        fn = "android-info.txt";
-    } else {
-        fprintf(stderr,"unknown partition '%s'\n", item);
-        return "";
+std::string find_item(const std::string& item) {
+    for (size_t i = 0; i < arraysize(images); ++i) {
+        if (images[i].nickname && item == images[i].nickname) {
+            return find_item_given_name(images[i].img_name);
+        }
     }
 
-    return find_item_given_name(fn, product);
+    if (item == "userdata") return find_item_given_name("userdata.img");
+    if (item == "cache") return find_item_given_name("cache.img");
+
+    fprintf(stderr, "unknown partition '%s'\n", item.c_str());
+    return "";
 }
 
 static int64_t get_file_size(int fd) {
@@ -325,8 +305,21 @@
     usb_open(list_devices_callback);
 }
 
-static void usage() {
-    fprintf(stderr,
+static void syntax_error(const char* fmt, ...) {
+    fprintf(stderr, "fastboot: usage: ");
+
+    va_list ap;
+    va_start(ap, fmt);
+    vfprintf(stderr, fmt, ap);
+    va_end(ap);
+
+    fprintf(stderr, "\n");
+    exit(1);
+}
+
+static int show_help() {
+    // clang-format off
+    fprintf(stdout,
 /*           1234567890123456789012345678901234567890123456789012345678901234567890123456 */
             "usage: fastboot [ <option> ] <command>\n"
             "\n"
@@ -364,7 +357,7 @@
             "  set_active <slot>                        Sets the active slot. If slots are\n"
             "                                           not supported, this does nothing.\n"
             "  boot <kernel> [ <ramdisk> [ <second> ] ] Download and boot kernel.\n"
-            "  flash:raw boot <kernel> [ <ramdisk> [ <second> ] ]\n"
+            "  flash:raw <bootable-partition> <kernel> [ <ramdisk> [ <second> ] ]\n"
             "                                           Create bootimage and flash it.\n"
             "  devices [-l]                             List all connected devices [with\n"
             "                                           device paths].\n"
@@ -390,7 +383,6 @@
             "                                           For ethernet, provide an address in the\n"
             "                                           form <protocol>:<hostname>[:port] where\n"
             "                                           <protocol> is either tcp or udp.\n"
-            "  -p <product>                             Specify product name.\n"
             "  -c <cmdline>                             Override kernel commandline.\n"
             "  -i <vendor id>                           Specify a custom USB vendor id.\n"
             "  -b, --base <base_addr>                   Specify a custom kernel base\n"
@@ -434,31 +426,22 @@
             "  --version                                Display version.\n"
             "  -h, --help                               show this message.\n"
         );
+    // clang-format off
+    return 0;
 }
 
-static void* load_bootable_image(const char* kernel, const char* ramdisk,
-                                 const char* secondstage, int64_t* sz,
+static void* load_bootable_image(const std::string& kernel, const std::string& ramdisk,
+                                 const std::string& second_stage, int64_t* sz,
                                  const char* cmdline) {
-    if (kernel == nullptr) {
-        fprintf(stderr, "no image specified\n");
-        return 0;
-    }
-
     int64_t ksize;
-    void* kdata = load_file(kernel, &ksize);
-    if (kdata == nullptr) {
-        fprintf(stderr, "cannot load '%s': %s\n", kernel, strerror(errno));
-        return 0;
-    }
+    void* kdata = load_file(kernel.c_str(), &ksize);
+    if (kdata == nullptr) die("cannot load '%s': %s\n", kernel.c_str(), strerror(errno));
 
     // Is this actually a boot image?
-    if(!memcmp(kdata, BOOT_MAGIC, BOOT_MAGIC_SIZE)) {
+    if (!memcmp(kdata, BOOT_MAGIC, BOOT_MAGIC_SIZE)) {
         if (cmdline) bootimg_set_cmdline((boot_img_hdr*) kdata, cmdline);
 
-        if (ramdisk) {
-            fprintf(stderr, "cannot boot a boot.img *and* ramdisk\n");
-            return 0;
-        }
+        if (!ramdisk.empty()) die("cannot boot a boot.img *and* ramdisk\n");
 
         *sz = ksize;
         return kdata;
@@ -466,22 +449,16 @@
 
     void* rdata = nullptr;
     int64_t rsize = 0;
-    if (ramdisk) {
-        rdata = load_file(ramdisk, &rsize);
-        if (rdata == nullptr) {
-            fprintf(stderr,"cannot load '%s': %s\n", ramdisk, strerror(errno));
-            return  0;
-        }
+    if (!ramdisk.empty()) {
+        rdata = load_file(ramdisk.c_str(), &rsize);
+        if (rdata == nullptr) die("cannot load '%s': %s\n", ramdisk.c_str(), strerror(errno));
     }
 
     void* sdata = nullptr;
     int64_t ssize = 0;
-    if (secondstage) {
-        sdata = load_file(secondstage, &ssize);
-        if (sdata == nullptr) {
-            fprintf(stderr,"cannot load '%s': %s\n", secondstage, strerror(errno));
-            return  0;
-        }
+    if (!second_stage.empty()) {
+        sdata = load_file(second_stage.c_str(), &ssize);
+        if (sdata == nullptr) die("cannot load '%s': %s\n", second_stage.c_str(), strerror(errno));
     }
 
     fprintf(stderr,"creating boot image...\n");
@@ -490,10 +467,8 @@
                       rdata, rsize, ramdisk_offset,
                       sdata, ssize, second_offset,
                       page_size, base_addr, tags_offset, &bsize);
-    if (bdata == nullptr) {
-        fprintf(stderr,"failed to create boot.img\n");
-        return 0;
-    }
+    if (bdata == nullptr) die("failed to create boot.img\n");
+
     if (cmdline) bootimg_set_cmdline((boot_img_hdr*) bdata, cmdline);
     fprintf(stderr, "creating boot image - %" PRId64 " bytes\n", bsize);
     *sz = bsize;
@@ -626,7 +601,7 @@
     }
 }
 
-static int unzip_to_file(ZipArchiveHandle zip, char* entry_name) {
+static int unzip_to_file(ZipArchiveHandle zip, const char* entry_name) {
     unique_fd fd(make_temporary_fd());
     if (fd == -1) {
         fprintf(stderr, "failed to create temporary file for '%s': %s\n",
@@ -1080,9 +1055,9 @@
     flash_buf(pname, &buf);
 }
 
-static void do_update_signature(ZipArchiveHandle zip, char* fn) {
+static void do_update_signature(ZipArchiveHandle zip, const char* filename) {
     int64_t sz;
-    void* data = unzip_file(zip, fn, &sz);
+    void* data = unzip_file(zip, filename, &sz);
     if (data == nullptr) return;
     fb_queue_download("signature", data, sz);
     fb_queue_command("signature", "installing signature");
@@ -1209,7 +1184,7 @@
 
     fb_queue_query_save("product", cur_product, sizeof(cur_product));
 
-    fname = find_item("info", product);
+    fname = find_item_given_name("android-info.txt");
     if (fname.empty()) die("cannot find android-info.txt");
 
     int64_t sz;
@@ -1241,7 +1216,7 @@
             slot = slot_override.c_str();
         }
         if (!slot) continue;
-        fname = find_item_given_name(images[i].img_name, product);
+        fname = find_item_given_name(images[i].img_name);
         fastboot_buffer buf;
         if (!load_buf(transport, fname.c_str(), &buf)) {
             if (images[i].is_optional) continue;
@@ -1265,41 +1240,35 @@
     }
 }
 
-#define skip(n) do { argc -= (n); argv += (n); } while (0)
-#define require(n) do { if (argc < (n)) {usage(); exit(1);}} while (0)
-
-static int do_bypass_unlock_command(int argc, char **argv)
-{
-    if (argc <= 2) return 0;
-    skip(2);
-
-    /*
-     * Process unlock_bootloader, we have to load the message file
-     * and send that to the remote device.
-     */
-    require(1);
-
-    int64_t sz;
-    void* data = load_file(*argv, &sz);
-    if (data == nullptr) die("could not load '%s': %s", *argv, strerror(errno));
-    fb_queue_download("unlock_message", data, sz);
-    fb_queue_command("flashing unlock_bootloader", "unlocking bootloader");
-    skip(1);
-    return 0;
+static std::string next_arg(std::vector<std::string>* args) {
+    if (args->empty()) syntax_error("expected argument");
+    std::string result = args->front();
+    args->erase(args->begin());
+    return result;
 }
 
-static int do_oem_command(int argc, char** argv) {
-    if (argc <= 1) return 0;
+static void do_bypass_unlock_command(std::vector<std::string>* args) {
+    if (args->empty()) syntax_error("missing unlock_bootloader request");
+
+    std::string filename = next_arg(args);
+
+    int64_t sz;
+    void* data = load_file(filename.c_str(), &sz);
+    if (data == nullptr) die("could not load '%s': %s", filename.c_str(), strerror(errno));
+    fb_queue_download("unlock_message", data, sz);
+    fb_queue_command("flashing unlock_bootloader", "unlocking bootloader");
+}
+
+static void do_oem_command(std::vector<std::string>* args) {
+    if (args->empty()) syntax_error("empty oem command");
 
     std::string command;
-    while (argc > 0) {
-        command += *argv;
-        skip(1);
-        if (argc != 0) command += " ";
+    while (!args->empty()) {
+        if (!command.empty()) command += ' ';
+        command += next_arg(args);
     }
 
     fb_queue_command(command.c_str(), "");
-    return 0;
 }
 
 static int64_t parse_num(const char *arg)
@@ -1375,7 +1344,7 @@
 
 static void fb_perform_format(Transport* transport,
                               const char* partition, int skip_if_not_supported,
-                              const char* type_override, const char* size_override,
+                              const std::string& type_override, const std::string& size_override,
                               const std::string& initial_dir) {
     std::string partition_type, partition_size;
 
@@ -1396,10 +1365,10 @@
         errMsg = "Can't determine partition type.\n";
         goto failed;
     }
-    if (type_override) {
+    if (!type_override.empty()) {
         if (partition_type != type_override) {
             fprintf(stderr, "Warning: %s type is %s, but %s was requested for formatting.\n",
-                    partition, partition_type.c_str(), type_override);
+                    partition, partition_type.c_str(), type_override.c_str());
         }
         partition_type = type_override;
     }
@@ -1408,10 +1377,10 @@
         errMsg = "Unable to get partition size\n";
         goto failed;
     }
-    if (size_override) {
+    if (!size_override.empty()) {
         if (partition_size != size_override) {
             fprintf(stderr, "Warning: %s size is %s, but %s was requested for formatting.\n",
-                    partition, partition_size.c_str(), size_override);
+                    partition, partition_size.c_str(), size_override.c_str());
         }
         partition_size = size_override;
     }
@@ -1508,7 +1477,7 @@
     serial = getenv("ANDROID_SERIAL");
 
     while (1) {
-        int c = getopt_long(argc, argv, "wub:k:n:r:t:s:S:lp:c:i:m:ha::", longopts, &longindex);
+        int c = getopt_long(argc, argv, "wub:k:n:r:t:s:S:lc:i:m:ha::", longopts, &longindex);
         if (c < 0) {
             break;
         }
@@ -1526,8 +1495,7 @@
             cmdline = optarg;
             break;
         case 'h':
-            usage();
-            return 1;
+            return show_help();
         case 'i': {
                 char *endptr = nullptr;
                 unsigned long val;
@@ -1548,9 +1516,6 @@
             page_size = (unsigned)strtoul(optarg, nullptr, 0);
             if (!page_size) die("invalid page size");
             break;
-        case 'p':
-            product = optarg;
-            break;
         case 'r':
             ramdisk_offset = strtoul(optarg, 0, 16);
             break;
@@ -1579,7 +1544,7 @@
                 setvbuf(stdout, nullptr, _IONBF, 0);
                 setvbuf(stderr, nullptr, _IONBF, 0);
             } else if (strcmp("version", longopts[longindex].name) == 0) {
-                fprintf(stdout, "fastboot version %s\n", FASTBOOT_REVISION);
+                fprintf(stdout, "fastboot version %s\n", FASTBOOT_VERSION);
                 fprintf(stdout, "Installed as %s\n", android::base::GetExecutablePath().c_str());
                 return 0;
             } else if (strcmp("slot", longopts[longindex].name) == 0) {
@@ -1607,20 +1572,15 @@
     argc -= optind;
     argv += optind;
 
-    if (argc == 0 && !wants_wipe && !wants_set_active) {
-        usage();
-        return 1;
-    }
+    if (argc == 0 && !wants_wipe && !wants_set_active) syntax_error("no command");
 
     if (argc > 0 && !strcmp(*argv, "devices")) {
-        skip(1);
         list_devices();
         return 0;
     }
 
     if (argc > 0 && !strcmp(*argv, "help")) {
-        usage();
-        return 0;
+        return show_help();
     }
 
     Transport* transport = open_device();
@@ -1649,17 +1609,19 @@
         }
     }
 
-    while (argc > 0) {
-        if (!strcmp(*argv, "getvar")) {
-            require(2);
-            fb_queue_display(argv[1], argv[1]);
-            skip(2);
-        } else if(!strcmp(*argv, "erase")) {
-            require(2);
+    std::vector<std::string> args(argv, argv + argc);
+    while (!args.empty()) {
+        std::string command = next_arg(&args);
 
-            auto erase = [&](const std::string &partition) {
+        if (command == "getvar") {
+            std::string variable = next_arg(&args);
+            fb_queue_display(variable.c_str(), variable.c_str());
+        } else if (command == "erase") {
+            std::string partition = next_arg(&args);
+            auto erase = [&](const std::string& partition) {
                 std::string partition_type;
-                if (fb_getvar(transport, std::string("partition-type:") + argv[1], &partition_type) &&
+                if (fb_getvar(transport, std::string("partition-type:") + partition,
+                              &partition_type) &&
                     fs_get_generator(partition_type) != nullptr) {
                     fprintf(stderr, "******** Did you mean to fastboot format this %s partition?\n",
                             partition_type.c_str());
@@ -1667,106 +1629,79 @@
 
                 fb_queue_erase(partition.c_str());
             };
-            do_for_partitions(transport, argv[1], slot_override, erase, true);
-            skip(2);
-        } else if(!strncmp(*argv, "format", strlen("format"))) {
-            char *overrides;
-            char *type_override = nullptr;
-            char *size_override = nullptr;
-            require(2);
-            /*
-             * Parsing for: "format[:[type][:[size]]]"
-             * Some valid things:
-             *  - select ontly the size, and leave default fs type:
-             *    format::0x4000000 userdata
-             *  - default fs type and size:
-             *    format userdata
-             *    format:: userdata
-             */
-            overrides = strchr(*argv, ':');
-            if (overrides) {
-                overrides++;
-                size_override = strchr(overrides, ':');
-                if (size_override) {
-                    size_override[0] = '\0';
-                    size_override++;
-                }
-                type_override = overrides;
-            }
-            if (type_override && !type_override[0]) type_override = nullptr;
-            if (size_override && !size_override[0]) size_override = nullptr;
+            do_for_partitions(transport, partition, slot_override, erase, true);
+        } else if (android::base::StartsWith(command, "format")) {
+            // Parsing for: "format[:[type][:[size]]]"
+            // Some valid things:
+            //  - select only the size, and leave default fs type:
+            //    format::0x4000000 userdata
+            //  - default fs type and size:
+            //    format userdata
+            //    format:: userdata
+            std::vector<std::string> pieces = android::base::Split(command, ":");
+            std::string type_override;
+            if (pieces.size() > 1) type_override = pieces[1].c_str();
+            std::string size_override;
+            if (pieces.size() > 2) size_override = pieces[2].c_str();
 
-            auto format = [&](const std::string &partition) {
+            std::string partition = next_arg(&args);
+
+            auto format = [&](const std::string& partition) {
                 if (erase_first && needs_erase(transport, partition.c_str())) {
                     fb_queue_erase(partition.c_str());
                 }
-                fb_perform_format(transport, partition.c_str(), 0,
-                    type_override, size_override, "");
+                fb_perform_format(transport, partition.c_str(), 0, type_override, size_override,
+                                  "");
             };
-            do_for_partitions(transport, argv[1], slot_override, format, true);
-            skip(2);
-        } else if(!strcmp(*argv, "signature")) {
-            require(2);
-            data = load_file(argv[1], &sz);
-            if (data == nullptr) die("could not load '%s': %s", argv[1], strerror(errno));
+            do_for_partitions(transport, partition.c_str(), slot_override, format, true);
+        } else if (command == "signature") {
+            std::string filename = next_arg(&args);
+            data = load_file(filename.c_str(), &sz);
+            if (data == nullptr) die("could not load '%s': %s", filename.c_str(), strerror(errno));
             if (sz != 256) die("signature must be 256 bytes");
             fb_queue_download("signature", data, sz);
             fb_queue_command("signature", "installing signature");
-            skip(2);
-        } else if(!strcmp(*argv, "reboot")) {
+        } else if (command == "reboot") {
             wants_reboot = true;
-            skip(1);
-            if (argc > 0) {
-                if (!strcmp(*argv, "bootloader")) {
+
+            if (args.size() == 1) {
+                std::string what = next_arg(&args);
+                if (what == "bootloader") {
                     wants_reboot = false;
                     wants_reboot_bootloader = true;
-                    skip(1);
-                } else if (!strcmp(*argv, "emergency")) {
+                } else if (what == "emergency") {
                     wants_reboot = false;
                     wants_reboot_emergency = true;
-                    skip(1);
+                } else {
+                    syntax_error("unknown reboot target %s", what.c_str());
                 }
+
             }
-            require(0);
-        } else if(!strcmp(*argv, "reboot-bootloader")) {
+            if (!args.empty()) syntax_error("junk after reboot command");
+        } else if (command == "reboot-bootloader") {
             wants_reboot_bootloader = true;
-            skip(1);
-        } else if (!strcmp(*argv, "continue")) {
+        } else if (command == "continue") {
             fb_queue_command("continue", "resuming boot");
-            skip(1);
-        } else if(!strcmp(*argv, "boot")) {
-            char *kname = 0;
-            char *rname = 0;
-            char *sname = 0;
-            skip(1);
-            if (argc > 0) {
-                kname = argv[0];
-                skip(1);
-            }
-            if (argc > 0) {
-                rname = argv[0];
-                skip(1);
-            }
-            if (argc > 0) {
-                sname = argv[0];
-                skip(1);
-            }
-            data = load_bootable_image(kname, rname, sname, &sz, cmdline);
-            if (data == 0) return 1;
+        } else if (command == "boot") {
+            std::string kernel = next_arg(&args);
+            std::string ramdisk;
+            if (!args.empty()) ramdisk = next_arg(&args);
+            std::string second_stage;
+            if (!args.empty()) second_stage = next_arg(&args);
+
+            data = load_bootable_image(kernel, ramdisk, second_stage, &sz, cmdline);
             fb_queue_download("boot.img", data, sz);
             fb_queue_command("boot", "booting");
-        } else if(!strcmp(*argv, "flash")) {
-            char* pname = argv[1];
+        } else if (command == "flash") {
+            std::string pname = next_arg(&args);
+
             std::string fname;
-            require(2);
-            if (argc > 2) {
-                fname = argv[2];
-                skip(3);
+            if (!args.empty()) {
+                fname = next_arg(&args);
             } else {
-                fname = find_item(pname, product);
-                skip(2);
+                fname = find_item(pname);
             }
-            if (fname.empty()) die("cannot determine image filename for '%s'", pname);
+            if (fname.empty()) die("cannot determine image filename for '%s'", pname.c_str());
 
             auto flash = [&](const std::string &partition) {
                 if (erase_first && needs_erase(transport, partition.c_str())) {
@@ -1774,29 +1709,21 @@
                 }
                 do_flash(transport, partition.c_str(), fname.c_str());
             };
-            do_for_partitions(transport, pname, slot_override, flash, true);
-        } else if(!strcmp(*argv, "flash:raw")) {
-            char *kname = argv[2];
-            char *rname = 0;
-            char *sname = 0;
-            require(3);
-            skip(3);
-            if (argc > 0) {
-                rname = argv[0];
-                skip(1);
-            }
-            if (argc > 0) {
-                sname = argv[0];
-                skip(1);
-            }
-            data = load_bootable_image(kname, rname, sname, &sz, cmdline);
-            if (data == 0) die("cannot load bootable image");
-            auto flashraw = [&](const std::string &partition) {
+            do_for_partitions(transport, pname.c_str(), slot_override, flash, true);
+        } else if (command == "flash:raw") {
+            std::string partition = next_arg(&args);
+            std::string kernel = next_arg(&args);
+            std::string ramdisk;
+            if (!args.empty()) ramdisk = next_arg(&args);
+            std::string second_stage;
+            if (!args.empty()) second_stage = next_arg(&args);
+
+            data = load_bootable_image(kernel, ramdisk, second_stage, &sz, cmdline);
+            auto flashraw = [&](const std::string& partition) {
                 fb_queue_flash(partition.c_str(), data, sz);
             };
-            do_for_partitions(transport, argv[1], slot_override, flashraw, true);
-        } else if(!strcmp(*argv, "flashall")) {
-            skip(1);
+            do_for_partitions(transport, partition, slot_override, flashraw, true);
+        } else if (command == "flashall") {
             if (slot_override == "all") {
                 fprintf(stderr, "Warning: slot set to 'all'. Secondary slots will not be flashed.\n");
                 do_flashall(transport, slot_override, erase_first, true);
@@ -1804,22 +1731,21 @@
                 do_flashall(transport, slot_override, erase_first, skip_secondary);
             }
             wants_reboot = true;
-        } else if(!strcmp(*argv, "update")) {
+        } else if (command == "update") {
             bool slot_all = (slot_override == "all");
             if (slot_all) {
                 fprintf(stderr, "Warning: slot set to 'all'. Secondary slots will not be flashed.\n");
             }
-            if (argc > 1) {
-                do_update(transport, argv[1], slot_override, erase_first, skip_secondary || slot_all);
-                skip(2);
-            } else {
-                do_update(transport, "update.zip", slot_override, erase_first, skip_secondary || slot_all);
-                skip(1);
+            std::string filename = "update.zip";
+            if (!args.empty()) {
+                filename = next_arg(&args);
             }
+            do_update(transport, filename.c_str(), slot_override, erase_first,
+                      skip_secondary || slot_all);
             wants_reboot = true;
-        } else if(!strcmp(*argv, "set_active")) {
-            require(2);
-            std::string slot = verify_slot(transport, std::string(argv[1]), false);
+        } else if (command == "set_active") {
+            std::string slot = verify_slot(transport, next_arg(&args), false);
+
             // Legacy support: verify_slot() removes leading underscores, we need to put them back
             // in for old bootloaders. Legacy bootloaders do not have the slot-count variable but
             // do have slot-suffixes.
@@ -1829,42 +1755,36 @@
                 slot = "_" + slot;
             }
             fb_set_active(slot.c_str());
-            skip(2);
-        } else if(!strcmp(*argv, "stage")) {
-            require(2);
-            std::string infile(argv[1]);
-            skip(2);
+        } else if (command == "stage") {
+            std::string filename = next_arg(&args);
+
             struct fastboot_buffer buf;
-            if (!load_buf(transport, infile.c_str(), &buf) || buf.type != FB_BUFFER_FD) {
-                die("cannot load '%s'", infile.c_str());
+            if (!load_buf(transport, filename.c_str(), &buf) || buf.type != FB_BUFFER_FD) {
+                die("cannot load '%s'", filename.c_str());
             }
-            fb_queue_download_fd(infile.c_str(), buf.fd, buf.sz);
-        } else if(!strcmp(*argv, "get_staged")) {
-            require(2);
-            char *outfile = argv[1];
-            skip(2);
-            fb_queue_upload(outfile);
-        } else if(!strcmp(*argv, "oem")) {
-            argc = do_oem_command(argc, argv);
-        } else if(!strcmp(*argv, "flashing")) {
-            if (argc == 2 && (!strcmp(*(argv+1), "unlock") ||
-                              !strcmp(*(argv+1), "lock") ||
-                              !strcmp(*(argv+1), "unlock_critical") ||
-                              !strcmp(*(argv+1), "lock_critical") ||
-                              !strcmp(*(argv+1), "get_unlock_ability") ||
-                              !strcmp(*(argv+1), "get_unlock_bootloader_nonce") ||
-                              !strcmp(*(argv+1), "lock_bootloader"))) {
-                argc = do_oem_command(argc, argv);
-            } else
-            if (argc == 3 && !strcmp(*(argv+1), "unlock_bootloader")) {
-                argc = do_bypass_unlock_command(argc, argv);
+            fb_queue_download_fd(filename.c_str(), buf.fd, buf.sz);
+        } else if (command == "get_staged") {
+            std::string filename = next_arg(&args);
+            fb_queue_upload(filename.c_str());
+        } else if (command == "oem") {
+            do_oem_command(&args);
+        } else if (command == "flashing") {
+            if (args.empty()) {
+                syntax_error("missing 'flashing' command");
+            } else if (args.size() == 1 && (args[0] == "unlock" || args[0] == "lock" ||
+                                            args[0] == "unlock_critical" ||
+                                            args[0] == "lock_critical" ||
+                                            args[0] == "get_unlock_ability" ||
+                                            args[0] == "get_unlock_bootloader_nonce" ||
+                                            args[0] == "lock_bootloader")) {
+                do_oem_command(&args);
+            } else if (args.size() == 2 && args[0] == "unlock_bootloader") {
+                do_bypass_unlock_command(&args);
             } else {
-              usage();
-              return 1;
+                syntax_error("unknown 'flashing' command %s", args[0].c_str());
             }
         } else {
-            usage();
-            return 1;
+            syntax_error("unknown command %s", command.c_str());
         }
     }
 
@@ -1877,17 +1797,17 @@
             if (initial_userdata_dir.empty()) {
                 return 1;
             }
-            fb_perform_format(transport, "userdata", 1, nullptr, nullptr, initial_userdata_dir);
+            fb_perform_format(transport, "userdata", 1, "", "", initial_userdata_dir);
             delete_fbemarker_tmpdir(initial_userdata_dir);
         } else {
-            fb_perform_format(transport, "userdata", 1, nullptr, nullptr, "");
+            fb_perform_format(transport, "userdata", 1, "", "", "");
         }
 
         std::string cache_type;
         if (fb_getvar(transport, "partition-type:cache", &cache_type) && !cache_type.empty()) {
             fprintf(stderr, "wiping cache...\n");
             fb_queue_erase("cache");
-            fb_perform_format(transport, "cache", 1, nullptr, nullptr, "");
+            fb_perform_format(transport, "cache", 1, "", "", "");
         }
     }
     if (wants_set_active) {
diff --git a/fastboot/fastboot.h b/fastboot/fastboot.h
index e30c6de..e3c60ae 100644
--- a/fastboot/fastboot.h
+++ b/fastboot/fastboot.h
@@ -66,7 +66,7 @@
 void fb_queue_command(const char *cmd, const char *msg);
 void fb_queue_download(const char *name, void *data, uint32_t size);
 void fb_queue_download_fd(const char *name, int fd, uint32_t sz);
-void fb_queue_upload(char *outfile);
+void fb_queue_upload(const char* outfile);
 void fb_queue_notice(const char *notice);
 void fb_queue_wait_for_disconnect(void);
 int64_t fb_execute_queue(Transport* transport);
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 6e9069e..df3c300 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -744,23 +744,6 @@
     }
 }
 
-static std::string extract_by_name_prefix(struct fstab* fstab) {
-    // We assume that there's an entry for the /misc mount point in the
-    // fstab file and use that to get the device file by-name prefix.
-    // The device needs not to have an actual /misc partition.
-    // e.g.,
-    //    - /dev/block/platform/soc.0/7824900.sdhci/by-name/misc ->
-    //    - /dev/block/platform/soc.0/7824900.sdhci/by-name/
-    struct fstab_rec* fstab_entry = fs_mgr_get_entry_for_mount_point(fstab, "/misc");
-    if (fstab_entry == nullptr) {
-        LERROR << "/misc mount point not found in fstab";
-        return "";
-    }
-    std::string full_path(fstab_entry->blk_device);
-    size_t end_slash = full_path.find_last_of("/");
-    return full_path.substr(0, end_slash + 1);
-}
-
 // TODO: add ueventd notifiers if they don't exist.
 // This is just doing a wait_for_device for maximum of 1s
 int fs_mgr_test_access(const char *device) {
@@ -850,7 +833,7 @@
 
         if (fstab->recs[i].fs_mgr_flags & MF_AVB) {
             if (!avb_handle) {
-                avb_handle = FsManagerAvbHandle::Open(extract_by_name_prefix(fstab));
+                avb_handle = FsManagerAvbHandle::Open(*fstab);
                 if (!avb_handle) {
                     LERROR << "Failed to open FsManagerAvbHandle";
                     return FS_MGR_MNTALL_FAIL;
@@ -1046,8 +1029,7 @@
         }
 
         int fs_stat = 0;
-        int force_check = do_quota_with_shutdown_check(fstab->recs[i].blk_device,
-                                                       fstab->recs[i].fs_type,
+        int force_check = do_quota_with_shutdown_check(n_blk_device, fstab->recs[i].fs_type,
                                                        &fstab->recs[i], &fs_stat);
 
         if ((fstab->recs[i].fs_mgr_flags & MF_CHECK) || force_check) {
@@ -1061,7 +1043,7 @@
 
         if (fstab->recs[i].fs_mgr_flags & MF_AVB) {
             if (!avb_handle) {
-                avb_handle = FsManagerAvbHandle::Open(extract_by_name_prefix(fstab));
+                avb_handle = FsManagerAvbHandle::Open(*fstab);
                 if (!avb_handle) {
                     LERROR << "Failed to open FsManagerAvbHandle";
                     return FS_MGR_DOMNT_FAILED;
diff --git a/fs_mgr/fs_mgr_avb.cpp b/fs_mgr/fs_mgr_avb.cpp
index 94cea57..c21139e 100644
--- a/fs_mgr/fs_mgr_avb.cpp
+++ b/fs_mgr/fs_mgr_avb.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include "fs_mgr_avb.h"
+
 #include <errno.h>
 #include <fcntl.h>
 #include <inttypes.h>
@@ -38,9 +40,8 @@
 #include <utils/Compat.h>
 
 #include "fs_mgr.h"
-#include "fs_mgr_avb.h"
-#include "fs_mgr_avb_ops.h"
 #include "fs_mgr_priv.h"
+#include "fs_mgr_priv_avb_ops.h"
 #include "fs_mgr_priv_dm_ioctl.h"
 #include "fs_mgr_priv_sha.h"
 
@@ -457,12 +458,21 @@
     return true;
 }
 
-FsManagerAvbUniquePtr FsManagerAvbHandle::Open(const std::string& device_file_by_name_prefix) {
-    if (device_file_by_name_prefix.empty()) {
-        LERROR << "Missing device file by-name prefix";
+FsManagerAvbUniquePtr FsManagerAvbHandle::Open(const fstab& fstab) {
+    FsManagerAvbOps avb_ops(fstab);
+    return DoOpen(&avb_ops);
+}
+
+FsManagerAvbUniquePtr FsManagerAvbHandle::Open(ByNameSymlinkMap&& by_name_symlink_map) {
+    if (by_name_symlink_map.empty()) {
+        LERROR << "Empty by_name_symlink_map when opening FsManagerAvbHandle";
         return nullptr;
     }
+    FsManagerAvbOps avb_ops(std::move(by_name_symlink_map));
+    return DoOpen(&avb_ops);
+}
 
+FsManagerAvbUniquePtr FsManagerAvbHandle::DoOpen(FsManagerAvbOps* avb_ops) {
     // Gets the expected hash value of vbmeta images from kernel cmdline.
     std::unique_ptr<FsManagerAvbVerifier> avb_verifier = FsManagerAvbVerifier::Create();
     if (!avb_verifier) {
@@ -476,20 +486,40 @@
         return nullptr;
     }
 
-    FsManagerAvbOps avb_ops(device_file_by_name_prefix);
-    AvbSlotVerifyResult verify_result = avb_ops.AvbSlotVerify(
+    AvbSlotVerifyResult verify_result = avb_ops->AvbSlotVerify(
         fs_mgr_get_slot_suffix(), avb_verifier->IsDeviceUnlocked(), &avb_handle->avb_slot_data_);
 
     // Only allow two verify results:
     //   - AVB_SLOT_VERIFY_RESULT_OK.
     //   - AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION (for UNLOCKED state).
-    if (verify_result == AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION) {
-        if (!avb_verifier->IsDeviceUnlocked()) {
-            LERROR << "ERROR_VERIFICATION isn't allowed";
+    //     If the device is UNLOCKED, i.e., |allow_verification_error| is true for
+    //     AvbSlotVerify(), then the following return values are all non-fatal:
+    //       * AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION
+    //       * AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED
+    //       * AVB_SLOT_VERIFY_RESULT_ERROR_ROLLBACK_INDEX
+    //     The latter two results were checked by bootloader prior to start fs_mgr so
+    //     we just need to handle the first result here. See *dummy* operations in
+    //     FsManagerAvbOps and the comments in external/avb/libavb/avb_slot_verify.h
+    //     for more details.
+    switch (verify_result) {
+        case AVB_SLOT_VERIFY_RESULT_OK:
+            avb_handle->status_ = kFsManagerAvbHandleSuccess;
+            break;
+        case AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION:
+            if (!avb_verifier->IsDeviceUnlocked()) {
+                LERROR << "ERROR_VERIFICATION isn't allowed when the device is LOCKED";
+                return nullptr;
+            }
+            avb_handle->status_ = kFsManagerAvbHandleErrorVerification;
+            break;
+        default:
+            LERROR << "avb_slot_verify failed, result: " << verify_result;
             return nullptr;
-        }
-    } else if (verify_result != AVB_SLOT_VERIFY_RESULT_OK) {
-        LERROR << "avb_slot_verify failed, result: " << verify_result;
+    }
+
+    // Verifies vbmeta images against the digest passed from bootloader.
+    if (!avb_verifier->VerifyVbmetaImages(*avb_handle->avb_slot_data_)) {
+        LERROR << "VerifyVbmetaImages failed";
         return nullptr;
     }
 
@@ -497,30 +527,20 @@
     avb_handle->avb_version_ =
         android::base::StringPrintf("%d.%d", AVB_VERSION_MAJOR, AVB_VERSION_MINOR);
 
-    // Verifies vbmeta images against the digest passed from bootloader.
-    if (!avb_verifier->VerifyVbmetaImages(*avb_handle->avb_slot_data_)) {
-        LERROR << "VerifyVbmetaImages failed";
-        return nullptr;
-    } else {
-        // Checks whether FLAGS_HASHTREE_DISABLED is set.
-        AvbVBMetaImageHeader vbmeta_header;
-        avb_vbmeta_image_header_to_host_byte_order(
-            (AvbVBMetaImageHeader*)avb_handle->avb_slot_data_->vbmeta_images[0].vbmeta_data,
-            &vbmeta_header);
+    // Checks whether FLAGS_HASHTREE_DISABLED is set.
+    AvbVBMetaImageHeader vbmeta_header;
+    avb_vbmeta_image_header_to_host_byte_order(
+        (AvbVBMetaImageHeader*)avb_handle->avb_slot_data_->vbmeta_images[0].vbmeta_data,
+        &vbmeta_header);
 
-        bool hashtree_disabled =
-            ((AvbVBMetaImageFlags)vbmeta_header.flags & AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED);
-        if (hashtree_disabled) {
-            avb_handle->status_ = kFsManagerAvbHandleHashtreeDisabled;
-            return avb_handle;
-        }
+    bool hashtree_disabled =
+        ((AvbVBMetaImageFlags)vbmeta_header.flags & AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED);
+    if (hashtree_disabled) {
+        avb_handle->status_ = kFsManagerAvbHandleHashtreeDisabled;
     }
 
-    if (verify_result == AVB_SLOT_VERIFY_RESULT_OK) {
-        avb_handle->status_ = kFsManagerAvbHandleSuccess;
-        return avb_handle;
-    }
-    return nullptr;
+    LINFO << "Returning avb_handle with status: " << avb_handle->status_;
+    return avb_handle;
 }
 
 bool FsManagerAvbHandle::SetUpAvb(struct fstab_rec* fstab_entry, bool wait_for_verity_dev) {
@@ -528,11 +548,12 @@
     if (!avb_slot_data_ || avb_slot_data_->num_vbmeta_images < 1) {
         return false;
     }
+
+    if (status_ == kFsManagerAvbHandleUninitialized) return false;
     if (status_ == kFsManagerAvbHandleHashtreeDisabled) {
         LINFO << "AVB HASHTREE disabled on:" << fstab_entry->mount_point;
         return true;
     }
-    if (status_ != kFsManagerAvbHandleSuccess) return false;
 
     std::string partition_name(basename(fstab_entry->mount_point));
     if (!avb_validate_utf8((const uint8_t*)partition_name.c_str(), partition_name.length())) {
diff --git a/fs_mgr/fs_mgr_avb_ops.cpp b/fs_mgr/fs_mgr_avb_ops.cpp
index caee4ec..8feeb53 100644
--- a/fs_mgr/fs_mgr_avb_ops.cpp
+++ b/fs_mgr/fs_mgr_avb_ops.cpp
@@ -22,6 +22,8 @@
  * SOFTWARE.
  */
 
+#include "fs_mgr_priv_avb_ops.h"
+
 #include <errno.h>
 #include <fcntl.h>
 #include <stdlib.h>
@@ -36,7 +38,6 @@
 #include <utils/Compat.h>
 
 #include "fs_mgr.h"
-#include "fs_mgr_avb_ops.h"
 #include "fs_mgr_priv.h"
 
 static AvbIOResult read_from_partition(AvbOps* ops, const char* partition, int64_t offset,
@@ -88,11 +89,7 @@
     return AVB_IO_RESULT_OK;
 }
 
-FsManagerAvbOps::FsManagerAvbOps(const std::string& device_file_by_name_prefix)
-    : device_file_by_name_prefix_(device_file_by_name_prefix) {
-    if (device_file_by_name_prefix_.back() != '/') {
-        device_file_by_name_prefix_ += '/';
-    }
+void FsManagerAvbOps::InitializeAvbOps() {
     // We only need to provide the implementation of read_from_partition()
     // operation since that's all what is being used by the avb_slot_verify().
     // Other I/O operations are only required in bootloader but not in
@@ -109,13 +106,31 @@
     avb_ops_.user_data = this;
 }
 
+FsManagerAvbOps::FsManagerAvbOps(std::map<std::string, std::string>&& by_name_symlink_map)
+    : by_name_symlink_map_(std::move(by_name_symlink_map)) {
+    InitializeAvbOps();
+}
+
+FsManagerAvbOps::FsManagerAvbOps(const fstab& fstab) {
+    // Constructs the by-name symlink map for each fstab record.
+    // /dev/block/platform/soc.0/7824900.sdhci/by-name/system_a =>
+    // by_name_symlink_map_["system_a"] = "/dev/block/platform/soc.0/7824900.sdhci/by-name/system_a"
+    for (int i = 0; i < fstab.num_entries; i++) {
+        std::string partition_name = basename(fstab.recs[i].blk_device);
+        by_name_symlink_map_[partition_name] = fstab.recs[i].blk_device;
+    }
+    InitializeAvbOps();
+}
+
 AvbIOResult FsManagerAvbOps::ReadFromPartition(const char* partition, int64_t offset,
                                                size_t num_bytes, void* buffer,
                                                size_t* out_num_read) {
-    // Appends |partition| to the device_file_by_name_prefix_, e.g.,
-    //    - /dev/block/platform/soc.0/7824900.sdhci/by-name/ ->
-    //    - /dev/block/platform/soc.0/7824900.sdhci/by-name/system_a
-    std::string path = device_file_by_name_prefix_ + partition;
+    const auto iter = by_name_symlink_map_.find(partition);
+    if (iter == by_name_symlink_map_.end()) {
+        LERROR << "by-name symlink not found for partition: '" << partition << "'";
+        return AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION;
+    }
+    std::string path = iter->second;
 
     // Ensures the device path (a symlink created by init) is ready to
     // access. fs_mgr_test_access() will test a few iterations if the
@@ -126,7 +141,7 @@
 
     android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC)));
     if (fd < 0) {
-        PERROR << "Failed to open " << path.c_str();
+        PERROR << "Failed to open " << path;
         return AVB_IO_RESULT_ERROR_IO;
     }
 
@@ -150,8 +165,7 @@
     // for EOF).
     ssize_t num_read = TEMP_FAILURE_RETRY(pread64(fd, buffer, num_bytes, offset));
     if (num_read < 0 || (size_t)num_read != num_bytes) {
-        PERROR << "Failed to read " << num_bytes << " bytes from " << path.c_str() << " offset "
-               << offset;
+        PERROR << "Failed to read " << num_bytes << " bytes from " << path << " offset " << offset;
         return AVB_IO_RESULT_ERROR_IO;
     }
 
diff --git a/fs_mgr/fs_mgr_avb_ops.h b/fs_mgr/fs_mgr_priv_avb_ops.h
similarity index 87%
rename from fs_mgr/fs_mgr_avb_ops.h
rename to fs_mgr/fs_mgr_priv_avb_ops.h
index ec4a8c9..a6b52e4 100644
--- a/fs_mgr/fs_mgr_avb_ops.h
+++ b/fs_mgr/fs_mgr_priv_avb_ops.h
@@ -22,8 +22,11 @@
  * SOFTWARE.
  */
 
-#ifndef __CORE_FS_MGR_AVB_OPS_H
-#define __CORE_FS_MGR_AVB_OPS_H
+#ifndef __CORE_FS_MGR_PRIV_AVB_OPS_H
+#define __CORE_FS_MGR_PRIV_AVB_OPS_H
+
+#include <map>
+#include <string>
 
 #include <libavb/libavb.h>
 
@@ -43,7 +46,8 @@
 //
 class FsManagerAvbOps {
   public:
-    FsManagerAvbOps(const std::string& device_file_by_name_prefix);
+    FsManagerAvbOps(const fstab& fstab);
+    FsManagerAvbOps(std::map<std::string, std::string>&& by_name_symlink_map);
 
     static FsManagerAvbOps* GetInstanceFromAvbOps(AvbOps* ops) {
         return reinterpret_cast<FsManagerAvbOps*>(ops->user_data);
@@ -56,7 +60,9 @@
                                       AvbSlotVerifyData** out_data);
 
   private:
+    void InitializeAvbOps();
+
     AvbOps avb_ops_;
-    std::string device_file_by_name_prefix_;
+    std::map<std::string, std::string> by_name_symlink_map_;
 };
-#endif /* __CORE_FS_MGR_AVB_OPS_H */
+#endif /* __CORE_FS_MGR_PRIV_AVB_OPS_H */
diff --git a/fs_mgr/fs_mgr_verity.cpp b/fs_mgr/fs_mgr_verity.cpp
index 0bf173b..5fa10bc 100644
--- a/fs_mgr/fs_mgr_verity.cpp
+++ b/fs_mgr/fs_mgr_verity.cpp
@@ -663,7 +663,7 @@
     /* use the kernel parameter if set */
     std::string veritymode;
     if (fs_mgr_get_boot_config("veritymode", &veritymode)) {
-        if (veritymode.compare("enforcing")) {
+        if (veritymode == "enforcing") {
             *mode = VERITY_MODE_DEFAULT;
         }
         return 0;
diff --git a/fs_mgr/include/fs_mgr_avb.h b/fs_mgr/include/fs_mgr_avb.h
index a66ff42..bbafe1a 100644
--- a/fs_mgr/include/fs_mgr_avb.h
+++ b/fs_mgr/include/fs_mgr_avb.h
@@ -17,6 +17,7 @@
 #ifndef __CORE_FS_MGR_AVB_H
 #define __CORE_FS_MGR_AVB_H
 
+#include <map>
 #include <memory>
 #include <string>
 
@@ -25,14 +26,19 @@
 #include "fs_mgr.h"
 
 enum FsManagerAvbHandleStatus {
+    kFsManagerAvbHandleUninitialized = -1,
     kFsManagerAvbHandleSuccess = 0,
     kFsManagerAvbHandleHashtreeDisabled = 1,
-    kFsManagerAvbHandleFail = 2,
+    kFsManagerAvbHandleErrorVerification = 2,
 };
 
+class FsManagerAvbOps;
+
 class FsManagerAvbHandle;
 using FsManagerAvbUniquePtr = std::unique_ptr<FsManagerAvbHandle>;
 
+using ByNameSymlinkMap = std::map<std::string, std::string>;
+
 // Provides a factory method to return a unique_ptr pointing to itself and the
 // SetUpAvb() function to extract dm-verity parameters from AVB metadata to
 // load verity table into kernel through ioctl.
@@ -48,6 +54,13 @@
     // A typical usage will be:
     //   - FsManagerAvbUniquePtr handle = FsManagerAvbHandle::Open();
     //
+    // There are two overloaded Open() functions with a single parameter.
+    // The argument can be a ByNameSymlinkMap describing the mapping from partition
+    // name to by-name symlink, or a fstab file to which the ByNameSymlinkMap is
+    // constructed from. e.g.,
+    //   - /dev/block/platform/soc.0/7824900.sdhci/by-name/system_a ->
+    //   - ByNameSymlinkMap["system_a"] = "/dev/block/platform/soc.0/7824900.sdhci/by-name/system_a"
+    //
     // Possible return values:
     //   - nullptr: any error when reading and verifying the metadata,
     //     e.g., I/O error, digest value mismatch, size mismatch, etc.
@@ -60,7 +73,8 @@
     //   - a valid unique_ptr with status kFsMgrAvbHandleSuccess: the metadata
     //     is verified and can be trusted.
     //
-    static FsManagerAvbUniquePtr Open(const std::string& device_file_by_name_prefix);
+    static FsManagerAvbUniquePtr Open(const fstab& fstab);
+    static FsManagerAvbUniquePtr Open(ByNameSymlinkMap&& by_name_symlink_map);
 
     // Sets up dm-verity on the given fstab entry.
     // The 'wait_for_verity_dev' parameter makes this function wait for the
@@ -87,10 +101,10 @@
         }
     };
 
-  protected:
-    FsManagerAvbHandle() : avb_slot_data_(nullptr), status_(kFsManagerAvbHandleFail) {}
-
   private:
+    FsManagerAvbHandle() : avb_slot_data_(nullptr), status_(kFsManagerAvbHandleUninitialized) {}
+    static FsManagerAvbUniquePtr DoOpen(FsManagerAvbOps* avb_ops);
+
     AvbSlotVerifyData* avb_slot_data_;
     FsManagerAvbHandleStatus status_;
     std::string avb_version_;
diff --git a/init/README.md b/init/README.md
index 9fc8d47..72b6c6b 100644
--- a/init/README.md
+++ b/init/README.md
@@ -356,9 +356,9 @@
   Init halts executing commands until the forked process exits.
 
 `exec_start <service>`
-> Start service a given service and halt processing of additional init commands
-  until it returns.  It functions similarly to the `exec` command, but uses an
-  existing service definition instead of providing them as arguments.
+> Start a given service and halt the processing of additional init commands
+  until it returns.  The command functions similarly to the `exec` command,
+  but uses an existing service definition in place of the exec argument vector.
 
 `export <name> <value>`
 > Set the environment variable _name_ equal to _value_ in the
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 1d98ef1..1eacb36 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -150,7 +150,12 @@
 }
 
 static int do_domainname(const std::vector<std::string>& args) {
-    return write_file("/proc/sys/kernel/domainname", args[1]) ? 0 : 1;
+    std::string err;
+    if (!WriteFile("/proc/sys/kernel/domainname", args[1], &err)) {
+        LOG(ERROR) << err;
+        return -1;
+    }
+    return 0;
 }
 
 static int do_enable(const std::vector<std::string>& args) {
@@ -174,7 +179,12 @@
 }
 
 static int do_hostname(const std::vector<std::string>& args) {
-    return write_file("/proc/sys/kernel/hostname", args[1]) ? 0 : 1;
+    std::string err;
+    if (!WriteFile("/proc/sys/kernel/hostname", args[1], &err)) {
+        LOG(ERROR) << err;
+        return -1;
+    }
+    return 0;
 }
 
 static int do_ifup(const std::vector<std::string>& args) {
@@ -205,7 +215,7 @@
         mode = std::strtoul(args[2].c_str(), 0, 8);
     }
 
-    ret = make_dir(args[1].c_str(), mode);
+    ret = make_dir(args[1].c_str(), mode, sehandle);
     /* chmod in case the directory already exists */
     if (ret == -1 && errno == EEXIST) {
         ret = fchmodat(AT_FDCWD, args[1].c_str(), mode, AT_SYMLINK_NOFOLLOW);
@@ -215,11 +225,19 @@
     }
 
     if (args.size() >= 4) {
-        uid_t uid = decode_uid(args[3].c_str());
+        uid_t uid;
+        std::string decode_uid_err;
+        if (!DecodeUid(args[3], &uid, &decode_uid_err)) {
+            LOG(ERROR) << "Unable to find UID for '" << args[3] << "': " << decode_uid_err;
+            return -1;
+        }
         gid_t gid = -1;
 
         if (args.size() == 5) {
-            gid = decode_uid(args[4].c_str());
+            if (!DecodeUid(args[4], &gid, &decode_uid_err)) {
+                LOG(ERROR) << "Unable to find GID for '" << args[3] << "': " << decode_uid_err;
+                return -1;
+            }
         }
 
         if (lchown(args[1].c_str(), uid, gid) == -1) {
@@ -643,29 +661,49 @@
 }
 
 static int do_write(const std::vector<std::string>& args) {
-    return write_file(args[1], args[2]) ? 0 : 1;
+    std::string err;
+    if (!WriteFile(args[1], args[2], &err)) {
+        LOG(ERROR) << err;
+        return -1;
+    }
+    return 0;
 }
 
 static int do_copy(const std::vector<std::string>& args) {
     std::string data;
-    if (read_file(args[1], &data)) {
-        return write_file(args[2], data) ? 0 : 1;
+    std::string err;
+    if (!ReadFile(args[1], &data, &err)) {
+        LOG(ERROR) << err;
+        return -1;
     }
-    return 1;
+    if (!WriteFile(args[2], data, &err)) {
+        LOG(ERROR) << err;
+        return -1;
+    }
+    return 0;
 }
 
 static int do_chown(const std::vector<std::string>& args) {
-    /* GID is optional. */
-    if (args.size() == 3) {
-        if (lchown(args[2].c_str(), decode_uid(args[1].c_str()), -1) == -1)
-            return -errno;
-    } else if (args.size() == 4) {
-        if (lchown(args[3].c_str(), decode_uid(args[1].c_str()),
-                   decode_uid(args[2].c_str())) == -1)
-            return -errno;
-    } else {
+    uid_t uid;
+    std::string decode_uid_err;
+    if (!DecodeUid(args[1], &uid, &decode_uid_err)) {
+        LOG(ERROR) << "Unable to find UID for '" << args[1] << "': " << decode_uid_err;
         return -1;
     }
+
+    // GID is optional and pushes the index of path out by one if specified.
+    const std::string& path = (args.size() == 4) ? args[3] : args[2];
+    gid_t gid = -1;
+
+    if (args.size() == 4) {
+        if (!DecodeUid(args[2], &gid, &decode_uid_err)) {
+            LOG(ERROR) << "Unable to find GID for '" << args[2] << "': " << decode_uid_err;
+            return -1;
+        }
+    }
+
+    if (lchown(path.c_str(), uid, gid) == -1) return -errno;
+
     return 0;
 }
 
@@ -724,7 +762,7 @@
             }
         } else {
             in_flags = false;
-            if (restorecon(args[i].c_str(), flag) < 0) {
+            if (selinux_android_restorecon(args[i].c_str(), flag) < 0) {
                 ret = -errno;
             }
         }
@@ -809,7 +847,7 @@
  * Callback to make a directory from the ext4 code
  */
 static int do_installkeys_ensure_dir_exists(const char* dir) {
-    if (make_dir(dir, 0700) && errno != EEXIST) {
+    if (make_dir(dir, 0700, sehandle) && errno != EEXIST) {
         return -1;
     }
 
@@ -835,7 +873,9 @@
 }
 
 static int do_init_user0(const std::vector<std::string>& args) {
-    return e4crypt_do_init_user0();
+    std::vector<std::string> exec_args = {"exec", "/system/bin/vdc", "--wait", "cryptfs",
+                                          "init_user0"};
+    return do_exec(exec_args);
 }
 
 const BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const {
diff --git a/init/descriptors.cpp b/init/descriptors.cpp
index bc6bc8d..6f729a3 100644
--- a/init/descriptors.cpp
+++ b/init/descriptors.cpp
@@ -23,6 +23,7 @@
 
 #include <android-base/logging.h>
 #include <android-base/stringprintf.h>
+#include <android-base/strings.h>
 #include <android-base/unique_fd.h>
 #include <cutils/android_get_control_file.h>
 #include <cutils/sockets.h>
@@ -77,10 +78,12 @@
 }
 
 int SocketInfo::Create(const std::string& context) const {
-  int flags = ((type() == "stream" ? SOCK_STREAM :
-                (type() == "dgram" ? SOCK_DGRAM :
-                 SOCK_SEQPACKET)));
-  return create_socket(name().c_str(), flags, perm(), uid(), gid(), context.c_str());
+    auto types = android::base::Split(type(), "+");
+    int flags =
+        ((types[0] == "stream" ? SOCK_STREAM : (types[0] == "dgram" ? SOCK_DGRAM : SOCK_SEQPACKET)));
+    bool passcred = types.size() > 1 && types[1] == "passcred";
+    return CreateSocket(name().c_str(), flags, passcred, perm(), uid(), gid(), context.c_str(),
+                        sehandle);
 }
 
 const std::string SocketInfo::key() const {
diff --git a/init/devices.cpp b/init/devices.cpp
index 11687f0..d8258cf 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -54,7 +54,11 @@
 #include "ueventd.h"
 #include "util.h"
 
-extern struct selabel_handle *sehandle;
+#ifdef _INIT_INIT_H
+#error "Do not include init.h in files used by ueventd or watchdogd; it will expose init's globals"
+#endif
+
+static selabel_handle* sehandle;
 
 static android::base::unique_fd device_fd;
 
@@ -247,7 +251,7 @@
 
     if (access(path.c_str(), F_OK) == 0) {
         LOG(VERBOSE) << "restorecon_recursive: " << path;
-        restorecon(path.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE);
+        selinux_android_restorecon(path.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE);
     }
 }
 
@@ -554,7 +558,7 @@
 }
 
 static void make_link_init(const std::string& oldpath, const std::string& newpath) {
-    if (mkdir_recursive(dirname(newpath.c_str()), 0755)) {
+    if (mkdir_recursive(dirname(newpath.c_str()), 0755, sehandle)) {
         PLOG(ERROR) << "Failed to create directory " << dirname(newpath.c_str());
     }
 
@@ -599,7 +603,7 @@
     if (uevent->major < 0 || uevent->minor < 0) return;
 
     const char* base = "/dev/block/";
-    make_dir(base, 0755);
+    make_dir(base, 0755, sehandle);
 
     std::string name = android::base::Basename(uevent->path);
     std::string devpath = base + name;
@@ -641,7 +645,7 @@
         devpath = "/dev/" + android::base::Basename(uevent->path);
     }
 
-    mkdir_recursive(android::base::Dirname(devpath), 0755);
+    mkdir_recursive(android::base::Dirname(devpath), 0755, sehandle);
 
     auto links = get_character_device_symlinks(uevent);
 
diff --git a/init/init.cpp b/init/init.cpp
index 99ce5e6..878f164 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -21,6 +21,7 @@
 #include <errno.h>
 #include <fcntl.h>
 #include <inttypes.h>
+#include <keyutils.h>
 #include <libgen.h>
 #include <paths.h>
 #include <signal.h>
@@ -852,7 +853,9 @@
             }
         }
 
-        if (!write_file("/sys/fs/selinux/checkreqprot", "0")) {
+        std::string err;
+        if (!WriteFile("/sys/fs/selinux/checkreqprot", "0", &err)) {
+            LOG(ERROR) << err;
             security_failure();
         }
 
@@ -863,25 +866,28 @@
     }
 }
 
-// The files and directories that were created before initial sepolicy load
-// need to have their security context restored to the proper value.
-// This must happen before /dev is populated by ueventd.
+// The files and directories that were created before initial sepolicy load or
+// files on ramdisk need to have their security context restored to the proper
+// value. This must happen before /dev is populated by ueventd.
 static void selinux_restore_context() {
     LOG(INFO) << "Running restorecon...";
-    restorecon("/dev");
-    restorecon("/dev/kmsg");
+    selinux_android_restorecon("/dev", 0);
+    selinux_android_restorecon("/dev/kmsg", 0);
     if constexpr (WORLD_WRITABLE_KMSG) {
-      restorecon("/dev/kmsg_debug");
+      selinux_android_restorecon("/dev/kmsg_debug", 0);
     }
-    restorecon("/dev/socket");
-    restorecon("/dev/random");
-    restorecon("/dev/urandom");
-    restorecon("/dev/__properties__");
-    restorecon("/plat_property_contexts");
-    restorecon("/nonplat_property_contexts");
-    restorecon("/sys", SELINUX_ANDROID_RESTORECON_RECURSE);
-    restorecon("/dev/block", SELINUX_ANDROID_RESTORECON_RECURSE);
-    restorecon("/dev/device-mapper");
+    selinux_android_restorecon("/dev/socket", 0);
+    selinux_android_restorecon("/dev/random", 0);
+    selinux_android_restorecon("/dev/urandom", 0);
+    selinux_android_restorecon("/dev/__properties__", 0);
+    selinux_android_restorecon("/plat_property_contexts", 0);
+    selinux_android_restorecon("/nonplat_property_contexts", 0);
+    selinux_android_restorecon("/sys", SELINUX_ANDROID_RESTORECON_RECURSE);
+    selinux_android_restorecon("/dev/block", SELINUX_ANDROID_RESTORECON_RECURSE);
+    selinux_android_restorecon("/dev/device-mapper", 0);
+
+    selinux_android_restorecon("/sbin/mke2fs", 0);
+    selinux_android_restorecon("/sbin/e2fsdroid", 0);
 }
 
 // Set the UDC controller for the ConfigFS USB Gadgets.
@@ -990,7 +996,7 @@
 
         // We're in the kernel domain, so re-exec init to transition to the init domain now
         // that the SELinux policy has been loaded.
-        if (restorecon("/init") == -1) {
+        if (selinux_android_restorecon("/init", 0) == -1) {
             PLOG(ERROR) << "restorecon failed";
             security_failure();
         }
@@ -1015,6 +1021,11 @@
     InitKernelLogging(argv);
     LOG(INFO) << "init second stage started!";
 
+    // Set up a session keyring that all processes will have access to. It
+    // will hold things like FBE encryption keys. No process should override
+    // its session keyring.
+    keyctl(KEYCTL_GET_KEYRING_ID, KEY_SPEC_SESSION_KEYRING, 1);
+
     // Indicate that booting is in progress to background fw loaders, etc.
     close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));
 
diff --git a/init/init.h b/init/init.h
index 6add75f..6725a70 100644
--- a/init/init.h
+++ b/init/init.h
@@ -19,6 +19,9 @@
 
 #include <string>
 
+// Note: These globals are *only* valid in init, so they should not be used in ueventd,
+// watchdogd, or any files that may be included in those, such as devices.cpp and util.cpp.
+// TODO: Have an Init class and remove all globals.
 extern const char *ENV[32];
 extern std::string default_console;
 extern struct selabel_handle *sehandle;
diff --git a/init/init_first_stage.cpp b/init/init_first_stage.cpp
index 43f1c15..9d2a0d1 100644
--- a/init/init_first_stage.cpp
+++ b/init/init_first_stage.cpp
@@ -47,18 +47,22 @@
     bool InitDevices();
 
   protected:
-    void InitRequiredDevices(std::set<std::string>* devices_partition_names);
+    void InitRequiredDevices();
     void InitVerityDevice(const std::string& verity_device);
     bool MountPartitions();
 
-    virtual bool GetRequiredDevices(std::set<std::string>* out_devices_partition_names,
-                                    bool* out_need_dm_verity) = 0;
+    virtual coldboot_action_t ColdbootCallback(uevent* uevent);
+
+    // Pure virtual functions.
+    virtual bool GetRequiredDevices() = 0;
     virtual bool SetUpDmVerity(fstab_rec* fstab_rec) = 0;
 
+    bool need_dm_verity_;
     // Device tree fstab entries.
     std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> device_tree_fstab_;
     // Eligible first stage mount candidates, only allow /system, /vendor and/or /odm.
     std::vector<fstab_rec*> mount_fstab_recs_;
+    std::set<std::string> required_devices_partition_names_;
 };
 
 class FirstStageMountVBootV1 : public FirstStageMount {
@@ -67,27 +71,26 @@
     ~FirstStageMountVBootV1() override = default;
 
   protected:
-    bool GetRequiredDevices(std::set<std::string>* out_devices_partition_names,
-                            bool* out_need_dm_verity) override;
+    bool GetRequiredDevices() override;
     bool SetUpDmVerity(fstab_rec* fstab_rec) override;
 };
 
 class FirstStageMountVBootV2 : public FirstStageMount {
   public:
+    friend void SetInitAvbVersionInRecovery();
+
     FirstStageMountVBootV2();
     ~FirstStageMountVBootV2() override = default;
 
-    const std::string& by_name_prefix() const { return device_tree_by_name_prefix_; }
-
   protected:
-    bool GetRequiredDevices(std::set<std::string>* out_devices_partition_names,
-                            bool* out_need_dm_verity) override;
+    coldboot_action_t ColdbootCallback(uevent* uevent) override;
+    bool GetRequiredDevices() override;
     bool SetUpDmVerity(fstab_rec* fstab_rec) override;
     bool InitAvbHandle();
 
     std::string device_tree_vbmeta_parts_;
-    std::string device_tree_by_name_prefix_;
     FsManagerAvbUniquePtr avb_handle_;
+    ByNameSymlinkMap by_name_symlink_map_;
 };
 
 // Static Functions
@@ -102,7 +105,8 @@
 
 // Class Definitions
 // -----------------
-FirstStageMount::FirstStageMount() : device_tree_fstab_(fs_mgr_read_fstab_dt(), fs_mgr_free_fstab) {
+FirstStageMount::FirstStageMount()
+    : need_dm_verity_(false), device_tree_fstab_(fs_mgr_read_fstab_dt(), fs_mgr_free_fstab) {
     if (!device_tree_fstab_) {
         LOG(ERROR) << "Failed to read fstab from device tree";
         return;
@@ -136,69 +140,71 @@
 }
 
 bool FirstStageMount::InitDevices() {
-    bool need_dm_verity;
-    std::set<std::string> devices_partition_names;
+    if (!GetRequiredDevices()) return false;
 
-    // The partition name in devices_partition_names MUST have A/B suffix when A/B is used.
-    if (!GetRequiredDevices(&devices_partition_names, &need_dm_verity)) return false;
+    InitRequiredDevices();
 
-    if (need_dm_verity) {
-        device_init("/sys/devices/virtual/misc/device-mapper",
-                    [&](uevent* uevent) -> coldboot_action_t { return COLDBOOT_STOP; });
-    }
-
-    bool success = false;
-    InitRequiredDevices(&devices_partition_names);
-
-    // InitRequiredDevices() will remove found partitions from devices_partition_names.
+    // InitRequiredDevices() will remove found partitions from required_devices_partition_names_.
     // So if it isn't empty here, it means some partitions are not found.
-    if (!devices_partition_names.empty()) {
+    if (!required_devices_partition_names_.empty()) {
         LOG(ERROR) << __FUNCTION__ << "(): partition(s) not found: "
-                   << android::base::Join(devices_partition_names, ", ");
+                   << android::base::Join(required_devices_partition_names_, ", ");
+        return false;
     } else {
-        success = true;
+        return true;
     }
-
-    device_close();
-    return success;
 }
 
-// Creates devices with uevent->partition_name matching one in the in/out
-// devices_partition_names. Found partitions will then be removed from the
-// devices_partition_names for the caller to check which devices are NOT created.
-void FirstStageMount::InitRequiredDevices(std::set<std::string>* devices_partition_names) {
-    if (devices_partition_names->empty()) {
+// Creates devices with uevent->partition_name matching one in the member variable
+// required_devices_partition_names_. Found partitions will then be removed from it
+// for the subsequent member function to check which devices are NOT created.
+void FirstStageMount::InitRequiredDevices() {
+    if (required_devices_partition_names_.empty()) {
         return;
     }
-    device_init(nullptr, [=](uevent* uevent) -> coldboot_action_t {
-        // We need platform devices to create symlinks.
-        if (uevent->subsystem == "platform") {
-            return COLDBOOT_CREATE;
-        }
 
-        // Ignores everything that is not a block device.
-        if (uevent->subsystem != "block") {
-            return COLDBOOT_CONTINUE;
-        }
+    if (need_dm_verity_) {
+        const std::string dm_path = "/devices/virtual/misc/device-mapper";
+        device_init(("/sys" + dm_path).c_str(), [&dm_path](uevent* uevent) -> coldboot_action_t {
+            if (uevent->path == dm_path) return COLDBOOT_STOP;
+            return COLDBOOT_CONTINUE;  // dm_path not found, continue to find it.
+        });
+    }
 
-        if (!uevent->partition_name.empty()) {
-            // Matches partition name to create device nodes.
-            // Both devices_partition_names and uevent->partition_name have A/B
-            // suffix when A/B is used.
-            auto iter = devices_partition_names->find(uevent->partition_name);
-            if (iter != devices_partition_names->end()) {
-                LOG(VERBOSE) << __FUNCTION__ << "(): found partition: " << *iter;
-                devices_partition_names->erase(iter);
-                if (devices_partition_names->empty()) {
-                    return COLDBOOT_STOP;  // Found all partitions, stop coldboot.
-                } else {
-                    return COLDBOOT_CREATE;  // Creates this device and continue to find others.
-                }
+    device_init(nullptr,
+                [this](uevent* uevent) -> coldboot_action_t { return ColdbootCallback(uevent); });
+
+    device_close();
+}
+
+coldboot_action_t FirstStageMount::ColdbootCallback(uevent* uevent) {
+    // We need platform devices to create symlinks.
+    if (uevent->subsystem == "platform") {
+        return COLDBOOT_CREATE;
+    }
+
+    // Ignores everything that is not a block device.
+    if (uevent->subsystem != "block") {
+        return COLDBOOT_CONTINUE;
+    }
+
+    if (!uevent->partition_name.empty()) {
+        // Matches partition name to create device nodes.
+        // Both required_devices_partition_names_ and uevent->partition_name have A/B
+        // suffix when A/B is used.
+        auto iter = required_devices_partition_names_.find(uevent->partition_name);
+        if (iter != required_devices_partition_names_.end()) {
+            LOG(VERBOSE) << __FUNCTION__ << "(): found partition: " << *iter;
+            required_devices_partition_names_.erase(iter);
+            if (required_devices_partition_names_.empty()) {
+                return COLDBOOT_STOP;  // Found all partitions, stop coldboot.
+            } else {
+                return COLDBOOT_CREATE;  // Creates this device and continue to find others.
             }
         }
-        // Not found a partition or find an unneeded partition, continue to find others.
-        return COLDBOOT_CONTINUE;
-    });
+    }
+    // Not found a partition or find an unneeded partition, continue to find others.
+    return COLDBOOT_CONTINUE;
 }
 
 // Creates "/dev/block/dm-XX" for dm-verity by running coldboot on /sys/block/dm-XX.
@@ -230,10 +236,9 @@
     return true;
 }
 
-bool FirstStageMountVBootV1::GetRequiredDevices(std::set<std::string>* out_devices_partition_names,
-                                                bool* out_need_dm_verity) {
+bool FirstStageMountVBootV1::GetRequiredDevices() {
     std::string verity_loc_device;
-    *out_need_dm_verity = false;
+    need_dm_verity_ = false;
 
     for (auto fstab_rec : mount_fstab_recs_) {
         // Don't allow verifyatboot in the first stage.
@@ -243,7 +248,7 @@
         }
         // Checks for verified partitions.
         if (fs_mgr_is_verified(fstab_rec)) {
-            *out_need_dm_verity = true;
+            need_dm_verity_ = true;
         }
         // Checks if verity metadata is on a separate partition. Note that it is
         // not partition specific, so there must be only one additional partition
@@ -262,11 +267,11 @@
     // Includes the partition names of fstab records and verity_loc_device (if any).
     // Notes that fstab_rec->blk_device has A/B suffix updated by fs_mgr when A/B is used.
     for (auto fstab_rec : mount_fstab_recs_) {
-        out_devices_partition_names->emplace(basename(fstab_rec->blk_device));
+        required_devices_partition_names_.emplace(basename(fstab_rec->blk_device));
     }
 
     if (!verity_loc_device.empty()) {
-        out_devices_partition_names->emplace(basename(verity_loc_device.c_str()));
+        required_devices_partition_names_.emplace(basename(verity_loc_device.c_str()));
     }
 
     return true;
@@ -289,15 +294,13 @@
 }
 
 // FirstStageMountVBootV2 constructor.
-// Gets the vbmeta configurations from device tree.
-// Specifically, the 'parts' and 'by_name_prefix' below.
+// Gets the vbmeta partitions from device tree.
 // /{
 //     firmware {
 //         android {
 //             vbmeta {
 //                 compatible = "android,vbmeta";
 //                 parts = "vbmeta,boot,system,vendor"
-//                 by_name_prefix = "/dev/block/platform/soc.0/f9824900.sdhci/by-name/"
 //             };
 //         };
 //     };
@@ -307,31 +310,24 @@
         PLOG(ERROR) << "Failed to read vbmeta/parts from device tree";
         return;
     }
-
-    // TODO: removes by_name_prefix to allow partitions on different block devices.
-    if (!read_android_dt_file("vbmeta/by_name_prefix", &device_tree_by_name_prefix_)) {
-        PLOG(ERROR) << "Failed to read vbmeta/by_name_prefix from dt";
-        return;
-    }
 }
 
-bool FirstStageMountVBootV2::GetRequiredDevices(std::set<std::string>* out_devices_partition_names,
-                                                bool* out_need_dm_verity) {
-    *out_need_dm_verity = false;
+bool FirstStageMountVBootV2::GetRequiredDevices() {
+    need_dm_verity_ = false;
 
     // fstab_rec->blk_device has A/B suffix.
     for (auto fstab_rec : mount_fstab_recs_) {
         if (fs_mgr_is_avb(fstab_rec)) {
-            *out_need_dm_verity = true;
+            need_dm_verity_ = true;
         }
-        out_devices_partition_names->emplace(basename(fstab_rec->blk_device));
+        required_devices_partition_names_.emplace(basename(fstab_rec->blk_device));
     }
 
     // libavb verifies AVB metadata on all verified partitions at once.
     // e.g., The device_tree_vbmeta_parts_ will be "vbmeta,boot,system,vendor"
     // for libavb to verify metadata, even if there is only /vendor in the
     // above mount_fstab_recs_.
-    if (*out_need_dm_verity) {
+    if (need_dm_verity_) {
         if (device_tree_vbmeta_parts_.empty()) {
             LOG(ERROR) << "Missing vbmeta parts in device tree";
             return false;
@@ -339,16 +335,45 @@
         std::vector<std::string> partitions = android::base::Split(device_tree_vbmeta_parts_, ",");
         std::string ab_suffix = fs_mgr_get_slot_suffix();
         for (const auto& partition : partitions) {
-            // out_devices_partition_names is of type std::set so it's not an issue to emplace
-            // a partition twice. e.g., /vendor might be in both places:
+            // required_devices_partition_names_ is of type std::set so it's not an issue
+            // to emplace a partition twice. e.g., /vendor might be in both places:
             //   - device_tree_vbmeta_parts_ = "vbmeta,boot,system,vendor"
             //   - mount_fstab_recs_: /vendor_a
-            out_devices_partition_names->emplace(partition + ab_suffix);
+            required_devices_partition_names_.emplace(partition + ab_suffix);
         }
     }
     return true;
 }
 
+coldboot_action_t FirstStageMountVBootV2::ColdbootCallback(uevent* uevent) {
+    // Invokes the parent function to see if any desired partition has been found.
+    // If yes, record the by-name symlink for creating FsManagerAvbHandle later.
+    coldboot_action_t parent_callback_ret = FirstStageMount::ColdbootCallback(uevent);
+
+    // Skips the uevent if the parent function returns COLDBOOT_CONTINUE (meaning
+    // that the uevent was skipped) or there is no uevent->partition_name to
+    // create the by-name symlink.
+    if (parent_callback_ret != COLDBOOT_CONTINUE && !uevent->partition_name.empty()) {
+        // get_block_device_symlinks() will return three symlinks at most, depending on
+        // the content of uevent. by-name symlink will be at [0] if uevent->partition_name
+        // is not empty. e.g.,
+        //   - /dev/block/platform/soc.0/f9824900.sdhci/by-name/modem
+        //   - /dev/block/platform/soc.0/f9824900.sdhci/by-num/p1
+        //   - /dev/block/platform/soc.0/f9824900.sdhci/mmcblk0p1
+        std::vector<std::string> links = get_block_device_symlinks(uevent);
+        if (!links.empty()) {
+            auto[it, inserted] = by_name_symlink_map_.emplace(uevent->partition_name, links[0]);
+            if (!inserted) {
+                LOG(ERROR) << "Partition '" << uevent->partition_name
+                           << "' already existed in the by-name symlink map with a value of '"
+                           << it->second << "', new value '" << links[0] << "' will be ignored.";
+            }
+        }
+    }
+
+    return parent_callback_ret;
+}
+
 bool FirstStageMountVBootV2::SetUpDmVerity(fstab_rec* fstab_rec) {
     if (fs_mgr_is_avb(fstab_rec)) {
         if (!InitAvbHandle()) return false;
@@ -368,7 +393,14 @@
 bool FirstStageMountVBootV2::InitAvbHandle() {
     if (avb_handle_) return true;  // Returns true if the handle is already initialized.
 
-    avb_handle_ = FsManagerAvbHandle::Open(device_tree_by_name_prefix_);
+    if (by_name_symlink_map_.empty()) {
+        LOG(ERROR) << "by_name_symlink_map_ is empty";
+        return false;
+    }
+
+    avb_handle_ = FsManagerAvbHandle::Open(std::move(by_name_symlink_map_));
+    by_name_symlink_map_.clear();  // Removes all elements after the above std::move().
+
     if (!avb_handle_) {
         PLOG(ERROR) << "Failed to open FsManagerAvbHandle";
         return false;
@@ -424,7 +456,8 @@
         return;
     }
 
-    FsManagerAvbUniquePtr avb_handle = FsManagerAvbHandle::Open(avb_first_mount.by_name_prefix());
+    FsManagerAvbUniquePtr avb_handle =
+        FsManagerAvbHandle::Open(std::move(avb_first_mount.by_name_symlink_map_));
     if (!avb_handle) {
         PLOG(ERROR) << "Failed to open FsManagerAvbHandle for INIT_AVB_VERSION";
         return;
diff --git a/init/init_parser.cpp b/init/init_parser.cpp
index 620367a..1b31cf2 100644
--- a/init/init_parser.cpp
+++ b/init/init_parser.cpp
@@ -110,7 +110,9 @@
     LOG(INFO) << "Parsing file " << path << "...";
     Timer t;
     std::string data;
-    if (!read_file(path, &data)) {
+    std::string err;
+    if (!ReadFile(path, &data, &err)) {
+        LOG(ERROR) << err;
         return false;
     }
 
diff --git a/init/init_parser_test.cpp b/init/init_parser_test.cpp
index d8fd2ba..86d60d0 100644
--- a/init/init_parser_test.cpp
+++ b/init/init_parser_test.cpp
@@ -86,19 +86,29 @@
         ASSERT_EQ("", svc->seclabel());
     }
     if (uid) {
-        ASSERT_EQ(decode_uid("log"), svc->uid());
+        uid_t decoded_uid;
+        std::string err;
+        ASSERT_TRUE(DecodeUid("log", &decoded_uid, &err));
+        ASSERT_EQ(decoded_uid, svc->uid());
     } else {
         ASSERT_EQ(0U, svc->uid());
     }
     if (gid) {
-        ASSERT_EQ(decode_uid("shell"), svc->gid());
+        uid_t decoded_uid;
+        std::string err;
+        ASSERT_TRUE(DecodeUid("shell", &decoded_uid, &err));
+        ASSERT_EQ(decoded_uid, svc->gid());
     } else {
         ASSERT_EQ(0U, svc->gid());
     }
     if (supplementary_gids) {
         ASSERT_EQ(2U, svc->supp_gids().size());
-        ASSERT_EQ(decode_uid("system"), svc->supp_gids()[0]);
-        ASSERT_EQ(decode_uid("adb"), svc->supp_gids()[1]);
+        uid_t decoded_uid;
+        std::string err;
+        ASSERT_TRUE(DecodeUid("system", &decoded_uid, &err));
+        ASSERT_EQ(decoded_uid, svc->supp_gids()[0]);
+        ASSERT_TRUE(DecodeUid("adb", &decoded_uid, &err));
+        ASSERT_EQ(decoded_uid, svc->supp_gids()[1]);
     } else {
         ASSERT_EQ(0U, svc->supp_gids().size());
     }
diff --git a/init/init_test.cpp b/init/init_test.cpp
index 3da14b5..7093ba9 100644
--- a/init/init_test.cpp
+++ b/init/init_test.cpp
@@ -152,10 +152,11 @@
                                "on boot\n"
                                "execute 3";
     // clang-format on
-    // write_file() ensures the right mode is set
-    ASSERT_TRUE(write_file(std::string(dir.path) + "/a.rc", dir_a_script));
+    // WriteFile() ensures the right mode is set
+    std::string err;
+    ASSERT_TRUE(WriteFile(std::string(dir.path) + "/a.rc", dir_a_script, &err));
 
-    ASSERT_TRUE(write_file(std::string(dir.path) + "/b.rc", "on boot\nexecute 5"));
+    ASSERT_TRUE(WriteFile(std::string(dir.path) + "/b.rc", "on boot\nexecute 5", &err));
 
     // clang-format off
     std::string start_script = "import " + std::string(first_import.path) + "\n"
diff --git a/init/keyutils.h b/init/keyutils.h
new file mode 100644
index 0000000..de01beb
--- /dev/null
+++ b/init/keyutils.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* Miniature version of a header-only keyutils.h (no library required) */
+
+#ifndef _INIT_KEYUTILS_H_
+#define _INIT_KEYUTILS_H_
+
+#ifndef KEYUTILS_H /* walk away if the _real_ one exists */
+
+#include <linux/keyctl.h>
+#include <stdarg.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+
+static inline long keyctl(int cmd, ...) {
+    va_list va;
+    unsigned long arg2, arg3, arg4, arg5;
+
+    va_start(va, cmd);
+    arg2 = va_arg(va, unsigned long);
+    arg3 = va_arg(va, unsigned long);
+    arg4 = va_arg(va, unsigned long);
+    arg5 = va_arg(va, unsigned long);
+    va_end(va);
+    return syscall(__NR_keyctl, cmd, arg2, arg3, arg4, arg5);
+}
+
+#endif
+
+#endif
diff --git a/init/property_service.cpp b/init/property_service.cpp
index aa47976..18e47e3 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -177,7 +177,7 @@
     }
 
     if (name == "selinux.restorecon_recursive" && valuelen > 0) {
-        if (restorecon(value.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE) != 0) {
+        if (selinux_android_restorecon(value.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE) != 0) {
             LOG(ERROR) << "Failed to restorecon_recursive " << value;
         }
     }
@@ -510,8 +510,9 @@
 static void load_properties_from_file(const char* filename, const char* filter) {
     Timer t;
     std::string data;
-    if (!read_file(filename, &data)) {
-        PLOG(WARNING) << "Couldn't load properties from " << filename;
+    std::string err;
+    if (!ReadFile(filename, &data, &err)) {
+        PLOG(WARNING) << "Couldn't load property file: " << err;
         return;
     }
     data.push_back('\n');
@@ -658,8 +659,8 @@
 void start_property_service() {
     property_set("ro.property_service.version", "2");
 
-    property_set_fd = create_socket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
-                                    0666, 0, 0, NULL);
+    property_set_fd = CreateSocket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
+                                   false, 0666, 0, 0, nullptr, sehandle);
     if (property_set_fd == -1) {
         PLOG(ERROR) << "start_property_service socket creation failed";
         exit(1);
diff --git a/init/service.cpp b/init/service.cpp
index 3a9f622..7c931da 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -209,21 +209,33 @@
 }
 
 void Service::KillProcessGroup(int signal) {
-    LOG(INFO) << "Sending signal " << signal
-              << " to service '" << name_
-              << "' (pid " << pid_ << ") process group...";
-    int r;
-    if (signal == SIGTERM) {
-        r = killProcessGroupOnce(uid_, pid_, signal);
-    } else {
-        r = killProcessGroup(uid_, pid_, signal);
-    }
-    if (r == -1) {
-        LOG(ERROR) << "killProcessGroup(" << uid_ << ", " << pid_ << ", " << signal << ") failed";
-    }
-    if (kill(-pid_, signal) == -1) {
+    // We ignore reporting errors of ESRCH as this commonly happens in the below case,
+    // 1) Terminate() is called, which sends SIGTERM to the process
+    // 2) The process successfully exits
+    // 3) ReapOneProcess() is called, which calls waitpid(-1, ...) which removes the pid entry.
+    // 4) Reap() is called, which sends SIGKILL, but the pid no longer exists.
+    // TODO: sigaction for SIGCHLD reports the pid of the exiting process,
+    // we should do this kill with that pid first before calling waitpid().
+    if (kill(-pid_, signal) == -1 && errno != ESRCH) {
         PLOG(ERROR) << "kill(" << pid_ << ", " << signal << ") failed";
     }
+
+    // If we've already seen a successful result from killProcessGroup*(), then we have removed
+    // the cgroup already and calling these functions a second time will simply result in an error.
+    // This is true regardless of which signal was sent.
+    // These functions handle their own logging, so no additional logging is needed.
+    if (!process_cgroup_empty_) {
+        LOG(INFO) << "Sending signal " << signal << " to service '" << name_ << "' (pid " << pid_
+                  << ") process group...";
+        int r;
+        if (signal == SIGTERM) {
+            r = killProcessGroupOnce(uid_, pid_, signal);
+        } else {
+            r = killProcessGroup(uid_, pid_, signal);
+        }
+
+        if (r == 0) process_cgroup_empty_ = true;
+    }
 }
 
 void Service::SetProcessAttributes() {
@@ -380,9 +392,18 @@
 }
 
 bool Service::ParseGroup(const std::vector<std::string>& args, std::string* err) {
-    gid_ = decode_uid(args[1].c_str());
+    std::string decode_uid_err;
+    if (!DecodeUid(args[1], &gid_, &decode_uid_err)) {
+        *err = "Unable to find GID for '" + args[1] + "': " + decode_uid_err;
+        return false;
+    }
     for (std::size_t n = 2; n < args.size(); n++) {
-        supp_gids_.emplace_back(decode_uid(args[n].c_str()));
+        gid_t gid;
+        if (!DecodeUid(args[n], &gid, &decode_uid_err)) {
+            *err = "Unable to find GID for '" + args[n] + "': " + decode_uid_err;
+            return false;
+        }
+        supp_gids_.emplace_back(gid);
     }
     return true;
 }
@@ -480,10 +501,25 @@
 template <typename T>
 bool Service::AddDescriptor(const std::vector<std::string>& args, std::string* err) {
     int perm = args.size() > 3 ? std::strtoul(args[3].c_str(), 0, 8) : -1;
-    uid_t uid = args.size() > 4 ? decode_uid(args[4].c_str()) : 0;
-    gid_t gid = args.size() > 5 ? decode_uid(args[5].c_str()) : 0;
+    uid_t uid = 0;
+    gid_t gid = 0;
     std::string context = args.size() > 6 ? args[6] : "";
 
+    std::string decode_uid_err;
+    if (args.size() > 4) {
+        if (!DecodeUid(args[4], &uid, &decode_uid_err)) {
+            *err = "Unable to find UID for '" + args[4] + "': " + decode_uid_err;
+            return false;
+        }
+    }
+
+    if (args.size() > 5) {
+        if (!DecodeUid(args[5], &gid, &decode_uid_err)) {
+            *err = "Unable to find GID for '" + args[5] + "': " + decode_uid_err;
+            return false;
+        }
+    }
+
     auto descriptor = std::make_unique<T>(args[1], args[2], uid, gid, perm, context);
 
     auto old =
@@ -501,7 +537,9 @@
 
 // name type perm [ uid gid context ]
 bool Service::ParseSocket(const std::vector<std::string>& args, std::string* err) {
-    if (args[2] != "dgram" && args[2] != "stream" && args[2] != "seqpacket") {
+    if (!android::base::StartsWith(args[2], "dgram") &&
+        !android::base::StartsWith(args[2], "stream") &&
+        !android::base::StartsWith(args[2], "seqpacket")) {
         *err = "socket type must be 'dgram', 'stream' or 'seqpacket'";
         return false;
     }
@@ -522,7 +560,11 @@
 }
 
 bool Service::ParseUser(const std::vector<std::string>& args, std::string* err) {
-    uid_ = decode_uid(args[1].c_str());
+    std::string decode_uid_err;
+    if (!DecodeUid(args[1], &uid_, &decode_uid_err)) {
+        *err = "Unable to find UID for '" + args[1] + "': " + decode_uid_err;
+        return false;
+    }
     return true;
 }
 
@@ -736,6 +778,7 @@
     time_started_ = boot_clock::now();
     pid_ = pid;
     flags_ |= SVC_RUNNING;
+    process_cgroup_empty_ = false;
 
     errno = -createProcessGroup(uid_, pid_);
     if (errno != 0) {
@@ -936,15 +979,28 @@
     }
     uid_t uid = 0;
     if (command_arg > 3) {
-        uid = decode_uid(args[2].c_str());
+        std::string decode_uid_err;
+        if (!DecodeUid(args[2], &uid, &decode_uid_err)) {
+            LOG(ERROR) << "Unable to find UID for '" << args[2] << "': " << decode_uid_err;
+            return nullptr;
+        }
     }
     gid_t gid = 0;
     std::vector<gid_t> supp_gids;
     if (command_arg > 4) {
-        gid = decode_uid(args[3].c_str());
+        std::string decode_uid_err;
+        if (!DecodeUid(args[3], &gid, &decode_uid_err)) {
+            LOG(ERROR) << "Unable to find GID for '" << args[3] << "': " << decode_uid_err;
+            return nullptr;
+        }
         std::size_t nr_supp_gids = command_arg - 1 /* -- */ - 4 /* exec SECLABEL UID GID */;
         for (size_t i = 0; i < nr_supp_gids; ++i) {
-            supp_gids.push_back(decode_uid(args[4 + i].c_str()));
+            gid_t supp_gid;
+            if (!DecodeUid(args[4 + i], &supp_gid, &decode_uid_err)) {
+                LOG(ERROR) << "Unable to find UID for '" << args[4 + i] << "': " << decode_uid_err;
+                return nullptr;
+            }
+            supp_gids.push_back(supp_gid);
         }
     }
 
diff --git a/init/service.h b/init/service.h
index 426577f..b9c270a 100644
--- a/init/service.h
+++ b/init/service.h
@@ -107,6 +107,7 @@
     int ioprio_pri() const { return ioprio_pri_; }
     int priority() const { return priority_; }
     int oom_score_adjust() const { return oom_score_adjust_; }
+    bool process_cgroup_empty() const { return process_cgroup_empty_; }
     const std::vector<std::string>& args() const { return args_; }
 
   private:
@@ -179,6 +180,8 @@
 
     int oom_score_adjust_;
 
+    bool process_cgroup_empty_ = false;
+
     std::vector<std::string> args_;
 };
 
diff --git a/init/service_test.cpp b/init/service_test.cpp
index 4493f25..b9c4627 100644
--- a/init/service_test.cpp
+++ b/init/service_test.cpp
@@ -45,6 +45,7 @@
     EXPECT_EQ(0, service_in_old_memory->ioprio_pri());
     EXPECT_EQ(0, service_in_old_memory->priority());
     EXPECT_EQ(-1000, service_in_old_memory->oom_score_adjust());
+    EXPECT_FALSE(service_in_old_memory->process_cgroup_empty());
 
     for (std::size_t i = 0; i < memory_size; ++i) {
         old_memory[i] = 0xFF;
@@ -64,4 +65,5 @@
     EXPECT_EQ(0, service_in_old_memory2->ioprio_pri());
     EXPECT_EQ(0, service_in_old_memory2->priority());
     EXPECT_EQ(-1000, service_in_old_memory2->oom_score_adjust());
+    EXPECT_FALSE(service_in_old_memory->process_cgroup_empty());
 }
diff --git a/init/util.cpp b/init/util.cpp
index a101ce5..75f81b9 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -40,56 +40,51 @@
 #include <cutils/android_reboot.h>
 #include <cutils/sockets.h>
 #include <selinux/android.h>
-#include <selinux/label.h>
 
-#include "init.h"
 #include "reboot.h"
 
+#ifdef _INIT_INIT_H
+#error "Do not include init.h in files used by ueventd or watchdogd; it will expose init's globals"
+#endif
+
 using android::base::boot_clock;
+using namespace std::literals::string_literals;
 
-static unsigned int do_decode_uid(const char *s)
-{
-    unsigned int v;
+// DecodeUid() - decodes and returns the given string, which can be either the
+// numeric or name representation, into the integer uid or gid. Returns
+// UINT_MAX on error.
+bool DecodeUid(const std::string& name, uid_t* uid, std::string* err) {
+    *uid = UINT_MAX;
+    *err = "";
 
-    if (!s || *s == '\0')
-        return UINT_MAX;
-
-    if (isalpha(s[0])) {
-        struct passwd* pwd = getpwnam(s);
-        if (!pwd)
-            return UINT_MAX;
-        return pwd->pw_uid;
+    if (isalpha(name[0])) {
+        passwd* pwd = getpwnam(name.c_str());
+        if (!pwd) {
+            *err = "getpwnam failed: "s + strerror(errno);
+            return false;
+        }
+        *uid = pwd->pw_uid;
+        return true;
     }
 
     errno = 0;
-    v = (unsigned int) strtoul(s, 0, 0);
-    if (errno)
-        return UINT_MAX;
-    return v;
-}
-
-/*
- * decode_uid - decodes and returns the given string, which can be either the
- * numeric or name representation, into the integer uid or gid. Returns
- * UINT_MAX on error.
- */
-unsigned int decode_uid(const char *s) {
-    unsigned int v = do_decode_uid(s);
-    if (v == UINT_MAX) {
-        LOG(ERROR) << "decode_uid: Unable to find UID for '" << s << "'; returning UINT_MAX";
+    uid_t result = static_cast<uid_t>(strtoul(name.c_str(), 0, 0));
+    if (errno) {
+        *err = "strtoul failed: "s + strerror(errno);
+        return false;
     }
-    return v;
+    *uid = result;
+    return true;
 }
 
 /*
- * create_socket - creates a Unix domain socket in ANDROID_SOCKET_DIR
+ * CreateSocket - creates a Unix domain socket in ANDROID_SOCKET_DIR
  * ("/dev/socket") as dictated in init.rc. This socket is inherited by the
  * daemon. We communicate the file descriptor's value via the environment
  * variable ANDROID_SOCKET_ENV_PREFIX<name> ("ANDROID_SOCKET_foo").
  */
-int create_socket(const char *name, int type, mode_t perm, uid_t uid,
-                  gid_t gid, const char *socketcon)
-{
+int CreateSocket(const char* name, int type, bool passcred, mode_t perm, uid_t uid, gid_t gid,
+                 const char* socketcon, selabel_handle* sehandle) {
     if (socketcon) {
         if (setsockcreatecon(socketcon) == -1) {
             PLOG(ERROR) << "setsockcreatecon(\"" << socketcon << "\") failed";
@@ -123,6 +118,14 @@
         }
     }
 
+    if (passcred) {
+        int on = 1;
+        if (setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on))) {
+            PLOG(ERROR) << "Failed to set SO_PASSCRED '" << name << "'";
+            return -1;
+        }
+    }
+
     int ret = bind(fd, (struct sockaddr *) &addr, sizeof (addr));
     int savederrno = errno;
 
@@ -156,12 +159,14 @@
     return -1;
 }
 
-bool read_file(const std::string& path, std::string* content) {
+bool ReadFile(const std::string& path, std::string* content, std::string* err) {
     content->clear();
+    *err = "";
 
     android::base::unique_fd fd(
         TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_NOFOLLOW | O_CLOEXEC)));
     if (fd == -1) {
+        *err = "Unable to open '" + path + "': " + strerror(errno);
         return false;
     }
 
@@ -169,42 +174,48 @@
     // or group-writable files.
     struct stat sb;
     if (fstat(fd, &sb) == -1) {
-        PLOG(ERROR) << "fstat failed for '" << path << "'";
+        *err = "fstat failed for '" + path + "': " + strerror(errno);
         return false;
     }
     if ((sb.st_mode & (S_IWGRP | S_IWOTH)) != 0) {
-        LOG(ERROR) << "skipping insecure file '" << path << "'";
+        *err = "Skipping insecure file '" + path + "'";
         return false;
     }
 
-    return android::base::ReadFdToString(fd, content);
+    if (!android::base::ReadFdToString(fd, content)) {
+        *err = "Unable to read '" + path + "': " + strerror(errno);
+        return false;
+    }
+    return true;
 }
 
-bool write_file(const std::string& path, const std::string& content) {
+bool WriteFile(const std::string& path, const std::string& content, std::string* err) {
+    *err = "";
+
     android::base::unique_fd fd(TEMP_FAILURE_RETRY(
         open(path.c_str(), O_WRONLY | O_CREAT | O_NOFOLLOW | O_TRUNC | O_CLOEXEC, 0600)));
     if (fd == -1) {
-        PLOG(ERROR) << "write_file: Unable to open '" << path << "'";
+        *err = "Unable to open '" + path + "': " + strerror(errno);
         return false;
     }
-    bool success = android::base::WriteStringToFd(content, fd);
-    if (!success) {
-        PLOG(ERROR) << "write_file: Unable to write to '" << path << "'";
+    if (!android::base::WriteStringToFd(content, fd)) {
+        *err = "Unable to write to '" + path + "': " + strerror(errno);
+        return false;
     }
-    return success;
+    return true;
 }
 
-int mkdir_recursive(const std::string& path, mode_t mode) {
+int mkdir_recursive(const std::string& path, mode_t mode, selabel_handle* sehandle) {
     std::string::size_type slash = 0;
     while ((slash = path.find('/', slash + 1)) != std::string::npos) {
         auto directory = path.substr(0, slash);
         struct stat info;
         if (stat(directory.c_str(), &info) != 0) {
-            auto ret = make_dir(directory.c_str(), mode);
+            auto ret = make_dir(directory.c_str(), mode, sehandle);
             if (ret && errno != EEXIST) return ret;
         }
     }
-    auto ret = make_dir(path.c_str(), mode);
+    auto ret = make_dir(path.c_str(), mode, sehandle);
     if (ret && errno != EEXIST) return ret;
     return 0;
 }
@@ -233,8 +244,7 @@
     }
 }
 
-int make_dir(const char *path, mode_t mode)
-{
+int make_dir(const char* path, mode_t mode, selabel_handle* sehandle) {
     int rc;
 
     char *secontext = NULL;
@@ -256,11 +266,6 @@
     return rc;
 }
 
-int restorecon(const char* pathname, int flags)
-{
-    return selinux_android_restorecon(pathname, flags);
-}
-
 /*
  * Writes hex_len hex characters (1/2 byte) to hex from bytes.
  */
diff --git a/init/util.h b/init/util.h
index 92b3a1d..1ad6b77 100644
--- a/init/util.h
+++ b/init/util.h
@@ -26,6 +26,7 @@
 #include <string>
 
 #include <android-base/chrono_utils.h>
+#include <selinux/label.h>
 
 #define COLDBOOT_DONE "/dev/.coldboot_done"
 
@@ -34,11 +35,11 @@
 using android::base::boot_clock;
 using namespace std::chrono_literals;
 
-int create_socket(const char *name, int type, mode_t perm,
-                  uid_t uid, gid_t gid, const char *socketcon);
+int CreateSocket(const char* name, int type, bool passcred, mode_t perm, uid_t uid, gid_t gid,
+                 const char* socketcon, selabel_handle* sehandle);
 
-bool read_file(const std::string& path, std::string* content);
-bool write_file(const std::string& path, const std::string& content);
+bool ReadFile(const std::string& path, std::string* content, std::string* err);
+bool WriteFile(const std::string& path, const std::string& content, std::string* err);
 
 class Timer {
   public:
@@ -60,14 +61,13 @@
 
 std::ostream& operator<<(std::ostream& os, const Timer& t);
 
-unsigned int decode_uid(const char *s);
+bool DecodeUid(const std::string& name, uid_t* uid, std::string* err);
 
-int mkdir_recursive(const std::string& pathname, mode_t mode);
+int mkdir_recursive(const std::string& pathname, mode_t mode, selabel_handle* sehandle);
 int wait_for_file(const char *filename, std::chrono::nanoseconds timeout);
 void import_kernel_cmdline(bool in_qemu,
                            const std::function<void(const std::string&, const std::string&, bool)>&);
-int make_dir(const char *path, mode_t mode);
-int restorecon(const char *pathname, int flags = 0);
+int make_dir(const char* path, mode_t mode, selabel_handle* sehandle);
 std::string bytes_to_hex(const uint8_t *bytes, size_t bytes_len);
 bool is_dir(const char* pathname);
 bool expand_props(const std::string& src, std::string* dst);
diff --git a/init/util_test.cpp b/init/util_test.cpp
index b8b409a..4bb8a83 100644
--- a/init/util_test.cpp
+++ b/init/util_test.cpp
@@ -24,53 +24,67 @@
 #include <android-base/test_utils.h>
 #include <gtest/gtest.h>
 
-TEST(util, read_file_ENOENT) {
-  std::string s("hello");
-  errno = 0;
-  EXPECT_FALSE(read_file("/proc/does-not-exist", &s));
-  EXPECT_EQ(ENOENT, errno);
-  EXPECT_EQ("", s); // s was cleared.
+using namespace std::literals::string_literals;
+
+TEST(util, ReadFile_ENOENT) {
+    std::string s("hello");
+    std::string err;
+    errno = 0;
+    EXPECT_FALSE(ReadFile("/proc/does-not-exist", &s, &err));
+    EXPECT_EQ("Unable to open '/proc/does-not-exist': No such file or directory", err);
+    EXPECT_EQ(ENOENT, errno);
+    EXPECT_EQ("", s);  // s was cleared.
 }
 
-TEST(util, read_file_group_writeable) {
+TEST(util, ReadFileGroupWriteable) {
     std::string s("hello");
     TemporaryFile tf;
+    std::string err;
     ASSERT_TRUE(tf.fd != -1);
-    EXPECT_TRUE(write_file(tf.path, s)) << strerror(errno);
+    EXPECT_TRUE(WriteFile(tf.path, s, &err)) << strerror(errno);
+    EXPECT_EQ("", err);
     EXPECT_NE(-1, fchmodat(AT_FDCWD, tf.path, 0620, AT_SYMLINK_NOFOLLOW)) << strerror(errno);
-    EXPECT_FALSE(read_file(tf.path, &s)) << strerror(errno);
+    EXPECT_FALSE(ReadFile(tf.path, &s, &err)) << strerror(errno);
+    EXPECT_EQ("Skipping insecure file '"s + tf.path + "'", err);
     EXPECT_EQ("", s);  // s was cleared.
 }
 
-TEST(util, read_file_world_writeable) {
+TEST(util, ReadFileWorldWiteable) {
     std::string s("hello");
     TemporaryFile tf;
+    std::string err;
     ASSERT_TRUE(tf.fd != -1);
-    EXPECT_TRUE(write_file(tf.path, s.c_str())) << strerror(errno);
+    EXPECT_TRUE(WriteFile(tf.path, s, &err)) << strerror(errno);
+    EXPECT_EQ("", err);
     EXPECT_NE(-1, fchmodat(AT_FDCWD, tf.path, 0602, AT_SYMLINK_NOFOLLOW)) << strerror(errno);
-    EXPECT_FALSE(read_file(tf.path, &s)) << strerror(errno);
+    EXPECT_FALSE(ReadFile(tf.path, &s, &err)) << strerror(errno);
+    EXPECT_EQ("Skipping insecure file '"s + tf.path + "'", err);
     EXPECT_EQ("", s);  // s was cleared.
 }
 
-TEST(util, read_file_symbolic_link) {
+TEST(util, ReadFileSymbolicLink) {
     std::string s("hello");
     errno = 0;
     // lrwxrwxrwx 1 root root 13 1970-01-01 00:00 charger -> /sbin/healthd
-    EXPECT_FALSE(read_file("/charger", &s));
+    std::string err;
+    EXPECT_FALSE(ReadFile("/charger", &s, &err));
+    EXPECT_EQ("Unable to open '/charger': Too many symbolic links encountered", err);
     EXPECT_EQ(ELOOP, errno);
     EXPECT_EQ("", s);  // s was cleared.
 }
 
-TEST(util, read_file_success) {
-  std::string s("hello");
-  EXPECT_TRUE(read_file("/proc/version", &s));
-  EXPECT_GT(s.length(), 6U);
-  EXPECT_EQ('\n', s[s.length() - 1]);
-  s[5] = 0;
-  EXPECT_STREQ("Linux", s.c_str());
+TEST(util, ReadFileSuccess) {
+    std::string s("hello");
+    std::string err;
+    EXPECT_TRUE(ReadFile("/proc/version", &s, &err));
+    EXPECT_EQ("", err);
+    EXPECT_GT(s.length(), 6U);
+    EXPECT_EQ('\n', s[s.length() - 1]);
+    s[5] = 0;
+    EXPECT_STREQ("Linux", s.c_str());
 }
 
-TEST(util, write_file_binary) {
+TEST(util, WriteFileBinary) {
     std::string contents("abcd");
     contents.push_back('\0');
     contents.push_back('\0');
@@ -78,22 +92,28 @@
     ASSERT_EQ(10u, contents.size());
 
     TemporaryFile tf;
+    std::string err;
     ASSERT_TRUE(tf.fd != -1);
-    EXPECT_TRUE(write_file(tf.path, contents)) << strerror(errno);
+    EXPECT_TRUE(WriteFile(tf.path, contents, &err)) << strerror(errno);
+    EXPECT_EQ("", err);
 
     std::string read_back_contents;
-    EXPECT_TRUE(read_file(tf.path, &read_back_contents)) << strerror(errno);
+    EXPECT_TRUE(ReadFile(tf.path, &read_back_contents, &err)) << strerror(errno);
+    EXPECT_EQ("", err);
     EXPECT_EQ(contents, read_back_contents);
     EXPECT_EQ(10u, read_back_contents.size());
 }
 
-TEST(util, write_file_not_exist) {
+TEST(util, WriteFileNotExist) {
     std::string s("hello");
     std::string s2("hello");
     TemporaryDir test_dir;
     std::string path = android::base::StringPrintf("%s/does-not-exist", test_dir.path);
-    EXPECT_TRUE(write_file(path, s));
-    EXPECT_TRUE(read_file(path, &s2));
+    std::string err;
+    EXPECT_TRUE(WriteFile(path, s, &err));
+    EXPECT_EQ("", err);
+    EXPECT_TRUE(ReadFile(path, &s2, &err));
+    EXPECT_EQ("", err);
     EXPECT_EQ(s, s2);
     struct stat sb;
     int fd = open(path.c_str(), O_RDONLY | O_NOFOLLOW | O_CLOEXEC);
@@ -103,22 +123,38 @@
     EXPECT_EQ(0, unlink(path.c_str()));
 }
 
-TEST(util, write_file_exist) {
+TEST(util, WriteFileExist) {
     std::string s2("");
     TemporaryFile tf;
     ASSERT_TRUE(tf.fd != -1);
-    EXPECT_TRUE(write_file(tf.path, "1hello1")) << strerror(errno);
-    EXPECT_TRUE(read_file(tf.path, &s2));
+    std::string err;
+    EXPECT_TRUE(WriteFile(tf.path, "1hello1", &err)) << strerror(errno);
+    EXPECT_EQ("", err);
+    EXPECT_TRUE(ReadFile(tf.path, &s2, &err));
+    EXPECT_EQ("", err);
     EXPECT_STREQ("1hello1", s2.c_str());
-    EXPECT_TRUE(write_file(tf.path, "2ll2"));
-    EXPECT_TRUE(read_file(tf.path, &s2));
+    EXPECT_TRUE(WriteFile(tf.path, "2ll2", &err));
+    EXPECT_EQ("", err);
+    EXPECT_TRUE(ReadFile(tf.path, &s2, &err));
+    EXPECT_EQ("", err);
     EXPECT_STREQ("2ll2", s2.c_str());
 }
 
-TEST(util, decode_uid) {
-  EXPECT_EQ(0U, decode_uid("root"));
-  EXPECT_EQ(UINT_MAX, decode_uid("toot"));
-  EXPECT_EQ(123U, decode_uid("123"));
+TEST(util, DecodeUid) {
+    uid_t decoded_uid;
+    std::string err;
+
+    EXPECT_TRUE(DecodeUid("root", &decoded_uid, &err));
+    EXPECT_EQ("", err);
+    EXPECT_EQ(0U, decoded_uid);
+
+    EXPECT_FALSE(DecodeUid("toot", &decoded_uid, &err));
+    EXPECT_EQ("getpwnam failed: No such file or directory", err);
+    EXPECT_EQ(UINT_MAX, decoded_uid);
+
+    EXPECT_TRUE(DecodeUid("123", &decoded_uid, &err));
+    EXPECT_EQ("", err);
+    EXPECT_EQ(123U, decoded_uid);
 }
 
 TEST(util, is_dir) {
@@ -128,15 +164,10 @@
     EXPECT_FALSE(is_dir(tf.path));
 }
 
-// sehandle is needed for make_dir()
-// TODO: Remove once sehandle is encapsulated
-#include <selinux/label.h>
-selabel_handle* sehandle;
-
 TEST(util, mkdir_recursive) {
     TemporaryDir test_dir;
     std::string path = android::base::StringPrintf("%s/three/directories/deep", test_dir.path);
-    EXPECT_EQ(0, mkdir_recursive(path, 0755));
+    EXPECT_EQ(0, mkdir_recursive(path, 0755, nullptr));
     std::string path1 = android::base::StringPrintf("%s/three", test_dir.path);
     EXPECT_TRUE(is_dir(path1.c_str()));
     std::string path2 = android::base::StringPrintf("%s/three/directories", test_dir.path);
@@ -148,7 +179,7 @@
 TEST(util, mkdir_recursive_extra_slashes) {
     TemporaryDir test_dir;
     std::string path = android::base::StringPrintf("%s/three////directories/deep//", test_dir.path);
-    EXPECT_EQ(0, mkdir_recursive(path, 0755));
+    EXPECT_EQ(0, mkdir_recursive(path, 0755, nullptr));
     std::string path1 = android::base::StringPrintf("%s/three", test_dir.path);
     EXPECT_TRUE(is_dir(path1.c_str()));
     std::string path2 = android::base::StringPrintf("%s/three/directories", test_dir.path);
diff --git a/init/watchdogd.cpp b/init/watchdogd.cpp
index 21c1e5b..7baa487 100644
--- a/init/watchdogd.cpp
+++ b/init/watchdogd.cpp
@@ -24,7 +24,10 @@
 #include <android-base/logging.h>
 
 #include "log.h"
-#include "util.h"
+
+#ifdef _INIT_INIT_H
+#error "Do not include init.h in files used by ueventd or watchdogd; it will expose init's globals"
+#endif
 
 #define DEV_NAME "/dev/watchdog"
 
diff --git a/libbacktrace/Android.bp b/libbacktrace/Android.bp
index 285aa6e..7dd9227 100644
--- a/libbacktrace/Android.bp
+++ b/libbacktrace/Android.bp
@@ -110,7 +110,7 @@
                 "libunwind",
             ],
 
-            static_libs: ["libcutils"],
+            static_libs: ["libasync_safe", "libcutils"],
         },
     },
 }
diff --git a/libbacktrace/BacktraceAsyncSafeLog.h b/libbacktrace/BacktraceAsyncSafeLog.h
new file mode 100644
index 0000000..14f51be
--- /dev/null
+++ b/libbacktrace/BacktraceAsyncSafeLog.h
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+
+#ifndef _LIBBACKTRACE_BACKTRACE_ASYNC_SAFE_LOG_H
+#define _LIBBACKTRACE_BACKTRACE_ASYNC_SAFE_LOG_H
+
+#if defined(__ANDROID__)
+
+#include <async_safe/log.h>
+
+// Logging macros for use in signal handler, only available on target.
+#define BACK_ASYNC_SAFE_LOGW(format, ...)                                                     \
+  async_safe_format_log(ANDROID_LOG_WARN, "libbacktrace", "%s: " format, __PRETTY_FUNCTION__, \
+                        ##__VA_ARGS__)
+
+#define BACK_ASYNC_SAFE_LOGE(format, ...)                                                      \
+  async_safe_format_log(ANDROID_LOG_ERROR, "libbacktrace", "%s: " format, __PRETTY_FUNCTION__, \
+                        ##__VA_ARGS__)
+
+#else
+
+#define BACK_ASYNC_SAFE_LOGW(format, ...)
+
+#define BACK_ASYNC_SAFE_LOGE(format, ...)
+
+#endif
+
+#endif  // _LIBBACKTRACE_BACKTRACE_ASYNC_SAFE_LOG_H
diff --git a/libbacktrace/BacktraceCurrent.cpp b/libbacktrace/BacktraceCurrent.cpp
index d7a3b01..fb76b85 100644
--- a/libbacktrace/BacktraceCurrent.cpp
+++ b/libbacktrace/BacktraceCurrent.cpp
@@ -31,8 +31,8 @@
 #include <backtrace/Backtrace.h>
 #include <backtrace/BacktraceMap.h>
 
+#include "BacktraceAsyncSafeLog.h"
 #include "BacktraceCurrent.h"
-#include "BacktraceLog.h"
 #include "ThreadEntry.h"
 #include "thread_utils.h"
 
@@ -47,7 +47,7 @@
     *out_value = *reinterpret_cast<word_t*>(ptr);
     return true;
   } else {
-    BACK_LOGW("pointer %p not in a readable map", reinterpret_cast<void*>(ptr));
+    BACK_ASYNC_SAFE_LOGW("pointer %p not in a readable map", reinterpret_cast<void*>(ptr));
     *out_value = static_cast<word_t>(-1);
     return false;
   }
@@ -114,7 +114,8 @@
 static void SignalLogOnly(int, siginfo_t*, void*) {
   ErrnoRestorer restore;
 
-  BACK_LOGE("pid %d, tid %d: Received a spurious signal %d\n", getpid(), gettid(), THREAD_SIGNAL);
+  BACK_ASYNC_SAFE_LOGE("pid %d, tid %d: Received a spurious signal %d\n", getpid(), gettid(),
+                       THREAD_SIGNAL);
 }
 
 static void SignalHandler(int, siginfo_t*, void* sigcontext) {
@@ -122,7 +123,7 @@
 
   ThreadEntry* entry = ThreadEntry::Get(getpid(), gettid(), false);
   if (!entry) {
-    BACK_LOGE("pid %d, tid %d entry not found", getpid(), gettid());
+    BACK_ASYNC_SAFE_LOGE("pid %d, tid %d entry not found", getpid(), gettid());
     return;
   }
 
@@ -141,7 +142,7 @@
     entry->Wake();
   } else {
     // At this point, it is possible that entry has been freed, so just exit.
-    BACK_LOGE("Timed out waiting for unwind thread to indicate it completed.");
+    BACK_ASYNC_SAFE_LOGE("Timed out waiting for unwind thread to indicate it completed.");
   }
 }
 
@@ -159,7 +160,7 @@
   act.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK;
   sigemptyset(&act.sa_mask);
   if (sigaction(THREAD_SIGNAL, &act, &oldact) != 0) {
-    BACK_LOGE("sigaction failed: %s", strerror(errno));
+    BACK_ASYNC_SAFE_LOGE("sigaction failed: %s", strerror(errno));
     ThreadEntry::Remove(entry);
     pthread_mutex_unlock(&g_sigaction_mutex);
     error_ = BACKTRACE_UNWIND_ERROR_INTERNAL;
@@ -212,7 +213,7 @@
     // Wait for the thread to indicate it is done with the ThreadEntry.
     if (!entry->Wait(3)) {
       // Send a warning, but do not mark as a failure to unwind.
-      BACK_LOGW("Timed out waiting for signal handler to indicate it finished.");
+      BACK_ASYNC_SAFE_LOGW("Timed out waiting for signal handler to indicate it finished.");
     }
   } else {
     // Check to see if the thread has disappeared.
@@ -220,7 +221,7 @@
       error_ = BACKTRACE_UNWIND_ERROR_THREAD_DOESNT_EXIST;
     } else {
       error_ = BACKTRACE_UNWIND_ERROR_THREAD_TIMEOUT;
-      BACK_LOGE("Timed out waiting for signal handler to get ucontext data.");
+      BACK_ASYNC_SAFE_LOGE("Timed out waiting for signal handler to get ucontext data.");
     }
   }
 
diff --git a/libbacktrace/ThreadEntry.cpp b/libbacktrace/ThreadEntry.cpp
index 084c1aa..9bd59e4 100644
--- a/libbacktrace/ThreadEntry.cpp
+++ b/libbacktrace/ThreadEntry.cpp
@@ -21,7 +21,7 @@
 #include <time.h>
 #include <ucontext.h>
 
-#include "BacktraceLog.h"
+#include "BacktraceAsyncSafeLog.h"
 #include "ThreadEntry.h"
 
 // Initialize static member variables.
@@ -106,7 +106,7 @@
   while (wait_value_ != value) {
     int ret = pthread_cond_timedwait(&wait_cond_, &wait_mutex_, &ts);
     if (ret != 0) {
-      BACK_LOGW("pthread_cond_timedwait for value %d failed: %s", value, strerror(ret));
+      BACK_ASYNC_SAFE_LOGW("pthread_cond_timedwait for value %d failed: %s", value, strerror(ret));
       wait_completed = false;
       break;
     }
diff --git a/libcutils/Android.bp b/libcutils/Android.bp
index 58170ec..245deb1 100644
--- a/libcutils/Android.bp
+++ b/libcutils/Android.bp
@@ -53,7 +53,7 @@
     host_supported: true,
     srcs: [
         "config_utils.c",
-        "fs_config.c",
+        "fs_config.cpp",
         "canned_fs_config.c",
         "hashmap.c",
         "iosched_policy.c",
@@ -94,6 +94,9 @@
             shared: {
                 enabled: false,
             },
+            cflags: [
+                "-D_GNU_SOURCE",
+            ],
         },
 
         android: {
diff --git a/libcutils/fs_config.c b/libcutils/fs_config.cpp
similarity index 86%
rename from libcutils/fs_config.c
rename to libcutils/fs_config.cpp
index e4541f7..2b3443f 100644
--- a/libcutils/fs_config.c
+++ b/libcutils/fs_config.cpp
@@ -14,15 +14,12 @@
  * limitations under the License.
  */
 
-/* This file is used to define the properties of the filesystem
-** images generated by build tools (mkbootfs and mkyaffs2image) and
-** by the device side of adb.
-*/
+// This file is used to define the properties of the filesystem
+// images generated by build tools (mkbootfs and mkyaffs2image) and
+// by the device side of adb.
 
 #define LOG_TAG "fs_config"
 
-#define _GNU_SOURCE
-
 #include <errno.h>
 #include <fcntl.h>
 #include <stdbool.h>
@@ -42,8 +39,10 @@
 #define O_BINARY 0
 #endif
 
-/* My kingdom for <endian.h> */
-static inline uint16_t get2LE(const uint8_t* src) { return src[0] | (src[1] << 8); }
+// My kingdom for <endian.h>
+static inline uint16_t get2LE(const uint8_t* src) {
+    return src[0] | (src[1] << 8);
+}
 
 static inline uint64_t get8LE(const uint8_t* src) {
     uint32_t low, high;
@@ -55,14 +54,13 @@
 
 #define ALIGN(x, alignment) (((x) + ((alignment)-1)) & ~((alignment)-1))
 
-/* Rules for directories.
-** These rules are applied based on "first match", so they
-** should start with the most specific path and work their
-** way up to the root.
-*/
+// Rules for directories.
+// These rules are applied based on "first match", so they
+// should start with the most specific path and work their
+// way up to the root.
 
 static const struct fs_path_config android_dirs[] = {
-    /* clang-format off */
+    // clang-format off
     { 00770, AID_SYSTEM,       AID_CACHE,        0, "cache" },
     { 00500, AID_ROOT,         AID_ROOT,         0, "config" },
     { 00771, AID_SYSTEM,       AID_SYSTEM,       0, "data/app" },
@@ -92,25 +90,26 @@
     { 00755, AID_ROOT,         AID_SHELL,        0, "system/xbin" },
     { 00755, AID_ROOT,         AID_SHELL,        0, "vendor" },
     { 00755, AID_ROOT,         AID_ROOT,         0, 0 },
-    /* clang-format on */
+    // clang-format on
 };
+#ifndef __ANDROID_VNDK__
+auto __for_testing_only__android_dirs = android_dirs;
+#endif
 
-/* Rules for files.
-** These rules are applied based on "first match", so they
-** should start with the most specific path and work their
-** way up to the root. Prefixes ending in * denotes wildcard
-** and will allow partial matches.
-*/
+// Rules for files.
+// These rules are applied based on "first match", so they
+// should start with the most specific path and work their
+// way up to the root. Prefixes ending in * denotes wildcard
+// and will allow partial matches.
 static const char sys_conf_dir[] = "/system/etc/fs_config_dirs";
 static const char sys_conf_file[] = "/system/etc/fs_config_files";
-/* No restrictions are placed on the vendor and oem file-system config files,
- * although the developer is advised to restrict the scope to the /vendor or
- * oem/ file-system since the intent is to provide support for customized
- * portions of a separate vendor.img or oem.img.  Has to remain open so that
- * customization can also land on /system/vendor, /system/oem or /system/odm.
- * We expect build-time checking or filtering when constructing the associated
- * fs_config_* files (see build/tools/fs_config/fs_config_generate.c)
- */
+// No restrictions are placed on the vendor and oem file-system config files,
+// although the developer is advised to restrict the scope to the /vendor or
+// oem/ file-system since the intent is to provide support for customized
+// portions of a separate vendor.img or oem.img.  Has to remain open so that
+// customization can also land on /system/vendor, /system/oem or /system/odm.
+// We expect build-time checking or filtering when constructing the associated
+// fs_config_* files (see build/tools/fs_config/fs_config_generate.c)
 static const char ven_conf_dir[] = "/vendor/etc/fs_config_dirs";
 static const char ven_conf_file[] = "/vendor/etc/fs_config_files";
 static const char oem_conf_dir[] = "/oem/etc/fs_config_dirs";
@@ -125,7 +124,7 @@
 };
 
 static const struct fs_path_config android_files[] = {
-    /* clang-format off */
+    // clang-format off
     { 00644, AID_SYSTEM,    AID_SYSTEM,    0, "data/app/*" },
     { 00644, AID_SYSTEM,    AID_SYSTEM,    0, "data/app-ephemeral/*" },
     { 00644, AID_SYSTEM,    AID_SYSTEM,    0, "data/app-private/*" },
@@ -173,13 +172,13 @@
     { 00444, AID_ROOT,      AID_ROOT,      0, ven_conf_dir + 1 },
     { 00444, AID_ROOT,      AID_ROOT,      0, ven_conf_file + 1 },
 
-    /* the following two files are INTENTIONALLY set-uid, but they
-     * are NOT included on user builds. */
+    // the following two files are INTENTIONALLY set-uid, but they
+    // are NOT included on user builds.
     { 06755, AID_ROOT,      AID_ROOT,      0, "system/xbin/procmem" },
     { 04750, AID_ROOT,      AID_SHELL,     0, "system/xbin/su" },
 
-    /* the following files have enhanced capabilities and ARE included
-     * in user builds. */
+    // the following files have enhanced capabilities and ARE included
+    // in user builds.
     { 00700, AID_SYSTEM,    AID_SHELL,     CAP_MASK_LONG(CAP_BLOCK_SUSPEND),
                                               "system/bin/inputflinger" },
     { 00550, AID_LOGD,      AID_LOGD,      CAP_MASK_LONG(CAP_SYSLOG) |
@@ -190,17 +189,17 @@
                                            CAP_MASK_LONG(CAP_SETGID),
                                               "system/bin/run-as" },
 
-    /* Support FIFO scheduling mode in SurfaceFlinger. */
+    // Support FIFO scheduling mode in SurfaceFlinger.
     { 00755, AID_SYSTEM,    AID_GRAPHICS,  CAP_MASK_LONG(CAP_SYS_NICE),
                                               "system/bin/surfaceflinger" },
 
-    /* Support hostapd administering a network interface. */
+    // Support hostapd administering a network interface.
     { 00755, AID_WIFI,      AID_WIFI,      CAP_MASK_LONG(CAP_NET_ADMIN) |
                                            CAP_MASK_LONG(CAP_NET_RAW),
                                               "system/bin/hostapd" },
 
-    /* Support Bluetooth legacy hal accessing /sys/class/rfkill
-     * Support RT scheduling in Bluetooth */
+    // Support Bluetooth legacy hal accessing /sys/class/rfkill
+    // Support RT scheduling in Bluetooth
     { 00700, AID_BLUETOOTH, AID_BLUETOOTH, CAP_MASK_LONG(CAP_NET_ADMIN) |
                                            CAP_MASK_LONG(CAP_SYS_NICE),
                                               "system/vendor/bin/hw/android.hardware.bluetooth@1.0-service" },
@@ -208,7 +207,7 @@
                                            CAP_MASK_LONG(CAP_SYS_NICE),
                                               "vendor/bin/hw/android.hardware.bluetooth@1.0-service" },
 
-    /* Support wifi_hal_legacy administering a network interface. */
+    // Support wifi_hal_legacy administering a network interface.
     { 00755, AID_WIFI,      AID_WIFI,      CAP_MASK_LONG(CAP_NET_ADMIN) |
                                            CAP_MASK_LONG(CAP_NET_RAW),
                                               "system/vendor/bin/hw/android.hardware.wifi@1.0-service" },
@@ -216,8 +215,7 @@
                                            CAP_MASK_LONG(CAP_NET_RAW),
                                               "vendor/bin/hw/android.hardware.wifi@1.0-service" },
 
-    /* A non-privileged zygote that spawns
-     * isolated processes for web rendering. */
+    // A non-privileged zygote that spawns isolated processes for web rendering.
     { 0750,  AID_ROOT,      AID_ROOT,      CAP_MASK_LONG(CAP_SETUID) |
                                            CAP_MASK_LONG(CAP_SETGID) |
                                            CAP_MASK_LONG(CAP_SETPCAP),
@@ -227,7 +225,7 @@
                                            CAP_MASK_LONG(CAP_SETPCAP),
                                               "system/bin/webview_zygote64" },
 
-    /* generic defaults */
+    // generic defaults
     { 00755, AID_ROOT,      AID_ROOT,      0, "bin/*" },
     { 00640, AID_ROOT,      AID_SHELL,     0, "fstab.*" },
     { 00750, AID_ROOT,      AID_SHELL,     0, "init*" },
@@ -241,8 +239,11 @@
     { 00755, AID_ROOT,      AID_SHELL,     0, "vendor/bin/*" },
     { 00755, AID_ROOT,      AID_SHELL,     0, "vendor/xbin/*" },
     { 00644, AID_ROOT,      AID_ROOT,      0, 0 },
-    /* clang-format on */
+    // clang-format on
 };
+#ifndef __ANDROID_VNDK__
+auto __for_testing_only__android_files = android_files;
+#endif
 
 static size_t strip(const char* path, size_t len, const char suffix[]) {
     if (len < strlen(suffix)) return len;
@@ -254,9 +255,9 @@
     int fd = -1;
 
     if (target_out_path && *target_out_path) {
-        /* target_out_path is the path to the directory holding content of
-         * system partition but as we cannot guarantee it ends with '/system'
-         * or with or without a trailing slash, need to strip them carefully. */
+        // target_out_path is the path to the directory holding content of
+        // system partition but as we cannot guarantee it ends with '/system'
+        // or with or without a trailing slash, need to strip them carefully.
         char* name = NULL;
         size_t len = strlen(target_out_path);
         len = strip(target_out_path, len, "/");
@@ -278,7 +279,7 @@
             return false;
         }
     } else {
-        /* If name ends in * then allow partial matches. */
+        // If name ends in * then allow partial matches.
         if (prefix[len - 1] == '*') {
             return !strncmp(prefix, path, len - 1);
         }
@@ -314,7 +315,7 @@
                 ALOGE("%s len is corrupted", conf[which][dir]);
                 break;
             }
-            prefix = calloc(1, remainder);
+            prefix = static_cast<char*>(calloc(1, remainder));
             if (!prefix) {
                 ALOGE("%s out of memory", conf[which][dir]);
                 break;
@@ -325,7 +326,7 @@
                 break;
             }
             len = strnlen(prefix, remainder);
-            if (len >= remainder) { /* missing a terminating null */
+            if (len >= remainder) {  // missing a terminating null
                 free(prefix);
                 ALOGE("%s is corrupted", conf[which][dir]);
                 break;
diff --git a/libcutils/include/private/android_filesystem_config.h b/libcutils/include/private/android_filesystem_config.h
index bbba853..fdead23 100644
--- a/libcutils/include/private/android_filesystem_config.h
+++ b/libcutils/include/private/android_filesystem_config.h
@@ -53,7 +53,7 @@
 #include <sys/cdefs.h>
 #include <sys/types.h>
 
-#if defined(__ANDROID__)
+#if defined(__BIONIC__)
 #include <linux/capability.h>
 #else
 #include "android_filesystem_capability.h"
diff --git a/libcutils/native_handle.c b/libcutils/native_handle.c
index 9f4840a..95bbc41 100644
--- a/libcutils/native_handle.c
+++ b/libcutils/native_handle.c
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "NativeHandle"
+#include <cutils/native_handle.h>
 
 #include <errno.h>
 #include <stdint.h>
@@ -22,15 +22,12 @@
 #include <string.h>
 #include <unistd.h>
 
-#include <android/log.h>
-#include <cutils/native_handle.h>
-
 static const int kMaxNativeFds = 1024;
 static const int kMaxNativeInts = 1024;
 
-native_handle_t* native_handle_init(char* storage, int numFds, int numInts)
-{
+native_handle_t* native_handle_init(char* storage, int numFds, int numInts) {
     if ((uintptr_t) storage % alignof(native_handle_t)) {
+        errno = EINVAL;
         return NULL;
     }
 
@@ -38,13 +35,12 @@
     handle->version = sizeof(native_handle_t);
     handle->numFds = numFds;
     handle->numInts = numInts;
-
     return handle;
 }
 
-native_handle_t* native_handle_create(int numFds, int numInts)
-{
+native_handle_t* native_handle_create(int numFds, int numInts) {
     if (numFds < 0 || numInts < 0 || numFds > kMaxNativeFds || numInts > kMaxNativeInts) {
+        errno = EINVAL;
         return NULL;
     }
 
@@ -58,14 +54,13 @@
     return h;
 }
 
-native_handle_t* native_handle_clone(const native_handle_t* handle)
-{
+native_handle_t* native_handle_clone(const native_handle_t* handle) {
     native_handle_t* clone = native_handle_create(handle->numFds, handle->numInts);
-    int i;
+    if (clone == NULL) return NULL;
 
-    for (i = 0; i < handle->numFds; i++) {
+    for (int i = 0; i < handle->numFds; i++) {
         clone->data[i] = dup(handle->data[i]);
-        if (clone->data[i] < 0) {
+        if (clone->data[i] == -1) {
             clone->numFds = i;
             native_handle_close(clone);
             native_handle_delete(clone);
@@ -74,30 +69,27 @@
     }
 
     memcpy(&clone->data[handle->numFds], &handle->data[handle->numFds],
-            sizeof(int) * handle->numInts);
+           sizeof(int) * handle->numInts);
 
     return clone;
 }
 
-int native_handle_delete(native_handle_t* h)
-{
+int native_handle_delete(native_handle_t* h) {
     if (h) {
-        if (h->version != sizeof(native_handle_t))
-            return -EINVAL;
+        if (h->version != sizeof(native_handle_t)) return -EINVAL;
         free(h);
     }
     return 0;
 }
 
-int native_handle_close(const native_handle_t* h)
-{
-    if (h->version != sizeof(native_handle_t))
-        return -EINVAL;
+int native_handle_close(const native_handle_t* h) {
+    if (h->version != sizeof(native_handle_t)) return -EINVAL;
 
+    int saved_errno = errno;
     const int numFds = h->numFds;
-    int i;
-    for (i=0 ; i<numFds ; i++) {
+    for (int i = 0; i < numFds; ++i) {
         close(h->data[i]);
     }
+    errno = saved_errno;
     return 0;
 }
diff --git a/libcutils/tests/Android.bp b/libcutils/tests/Android.bp
index a0b1d7b..7884190 100644
--- a/libcutils/tests/Android.bp
+++ b/libcutils/tests/Android.bp
@@ -27,7 +27,8 @@
                 "test_str_parms.cpp",
                 "android_get_control_socket_test.cpp",
                 "android_get_control_file_test.cpp",
-                "multiuser_test.cpp"
+                "multiuser_test.cpp",
+                "fs_config.cpp",
             ],
         },
 
diff --git a/libcutils/tests/AshmemTest.cpp b/libcutils/tests/AshmemTest.cpp
index 51c679f..a87e23e 100644
--- a/libcutils/tests/AshmemTest.cpp
+++ b/libcutils/tests/AshmemTest.cpp
@@ -14,10 +14,11 @@
  * limitations under the License.
  */
 
-#include <sys/mman.h>
+#include <android-base/unique_fd.h>
 #include <cutils/ashmem.h>
 #include <gtest/gtest.h>
-#include <android-base/unique_fd.h>
+#include <linux/fs.h>
+#include <sys/mman.h>
 
 using android::base::unique_fd;
 
@@ -29,8 +30,8 @@
     ASSERT_EQ(0, ashmem_set_prot_region(fd, prot));
 }
 
-void TestMmap(const unique_fd &fd, size_t size, int prot, void **region) {
-    *region = mmap(nullptr, size, prot, MAP_SHARED, fd, 0);
+void TestMmap(const unique_fd& fd, size_t size, int prot, void** region, off_t off = 0) {
+    *region = mmap(nullptr, size, prot, MAP_SHARED, fd, off);
     ASSERT_NE(MAP_FAILED, *region);
 }
 
@@ -38,6 +39,10 @@
     EXPECT_EQ(MAP_FAILED, mmap(nullptr, size, prot, MAP_SHARED, fd, 0));
 }
 
+void TestProtIs(const unique_fd& fd, int prot) {
+    EXPECT_EQ(prot, ioctl(fd, ASHMEM_GET_PROT_MASK));
+}
+
 void FillData(uint8_t* data, size_t dataLen) {
     for (size_t i = 0; i < dataLen; i++) {
         data[i] = i & 0xFF;
@@ -101,6 +106,63 @@
     EXPECT_EQ(0, munmap(region2, size));
 }
 
+TEST(AshmemTest, FileOperationsTest) {
+    unique_fd fd;
+    void* region;
+
+    // Allocate a 4-page buffer, but leave page-sized holes on either side
+    constexpr size_t size = PAGE_SIZE * 4;
+    constexpr size_t dataSize = PAGE_SIZE * 2;
+    constexpr size_t holeSize = PAGE_SIZE;
+    ASSERT_NO_FATAL_FAILURE(TestCreateRegion(size, fd, PROT_READ | PROT_WRITE));
+    ASSERT_NO_FATAL_FAILURE(TestMmap(fd, dataSize, PROT_READ | PROT_WRITE, &region, holeSize));
+
+    uint8_t data[dataSize];
+    FillData(data, dataSize);
+    memcpy(region, data, dataSize);
+
+    constexpr off_t dataStart = holeSize;
+    constexpr off_t dataEnd = dataStart + dataSize;
+
+    // The sequence of seeks below looks something like this:
+    //
+    // [    ][data][data][    ]
+    // --^                          lseek(99, SEEK_SET)
+    //   ------^                    lseek(dataStart, SEEK_CUR)
+    // ------^                      lseek(0, SEEK_DATA)
+    //       ------------^          lseek(dataStart, SEEK_HOLE)
+    //                      ^--     lseek(-99, SEEK_END)
+    //                ^------       lseek(-dataStart, SEEK_CUR)
+    const struct {
+        // lseek() parameters
+        off_t offset;
+        int whence;
+        // Expected lseek() return value
+        off_t ret;
+    } seeks[] = {
+        {99, SEEK_SET, 99},         {dataStart, SEEK_CUR, dataStart + 99},
+        {0, SEEK_DATA, dataStart},  {dataStart, SEEK_HOLE, dataEnd},
+        {-99, SEEK_END, size - 99}, {-dataStart, SEEK_CUR, dataEnd - 99},
+    };
+    for (const auto& cfg : seeks) {
+        errno = 0;
+        auto off = lseek(fd, cfg.offset, cfg.whence);
+        ASSERT_EQ(cfg.ret, off) << "lseek(" << cfg.offset << ", " << cfg.whence << ") failed"
+                                << (errno ? ": " : "") << (errno ? strerror(errno) : "");
+
+        if (off >= dataStart && off < dataEnd) {
+            off_t dataOff = off - dataStart;
+            ssize_t readSize = dataSize - dataOff;
+            uint8_t buf[readSize];
+
+            ASSERT_EQ(readSize, TEMP_FAILURE_RETRY(read(fd, buf, readSize)));
+            EXPECT_EQ(0, memcmp(buf, data + dataOff, readSize));
+        }
+    }
+
+    EXPECT_EQ(0, munmap(region, dataSize));
+}
+
 TEST(AshmemTest, ProtTest) {
     unique_fd fd;
     constexpr size_t size = PAGE_SIZE;
@@ -108,13 +170,25 @@
 
     ASSERT_NO_FATAL_FAILURE(TestCreateRegion(size, fd, PROT_READ));
     TestProtDenied(fd, size, PROT_WRITE);
+    TestProtIs(fd, PROT_READ);
     ASSERT_NO_FATAL_FAILURE(TestMmap(fd, size, PROT_READ, &region));
     EXPECT_EQ(0, munmap(region, size));
 
     ASSERT_NO_FATAL_FAILURE(TestCreateRegion(size, fd, PROT_WRITE));
     TestProtDenied(fd, size, PROT_READ);
+    TestProtIs(fd, PROT_WRITE);
     ASSERT_NO_FATAL_FAILURE(TestMmap(fd, size, PROT_WRITE, &region));
     EXPECT_EQ(0, munmap(region, size));
+
+    ASSERT_NO_FATAL_FAILURE(TestCreateRegion(size, fd, PROT_READ | PROT_WRITE));
+    TestProtIs(fd, PROT_READ | PROT_WRITE);
+    ASSERT_EQ(0, ashmem_set_prot_region(fd, PROT_READ));
+    errno = 0;
+    ASSERT_EQ(-1, ashmem_set_prot_region(fd, PROT_READ | PROT_WRITE))
+        << "kernel shouldn't allow adding protection bits";
+    EXPECT_EQ(EINVAL, errno);
+    TestProtIs(fd, PROT_READ);
+    TestProtDenied(fd, size, PROT_WRITE);
 }
 
 TEST(AshmemTest, ForkProtTest) {
diff --git a/libcutils/tests/fs_config.cpp b/libcutils/tests/fs_config.cpp
new file mode 100644
index 0000000..3917a0b
--- /dev/null
+++ b/libcutils/tests/fs_config.cpp
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <string>
+
+#include <gtest/gtest.h>
+
+#include <android-base/strings.h>
+
+#include <private/android_filesystem_config.h>
+
+extern const struct fs_path_config* __for_testing_only__android_dirs;
+extern const struct fs_path_config* __for_testing_only__android_files;
+
+static void check_one(const struct fs_path_config* paths, const std::string& prefix,
+                      const std::string& alternate) {
+    for (size_t idx = 0; paths[idx].prefix; ++idx) {
+        std::string path(paths[idx].prefix);
+        if (android::base::StartsWith(path, prefix.c_str())) {
+            path = alternate + path.substr(prefix.length());
+            size_t second;
+            for (second = 0; paths[second].prefix; ++second) {
+                if (path == paths[second].prefix) break;
+            }
+            if (!paths[second].prefix) {
+                // guaranteed to fail expectations, trigger test failure with
+                // a message that reports the violation as an inequality.
+                EXPECT_STREQ((prefix + path.substr(alternate.length())).c_str(), path.c_str());
+            }
+        }
+    }
+}
+
+static void check_two(const struct fs_path_config* paths, const std::string& prefix) {
+    ASSERT_FALSE(paths == nullptr);
+    std::string alternate = "system/" + prefix;
+    check_one(paths, prefix, alternate);
+    check_one(paths, alternate, prefix);
+}
+
+TEST(fs_config, vendor_dirs_alias) {
+    check_two(__for_testing_only__android_dirs, "vendor/");
+}
+
+TEST(fs_config, vendor_files_alias) {
+    check_two(__for_testing_only__android_files, "vendor/");
+}
+
+TEST(fs_config, oem_dirs_alias) {
+    check_two(__for_testing_only__android_dirs, "oem/");
+}
+
+TEST(fs_config, oem_files_alias) {
+    check_two(__for_testing_only__android_files, "oem/");
+}
+
+TEST(fs_config, odm_dirs_alias) {
+    check_two(__for_testing_only__android_dirs, "odm/");
+}
+
+TEST(fs_config, odm_files_alias) {
+    check_two(__for_testing_only__android_files, "odm/");
+}
diff --git a/liblog/fake_log_device.c b/liblog/fake_log_device.c
index ae7a334..1483c24 100644
--- a/liblog/fake_log_device.c
+++ b/liblog/fake_log_device.c
@@ -15,9 +15,7 @@
  */
 /*
  * Intercepts log messages intended for the Android log device.
- * When running in the context of the simulator, the messages are
- * passed on to the underlying (fake) log device.  When not in the
- * simulator, messages are printed to stderr.
+ * Messages are printed to stderr.
  */
 #include <ctype.h>
 #include <errno.h>
@@ -561,7 +559,8 @@
  *  tag (N bytes -- null-terminated ASCII string)
  *  message (N bytes -- null-terminated ASCII string)
  */
-static ssize_t logWritev(int fd, const struct iovec* vector, int count) {
+LIBLOG_HIDDEN ssize_t fakeLogWritev(int fd, const struct iovec* vector,
+                                    int count) {
   LogState* state;
 
   /* Make sure that no-one frees the LogState while we're using it.
@@ -626,8 +625,18 @@
 
 /*
  * Free up our state and close the fake descriptor.
+ *
+ * The logger API has no means or need to 'stop' or 'close' using the logs,
+ * and as such, there is no way for that 'stop' or 'close' to translate into
+ * a close operation to the fake log handler. fakeLogClose is provided for
+ * completeness only.
+ *
+ * We have no intention of adding a log close operation as it would complicate
+ * every user of the logging API with no gain since the only valid place to
+ * call is in the exit handler. Logging can continue in the exit handler to
+ * help debug HOST tools ...
  */
-static int logClose(int fd) {
+LIBLOG_HIDDEN int fakeLogClose(int fd) {
   deleteFakeFd(fd);
   return 0;
 }
@@ -635,7 +644,7 @@
 /*
  * Open a log output device and return a fake fd.
  */
-static int logOpen(const char* pathName, int flags __unused) {
+LIBLOG_HIDDEN int fakeLogOpen(const char* pathName) {
   LogState* logState;
   int fd = -1;
 
@@ -654,66 +663,6 @@
   return fd;
 }
 
-/*
- * Runtime redirection.  If this binary is running in the simulator,
- * just pass log messages to the emulated device.  If it's running
- * outside of the simulator, write the log messages to stderr.
- */
-
-static int (*redirectOpen)(const char* pathName, int flags) = NULL;
-static int (*redirectClose)(int fd) = NULL;
-static ssize_t (*redirectWritev)(int fd, const struct iovec* vector,
-                                 int count) = NULL;
-
-static void setRedirects() {
-  const char* ws;
-
-  /* Wrapsim sets this environment variable on children that it's
-   * created using its LD_PRELOAD wrapper.
-   */
-  ws = getenv("ANDROID_WRAPSIM");
-  if (ws != NULL && strcmp(ws, "1") == 0) {
-    /* We're running inside wrapsim, so we can just write to the device. */
-    redirectOpen = (int (*)(const char* pathName, int flags))open;
-    redirectClose = close;
-    redirectWritev = writev;
-  } else {
-    /* There's no device to delegate to; handle the logging ourselves. */
-    redirectOpen = logOpen;
-    redirectClose = logClose;
-    redirectWritev = logWritev;
-  }
-}
-
-LIBLOG_HIDDEN int fakeLogOpen(const char* pathName, int flags) {
-  if (redirectOpen == NULL) {
-    setRedirects();
-  }
-  return redirectOpen(pathName, flags);
-}
-
-/*
- * The logger API has no means or need to 'stop' or 'close' using the logs,
- * and as such, there is no way for that 'stop' or 'close' to translate into
- * a close operation to the fake log handler. fakeLogClose is provided for
- * completeness only.
- *
- * We have no intention of adding a log close operation as it would complicate
- * every user of the logging API with no gain since the only valid place to
- * call is in the exit handler. Logging can continue in the exit handler to
- * help debug HOST tools ...
- */
-LIBLOG_HIDDEN int fakeLogClose(int fd) {
-  /* Assume that open() was called first. */
-  return redirectClose(fd);
-}
-
-LIBLOG_HIDDEN ssize_t fakeLogWritev(int fd, const struct iovec* vector,
-                                    int count) {
-  /* Assume that open() was called first. */
-  return redirectWritev(fd, vector, count);
-}
-
 LIBLOG_HIDDEN ssize_t __send_log_msg(char* buf __unused,
                                      size_t buf_size __unused) {
   return -ENODEV;
diff --git a/liblog/fake_log_device.h b/liblog/fake_log_device.h
index 462d026..7b0e745 100644
--- a/liblog/fake_log_device.h
+++ b/liblog/fake_log_device.h
@@ -23,7 +23,7 @@
 
 struct iovec;
 
-LIBLOG_HIDDEN int fakeLogOpen(const char* pathName, int flags);
+LIBLOG_HIDDEN int fakeLogOpen(const char* pathName);
 LIBLOG_HIDDEN int fakeLogClose(int fd);
 LIBLOG_HIDDEN ssize_t fakeLogWritev(int fd, const struct iovec* vector,
                                     int count);
diff --git a/liblog/fake_writer.c b/liblog/fake_writer.c
index 969661a..403dc72 100644
--- a/liblog/fake_writer.c
+++ b/liblog/fake_writer.c
@@ -55,9 +55,9 @@
       continue;
     }
     snprintf(buf, sizeof(buf), "/dev/log_%s", android_log_id_to_name(i));
-    logFds[i] = fakeLogOpen(buf, O_WRONLY);
+    logFds[i] = fakeLogOpen(buf);
     if (logFds[i] < 0) {
-      fprintf(stderr, "fakeLogOpen(%s, O_WRONLY) failed\n", buf);
+      fprintf(stderr, "fakeLogOpen(%s) failed\n", buf);
     }
   }
   return 0;
diff --git a/liblog/tests/Android.mk b/liblog/tests/Android.mk
index 0e6432c..ab96429 100644
--- a/liblog/tests/Android.mk
+++ b/liblog/tests/Android.mk
@@ -55,6 +55,7 @@
     -fno-builtin \
 
 test_src_files := \
+    libc_test.cpp \
     liblog_test_default.cpp \
     liblog_test_local.cpp \
     liblog_test_stderr.cpp \
@@ -65,14 +66,6 @@
     log_system_test.cpp \
     log_time_test.cpp
 
-# to prevent breaking the build if bionic not relatively visible to us
-ifneq ($(wildcard $(LOCAL_PATH)/../../../../bionic/libc/bionic/libc_logging.cpp),)
-
-test_src_files += \
-    libc_test.cpp
-
-endif
-
 # Build tests for the device (with .so). Run with:
 #   adb shell /data/nativetest/liblog-unit-tests/liblog-unit-tests
 include $(CLEAR_VARS)
diff --git a/libmemunreachable/.clang-format b/libmemunreachable/.clang-format
new file mode 120000
index 0000000..fd0645f
--- /dev/null
+++ b/libmemunreachable/.clang-format
@@ -0,0 +1 @@
+../.clang-format-2
\ No newline at end of file
diff --git a/libmemunreachable/Android.bp b/libmemunreachable/Android.bp
index 4662368..cdac76b 100644
--- a/libmemunreachable/Android.bp
+++ b/libmemunreachable/Android.bp
@@ -9,8 +9,16 @@
     clang: true,
     shared_libs: [
         "libbase",
-        "liblog",
     ],
+
+    target: {
+        android: {
+            static_libs: ["libasync_safe"],
+        },
+        host: {
+            shared_libs: ["liblog"],
+        }
+    },
 }
 
 cc_library_shared {
@@ -30,7 +38,6 @@
 
     static_libs: [
         "libc_malloc_debug_backtrace",
-        "libc_logging",
     ],
     // Only need this for arm since libc++ uses its own unwind code that
     // doesn't mix with the other default unwind code.
diff --git a/libmemunreachable/HeapWalker.cpp b/libmemunreachable/HeapWalker.cpp
index 62366f2..c365ae5 100644
--- a/libmemunreachable/HeapWalker.cpp
+++ b/libmemunreachable/HeapWalker.cpp
@@ -42,11 +42,9 @@
   } else {
     Range overlap = inserted.first->first;
     if (overlap != range) {
-      ALOGE("range %p-%p overlaps with existing range %p-%p",
-          reinterpret_cast<void*>(begin),
-          reinterpret_cast<void*>(end),
-          reinterpret_cast<void*>(overlap.begin),
-          reinterpret_cast<void*>(overlap.end));
+      MEM_ALOGE("range %p-%p overlaps with existing range %p-%p", reinterpret_cast<void*>(begin),
+                reinterpret_cast<void*>(end), reinterpret_cast<void*>(overlap.begin),
+                reinterpret_cast<void*>(overlap.end));
     }
     return false;
   }
@@ -154,7 +152,7 @@
 
   void* ret = mmap(page, page_size, PROT_READ, MAP_ANONYMOUS|MAP_PRIVATE|MAP_FIXED, -1, 0);
   if (ret == MAP_FAILED) {
-    ALOGE("failed to map page at %p: %s", page, strerror(errno));
+    MEM_ALOGE("failed to map page at %p: %s", page, strerror(errno));
     return false;
   }
 
@@ -167,7 +165,7 @@
     handler.reset();
     return;
   }
-  ALOGW("failed to read page at %p, signal %d", si->si_addr, signal);
+  MEM_ALOGW("failed to read page at %p, signal %d", si->si_addr, signal);
   if (!MapOverPage(si->si_addr)) {
     handler.reset();
   }
diff --git a/libmemunreachable/LeakPipe.cpp b/libmemunreachable/LeakPipe.cpp
index 080f8a7..78117e2 100644
--- a/libmemunreachable/LeakPipe.cpp
+++ b/libmemunreachable/LeakPipe.cpp
@@ -44,11 +44,11 @@
 
   int ret = sendmsg(sock, &hdr, 0);
   if (ret < 0) {
-    ALOGE("failed to send fd: %s", strerror(errno));
+    MEM_ALOGE("failed to send fd: %s", strerror(errno));
     return false;
   }
   if (ret == 0) {
-    ALOGE("eof when sending fd");
+    MEM_ALOGE("eof when sending fd");
     return false;
   }
 
@@ -71,17 +71,17 @@
 
   int ret = recvmsg(sock, &hdr, 0);
   if (ret < 0) {
-    ALOGE("failed to receive fd: %s", strerror(errno));
+    MEM_ALOGE("failed to receive fd: %s", strerror(errno));
     return -1;
   }
   if (ret == 0) {
-    ALOGE("eof when receiving fd");
+    MEM_ALOGE("eof when receiving fd");
     return -1;
   }
 
   struct cmsghdr* cmsg = CMSG_FIRSTHDR(&hdr);
   if (cmsg == NULL || cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS) {
-    ALOGE("missing fd while receiving fd");
+    MEM_ALOGE("missing fd while receiving fd");
     return -1;
   }
 
diff --git a/libmemunreachable/LeakPipe.h b/libmemunreachable/LeakPipe.h
index 3f4e0b7..3ea2d8f 100644
--- a/libmemunreachable/LeakPipe.h
+++ b/libmemunreachable/LeakPipe.h
@@ -36,7 +36,7 @@
   LeakPipe() {
     int ret = socketpair(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0, sv_);
     if (ret < 0) {
-      LOG_ALWAYS_FATAL("failed to create socketpair: %s", strerror(errno));
+      MEM_LOG_ALWAYS_FATAL("failed to create socketpair: %s", strerror(errno));
     }
   }
 
@@ -105,10 +105,10 @@
     bool Send(const T& value) {
       ssize_t ret = TEMP_FAILURE_RETRY(write(fd_, &value, sizeof(T)));
       if (ret < 0) {
-        ALOGE("failed to send value: %s", strerror(errno));
+        MEM_ALOGE("failed to send value: %s", strerror(errno));
         return false;
       } else if (static_cast<size_t>(ret) != sizeof(T)) {
-        ALOGE("eof while writing value");
+        MEM_ALOGE("eof while writing value");
         return false;
       }
 
@@ -124,10 +124,10 @@
 
       ssize_t ret = TEMP_FAILURE_RETRY(write(fd_, vector.data(), size));
       if (ret < 0) {
-        ALOGE("failed to send vector: %s", strerror(errno));
+        MEM_ALOGE("failed to send vector: %s", strerror(errno));
         return false;
       } else if (static_cast<size_t>(ret) != size) {
-        ALOGE("eof while writing vector");
+        MEM_ALOGE("eof while writing vector");
         return false;
       }
 
@@ -143,10 +143,10 @@
     bool Receive(T* value) {
       ssize_t ret = TEMP_FAILURE_RETRY(read(fd_, reinterpret_cast<void*>(value), sizeof(T)));
       if (ret < 0) {
-        ALOGE("failed to receive value: %s", strerror(errno));
+        MEM_ALOGE("failed to receive value: %s", strerror(errno));
         return false;
       } else if (static_cast<size_t>(ret) != sizeof(T)) {
-        ALOGE("eof while receiving value");
+        MEM_ALOGE("eof while receiving value");
         return false;
       }
 
@@ -166,10 +166,10 @@
       while (size > 0) {
         ssize_t ret = TEMP_FAILURE_RETRY(read(fd_, ptr, size));
         if (ret < 0) {
-          ALOGE("failed to send vector: %s", strerror(errno));
+          MEM_ALOGE("failed to send vector: %s", strerror(errno));
           return false;
         } else if (ret == 0) {
-          ALOGE("eof while reading vector");
+          MEM_ALOGE("eof while reading vector");
           return false;
         }
         size -= ret;
diff --git a/libmemunreachable/MemUnreachable.cpp b/libmemunreachable/MemUnreachable.cpp
index ac19a66..e7c0beb 100644
--- a/libmemunreachable/MemUnreachable.cpp
+++ b/libmemunreachable/MemUnreachable.cpp
@@ -78,51 +78,49 @@
 
 bool MemUnreachable::CollectAllocations(const allocator::vector<ThreadInfo>& threads,
     const allocator::vector<Mapping>& mappings) {
-  ALOGI("searching process %d for allocations", pid_);
+  MEM_ALOGI("searching process %d for allocations", pid_);
   allocator::vector<Mapping> heap_mappings{mappings};
   allocator::vector<Mapping> anon_mappings{mappings};
   allocator::vector<Mapping> globals_mappings{mappings};
   allocator::vector<Mapping> stack_mappings{mappings};
-  if (!ClassifyMappings(mappings, heap_mappings, anon_mappings,
-      globals_mappings, stack_mappings)) {
+  if (!ClassifyMappings(mappings, heap_mappings, anon_mappings, globals_mappings, stack_mappings)) {
     return false;
   }
 
   for (auto it = heap_mappings.begin(); it != heap_mappings.end(); it++) {
-    ALOGV("Heap mapping %" PRIxPTR "-%" PRIxPTR " %s", it->begin, it->end, it->name);
-    HeapIterate(*it, [&](uintptr_t base, size_t size) {
-      heap_walker_.Allocation(base, base + size);
-    });
+    MEM_ALOGV("Heap mapping %" PRIxPTR "-%" PRIxPTR " %s", it->begin, it->end, it->name);
+    HeapIterate(*it,
+                [&](uintptr_t base, size_t size) { heap_walker_.Allocation(base, base + size); });
   }
 
   for (auto it = anon_mappings.begin(); it != anon_mappings.end(); it++) {
-    ALOGV("Anon mapping %" PRIxPTR "-%" PRIxPTR " %s", it->begin, it->end, it->name);
+    MEM_ALOGV("Anon mapping %" PRIxPTR "-%" PRIxPTR " %s", it->begin, it->end, it->name);
     heap_walker_.Allocation(it->begin, it->end);
   }
 
   for (auto it = globals_mappings.begin(); it != globals_mappings.end(); it++) {
-    ALOGV("Globals mapping %" PRIxPTR "-%" PRIxPTR " %s", it->begin, it->end, it->name);
+    MEM_ALOGV("Globals mapping %" PRIxPTR "-%" PRIxPTR " %s", it->begin, it->end, it->name);
     heap_walker_.Root(it->begin, it->end);
   }
 
   for (auto thread_it = threads.begin(); thread_it != threads.end(); thread_it++) {
     for (auto it = stack_mappings.begin(); it != stack_mappings.end(); it++) {
       if (thread_it->stack.first >= it->begin && thread_it->stack.first <= it->end) {
-        ALOGV("Stack %" PRIxPTR "-%" PRIxPTR " %s", thread_it->stack.first, it->end, it->name);
+        MEM_ALOGV("Stack %" PRIxPTR "-%" PRIxPTR " %s", thread_it->stack.first, it->end, it->name);
         heap_walker_.Root(thread_it->stack.first, it->end);
       }
     }
     heap_walker_.Root(thread_it->regs);
   }
 
-  ALOGI("searching done");
+  MEM_ALOGI("searching done");
 
   return true;
 }
 
 bool MemUnreachable::GetUnreachableMemory(allocator::vector<Leak>& leaks,
     size_t limit, size_t* num_leaks, size_t* leak_bytes) {
-  ALOGI("sweeping process %d for unreachable memory", pid_);
+  MEM_ALOGI("sweeping process %d for unreachable memory", pid_);
   leaks.clear();
 
   if (!heap_walker_.DetectLeaks()) {
@@ -133,9 +131,9 @@
   allocator::vector<Range> leaked1{allocator_};
   heap_walker_.Leaked(leaked1, 0, num_leaks, leak_bytes);
 
-  ALOGI("sweeping done");
+  MEM_ALOGI("sweeping done");
 
-  ALOGI("folding related leaks");
+  MEM_ALOGI("folding related leaks");
 
   LeakFolding folding(allocator_, heap_walker_);
   if (!folding.FoldLeaks()) {
@@ -188,7 +186,7 @@
         std::min(leak->size, Leak::contents_length));
   }
 
-  ALOGI("folding done");
+  MEM_ALOGI("folding done");
 
   std::sort(leaks.begin(), leaks.end(), [](const Leak& a, const Leak& b) {
     return a.total_size > b.total_size;
@@ -276,7 +274,7 @@
     /////////////////////////////////////////////
     // Collection thread
     /////////////////////////////////////////////
-    ALOGI("collecting thread info for process %d...", parent_pid);
+    MEM_ALOGI("collecting thread info for process %d...", parent_pid);
 
     ThreadCapture thread_capture(parent_pid, heap);
     allocator::vector<ThreadInfo> thread_info(heap);
@@ -351,7 +349,7 @@
     } else {
       // Nothing left to do in the collection thread, return immediately,
       // releasing all the captured threads.
-      ALOGI("collection thread done");
+      MEM_ALOGI("collection thread done");
       return 0;
     }
   }};
@@ -397,10 +395,10 @@
     return false;
   }
 
-  ALOGI("unreachable memory detection done");
-  ALOGE("%zu bytes in %zu allocation%s unreachable out of %zu bytes in %zu allocation%s",
-      info.leak_bytes, info.num_leaks, plural(info.num_leaks),
-      info.allocation_bytes, info.num_allocations, plural(info.num_allocations));
+  MEM_ALOGI("unreachable memory detection done");
+  MEM_ALOGE("%zu bytes in %zu allocation%s unreachable out of %zu bytes in %zu allocation%s",
+            info.leak_bytes, info.num_leaks, plural(info.num_leaks), info.allocation_bytes,
+            info.num_allocations, plural(info.num_allocations));
   return true;
 }
 
@@ -517,7 +515,7 @@
   }
 
   for (auto it = info.leaks.begin(); it != info.leaks.end(); it++) {
-    ALOGE("%s", it->ToString(log_contents).c_str());
+    MEM_ALOGE("%s", it->ToString(log_contents).c_str());
   }
   return true;
 }
diff --git a/libmemunreachable/PtracerThread.cpp b/libmemunreachable/PtracerThread.cpp
index 4e3c41e..73b0493 100644
--- a/libmemunreachable/PtracerThread.cpp
+++ b/libmemunreachable/PtracerThread.cpp
@@ -70,7 +70,7 @@
     child_pid_(0) {
   stack_ = std::make_unique<Stack>(PTHREAD_STACK_MIN);
   if (stack_->top() == nullptr) {
-    LOG_ALWAYS_FATAL("failed to mmap child stack: %s", strerror(errno));
+    MEM_LOG_ALWAYS_FATAL("failed to mmap child stack: %s", strerror(errno));
   }
 
   func_ = std::function<int()>{[&, func]() -> int {
@@ -102,7 +102,7 @@
        CLONE_VM|CLONE_FS|CLONE_FILES/*|CLONE_UNTRACED*/,
        reinterpret_cast<void*>(&func_));
   if (child_pid_ < 0) {
-    ALOGE("failed to clone child: %s", strerror(errno));
+    MEM_ALOGE("failed to clone child: %s", strerror(errno));
     return false;
   }
 
@@ -120,7 +120,7 @@
   int status;
   int ret = TEMP_FAILURE_RETRY(waitpid(child_pid_, &status, __WALL));
   if (ret < 0) {
-    ALOGE("waitpid %d failed: %s", child_pid_, strerror(errno));
+    MEM_ALOGE("waitpid %d failed: %s", child_pid_, strerror(errno));
     return -1;
   }
 
@@ -131,7 +131,7 @@
   } else if (WIFSIGNALED(status)) {
     return -WTERMSIG(status);
   } else {
-    ALOGE("unexpected status %x", status);
+    MEM_ALOGE("unexpected status %x", status);
     return -1;
   }
 }
diff --git a/libmemunreachable/ScopedPipe.h b/libmemunreachable/ScopedPipe.h
index 9beef9a..7f44953 100644
--- a/libmemunreachable/ScopedPipe.h
+++ b/libmemunreachable/ScopedPipe.h
@@ -26,7 +26,7 @@
   ScopedPipe() : pipefd_{-1, -1} {
     int ret = pipe2(pipefd_, O_CLOEXEC);
     if (ret < 0) {
-      LOG_ALWAYS_FATAL("failed to open pipe");
+      MEM_LOG_ALWAYS_FATAL("failed to open pipe");
     }
   }
   ~ScopedPipe() {
diff --git a/libmemunreachable/ScopedSignalHandler.h b/libmemunreachable/ScopedSignalHandler.h
index 1fd9d4d..ada2ae4 100644
--- a/libmemunreachable/ScopedSignalHandler.h
+++ b/libmemunreachable/ScopedSignalHandler.h
@@ -37,22 +37,18 @@
 
   template <class F>
   void install(int signal, F&& f) {
-    LOG_ALWAYS_FATAL_IF(signal_ != -1, "ScopedSignalHandler already installed");
+    MEM_LOG_ALWAYS_FATAL_IF(signal_ != -1, "ScopedSignalHandler already installed");
 
     handler_ = SignalFn(std::allocator_arg, allocator_,
-        [=](int signal, siginfo_t* si, void* uctx) {
-          f(*this, signal, si, uctx);
-        });
+                        [=](int signal, siginfo_t* si, void* uctx) { f(*this, signal, si, uctx); });
 
-    struct sigaction act{};
-    act.sa_sigaction = [](int signal, siginfo_t* si, void* uctx) {
-      handler_(signal, si, uctx);
-    };
+    struct sigaction act {};
+    act.sa_sigaction = [](int signal, siginfo_t* si, void* uctx) { handler_(signal, si, uctx); };
     act.sa_flags = SA_SIGINFO;
 
     int ret = sigaction(signal, &act, &old_act_);
     if (ret < 0) {
-      LOG_ALWAYS_FATAL("failed to install segfault handler: %s", strerror(errno));
+      MEM_LOG_ALWAYS_FATAL("failed to install segfault handler: %s", strerror(errno));
     }
 
     signal_ = signal;
@@ -62,7 +58,7 @@
     if (signal_ != -1) {
       int ret = sigaction(signal_, &old_act_, NULL);
       if (ret < 0) {
-        ALOGE("failed to uninstall segfault handler");
+        MEM_ALOGE("failed to uninstall segfault handler");
       }
       handler_ = SignalFn{};
       signal_ = -1;
diff --git a/libmemunreachable/ThreadCapture.cpp b/libmemunreachable/ThreadCapture.cpp
index 9155c29..3891f2d 100644
--- a/libmemunreachable/ThreadCapture.cpp
+++ b/libmemunreachable/ThreadCapture.cpp
@@ -110,7 +110,7 @@
 
   android::base::unique_fd fd(open(path, O_CLOEXEC | O_DIRECTORY | O_RDONLY));
   if (fd == -1) {
-    ALOGE("failed to open %s: %s", path, strerror(errno));
+    MEM_ALOGE("failed to open %s: %s", path, strerror(errno));
     return false;
   }
 
@@ -126,7 +126,7 @@
   do {
     nread = syscall(SYS_getdents64, fd.get(), dirent_buf, sizeof(dirent_buf));
     if (nread < 0) {
-      ALOGE("failed to get directory entries from %s: %s", path, strerror(errno));
+      MEM_ALOGE("failed to get directory entries from %s: %s", path, strerror(errno));
       return false;
     } else if (nread > 0) {
       ssize_t off = 0;
@@ -177,8 +177,7 @@
 void ThreadCaptureImpl::PtraceDetach(pid_t tid, unsigned int signal) {
   void* sig_ptr = reinterpret_cast<void*>(static_cast<uintptr_t>(signal));
   if (ptrace(PTRACE_DETACH, tid, NULL, sig_ptr) < 0 && errno != ESRCH) {
-    ALOGE("failed to detach from thread %d of process %d: %s", tid, pid_,
-        strerror(errno));
+    MEM_ALOGE("failed to detach from thread %d of process %d: %s", tid, pid_, strerror(errno));
   }
 }
 
@@ -187,8 +186,7 @@
 int ThreadCaptureImpl::PtraceAttach(pid_t tid) {
   int ret = ptrace(PTRACE_SEIZE, tid, NULL, NULL);
   if (ret < 0) {
-    ALOGE("failed to attach to thread %d of process %d: %s", tid, pid_,
-        strerror(errno));
+    MEM_ALOGE("failed to attach to thread %d of process %d: %s", tid, pid_, strerror(errno));
     return -1;
   }
 
@@ -200,8 +198,7 @@
     if (errno == ESRCH) {
       return 0;
     } else {
-      ALOGE("failed to interrupt thread %d of process %d: %s", tid, pid_,
-          strerror(errno));
+      MEM_ALOGE("failed to interrupt thread %d of process %d: %s", tid, pid_, strerror(errno));
       PtraceDetach(tid, 0);
       return -1;
     }
@@ -219,8 +216,7 @@
   iovec.iov_len = sizeof(regs);
 
   if (ptrace(PTRACE_GETREGSET, tid, reinterpret_cast<void*>(NT_PRSTATUS), &iovec)) {
-    ALOGE("ptrace getregset for thread %d of process %d failed: %s",
-        tid, pid_, strerror(errno));
+    MEM_ALOGE("ptrace getregset for thread %d of process %d failed: %s", tid, pid_, strerror(errno));
     return false;
   }
 
@@ -258,15 +254,13 @@
 
   int status = 0;
   if (TEMP_FAILURE_RETRY(waitpid(tid, &status, __WALL)) < 0) {
-    ALOGE("failed to wait for pause of thread %d of process %d: %s", tid, pid_,
-        strerror(errno));
+    MEM_ALOGE("failed to wait for pause of thread %d of process %d: %s", tid, pid_, strerror(errno));
     PtraceDetach(tid, 0);
     return -1;
   }
 
   if (!WIFSTOPPED(status)) {
-    ALOGE("thread %d of process %d was not paused after waitpid, killed?",
-        tid, pid_);
+    MEM_ALOGE("thread %d of process %d was not paused after waitpid, killed?", tid, pid_);
     return 0;
   }
 
@@ -285,8 +279,8 @@
         // normal ptrace interrupt stop
         break;
       default:
-        ALOGE("unexpected signal %d with PTRACE_EVENT_STOP for thread %d of process %d",
-            signal, tid, pid_);
+        MEM_ALOGE("unexpected signal %d with PTRACE_EVENT_STOP for thread %d of process %d", signal,
+                  tid, pid_);
         return -1;
     }
   } else {
diff --git a/libmemunreachable/log.h b/libmemunreachable/log.h
index cdfbfd9..1725c53 100644
--- a/libmemunreachable/log.h
+++ b/libmemunreachable/log.h
@@ -19,6 +19,32 @@
 
 #define LOG_TAG "libmemunreachable"
 
+#if defined(__ANDROID__)
+
+#include <async_safe/log.h>
+
+#define MEM_ALOGE(...) async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, ##__VA_ARGS__)
+#define MEM_ALOGW(...) async_safe_format_log(ANDROID_LOG_WARN, LOG_TAG, ##__VA_ARGS__)
+#define MEM_ALOGI(...) async_safe_format_log(ANDROID_LOG_INFO, LOG_TAG, ##__VA_ARGS__)
+#define MEM_ALOGV(...) async_safe_format_log(ANDROID_LOG_VERBOSE, LOG_TAG, ##__VA_ARGS__)
+
+#define MEM_LOG_ALWAYS_FATAL(...) async_safe_fatal(__VA_ARGS__)
+
+#define MEM_LOG_ALWAYS_FATAL_IF(cond, ...) \
+  ((__predict_false(cond)) ? async_safe_fatal(__VA_ARGS__) : (void)0)
+
+#else
+
 #include <log/log.h>
 
+#define MEM_ALOGW ALOGW
+#define MEM_ALOGE ALOGE
+#define MEM_ALOGV ALOGV
+#define MEM_ALOGI ALOGI
+
+#define MEM_LOG_ALWAYS_FATAL LOG_ALWAYS_FATAL
+#define MEM_LOG_ALWAYS_FATAL_IF LOG_ALWAYS_FATAL_IF
+
+#endif
+
 #endif // LIBMEMUNREACHABLE_LOG_H_
diff --git a/libmemunreachable/tests/DisableMalloc_test.cpp b/libmemunreachable/tests/DisableMalloc_test.cpp
index ea5c22c..4e6155b 100644
--- a/libmemunreachable/tests/DisableMalloc_test.cpp
+++ b/libmemunreachable/tests/DisableMalloc_test.cpp
@@ -74,7 +74,7 @@
     {
       alarm(100ms);
       ScopedDisableMalloc disable_malloc;
-      char* ptr = new(char);
+      char* ptr = new (std::nothrow)(char);
       ASSERT_NE(ptr, nullptr);
       delete(ptr);
     }
@@ -89,6 +89,8 @@
       alarm(250ms);
       ScopedDisableMalloc disable_malloc;
       delete(ptr);
+      // Force ptr usage or this code gets optimized away by the arm64 compiler.
+      ASSERT_NE(ptr, nullptr);
     }
   }, "");
 }
diff --git a/libmemunreachable/tests/MemUnreachable_test.cpp b/libmemunreachable/tests/MemUnreachable_test.cpp
index 2ae3db8..71da365 100644
--- a/libmemunreachable/tests/MemUnreachable_test.cpp
+++ b/libmemunreachable/tests/MemUnreachable_test.cpp
@@ -23,8 +23,6 @@
 
 #include <memunreachable/memunreachable.h>
 
-void* ptr;
-
 class HiddenPointer {
  public:
   explicit HiddenPointer(size_t size = 256) {
@@ -92,10 +90,12 @@
   }
 }
 
+void* g_ptr;
+
 TEST(MemunreachableTest, global) {
   HiddenPointer hidden_ptr;
 
-  ptr = hidden_ptr.Get();
+  g_ptr = hidden_ptr.Get();
 
   {
     UnreachableMemoryInfo info;
@@ -104,7 +104,7 @@
     ASSERT_EQ(0U, info.leaks.size());
   }
 
-  ptr = NULL;
+  g_ptr = nullptr;
 
   {
     UnreachableMemoryInfo info;
@@ -126,7 +126,7 @@
 TEST(MemunreachableTest, tls) {
   HiddenPointer hidden_ptr;
   pthread_key_t key;
-  pthread_key_create(&key, NULL);
+  pthread_key_create(&key, nullptr);
 
   pthread_setspecific(key, hidden_ptr.Get());
 
diff --git a/libmemunreachable/tests/ThreadCapture_test.cpp b/libmemunreachable/tests/ThreadCapture_test.cpp
index 41ed84e..44aabd7 100644
--- a/libmemunreachable/tests/ThreadCapture_test.cpp
+++ b/libmemunreachable/tests/ThreadCapture_test.cpp
@@ -55,7 +55,7 @@
     threads_.reserve(threads);
     tids_.reserve(threads);
     for (unsigned int i = 0; i < threads; i++) {
-      threads_.emplace_back([&, i, threads, this]() {
+      threads_.emplace_back([&, threads, this]() {
         {
           std::lock_guard<std::mutex> lk(m_);
           tids_.push_back(gettid());
diff --git a/libnativebridge/Android.bp b/libnativebridge/Android.bp
index 5fb56f2..377b7dd 100644
--- a/libnativebridge/Android.bp
+++ b/libnativebridge/Android.bp
@@ -22,3 +22,5 @@
         },
     },
 }
+
+subdirs = ["tests"]
\ No newline at end of file
diff --git a/libnativebridge/tests/Android.bp b/libnativebridge/tests/Android.bp
new file mode 100644
index 0000000..efd3978
--- /dev/null
+++ b/libnativebridge/tests/Android.bp
@@ -0,0 +1,53 @@
+//
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_defaults {
+    name: "libnativebridge-dummy-defaults",
+
+    host_supported: true,
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+    ],
+    cppflags: ["-fvisibility=protected"],
+    target: {
+        android: {
+            shared_libs: ["libdl"],
+        },
+        host: {
+            host_ldlibs: ["-ldl"],
+        },
+    },
+}
+
+cc_library_shared {
+    name: "libnativebridge-dummy",
+    srcs: ["DummyNativeBridge.cpp"],
+    defaults: ["libnativebridge-dummy-defaults"],
+}
+
+cc_library_shared {
+    name: "libnativebridge2-dummy",
+    srcs: ["DummyNativeBridge2.cpp"],
+    defaults: ["libnativebridge-dummy-defaults"],
+}
+
+cc_library_shared {
+    name: "libnativebridge3-dummy",
+    srcs: ["DummyNativeBridge3.cpp"],
+    defaults: ["libnativebridge-dummy-defaults"],
+}
diff --git a/libnativebridge/tests/Android.mk b/libnativebridge/tests/Android.mk
index c9468f0..70b3fcc 100644
--- a/libnativebridge/tests/Android.mk
+++ b/libnativebridge/tests/Android.mk
@@ -1,8 +1,6 @@
 # Build the unit tests.
 LOCAL_PATH := $(call my-dir)
 
-include $(LOCAL_PATH)/Android.nativebridge-dummy.mk
-
 include $(CLEAR_VARS)
 
 # Build the unit tests.
diff --git a/libnativebridge/tests/Android.nativebridge-dummy.mk b/libnativebridge/tests/Android.nativebridge-dummy.mk
deleted file mode 100644
index 2d78be0..0000000
--- a/libnativebridge/tests/Android.nativebridge-dummy.mk
+++ /dev/null
@@ -1,108 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-
-NATIVE_BRIDGE_COMMON_SRC_FILES := \
-  DummyNativeBridge.cpp
-
-# Shared library for target
-# ========================================================
-include $(CLEAR_VARS)
-
-LOCAL_MODULE:= libnativebridge-dummy
-
-LOCAL_SRC_FILES:= $(NATIVE_BRIDGE_COMMON_SRC_FILES)
-LOCAL_CLANG := true
-LOCAL_CFLAGS += -Werror -Wall
-LOCAL_CPPFLAGS := -fvisibility=protected
-LOCAL_SHARED_LIBRARIES := libdl
-LOCAL_MULTILIB := both
-
-include $(BUILD_SHARED_LIBRARY)
-
-# Shared library for host
-# ========================================================
-include $(CLEAR_VARS)
-
-LOCAL_MODULE:= libnativebridge-dummy
-
-LOCAL_SRC_FILES:= $(NATIVE_BRIDGE_COMMON_SRC_FILES)
-LOCAL_CLANG := true
-LOCAL_CFLAGS += -Werror -Wall
-LOCAL_CPPFLAGS := -fvisibility=protected
-LOCAL_LDFLAGS := -ldl
-LOCAL_MULTILIB := both
-
-include $(BUILD_HOST_SHARED_LIBRARY)
-
-
-# v2.
-
-NATIVE_BRIDGE2_COMMON_SRC_FILES := \
-  DummyNativeBridge2.cpp
-
-# Shared library for target
-# ========================================================
-include $(CLEAR_VARS)
-
-LOCAL_MODULE:= libnativebridge2-dummy
-
-LOCAL_SRC_FILES:= $(NATIVE_BRIDGE2_COMMON_SRC_FILES)
-LOCAL_CLANG := true
-LOCAL_CFLAGS += -Werror -Wall
-LOCAL_CPPFLAGS := -fvisibility=protected
-LOCAL_SHARED_LIBRARIES := libdl
-LOCAL_MULTILIB := both
-
-include $(BUILD_SHARED_LIBRARY)
-
-# Shared library for host
-# ========================================================
-include $(CLEAR_VARS)
-
-LOCAL_MODULE:= libnativebridge2-dummy
-
-LOCAL_SRC_FILES:= $(NATIVE_BRIDGE2_COMMON_SRC_FILES)
-LOCAL_CLANG := true
-LOCAL_CFLAGS += -Werror -Wall
-LOCAL_CPPFLAGS := -fvisibility=protected
-LOCAL_LDFLAGS := -ldl
-LOCAL_MULTILIB := both
-
-include $(BUILD_HOST_SHARED_LIBRARY)
-
-
-# v3.
-
-NATIVE_BRIDGE3_COMMON_SRC_FILES := \
-  DummyNativeBridge3.cpp
-
-# Shared library for target
-# ========================================================
-include $(CLEAR_VARS)
-
-LOCAL_MODULE:= libnativebridge3-dummy
-
-LOCAL_SRC_FILES:= $(NATIVE_BRIDGE3_COMMON_SRC_FILES)
-LOCAL_CLANG := true
-LOCAL_CFLAGS += -Werror -Wall
-LOCAL_CPPFLAGS := -std=gnu++11 -fvisibility=protected
-LOCAL_LDFLAGS := -ldl
-LOCAL_MULTILIB := both
-
-include $(BUILD_SHARED_LIBRARY)
-
-# Shared library for host
-# ========================================================
-include $(CLEAR_VARS)
-
-LOCAL_MODULE:= libnativebridge3-dummy
-
-LOCAL_SRC_FILES:= $(NATIVE_BRIDGE3_COMMON_SRC_FILES)
-LOCAL_CLANG := true
-LOCAL_CFLAGS += -Werror -Wall
-LOCAL_CPPFLAGS := -std=gnu++11 -fvisibility=protected
-LOCAL_LDFLAGS := -ldl
-LOCAL_MULTILIB := both
-
-include $(BUILD_HOST_SHARED_LIBRARY)
-
-
diff --git a/libnativeloader/include/nativeloader/dlext_namespaces.h b/libnativeloader/include/nativeloader/dlext_namespaces.h
index ac64f71..9121277 100644
--- a/libnativeloader/include/nativeloader/dlext_namespaces.h
+++ b/libnativeloader/include/nativeloader/dlext_namespaces.h
@@ -55,6 +55,12 @@
    * permitted_path from the caller's namespace.
    */
   ANDROID_NAMESPACE_TYPE_SHARED = 2,
+
+  /* This flag instructs linker to enable grey-list workaround for the namespace.
+   * See http://b/26394120 for details.
+   */
+  ANDROID_NAMESPACE_TYPE_GREYLIST_ENABLED = 0x08000000,
+
   ANDROID_NAMESPACE_TYPE_SHARED_ISOLATED = ANDROID_NAMESPACE_TYPE_SHARED |
                                            ANDROID_NAMESPACE_TYPE_ISOLATED,
 };
diff --git a/libnativeloader/native_loader.cpp b/libnativeloader/native_loader.cpp
index f3391d1..d9cb90d 100644
--- a/libnativeloader/native_loader.cpp
+++ b/libnativeloader/native_loader.cpp
@@ -99,6 +99,7 @@
   LibraryNamespaces() : initialized_(false) { }
 
   bool Create(JNIEnv* env,
+              uint32_t target_sdk_version,
               jobject class_loader,
               bool is_shared,
               jstring java_library_path,
@@ -141,6 +142,10 @@
       namespace_type |= ANDROID_NAMESPACE_TYPE_SHARED;
     }
 
+    if (target_sdk_version < 24) {
+      namespace_type |= ANDROID_NAMESPACE_TYPE_GREYLIST_ENABLED;
+    }
+
     NativeLoaderNamespace parent_ns;
     bool found_parent_namespace = FindParentNamespaceByClassLoader(env, class_loader, &parent_ns);
 
@@ -397,12 +402,12 @@
                                    jstring library_path,
                                    jstring permitted_path) {
 #if defined(__ANDROID__)
-  UNUSED(target_sdk_version);
   std::lock_guard<std::mutex> guard(g_namespaces_mutex);
 
   std::string error_msg;
   NativeLoaderNamespace ns;
   bool success = g_namespaces->Create(env,
+                                      target_sdk_version,
                                       class_loader,
                                       is_shared,
                                       library_path,
@@ -439,7 +444,14 @@
   if (!g_namespaces->FindNamespaceByClassLoader(env, class_loader, &ns)) {
     // This is the case where the classloader was not created by ApplicationLoaders
     // In this case we create an isolated not-shared namespace for it.
-    if (!g_namespaces->Create(env, class_loader, false, library_path, nullptr, &ns, error_msg)) {
+    if (!g_namespaces->Create(env,
+                              target_sdk_version,
+                              class_loader,
+                              false,
+                              library_path,
+                              nullptr,
+                              &ns,
+                              error_msg)) {
       return nullptr;
     }
   }
diff --git a/libprocessgroup/include/processgroup/processgroup.h b/libprocessgroup/include/processgroup/processgroup.h
index 47f6ff3..f0c3795 100644
--- a/libprocessgroup/include/processgroup/processgroup.h
+++ b/libprocessgroup/include/processgroup/processgroup.h
@@ -22,8 +22,13 @@
 
 __BEGIN_DECLS
 
+// Return 0 and removes the cgroup if there are no longer any processes in it.
+// Returns -1 in the case of an error occurring or if there are processes still running
+// even after retrying for up to 200ms.
 int killProcessGroup(uid_t uid, int initialPid, int signal);
 
+// Returns the same as killProcessGroup(), however it does not retry, which means
+// that it only returns 0 in the case that the cgroup exists and it contains no processes.
 int killProcessGroupOnce(uid_t uid, int initialPid, int signal);
 
 int createProcessGroup(uid_t uid, int initialPid);
diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp
index 1572cb3..27b4065 100644
--- a/libprocessgroup/processgroup.cpp
+++ b/libprocessgroup/processgroup.cpp
@@ -34,6 +34,7 @@
 #include <thread>
 
 #include <android-base/logging.h>
+#include <android-base/unique_fd.h>
 #include <private/android_filesystem_config.h>
 
 #include <processgroup/processgroup.h>
@@ -64,12 +65,27 @@
 
 std::once_flag init_path_flag;
 
-struct ctx {
-    bool initialized;
-    int fd;
-    char buf[128];
-    char *buf_ptr;
-    size_t buf_len;
+class ProcessGroup {
+  public:
+    ProcessGroup() : buf_ptr_(buf_), buf_len_(0) {}
+
+    bool Open(uid_t uid, int pid);
+
+    // Return positive number and sets *pid = next pid in process cgroup on success
+    // Returns 0 if there are no pids left in the process cgroup
+    // Returns -errno if an error was encountered
+    int GetOneAppProcess(pid_t* pid);
+
+  private:
+    // Returns positive number of bytes filled on success
+    // Returns 0 if there was nothing to read
+    // Returns -errno if an error was encountered
+    int RefillBuffer();
+
+    android::base::unique_fd fd_;
+    char buf_[128];
+    char* buf_ptr_;
+    size_t buf_len_;
 };
 
 static const char* getCgroupRootPath() {
@@ -105,87 +121,67 @@
             pid);
 }
 
-static int initCtx(uid_t uid, int pid, struct ctx *ctx)
-{
-    int ret;
+bool ProcessGroup::Open(uid_t uid, int pid) {
     char path[PROCESSGROUP_MAX_PATH_LEN] = {0};
     convertUidPidToPath(path, sizeof(path), uid, pid);
     strlcat(path, PROCESSGROUP_CGROUP_PROCS_FILE, sizeof(path));
 
     int fd = open(path, O_RDONLY);
-    if (fd < 0) {
-        ret = -errno;
-        PLOG(WARNING) << "failed to open " << path;
-        return ret;
-    }
+    if (fd < 0) return false;
 
-    ctx->fd = fd;
-    ctx->buf_ptr = ctx->buf;
-    ctx->buf_len = 0;
-    ctx->initialized = true;
+    fd_.reset(fd);
 
     LOG(VERBOSE) << "Initialized context for " << path;
 
-    return 0;
+    return true;
 }
 
-static int refillBuffer(struct ctx *ctx)
-{
-    memmove(ctx->buf, ctx->buf_ptr, ctx->buf_len);
-    ctx->buf_ptr = ctx->buf;
+int ProcessGroup::RefillBuffer() {
+    memmove(buf_, buf_ptr_, buf_len_);
+    buf_ptr_ = buf_;
 
-    ssize_t ret = read(ctx->fd, ctx->buf_ptr + ctx->buf_len,
-                sizeof(ctx->buf) - ctx->buf_len - 1);
+    ssize_t ret = read(fd_, buf_ptr_ + buf_len_, sizeof(buf_) - buf_len_ - 1);
     if (ret < 0) {
         return -errno;
     } else if (ret == 0) {
         return 0;
     }
 
-    ctx->buf_len += ret;
-    ctx->buf[ctx->buf_len] = 0;
-    LOG(VERBOSE) << "Read " << ret << " to buffer: " << ctx->buf;
+    buf_len_ += ret;
+    buf_[buf_len_] = 0;
+    LOG(VERBOSE) << "Read " << ret << " to buffer: " << buf_;
 
-    assert(ctx->buf_len <= sizeof(ctx->buf));
+    assert(buf_len_ <= sizeof(buf_));
 
     return ret;
 }
 
-static pid_t getOneAppProcess(uid_t uid, int appProcessPid, struct ctx *ctx)
-{
-    if (!ctx->initialized) {
-        int ret = initCtx(uid, appProcessPid, ctx);
-        if (ret < 0) {
-            return ret;
-        }
-    }
+int ProcessGroup::GetOneAppProcess(pid_t* out_pid) {
+    *out_pid = 0;
 
-    char *eptr;
-    while ((eptr = (char *)memchr(ctx->buf_ptr, '\n', ctx->buf_len)) == NULL) {
-        int ret = refillBuffer(ctx);
-        if (ret == 0) {
-            return -ERANGE;
-        }
-        if (ret < 0) {
-            return ret;
-        }
+    char* eptr;
+    while ((eptr = static_cast<char*>(memchr(buf_ptr_, '\n', buf_len_))) == nullptr) {
+        int ret = RefillBuffer();
+        if (ret <= 0) return ret;
     }
 
     *eptr = '\0';
-    char *pid_eptr = NULL;
+    char* pid_eptr = nullptr;
     errno = 0;
-    long pid = strtol(ctx->buf_ptr, &pid_eptr, 10);
+    long pid = strtol(buf_ptr_, &pid_eptr, 10);
     if (errno != 0) {
         return -errno;
     }
     if (pid_eptr != eptr) {
-        return -EINVAL;
+        errno = EINVAL;
+        return -errno;
     }
 
-    ctx->buf_len -= (eptr - ctx->buf_ptr) + 1;
-    ctx->buf_ptr = eptr + 1;
+    buf_len_ -= (eptr - buf_ptr_) + 1;
+    buf_ptr_ = eptr + 1;
 
-    return (pid_t)pid;
+    *out_pid = static_cast<pid_t>(pid);
+    return 1;
 }
 
 static int removeProcessGroup(uid_t uid, int pid)
@@ -219,8 +215,8 @@
             }
 
             snprintf(path, sizeof(path), "%s/%s", uid_path, dir->d_name);
-            LOG(VERBOSE) << "removing " << path;
-            if (rmdir(path) == -1) PLOG(WARNING) << "failed to remove " << path;
+            LOG(VERBOSE) << "Removing " << path;
+            if (rmdir(path) == -1) PLOG(WARNING) << "Failed to remove " << path;
         }
     }
 }
@@ -231,7 +227,7 @@
     const char* cgroup_root_path = getCgroupRootPath();
     std::unique_ptr<DIR, decltype(&closedir)> root(opendir(cgroup_root_path), closedir);
     if (root == NULL) {
-        PLOG(ERROR) << "failed to open " << cgroup_root_path;
+        PLOG(ERROR) << "Failed to open " << cgroup_root_path;
     } else {
         dirent* dir;
         while ((dir = readdir(root.get())) != nullptr) {
@@ -246,20 +242,26 @@
 
             snprintf(path, sizeof(path), "%s/%s", cgroup_root_path, dir->d_name);
             removeUidProcessGroups(path);
-            LOG(VERBOSE) << "removing " << path;
-            if (rmdir(path) == -1) PLOG(WARNING) << "failed to remove " << path;
+            LOG(VERBOSE) << "Removing " << path;
+            if (rmdir(path) == -1) PLOG(WARNING) << "Failed to remove " << path;
         }
     }
 }
 
+// Returns number of processes killed on success
+// Returns 0 if there are no processes in the process cgroup left to kill
+// Returns -errno on error
 static int doKillProcessGroupOnce(uid_t uid, int initialPid, int signal) {
-    int processes = 0;
-    struct ctx ctx;
+    ProcessGroup process_group;
+    if (!process_group.Open(uid, initialPid)) {
+        PLOG(WARNING) << "Failed to open process cgroup uid " << uid << " pid " << initialPid;
+        return -errno;
+    }
+
+    int ret;
     pid_t pid;
-
-    ctx.initialized = false;
-
-    while ((pid = getOneAppProcess(uid, initialPid, &ctx)) >= 0) {
+    int processes = 0;
+    while ((ret = process_group.GetOneAppProcess(&pid)) > 0 && pid >= 0) {
         processes++;
         if (pid == 0) {
             // Should never happen...  but if it does, trying to kill this
@@ -267,55 +269,68 @@
             LOG(WARNING) << "Yikes, we've been told to kill pid 0!  How about we don't do that?";
             continue;
         }
-        LOG(VERBOSE) << "Killing pid " << pid << " in uid " << uid
-                     << " as part of process group " << initialPid;
+        LOG(VERBOSE) << "Killing pid " << pid << " in uid " << uid << " as part of process cgroup "
+                     << initialPid;
         if (kill(pid, signal) == -1) {
             PLOG(WARNING) << "kill(" << pid << ", " << signal << ") failed";
         }
     }
 
-    if (ctx.initialized) {
-        close(ctx.fd);
-    }
-
-    return processes;
+    return ret >= 0 ? processes : ret;
 }
 
-static int killProcessGroup(uid_t uid, int initialPid, int signal, int retry) {
+static int killProcessGroup(uid_t uid, int initialPid, int signal, int retries) {
     std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
 
+    int retry = retries;
     int processes;
     while ((processes = doKillProcessGroupOnce(uid, initialPid, signal)) > 0) {
-        LOG(VERBOSE) << "killed " << processes << " processes for processgroup " << initialPid;
+        LOG(VERBOSE) << "Killed " << processes << " processes for processgroup " << initialPid;
         if (retry > 0) {
             std::this_thread::sleep_for(5ms);
             --retry;
         } else {
-            LOG(ERROR) << "failed to kill " << processes << " processes for processgroup "
-                       << initialPid;
             break;
         }
     }
 
-    std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();
+    if (processes < 0) {
+        PLOG(ERROR) << "Error encountered killing process cgroup uid " << uid << " pid "
+                    << initialPid;
+        return -1;
+    }
 
+    std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();
     auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
-    LOG(VERBOSE) << "Killed process group uid " << uid << " pid " << initialPid << " in "
-                 << static_cast<int>(ms) << "ms, " << processes << " procs remain";
+
+    // We only calculate the number of 'processes' when killing the processes.
+    // In the retries == 0 case, we only kill the processes once and therefore
+    // will not have waited then recalculated how many processes are remaining
+    // after the first signals have been sent.
+    // Logging anything regarding the number of 'processes' here does not make sense.
 
     if (processes == 0) {
+        if (retries > 0) {
+            LOG(INFO) << "Successfully killed process cgroup uid " << uid << " pid " << initialPid
+                      << " in " << static_cast<int>(ms) << "ms";
+        }
         return removeProcessGroup(uid, initialPid);
     } else {
+        if (retries > 0) {
+            LOG(ERROR) << "Failed to kill process cgroup uid " << uid << " pid " << initialPid
+                       << " in " << static_cast<int>(ms) << "ms, " << processes
+                       << " processes remain";
+        }
         return -1;
     }
 }
 
 int killProcessGroup(uid_t uid, int initialPid, int signal) {
-    return killProcessGroup(uid, initialPid, signal, 40 /*maxRetry*/);
+    return killProcessGroup(uid, initialPid, signal, 40 /*retries*/);
 }
 
 int killProcessGroupOnce(uid_t uid, int initialPid, int signal) {
-    return killProcessGroup(uid, initialPid, signal, 0 /*maxRetry*/);
+    return killProcessGroup(uid, initialPid, signal, 0 /*retries*/);
 }
 
 static bool mkdirAndChown(const char *path, mode_t mode, uid_t uid, gid_t gid)
@@ -341,14 +356,14 @@
     convertUidToPath(path, sizeof(path), uid);
 
     if (!mkdirAndChown(path, 0750, AID_SYSTEM, AID_SYSTEM)) {
-        PLOG(ERROR) << "failed to make and chown " << path;
+        PLOG(ERROR) << "Failed to make and chown " << path;
         return -errno;
     }
 
     convertUidPidToPath(path, sizeof(path), uid, initialPid);
 
     if (!mkdirAndChown(path, 0750, AID_SYSTEM, AID_SYSTEM)) {
-        PLOG(ERROR) << "failed to make and chown " << path;
+        PLOG(ERROR) << "Failed to make and chown " << path;
         return -errno;
     }
 
@@ -357,7 +372,7 @@
     int fd = open(path, O_WRONLY);
     if (fd == -1) {
         int ret = -errno;
-        PLOG(ERROR) << "failed to open " << path;
+        PLOG(ERROR) << "Failed to open " << path;
         return ret;
     }
 
@@ -367,7 +382,7 @@
     int ret = 0;
     if (write(fd, pid, len) < 0) {
         ret = -errno;
-        PLOG(ERROR) << "failed to write '" << pid << "' to " << path;
+        PLOG(ERROR) << "Failed to write '" << pid << "' to " << path;
     }
 
     close(fd);
diff --git a/logd/LogListener.cpp b/logd/LogListener.cpp
index 709646e..3c0d08d 100644
--- a/logd/LogListener.cpp
+++ b/logd/LogListener.cpp
@@ -119,14 +119,14 @@
     static const char socketName[] = "logdw";
     int sock = android_get_control_socket(socketName);
 
-    if (sock < 0) {
+    if (sock < 0) {  // logd started up in init.sh
         sock = socket_local_server(
             socketName, ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_DGRAM);
-    }
 
-    int on = 1;
-    if (setsockopt(sock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)) < 0) {
-        return -1;
+        int on = 1;
+        if (setsockopt(sock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on))) {
+            return -1;
+        }
     }
     return sock;
 }
diff --git a/logd/logd.rc b/logd/logd.rc
index ee89b83..8804246 100644
--- a/logd/logd.rc
+++ b/logd/logd.rc
@@ -1,11 +1,11 @@
 service logd /system/bin/logd
     socket logd stream 0666 logd logd
     socket logdr seqpacket 0666 logd logd
-    socket logdw dgram 0222 logd logd
+    socket logdw dgram+passcred 0222 logd logd
     file /proc/kmsg r
     file /dev/kmsg w
     user logd
-    group logd system readproc
+    group logd system package_info readproc
     writepid /dev/cpuset/system-background/tasks
 
 service logd-reinit /system/bin/logd --reinit
diff --git a/logd/main.cpp b/logd/main.cpp
index 18029eb..c8183f0 100644
--- a/logd/main.cpp
+++ b/logd/main.cpp
@@ -240,23 +240,36 @@
     set_sched_policy(0, SP_BACKGROUND);
     setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_BACKGROUND);
 
+    // We should drop to AID_LOGD, if we are anything else, we have
+    // even lesser privileges and accept our fate.
+    gid_t groups[] = {
+        AID_SYSTEM,        // search access to /data/system path
+        AID_PACKAGE_INFO,  // readonly access to /data/system/packages.list
+    };
+    if (setgroups(arraysize(groups), groups) == -1) {
+        android::prdebug(
+            "logd.daemon: failed to set AID_SYSTEM AID_PACKAGE_INFO groups");
+    }
+    if (setgid(AID_LOGD) != 0) {
+        android::prdebug("logd.daemon: failed to set AID_LOGD gid");
+    }
+    if (setuid(AID_LOGD) != 0) {
+        android::prdebug("logd.daemon: failed to set AID_LOGD uid");
+    }
+
     cap_t caps = cap_init();
     (void)cap_clear(caps);
     (void)cap_set_proc(caps);
     (void)cap_free(caps);
 
-    // If we are AID_ROOT, we should drop to AID_LOGD+AID_SYSTEM, if we are
-    // anything else, we have even lesser privileges and accept our fate. Not
-    // worth checking for error returns setting this thread's privileges.
-    (void)setgid(AID_SYSTEM);  // readonly access to /data/system/packages.list
-    (void)setuid(AID_LOGD);    // access to everything logd, eg /data/misc/logd
-
     while (reinit_running && !sem_wait(&reinit) && reinit_running) {
         // uidToName Privileged Worker
         if (uid) {
             name = nullptr;
 
-            packagelist_parse(package_list_parser_cb, nullptr);
+            // if we got the perms wrong above, this would spam if we reported
+            // problems with acquisition of an uid name from the packages.
+            (void)packagelist_parse(package_list_parser_cb, nullptr);
 
             uid = 0;
             sem_post(&uidName);
diff --git a/logd/tests/logd_test.cpp b/logd/tests/logd_test.cpp
index bd9fa93..f90c9fb 100644
--- a/logd/tests/logd_test.cpp
+++ b/logd/tests/logd_test.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <ctype.h>
 #include <fcntl.h>
 #include <inttypes.h>
 #include <poll.h>
@@ -26,6 +27,7 @@
 
 #include <string>
 
+#include <android-base/file.h>
 #include <android-base/macros.h>
 #include <android-base/stringprintf.h>
 #include <cutils/sockets.h>
@@ -85,7 +87,7 @@
         size_t ret = atol(buf) + 1;
         if (ret < 4) {
             delete[] buf;
-            buf = NULL;
+            buf = nullptr;
             break;
         }
         bool check = ret <= len;
@@ -107,7 +109,7 @@
     // UID   PACKAGE                                                BYTES LINES
     // 0     root                                                  54164 147569
     //
-    char* benchmark = NULL;
+    char* benchmark = nullptr;
     do {
         static const char signature[] = "\n0     root ";
 
@@ -122,7 +124,7 @@
         benchmark = cp;
 #ifdef DEBUG
         char* end = strstr(benchmark, "\n");
-        if (end == NULL) {
+        if (end == nullptr) {
             end = benchmark + strlen(benchmark);
         }
         fprintf(stderr, "parse for spam counter in \"%.*s\"\n",
@@ -154,7 +156,7 @@
         if (value > 10UL) {
             break;
         }
-        benchmark = NULL;
+        benchmark = nullptr;
     } while (*cp);
     return benchmark;
 }
@@ -165,9 +167,14 @@
     size_t len;
     char* buf;
 
+    // Drop cache so that any access problems can be discovered.
+    if (!android::base::WriteStringToFile("3\n", "/proc/sys/vm/drop_caches")) {
+        GTEST_LOG_(INFO) << "Could not open trigger dropping inode cache";
+    }
+
     alloc_statistics(&buf, &len);
 
-    ASSERT_TRUE(NULL != buf);
+    ASSERT_TRUE(nullptr != buf);
 
     // remove trailing FF
     char* cp = buf + len - 1;
@@ -192,19 +199,38 @@
     EXPECT_EQ(0, truncated);
 
     char* main_logs = strstr(cp, "\nChattiest UIDs in main ");
-    EXPECT_TRUE(NULL != main_logs);
+    EXPECT_TRUE(nullptr != main_logs);
 
     char* radio_logs = strstr(cp, "\nChattiest UIDs in radio ");
     if (!radio_logs)
-        GTEST_LOG_(INFO) << "Value of: NULL != radio_logs\n"
+        GTEST_LOG_(INFO) << "Value of: nullptr != radio_logs\n"
                             "Actual: false\n"
                             "Expected: false\n";
 
     char* system_logs = strstr(cp, "\nChattiest UIDs in system ");
-    EXPECT_TRUE(NULL != system_logs);
+    EXPECT_TRUE(nullptr != system_logs);
 
     char* events_logs = strstr(cp, "\nChattiest UIDs in events ");
-    EXPECT_TRUE(NULL != events_logs);
+    EXPECT_TRUE(nullptr != events_logs);
+
+    // Check if there is any " u0_a#### " as this means packagelistparser broken
+    char* used_getpwuid = nullptr;
+    int used_getpwuid_len;
+    char* uid_name = cp;
+    static const char getpwuid_prefix[] = " u0_a";
+    while ((uid_name = strstr(uid_name, getpwuid_prefix)) != nullptr) {
+        used_getpwuid = uid_name + 1;
+        uid_name += strlen(getpwuid_prefix);
+        while (isdigit(*uid_name)) ++uid_name;
+        used_getpwuid_len = uid_name - used_getpwuid;
+        if (isspace(*uid_name)) break;
+        used_getpwuid = nullptr;
+    }
+    EXPECT_TRUE(nullptr == used_getpwuid);
+    if (used_getpwuid) {
+        fprintf(stderr, "libpackagelistparser failed to pick up %.*s\n",
+                used_getpwuid_len, used_getpwuid);
+    }
 
     delete[] buf;
 #else
@@ -352,7 +378,7 @@
         }
 
         alarm(old_alarm);
-        sigaction(SIGALRM, &old_sigaction, NULL);
+        sigaction(SIGALRM, &old_sigaction, nullptr);
 
         close(fd);
     }
@@ -434,7 +460,7 @@
 
     // Introduce some extreme spam for the worst UID filter
     ASSERT_TRUE(
-        NULL !=
+        nullptr !=
         (fp = popen("/data/nativetest/liblog-benchmarks/liblog-benchmarks"
                     " BM_log_maximum_retry"
                     " BM_log_maximum"
@@ -505,10 +531,10 @@
     bool collected_statistics = !!buf;
     EXPECT_EQ(true, collected_statistics);
 
-    ASSERT_TRUE(NULL != buf);
+    ASSERT_TRUE(nullptr != buf);
 
     char* benchmark_statistics_found = find_benchmark_spam(buf);
-    ASSERT_TRUE(benchmark_statistics_found != NULL);
+    ASSERT_TRUE(benchmark_statistics_found != nullptr);
 
     // Check how effective the SPAM filter is, parse out Now size.
     // 0     root                      54164 147569
@@ -603,7 +629,7 @@
         written = write(fd, ask.c_str(), len) == (ssize_t)len;
         if (!written) {
             alarm(old_alarm);
-            sigaction(SIGALRM, &old_sigaction, NULL);
+            sigaction(SIGALRM, &old_sigaction, nullptr);
             close(fd);
             continue;
         }
@@ -625,7 +651,7 @@
                                    : (old_alarm > (1 + 3 - alarm_wrap))
                                          ? old_alarm - 3 + alarm_wrap
                                          : 2);
-        sigaction(SIGALRM, &old_sigaction, NULL);
+        sigaction(SIGALRM, &old_sigaction, nullptr);
 
         close(fd);
 
@@ -648,7 +674,7 @@
     EXPECT_TRUE(content_timeout);
     EXPECT_NE(0U, alarm_timeout);
 #else
-    command = NULL;
+    command = nullptr;
     GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif
 }
@@ -713,7 +739,7 @@
         written = write(fd, ask.c_str(), len) == (ssize_t)len;
         if (!written) {
             alarm(old_alarm);
-            sigaction(SIGALRM, &old_sigaction, NULL);
+            sigaction(SIGALRM, &old_sigaction, nullptr);
             close(fd);
             continue;
         }
@@ -735,7 +761,7 @@
                                    : (old_alarm > (1 + 3 - alarm_wrap))
                                          ? old_alarm - 3 + alarm_wrap
                                          : 2);
-        sigaction(SIGALRM, &old_sigaction, NULL);
+        sigaction(SIGALRM, &old_sigaction, nullptr);
 
         close(fd);
 
@@ -837,7 +863,7 @@
     int save_errno = (recv_ret < 0) ? errno : 0;
 
     EXPECT_NE(0U, alarm(old_alarm));
-    sigaction(SIGALRM, &old_sigaction, NULL);
+    sigaction(SIGALRM, &old_sigaction, nullptr);
 
     EXPECT_EQ(0, recv_ret);
     if (recv_ret > 0) {
@@ -876,8 +902,8 @@
     char* cp;
     long ret = strtol(buffer, &cp, 10);
     EXPECT_GT(ret, 16);
-    EXPECT_TRUE(strstr(buffer, "\t(to life the universe etc|3)") != NULL);
-    EXPECT_TRUE(strstr(buffer, "answer") != NULL);
+    EXPECT_TRUE(strstr(buffer, "\t(to life the universe etc|3)") != nullptr);
+    EXPECT_TRUE(strstr(buffer, "answer") != nullptr);
 #else
     GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif
@@ -897,8 +923,8 @@
     char* cp;
     long ret = strtol(buffer, &cp, 10);
     EXPECT_GT(ret, 16);
-    EXPECT_TRUE(strstr(buffer, "\t(new|1)") != NULL);
-    EXPECT_TRUE(strstr(buffer, name) != NULL);
+    EXPECT_TRUE(strstr(buffer, "\t(new|1)") != nullptr);
+    EXPECT_TRUE(strstr(buffer, name) != nullptr);
 // ToDo: also look for this in /data/misc/logd/event-log-tags and
 // /dev/event-log-tags.
 #else
@@ -942,7 +968,7 @@
     ASSERT_EQ(0, info.si_status);
 
     struct logger_list* logger_list;
-    ASSERT_TRUE(NULL !=
+    ASSERT_TRUE(nullptr !=
                 (logger_list = android_logger_list_open(
                      LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK,
                      0, pid)));
diff --git a/platform_tools_tool_version.mk b/platform_tools_tool_version.mk
new file mode 100644
index 0000000..73229e6
--- /dev/null
+++ b/platform_tools_tool_version.mk
@@ -0,0 +1,22 @@
+# Copyright (C) 2017 Google Inc.
+#
+# 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.
+
+# We rewrite ${PLATFORM_SDK_VERSION} with 0 rather than $(PLATFORM_SDK_VERSION)
+# because on the actual platform tools release branches the file contains a
+# literal instead. Using 0 lets us easily distinguish non-canonical builds.
+platform_tools_version := $(shell sed \
+    's/$${PLATFORM_SDK_VERSION}/0/ ; s/^Pkg.Revision=\(.*\)/\1/p ; d' \
+    $(ANDROID_BUILD_TOP)/development/sdk/plat_tools_source.prop_template \
+  )
+tool_version := $(platform_tools_version)-$(BUILD_NUMBER_FROM_FILE)
diff --git a/toolbox/Android.mk b/toolbox/Android.mk
index aa755ed..d6ead1a 100644
--- a/toolbox/Android.mk
+++ b/toolbox/Android.mk
@@ -95,13 +95,3 @@
 LOCAL_MODULE := grep
 LOCAL_POST_INSTALL_CMD := $(hide) $(foreach t,egrep fgrep,ln -sf grep $(TARGET_OUT)/bin/$(t);)
 include $(BUILD_EXECUTABLE)
-
-
-# We build gzip separately, so it can provide gunzip and zcat too.
-include $(CLEAR_VARS)
-LOCAL_MODULE := gzip
-LOCAL_SRC_FILES := gzip.c
-LOCAL_CFLAGS += -Wall -Werror
-LOCAL_SHARED_LIBRARIES += libz
-LOCAL_POST_INSTALL_CMD := $(hide) $(foreach t,gunzip zcat,ln -sf gzip $(TARGET_OUT)/bin/$(t);)
-include $(BUILD_EXECUTABLE)
diff --git a/toolbox/gzip.c b/toolbox/gzip.c
deleted file mode 100644
index 62c4518..0000000
--- a/toolbox/gzip.c
+++ /dev/null
@@ -1,261 +0,0 @@
-/* gzip.c - gzip/gunzip/zcat tools for gzip data
- *
- * Copyright 2017 The Android Open Source Project
- *
- * GZIP RFC: http://www.ietf.org/rfc/rfc1952.txt
-
-TODO: port to toybox.
-
-*/
-
-#define _GNU_SOURCE
-
-#include <errno.h>
-#include <error.h>
-#include <fcntl.h>
-#include <getopt.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-
-#include <zlib.h>
-
-// toybox-style flags/globals.
-#define FLAG_c 1
-#define FLAG_d 2
-#define FLAG_f 4
-#define FLAG_k 8
-static struct {
-  int optflags;
-} toys;
-static struct {
-  int level;
-} TT;
-
-static void xstat(const char *path, struct stat *sb)
-{
-  if (stat(path, sb)) error(1, errno, "stat %s", path);
-}
-
-static void fix_time(const char *path, struct stat *sb)
-{
-  struct timespec times[] = { sb->st_atim, sb->st_mtim };
-
-  if (utimensat(AT_FDCWD, path, times, 0)) error(1, errno, "utimes");
-}
-
-static FILE *xfdopen(const char *name, int flags, mode_t open_mode,
-    const char *mode)
-{
-  FILE *fp;
-  int fd;
-
-  if (!strcmp(name, "-")) fd = dup((*mode == 'r') ? 0 : 1);
-  else fd = open(name, flags, open_mode);
-
-  if (fd == -1) error(1, errno, "open %s (%s)", name, mode);
-  fp = fdopen(fd, mode);
-  if (fp == NULL) error(1, errno, "fopen %s (%s)", name, mode);
-  return fp;
-}
-
-static gzFile xgzopen(const char *name, int flags, mode_t open_mode,
-    const char *mode)
-{
-  gzFile f;
-  int fd;
-
-  if (!strcmp(name, "-")) fd = dup((*mode == 'r') ? 0 : 1);
-  else fd = open(name, flags, open_mode);
-
-  if (fd == -1) error(1, errno, "open %s (%s)", name, mode);
-  f = gzdopen(fd, mode);
-  if (f == NULL) error(1, errno, "gzdopen %s (%s)", name, mode);
-  return f;
-}
-
-static void gzfatal(gzFile f, char *what)
-{
-  int err;
-  const char *msg = gzerror(f, &err);
-
-  error(1, (err == Z_ERRNO) ? errno : 0, "%s: %s", what, msg);
-}
-
-static void gunzip(char *arg)
-{
-  struct stat sb;
-  char buf[BUFSIZ];
-  int len, both_files;
-  char *in_name, *out_name;
-  gzFile in;
-  FILE *out;
-
-  // "gunzip x.gz" will decompress "x.gz" to "x".
-  len = strlen(arg);
-  if (len > 3 && !strcmp(arg+len-3, ".gz")) {
-    in_name = strdup(arg);
-    out_name = strdup(arg);
-    out_name[len-3] = '\0';
-  } else if (!strcmp(arg, "-")) {
-    // "-" means stdin; assume output to stdout.
-    // TODO: require -f to read compressed data from tty?
-    in_name = strdup("-");
-    out_name = strdup("-");
-  } else error(1, 0, "unknown suffix");
-
-  if (toys.optflags&FLAG_c) {
-    free(out_name);
-    out_name = strdup("-");
-  }
-
-  both_files = strcmp(in_name, "-") && strcmp(out_name, "-");
-  if (both_files) xstat(in_name, &sb);
-
-  in = xgzopen(in_name, O_RDONLY, 0, "r");
-  out = xfdopen(out_name, O_CREAT|O_WRONLY|((toys.optflags&FLAG_f)?0:O_EXCL),
-      both_files?sb.st_mode:0666, "w");
-
-  while ((len = gzread(in, buf, sizeof(buf))) > 0) {
-    if (fwrite(buf, 1, len, out) != (size_t) len) error(1, errno, "fwrite");
-  }
-  if (len < 0) gzfatal(in, "gzread");
-  if (fclose(out)) error(1, errno, "fclose");
-  if (gzclose(in) != Z_OK) error(1, 0, "gzclose");
-
-  if (both_files) fix_time(out_name, &sb);
-  if (!(toys.optflags&(FLAG_c|FLAG_k))) unlink(in_name);
-  free(in_name);
-  free(out_name);
-}
-
-static void gzip(char *in_name)
-{
-  char buf[BUFSIZ];
-  size_t len;
-  char *out_name;
-  FILE *in;
-  gzFile out;
-  struct stat sb;
-  int both_files;
-
-  if (toys.optflags&FLAG_c) {
-    out_name = strdup("-");
-  } else {
-    if (asprintf(&out_name, "%s.gz", in_name) == -1) {
-      error(1, errno, "asprintf");
-    }
-  }
-
-  both_files = strcmp(in_name, "-") && strcmp(out_name, "-");
-  if (both_files) xstat(in_name, &sb);
-
-  snprintf(buf, sizeof(buf), "w%d", TT.level);
-  in = xfdopen(in_name, O_RDONLY, 0, "r");
-  out = xgzopen(out_name, O_CREAT|O_WRONLY|((toys.optflags&FLAG_f)?0:O_EXCL),
-      both_files?sb.st_mode:0, buf);
-
-  while ((len = fread(buf, 1, sizeof(buf), in)) > 0) {
-    if (gzwrite(out, buf, len) != (int) len) gzfatal(out, "gzwrite");
-  }
-  if (ferror(in)) error(1, errno, "fread");
-  if (fclose(in)) error(1, errno, "fclose");
-  if (gzclose(out) != Z_OK) error(1, 0, "gzclose");
-
-  if (both_files) fix_time(out_name, &sb);
-  if (!(toys.optflags&(FLAG_c|FLAG_k))) unlink(in_name);
-  free(out_name);
-}
-
-static void do_file(char *arg)
-{
-  if (toys.optflags&FLAG_d) gunzip(arg);
-  else gzip(arg);
-}
-
-static void usage()
-{
-  char *cmd = basename(getprogname());
-
-  printf("usage: %s [-c] [-d] [-f] [-#] [FILE...]\n", cmd);
-  printf("\n");
-  if (!strcmp(cmd, "zcat")) {
-    printf("Decompress files to stdout. Like `gzip -dc`.\n");
-    printf("\n");
-    printf("-c\tOutput to stdout\n");
-    printf("-f\tForce: allow read from tty\n");
-  } else if (!strcmp(cmd, "gunzip")) {
-    printf("Decompress files. With no files, decompresses stdin to stdout.\n");
-    printf("On success, the input files are removed and replaced by new\n");
-    printf("files without the .gz suffix.\n");
-    printf("\n");
-    printf("-c\tOutput to stdout\n");
-    printf("-f\tForce: allow read from tty\n");
-    printf("-k\tKeep input files (don't remove)\n");
-  } else { // gzip
-    printf("Compress files. With no files, compresses stdin to stdout.\n");
-    printf("On success, the input files are removed and replaced by new\n");
-    printf("files with the .gz suffix.\n");
-    printf("\n");
-    printf("-c\tOutput to stdout\n");
-    printf("-d\tDecompress (act as gunzip)\n");
-    printf("-f\tForce: allow overwrite of output file\n");
-    printf("-k\tKeep input files (don't remove)\n");
-    printf("-#\tCompression level 1-9 (1:fastest, 6:default, 9:best)\n");
-  }
-  printf("\n");
-}
-
-int main(int argc, char *argv[])
-{
-  char *cmd = basename(argv[0]);
-  int opt_ch;
-
-  toys.optflags = 0;
-  TT.level = 6;
-
-  if (!strcmp(cmd, "gunzip")) {
-    // gunzip == gzip -d
-    toys.optflags = FLAG_d;
-  } else if (!strcmp(cmd, "zcat")) {
-    // zcat == gzip -dc
-    toys.optflags = (FLAG_c|FLAG_d);
-  }
-
-  while ((opt_ch = getopt(argc, argv, "cdfhk123456789")) != -1) {
-    switch (opt_ch) {
-    case 'c': toys.optflags |= FLAG_c; break;
-    case 'd': toys.optflags |= FLAG_d; break;
-    case 'f': toys.optflags |= FLAG_f; break;
-    case 'k': toys.optflags |= FLAG_k; break;
-
-    case '1':
-    case '2':
-    case '3':
-    case '4':
-    case '5':
-    case '6':
-    case '7':
-    case '8':
-    case '9':
-      TT.level = opt_ch - '0';
-      break;
-
-    default:
-      usage();
-      return 1;
-    }
-  }
-
-  if (optind == argc) {
-    // With no arguments, we go from stdin to stdout.
-    toys.optflags |= FLAG_c;
-    do_file("-");
-    return 0;
-  }
-
-  // Otherwise process each file in turn.
-  while (optind < argc) do_file(argv[optind++]);
-  return 0;
-}
diff --git a/trusty/Android.bp b/trusty/Android.bp
index 1b2e2c7..386fbe6 100644
--- a/trusty/Android.bp
+++ b/trusty/Android.bp
@@ -1,3 +1,7 @@
 subdirs = [
+    "gatekeeper",
+    "keymaster",
     "libtrusty",
+    "nvram",
+    "storage/*",
 ]
diff --git a/trusty/gatekeeper/Android.bp b/trusty/gatekeeper/Android.bp
new file mode 100644
index 0000000..a9566a1
--- /dev/null
+++ b/trusty/gatekeeper/Android.bp
@@ -0,0 +1,46 @@
+//
+// 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.
+//
+
+// WARNING: Everything listed here will be built on ALL platforms,
+// including x86, the emulator, and the SDK.  Modules must be uniquely
+// named (liblights.panda), and must build everywhere, or limit themselves
+// to only building on ARM if they include assembly. Individual makefiles
+// are responsible for having their own logic, for fine-grained control.
+
+cc_library_shared {
+    name: "gatekeeper.trusty",
+
+    relative_install_path: "hw",
+
+    srcs: [
+        "module.cpp",
+        "trusty_gatekeeper_ipc.c",
+        "trusty_gatekeeper.cpp",
+    ],
+
+    cflags: [
+        "-fvisibility=hidden",
+        "-Wall",
+        "-Werror",
+    ],
+
+    shared_libs: [
+        "libgatekeeper",
+        "liblog",
+        "libcutils",
+        "libtrusty",
+    ],
+}
diff --git a/trusty/gatekeeper/Android.mk b/trusty/gatekeeper/Android.mk
deleted file mode 100644
index 3982c8f..0000000
--- a/trusty/gatekeeper/Android.mk
+++ /dev/null
@@ -1,46 +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.
-#
-
-# WARNING: Everything listed here will be built on ALL platforms,
-# including x86, the emulator, and the SDK.  Modules must be uniquely
-# named (liblights.panda), and must build everywhere, or limit themselves
-# to only building on ARM if they include assembly. Individual makefiles
-# are responsible for having their own logic, for fine-grained control.
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := gatekeeper.trusty
-
-LOCAL_MODULE_RELATIVE_PATH := hw
-
-LOCAL_SRC_FILES := \
-	module.cpp \
-	trusty_gatekeeper_ipc.c \
-	trusty_gatekeeper.cpp
-
-LOCAL_CLFAGS = -fvisibility=hidden -Wall -Werror
-
-LOCAL_SHARED_LIBRARIES := \
-	libgatekeeper \
-	liblog \
-	libcutils \
-	libtrusty
-
-LOCAL_MODULE_TAGS := optional
-
-include $(BUILD_SHARED_LIBRARY)
diff --git a/trusty/keymaster/Android.bp b/trusty/keymaster/Android.bp
new file mode 100644
index 0000000..3b6867c
--- /dev/null
+++ b/trusty/keymaster/Android.bp
@@ -0,0 +1,67 @@
+//
+// 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.
+//
+
+// WARNING: Everything listed here will be built on ALL platforms,
+// including x86, the emulator, and the SDK.  Modules must be uniquely
+// named (liblights.panda), and must build everywhere, or limit themselves
+// to only building on ARM if they include assembly. Individual makefiles
+// are responsible for having their own logic, for fine-grained control.
+
+// trusty_keymaster is a binary used only for on-device testing.  It
+// runs Trusty Keymaster through a basic set of operations with RSA
+// and ECDSA keys.
+cc_binary {
+    name: "trusty_keymaster_tipc",
+    srcs: [
+        "trusty_keymaster_device.cpp",
+        "trusty_keymaster_ipc.cpp",
+        "trusty_keymaster_main.cpp",
+    ],
+    shared_libs: [
+        "libcrypto",
+        "libcutils",
+        "libkeymaster1",
+        "libtrusty",
+        "libkeymaster_messages",
+        "libsoftkeymasterdevice",
+        "liblog",
+    ],
+}
+
+// keystore.trusty is the HAL used by keystore on Trusty devices.
+cc_library_shared {
+    name: "keystore.trusty",
+    relative_install_path: "hw",
+    srcs: [
+        "module.cpp",
+        "trusty_keymaster_ipc.cpp",
+        "trusty_keymaster_device.cpp",
+    ],
+
+    cflags: [
+        "-fvisibility=hidden",
+        "-Wall",
+        "-Werror",
+    ],
+
+    shared_libs: [
+        "libcrypto",
+        "libkeymaster_messages",
+        "libtrusty",
+        "liblog",
+        "libcutils",
+    ],
+}
diff --git a/trusty/keymaster/Android.mk b/trusty/keymaster/Android.mk
deleted file mode 100644
index 2d614ae..0000000
--- a/trusty/keymaster/Android.mk
+++ /dev/null
@@ -1,68 +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.
-#
-
-# WARNING: Everything listed here will be built on ALL platforms,
-# including x86, the emulator, and the SDK.  Modules must be uniquely
-# named (liblights.panda), and must build everywhere, or limit themselves
-# to only building on ARM if they include assembly. Individual makefiles
-# are responsible for having their own logic, for fine-grained control.
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-###
-# trusty_keymaster is a binary used only for on-device testing.  It
-# runs Trusty Keymaster through a basic set of operations with RSA
-# and ECDSA keys.
-###
-LOCAL_MODULE := trusty_keymaster_tipc
-LOCAL_SRC_FILES := \
-	trusty_keymaster_device.cpp \
-	trusty_keymaster_ipc.cpp \
-	trusty_keymaster_main.cpp
-LOCAL_SHARED_LIBRARIES := \
-	libcrypto \
-	libcutils \
-	libkeymaster1 \
-	libtrusty \
-	libkeymaster_messages \
-	libsoftkeymasterdevice \
-	liblog
-
-include $(BUILD_EXECUTABLE)
-
-###
-# keystore.trusty is the HAL used by keystore on Trusty devices.
-##
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := keystore.trusty
-LOCAL_MODULE_RELATIVE_PATH := hw
-LOCAL_SRC_FILES := module.cpp \
-	trusty_keymaster_ipc.cpp \
-	trusty_keymaster_device.cpp
-LOCAL_CLFAGS = -fvisibility=hidden -Wall -Werror
-LOCAL_SHARED_LIBRARIES := \
-	libcrypto \
-	libkeymaster_messages \
-	libtrusty \
-	liblog \
-	libcutils
-LOCAL_MODULE_TAGS := optional
-
-include $(BUILD_SHARED_LIBRARY)
diff --git a/trusty/nvram/Android.bp b/trusty/nvram/Android.bp
new file mode 100644
index 0000000..15e6c3e
--- /dev/null
+++ b/trusty/nvram/Android.bp
@@ -0,0 +1,61 @@
+//
+// 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.
+//
+
+// nvram.trusty is the Trusty NVRAM HAL module.
+cc_library_shared {
+    name: "nvram.trusty",
+    relative_install_path: "hw",
+    srcs: [
+        "module.c",
+        "trusty_nvram_device.cpp",
+        "trusty_nvram_implementation.cpp",
+    ],
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wextra",
+        "-fvisibility=hidden",
+    ],
+    static_libs: ["libnvram-hal"],
+    shared_libs: [
+        "libtrusty",
+        "libnvram-messages",
+        "liblog",
+    ],
+}
+
+// nvram-wipe is a helper tool for clearing NVRAM state.
+cc_binary {
+    name: "nvram-wipe",
+    srcs: [
+        "nvram_wipe.cpp",
+        "trusty_nvram_implementation.cpp",
+    ],
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wextra",
+        "-fvisibility=hidden",
+    ],
+    static_libs: ["libnvram-hal"],
+    shared_libs: [
+        "libtrusty",
+        "libnvram-messages",
+        "liblog",
+    ],
+}
diff --git a/trusty/nvram/Android.mk b/trusty/nvram/Android.mk
deleted file mode 100644
index 44e2212..0000000
--- a/trusty/nvram/Android.mk
+++ /dev/null
@@ -1,43 +0,0 @@
-#
-# Copyright (C) 2016 The Android Open-Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-LOCAL_PATH := $(call my-dir)
-
-# nvram.trusty is the Trusty NVRAM HAL module.
-include $(CLEAR_VARS)
-LOCAL_MODULE := nvram.trusty
-LOCAL_MODULE_RELATIVE_PATH := hw
-LOCAL_SRC_FILES := \
-	module.c \
-	trusty_nvram_device.cpp \
-	trusty_nvram_implementation.cpp
-LOCAL_MODULE_TAGS := optional
-LOCAL_CFLAGS := -Wall -Werror -Wextra -fvisibility=hidden
-LOCAL_STATIC_LIBRARIES := libnvram-hal
-LOCAL_SHARED_LIBRARIES := libtrusty libnvram-messages liblog
-include $(BUILD_SHARED_LIBRARY)
-
-# nvram-wipe is a helper tool for clearing NVRAM state.
-include $(CLEAR_VARS)
-LOCAL_MODULE := nvram-wipe
-LOCAL_SRC_FILES := \
-	nvram_wipe.cpp \
-	trusty_nvram_implementation.cpp
-LOCAL_MODULE_TAGS := optional
-LOCAL_CFLAGS := -Wall -Werror -Wextra -fvisibility=hidden
-LOCAL_STATIC_LIBRARIES := libnvram-hal
-LOCAL_SHARED_LIBRARIES := libtrusty libnvram-messages liblog
-include $(BUILD_EXECUTABLE)
diff --git a/trusty/storage/interface/Android.bp b/trusty/storage/interface/Android.bp
new file mode 100644
index 0000000..a551c37
--- /dev/null
+++ b/trusty/storage/interface/Android.bp
@@ -0,0 +1,20 @@
+//
+// 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.
+//
+
+cc_library_static {
+    name: "libtrustystorageinterface",
+    export_include_dirs: ["include"],
+}
diff --git a/trusty/storage/interface/Android.mk b/trusty/storage/interface/Android.mk
deleted file mode 100644
index 15cb6f3..0000000
--- a/trusty/storage/interface/Android.mk
+++ /dev/null
@@ -1,25 +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)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := libtrustystorageinterface
-
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-
-include $(BUILD_STATIC_LIBRARY)
diff --git a/trusty/storage/lib/Android.bp b/trusty/storage/lib/Android.bp
new file mode 100644
index 0000000..5eb3f07
--- /dev/null
+++ b/trusty/storage/lib/Android.bp
@@ -0,0 +1,35 @@
+//
+// Copyright (C) 2015 The Android Open-Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_library_static {
+    name: "libtrustystorage",
+
+    srcs: ["storage.c"],
+
+    export_include_dirs: ["include"],
+
+    static_libs: [
+        "liblog",
+        "libtrusty",
+        "libtrustystorageinterface",
+    ],
+
+    cflags: [
+        "-fvisibility=hidden",
+        "-Wall",
+        "-Werror",
+    ]
+}
diff --git a/trusty/storage/lib/Android.mk b/trusty/storage/lib/Android.mk
deleted file mode 100644
index 7e0fc9d..0000000
--- a/trusty/storage/lib/Android.mk
+++ /dev/null
@@ -1,37 +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)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := libtrustystorage
-
-LOCAL_SRC_FILES := \
-	storage.c \
-
-LOCAL_CLFAGS = -fvisibility=hidden -Wall -Werror
-
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-
-LOCAL_STATIC_LIBRARIES := \
-	liblog \
-	libtrusty \
-	libtrustystorageinterface
-
-include $(BUILD_STATIC_LIBRARY)
-
diff --git a/trusty/storage/proxy/Android.bp b/trusty/storage/proxy/Android.bp
new file mode 100644
index 0000000..eb34df0
--- /dev/null
+++ b/trusty/storage/proxy/Android.bp
@@ -0,0 +1,38 @@
+//
+// 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.
+//
+
+cc_binary {
+    name: "storageproxyd",
+
+    srcs: [
+        "ipc.c",
+        "rpmb.c",
+        "storage.c",
+        "proxy.c",
+    ],
+
+    shared_libs: ["liblog"],
+
+    static_libs: [
+        "libtrustystorageinterface",
+        "libtrusty",
+    ],
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ]
+}
diff --git a/trusty/storage/proxy/Android.mk b/trusty/storage/proxy/Android.mk
deleted file mode 100644
index 745e302..0000000
--- a/trusty/storage/proxy/Android.mk
+++ /dev/null
@@ -1,41 +0,0 @@
-#
-# Copyright (C) 2016 The Android Open-Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := storageproxyd
-
-LOCAL_C_INCLUDES += bionic/libc/kernel/uapi
-
-LOCAL_SRC_FILES := \
-	ipc.c \
-	rpmb.c \
-	storage.c \
-	proxy.c
-
-LOCAL_CLFAGS = -Wall -Werror
-
-LOCAL_SHARED_LIBRARIES := \
-	liblog \
-
-LOCAL_STATIC_LIBRARIES := \
-	libtrustystorageinterface \
-	libtrusty
-
-include $(BUILD_EXECUTABLE)
-
diff --git a/trusty/storage/proxy/proxy.c b/trusty/storage/proxy/proxy.c
index d645ac0..27e5891 100644
--- a/trusty/storage/proxy/proxy.c
+++ b/trusty/storage/proxy/proxy.c
@@ -229,7 +229,6 @@
 int main(int argc, char *argv[])
 {
     int rc;
-    uint retry_cnt;
 
     /* drop privileges */
     if (drop_privs() < 0)
diff --git a/trusty/storage/tests/Android.bp b/trusty/storage/tests/Android.bp
new file mode 100644
index 0000000..3eff3f2
--- /dev/null
+++ b/trusty/storage/tests/Android.bp
@@ -0,0 +1,36 @@
+//
+// 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.
+//
+
+cc_test {
+    name: "secure-storage-unit-test",
+
+    cflags: [
+        "-g",
+        "-Wall",
+        "-Werror",
+        "-std=gnu++11",
+        "-Wno-missing-field-initializers",
+    ],
+
+    static_libs: [
+        "libtrustystorageinterface",
+        "libtrustystorage",
+        "libtrusty",
+        "liblog",
+    ],
+
+    srcs: ["main.cpp"],
+}
diff --git a/trusty/storage/tests/Android.mk b/trusty/storage/tests/Android.mk
deleted file mode 100644
index 71c904d..0000000
--- a/trusty/storage/tests/Android.mk
+++ /dev/null
@@ -1,29 +0,0 @@
-#
-# Copyright (C) 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := secure-storage-unit-test
-LOCAL_CFLAGS += -g -Wall -Werror -std=gnu++11 -Wno-missing-field-initializers
-LOCAL_STATIC_LIBRARIES := \
-	libtrustystorageinterface \
-	libtrustystorage \
-	libtrusty \
-	liblog
-LOCAL_SRC_FILES := main.cpp
-include $(BUILD_NATIVE_TEST)
-